diff --git a/src/ImageSharp.Drawing/Common/Extensions/GraphicsOptionsExtensions.cs b/src/ImageSharp.Drawing/Common/Extensions/GraphicsOptionsExtensions.cs new file mode 100644 index 000000000..c32d0a46e --- /dev/null +++ b/src/ImageSharp.Drawing/Common/Extensions/GraphicsOptionsExtensions.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Extensions methods fpor the class. + /// + internal static class GraphicsOptionsExtensions + { + /// + /// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings. + /// + /// The graphics options. + /// The source color. + /// true if the color can be considered opaque + /// + /// Blending and composition is an expensive operation, in some cases, like + /// filling with a solid color, the blending can be avoided by a plain color replacement. + /// This method can be useful for such processors to select the fast path. + /// + public static bool IsOpaqueColorWithoutBlending(this GraphicsOptions options, Color color) + { + if (options.ColorBlendingMode != PixelColorBlendingMode.Normal) + { + return false; + } + + if (options.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver + && options.AlphaCompositionMode != PixelAlphaCompositionMode.Src) + { + return false; + } + + const float Opaque = 1F; + + if (options.BlendPercentage != Opaque) + { + return false; + } + + if (((Vector4)color).W != Opaque) + { + return false; + } + + return true; + } + } +} diff --git a/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs b/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs index f4a645820..c008f4419 100644 --- a/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs +++ b/src/ImageSharp.Drawing/Primitives/ShapeRegion.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Primitives using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(buffer.Length)) { - Span innerBuffer = tempBuffer.GetSpan(); + Span innerBuffer = tempBuffer.Memory.Span; int count = this.Shape.FindIntersections(start, end, innerBuffer); for (int i = 0; i < count; i++) @@ -61,4 +61,4 @@ namespace SixLabors.ImageSharp.Primitives } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs index 7e75d7eff..a9df07ced 100644 --- a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs @@ -1,68 +1,85 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Buffers; - using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; namespace SixLabors.ImageSharp.Processing { /// - /// primitive that converts a point in to a color for discovering the fill color based on an implementation + /// A primitive that converts a point into a color for discovering the fill color based on an implementation. /// /// The pixel format. - /// - public abstract class BrushApplicator : IDisposable // disposable will be required if/when there is an ImageBrush + /// + public abstract class BrushApplicator : IDisposable where TPixel : struct, IPixel { /// /// Initializes a new instance of the class. /// + /// The configuration instance to use when performing operations. + /// The graphics options. /// The target. - /// The options. - internal BrushApplicator(ImageFrame target, GraphicsOptions options) + internal BrushApplicator(Configuration configuration, GraphicsOptions options, ImageFrame target) { + this.Configuration = configuration; this.Target = target; this.Options = options; this.Blender = PixelOperations.Instance.GetPixelBlender(options); } /// - /// Gets the blender + /// Gets the configuration instance to use when performing operations. + /// + protected Configuration Configuration { get; } + + /// + /// Gets the pixel blender. /// internal PixelBlender Blender { get; } /// - /// Gets the destination + /// Gets the target image. /// protected ImageFrame Target { get; } /// - /// Gets the blend percentage + /// Gets thegraphics options /// protected GraphicsOptions Options { get; } /// - /// Gets the color for a single pixel. + /// Gets the overlay pixel at the specified position. /// - /// The x coordinate. - /// The y coordinate. - /// The a that should be applied to the pixel. + /// The x-coordinate. + /// The y-coordinate. + /// The at the specified position. internal abstract TPixel this[int x, int y] { get; } /// - public abstract void Dispose(); + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + /// Whether to dispose managed and unmanaged objects. + protected virtual void Dispose(bool disposing) + { + } /// /// Applies the opacity 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 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. + /// A collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target. + /// 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(Span scanline, int x, int y) { @@ -71,8 +88,8 @@ namespace SixLabors.ImageSharp.Processing using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) using (IMemoryOwner overlay = memoryAllocator.Allocate(scanline.Length)) { - Span amountSpan = amountBuffer.GetSpan(); - Span overlaySpan = overlay.GetSpan(); + Span amountSpan = amountBuffer.Memory.Span; + Span overlaySpan = overlay.Memory.Span; for (int i = 0; i < scanline.Length; i++) { @@ -89,7 +106,7 @@ namespace SixLabors.ImageSharp.Processing } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(this.Target.Configuration, destinationRow, destinationRow, overlaySpan, amountSpan); + this.Blender.Blend(this.Configuration, destinationRow, destinationRow, overlaySpan, amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs b/src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs index 91da332a1..fbab3605d 100644 --- a/src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/EllipticGradientBrush.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -47,12 +47,14 @@ namespace SixLabors.ImageSharp.Processing /// public override BrushApplicator CreateApplicator( + Configuration configuration, + GraphicsOptions options, ImageFrame source, - RectangleF region, - GraphicsOptions options) => + RectangleF region) => new RadialGradientBrushApplicator( - source, + configuration, options, + source, this.center, this.referenceAxisEnd, this.axisRatio, @@ -86,24 +88,26 @@ namespace SixLabors.ImageSharp.Processing /// /// Initializes a new instance of the class. /// - /// The target image - /// The options - /// Center of the ellipse + /// The configuration instance to use when performing operations. + /// The graphics options. + /// The target image. + /// Center of the ellipse. /// Point on one angular points of the ellipse. /// /// Ratio of the axis length's. Used to determine the length of the second axis, /// the first is defined by and . - /// Definition of colors + /// Definition of colors. /// Defines how the gradient colors are repeated. public RadialGradientBrushApplicator( - ImageFrame target, + Configuration configuration, GraphicsOptions options, + ImageFrame target, PointF center, PointF referenceAxisEnd, float axisRatio, ColorStop[] colorStops, GradientRepetitionMode repetitionMode) - : base(target, options, colorStops, repetitionMode) + : base(configuration, options, target, colorStops, repetitionMode) { this.center = center; this.referenceAxisEnd = referenceAxisEnd; @@ -122,11 +126,6 @@ namespace SixLabors.ImageSharp.Processing this.cosRotation = (float)Math.Cos(this.rotation); } - /// - public override void Dispose() - { - } - /// protected override float PositionOnGradient(float xt, float yt) { @@ -139,16 +138,13 @@ namespace SixLabors.ImageSharp.Processing float xSquared = x * x; float ySquared = y * y; - var inBoundaryChecker = (xSquared / this.referenceRadiusSquared) - + (ySquared / this.secondRadiusSquared); - - return inBoundaryChecker; + return (xSquared / this.referenceRadiusSquared) + (ySquared / this.secondRadiusSquared); } private float AngleBetween(PointF junction, PointF a, PointF b) { - var vA = a - junction; - var vB = b - junction; + PointF vA = a - junction; + PointF vB = b - junction; return MathF.Atan2(vB.Y, vB.X) - MathF.Atan2(vA.Y, vA.X); } @@ -156,6 +152,7 @@ namespace SixLabors.ImageSharp.Processing PointF p1, PointF p2) { + // TODO: Can we not just use Vector2 distance here? float dX = p1.X - p2.X; float dXsquared = dX * dX; @@ -165,4 +162,4 @@ namespace SixLabors.ImageSharp.Processing } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs index 981cf1bef..6c7998437 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/DrawImageExtensions.cs @@ -22,14 +22,17 @@ namespace SixLabors.ImageSharp.Processing public static IImageProcessingContext DrawImage( this IImageProcessingContext source, Image image, - float opacity) => - source.ApplyProcessor( + float opacity) + { + var options = new GraphicsOptions(); + return source.ApplyProcessor( new DrawImageProcessor( - image, - Point.Empty, - GraphicsOptions.Default.ColorBlendingMode, - GraphicsOptions.Default.AlphaCompositionMode, - opacity)); + image, + Point.Empty, + options.ColorBlendingMode, + options.AlphaCompositionMode, + opacity)); + } /// /// Draws the given image together with the current one by blending their pixels. @@ -49,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing image, Point.Empty, colorBlending, - GraphicsOptions.Default.AlphaCompositionMode, + new GraphicsOptions().AlphaCompositionMode, opacity)); /// @@ -100,14 +103,17 @@ namespace SixLabors.ImageSharp.Processing this IImageProcessingContext source, Image image, Point location, - float opacity) => - source.ApplyProcessor( + float opacity) + { + var options = new GraphicsOptions(); + return source.ApplyProcessor( new DrawImageProcessor( - image, - location, - GraphicsOptions.Default.ColorBlendingMode, - GraphicsOptions.Default.AlphaCompositionMode, - opacity)); + image, + location, + options.ColorBlendingMode, + options.AlphaCompositionMode, + opacity)); + } /// /// Draws the given image together with the current one by blending their pixels. @@ -129,7 +135,7 @@ namespace SixLabors.ImageSharp.Processing image, location, colorBlending, - GraphicsOptions.Default.AlphaCompositionMode, + new GraphicsOptions().AlphaCompositionMode, opacity)); /// @@ -172,4 +178,4 @@ namespace SixLabors.ImageSharp.Processing options.AlphaCompositionMode, options.BlendPercentage)); } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs index a68b69a44..90b8c68ac 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing /// The . public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths) => - source.Draw(GraphicsOptions.Default, pen, paths); + source.Draw(new GraphicsOptions(), pen, paths); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs index dfe30f6a3..822375ca9 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing /// The path. /// The . public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) => - source.Draw(GraphicsOptions.Default, pen, path); + source.Draw(new GraphicsOptions(), pen, path); /// /// Draws the outline of the polygon with the provided brush at the provided thickness. diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs index 86d8e9e2e..d51e58645 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing this IImageProcessingContext source, IPen pen, params PointF[] points) => - source.Draw(GraphicsOptions.Default, pen, new Polygon(new LinearLineSegment(points))); + source.Draw(new GraphicsOptions(), pen, new Polygon(new LinearLineSegment(points))); /// /// Draws the provided Points as a closed Linear Polygon with the provided Pen. diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs index da78ab2ec..b3b5dd76a 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing /// The shape. /// The . public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape) => - source.Draw(GraphicsOptions.Default, pen, shape); + source.Draw(new GraphicsOptions(), pen, shape); /// /// Draws the outline of the rectangle with the provided brush at the provided thickness. diff --git a/src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs index 05cd3a1ae..82dbb8d97 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing Font font, Color color, PointF location) => - source.DrawText(TextGraphicsOptions.Default, text, font, color, location); + source.DrawText(new TextGraphicsOptions(), text, font, color, location); /// /// Draws the text onto the the image filled via the brush. @@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing Font font, IBrush brush, PointF location) => - source.DrawText(TextGraphicsOptions.Default, text, font, brush, location); + source.DrawText(new TextGraphicsOptions(), text, font, brush, location); /// /// Draws the text onto the the image filled via the brush. @@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Processing Font font, IPen pen, PointF location) => - source.DrawText(TextGraphicsOptions.Default, text, font, pen, location); + source.DrawText(new TextGraphicsOptions(), text, font, pen, location); /// /// Draws the text onto the the image outlined via the pen. @@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Processing IBrush brush, IPen pen, PointF location) => - source.DrawText(TextGraphicsOptions.Default, text, font, brush, pen, location); + source.DrawText(new TextGraphicsOptions(), text, font, brush, pen, location); /// /// Draws the text using the default resolution of 72dpi onto the the image filled via the brush then outlined via the pen. diff --git a/src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs index 5de9c6d4e..030fe6ff1 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Processing this IImageProcessingContext source, IBrush brush, Action path) => - source.Fill(GraphicsOptions.Default, brush, path); + source.Fill(new GraphicsOptions(), brush, path); /// /// Flood fills the image in the shape of the provided polygon with the specified brush. diff --git a/src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs index 776e1f7e4..5d8aaf307 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Processing this IImageProcessingContext source, IBrush brush, IPathCollection paths) => - source.Fill(GraphicsOptions.Default, brush, paths); + source.Fill(new GraphicsOptions(), brush, paths); /// /// Flood fills the image in the shape of the provided polygon with the specified brush. diff --git a/src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs index 718016a9e..4d262aa5f 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/FillPathExtensions.cs @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing /// The path. /// The . public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) => - source.Fill(GraphicsOptions.Default, brush, new ShapeRegion(path)); + source.Fill(new GraphicsOptions(), brush, new ShapeRegion(path)); /// /// Flood fills the image in the shape of the provided polygon with the specified brush.. diff --git a/src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs b/src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs index 294e57514..fbb6dbda5 100644 --- a/src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs +++ b/src/ImageSharp.Drawing/Processing/Extensions/FillRegionExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Primitives; @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// The details how to fill the region of interest. /// The . public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) => - source.Fill(GraphicsOptions.Default, brush); + source.Fill(new GraphicsOptions(), brush); /// /// Flood fills the image with the specified color. @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing /// The region. /// The . public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) => - source.Fill(GraphicsOptions.Default, brush, region); + source.Fill(new GraphicsOptions(), brush, region); /// /// Flood fills the image with in the region with the specified color. @@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Processing GraphicsOptions options, IBrush brush, Region region) => - source.ApplyProcessor(new FillRegionProcessor(brush, region, options)); + source.ApplyProcessor(new FillRegionProcessor(options, brush, region)); /// /// Flood fills the image with the specified brush. @@ -90,6 +90,6 @@ namespace SixLabors.ImageSharp.Processing this IImageProcessingContext source, GraphicsOptions options, IBrush brush) => - source.ApplyProcessor(new FillProcessor(brush, options)); + source.ApplyProcessor(new FillProcessor(options, brush)); } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/GradientBrush.cs b/src/ImageSharp.Drawing/Processing/GradientBrush.cs index 9826748c4..3be56c042 100644 --- a/src/ImageSharp.Drawing/Processing/GradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/GradientBrush.cs @@ -1,11 +1,9 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Numerics; - using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing @@ -38,9 +36,10 @@ namespace SixLabors.ImageSharp.Processing /// public abstract BrushApplicator CreateApplicator( + Configuration configuration, + GraphicsOptions options, ImageFrame source, - RectangleF region, - GraphicsOptions options) + RectangleF region) where TPixel : struct, IPixel; /// @@ -58,27 +57,24 @@ namespace SixLabors.ImageSharp.Processing /// /// Initializes a new instance of the class. /// - /// The target. - /// The options. + /// The configuration instance to use when performing operations. + /// The graphics options. + /// The target image. /// An array of color stops sorted by their position. /// Defines if and how the gradient should be repeated. protected GradientBrushApplicator( - ImageFrame target, + Configuration configuration, GraphicsOptions options, + ImageFrame target, ColorStop[] colorStops, GradientRepetitionMode repetitionMode) - : base(target, options) + : base(configuration, options, target) { this.colorStops = colorStops; // TODO: requires colorStops to be sorted by position - should that be checked? this.repetitionMode = repetitionMode; } - /// - /// Base implementation of the indexer for gradients - /// (follows the facade pattern, using abstract methods) - /// - /// X coordinate of the Pixel. - /// Y coordinate of the Pixel. + /// internal override TPixel this[int x, int y] { get @@ -92,10 +88,10 @@ namespace SixLabors.ImageSharp.Processing // onLocalGradient = Math.Min(0, Math.Max(1, onLocalGradient)); break; case GradientRepetitionMode.Repeat: - positionOnCompleteGradient = positionOnCompleteGradient % 1; + positionOnCompleteGradient %= 1; break; case GradientRepetitionMode.Reflect: - positionOnCompleteGradient = positionOnCompleteGradient % 2; + positionOnCompleteGradient %= 2; if (positionOnCompleteGradient > 1) { positionOnCompleteGradient = 2 - positionOnCompleteGradient; @@ -121,19 +117,8 @@ namespace SixLabors.ImageSharp.Processing } else { - var fromAsVector = from.Color.ToVector4(); - var toAsVector = to.Color.ToVector4(); float onLocalGradient = (positionOnCompleteGradient - from.Ratio) / (to.Ratio - from.Ratio); - - // TODO: this should be changeble for different gradienting functions - Vector4 result = PorterDuffFunctions.NormalSrcOver( - fromAsVector, - toAsVector, - onLocalGradient); - - TPixel resultColor = default; - resultColor.FromVector4(result); - return resultColor; + return new Color(Vector4.Lerp((Vector4)from.Color, (Vector4)to.Color, onLocalGradient)).ToPixel(); } } } @@ -142,8 +127,8 @@ namespace SixLabors.ImageSharp.Processing /// calculates the position on the gradient for a given point. /// This method is abstract as it's content depends on the shape of the gradient. /// - /// The x coordinate of the point - /// The y coordinate of the point + /// The x-coordinate of the point. + /// The y-coordinate of the point. /// /// The position the given point has on the gradient. /// The position is not bound to the [0..1] interval. @@ -176,4 +161,4 @@ namespace SixLabors.ImageSharp.Processing } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/IBrush.cs b/src/ImageSharp.Drawing/Processing/IBrush.cs index 0cd2e20fd..f4c7ef7cb 100644 --- a/src/ImageSharp.Drawing/Processing/IBrush.cs +++ b/src/ImageSharp.Drawing/Processing/IBrush.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -19,20 +19,22 @@ namespace SixLabors.ImageSharp.Processing /// Creates the applicator for this brush. /// /// The pixel type. + /// The configuration instance to use when performing operations. + /// The graphic options. /// The source image. /// The region the brush will be applied to. - /// The graphic options /// - /// The brush applicator for this brush + /// The for this brush. /// /// /// The when being applied to things like shapes would usually be the - /// bounding box of the shape not necessarily the bounds of the whole image + /// bounding box of the shape not necessarily the bounds of the whole image. /// BrushApplicator CreateApplicator( + Configuration configuration, + GraphicsOptions options, ImageFrame source, - RectangleF region, - GraphicsOptions options) + RectangleF region) where TPixel : struct, IPixel; } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/ImageBrush.cs b/src/ImageSharp.Drawing/Processing/ImageBrush.cs index 8485ddfd0..e38614070 100644 --- a/src/ImageSharp.Drawing/Processing/ImageBrush.cs +++ b/src/ImageSharp.Drawing/Processing/ImageBrush.cs @@ -1,11 +1,10 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Buffers; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -32,19 +31,20 @@ namespace SixLabors.ImageSharp.Processing /// public BrushApplicator CreateApplicator( + Configuration configuration, + GraphicsOptions options, ImageFrame source, - RectangleF region, - GraphicsOptions options) + RectangleF region) where TPixel : struct, IPixel { if (this.image is Image specificImage) { - return new ImageBrushApplicator(source, specificImage, region, options, false); + return new ImageBrushApplicator(configuration, options, source, specificImage, region, false); } specificImage = this.image.CloneAs(); - return new ImageBrushApplicator(source, specificImage, region, options, true); + return new ImageBrushApplicator(configuration, options, source, specificImage, region, true); } /// @@ -79,21 +79,25 @@ namespace SixLabors.ImageSharp.Processing /// private readonly int offsetX; + private bool isDisposed; + /// /// Initializes a new instance of the class. /// + /// The configuration instance to use when performing operations. + /// The graphics options. /// The target image. /// The image. /// The region. - /// The options /// Whether to dispose the image on disposal of the applicator. public ImageBrushApplicator( + Configuration configuration, + GraphicsOptions options, ImageFrame target, Image image, RectangleF region, - GraphicsOptions options, bool shouldDisposeImage) - : base(target, options) + : base(configuration, options, target) { this.sourceImage = image; this.sourceFrame = image.Frames.RootFrame; @@ -104,14 +108,7 @@ namespace SixLabors.ImageSharp.Processing this.offsetX = (int)MathF.Max(MathF.Floor(region.Left), 0); } - /// - /// Gets the color for a single pixel. - /// - /// The x. - /// The y. - /// - /// The color - /// + /// internal override TPixel this[int x, int y] { get @@ -123,14 +120,21 @@ namespace SixLabors.ImageSharp.Processing } /// - public override void Dispose() + protected override void Dispose(bool disposing) { - if (this.shouldDisposeImage) + if (this.isDisposed) + { + return; + } + + if (disposing && this.shouldDisposeImage) { this.sourceImage?.Dispose(); - this.sourceImage = null; - this.sourceFrame = null; } + + this.sourceImage = null; + this.sourceFrame = null; + this.isDisposed = true; } /// @@ -140,8 +144,8 @@ namespace SixLabors.ImageSharp.Processing using (IMemoryOwner amountBuffer = this.Target.MemoryAllocator.Allocate(scanline.Length)) using (IMemoryOwner overlay = this.Target.MemoryAllocator.Allocate(scanline.Length)) { - Span amountSpan = amountBuffer.GetSpan(); - Span overlaySpan = overlay.GetSpan(); + Span amountSpan = amountBuffer.Memory.Span; + Span overlaySpan = overlay.Memory.Span; int sourceY = (y - this.offsetY) % this.yLength; int offsetX = x - this.offsetX; @@ -152,13 +156,12 @@ namespace SixLabors.ImageSharp.Processing amountSpan[i] = scanline[i] * this.Options.BlendPercentage; int sourceX = (i + offsetX) % this.xLength; - TPixel pixel = sourceRow[sourceX]; - overlaySpan[i] = pixel; + overlaySpan[i] = sourceRow[sourceX]; } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend( - this.sourceFrame.Configuration, + this.Configuration, destinationRow, destinationRow, overlaySpan, @@ -167,4 +170,4 @@ namespace SixLabors.ImageSharp.Processing } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs b/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs index bb99eeb26..044bee72c 100644 --- a/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/LinearGradientBrush.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -39,16 +39,18 @@ namespace SixLabors.ImageSharp.Processing /// public override BrushApplicator CreateApplicator( + Configuration configuration, + GraphicsOptions options, ImageFrame source, - RectangleF region, - GraphicsOptions options) => + RectangleF region) => new LinearGradientBrushApplicator( + configuration, + options, source, this.p1, this.p2, this.ColorStops, - this.RepetitionMode, - options); + this.RepetitionMode); /// /// The linear gradient brush applicator. @@ -93,20 +95,22 @@ namespace SixLabors.ImageSharp.Processing /// /// Initializes a new instance of the class. /// - /// The source - /// start point of the gradient - /// end point of the gradient - /// tuple list of colors and their respective position between 0 and 1 on the line - /// defines how the gradient colors are repeated. - /// the graphics options + /// The configuration instance to use when performing operations. + /// The graphics options. + /// The source image. + /// The start point of the gradient. + /// The end point of the gradient. + /// A tuple list of colors and their respective position between 0 and 1 on the line. + /// Defines how the gradient colors are repeated. public LinearGradientBrushApplicator( + Configuration configuration, + GraphicsOptions options, ImageFrame source, PointF start, PointF end, ColorStop[] colorStops, - GradientRepetitionMode repetitionMode, - GraphicsOptions options) - : base(source, options, colorStops, repetitionMode) + GradientRepetitionMode repetitionMode) + : base(configuration, options, source, colorStops, repetitionMode) { this.start = start; this.end = end; @@ -148,14 +152,9 @@ namespace SixLabors.ImageSharp.Processing float distance = MathF.Sqrt(MathF.Pow(x4 - this.start.X, 2) + MathF.Pow(y4 - this.start.Y, 2)); // get and return ratio - float ratio = distance / this.length; - return ratio; + return distance / this.length; } } - - public override void Dispose() - { - } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs index 7315dc5a3..9e354120e 100644 --- a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs @@ -83,12 +83,13 @@ namespace SixLabors.ImageSharp.Processing /// public BrushApplicator CreateApplicator( + Configuration configuration, + GraphicsOptions options, ImageFrame source, - RectangleF region, - GraphicsOptions options) + RectangleF region) where TPixel : struct, IPixel { - return new PathGradientBrushApplicator(source, this.edges, this.centerColor, options); + return new PathGradientBrushApplicator(configuration, options, source, this.edges, this.centerColor); } private static Color CalculateCenterColor(Color[] colors) @@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Processing "One or more color is needed to construct a path gradient brush."); } - return new Color(colors.Select(c => c.ToVector4()).Aggregate((p1, p2) => p1 + p2) / colors.Length); + return new Color(colors.Select(c => (Vector4)c).Aggregate((p1, p2) => p1 + p2) / colors.Length); } private static float DistanceBetween(PointF p1, PointF p2) => ((Vector2)(p2 - p1)).Length(); @@ -141,10 +142,10 @@ namespace SixLabors.ImageSharp.Processing Vector2[] points = path.LineSegments.SelectMany(s => s.Flatten()).Select(p => (Vector2)p).ToArray(); this.Start = points.First(); - this.StartColor = startColor.ToVector4(); + this.StartColor = (Vector4)startColor; this.End = points.Last(); - this.EndColor = endColor.ToVector4(); + this.EndColor = (Vector4)endColor; this.length = DistanceBetween(this.End, this.Start); this.buffer = new PointF[this.path.MaxIntersections]; @@ -199,23 +200,25 @@ namespace SixLabors.ImageSharp.Processing /// /// Initializes a new instance of the class. /// + /// The configuration instance to use when performing operations. + /// The graphics options. /// The source image. /// Edges of the polygon. /// Color at the center of the gradient area to which the other colors converge. - /// The options. public PathGradientBrushApplicator( + Configuration configuration, + GraphicsOptions options, ImageFrame source, IList edges, - Color centerColor, - GraphicsOptions options) - : base(source, options) + Color centerColor) + : base(configuration, options, source) { this.edges = edges; PointF[] points = edges.Select(s => s.Start).ToArray(); this.center = points.Aggregate((p1, p2) => p1 + p2) / edges.Count; - this.centerColor = centerColor.ToVector4(); + this.centerColor = (Vector4)centerColor; this.maxDistance = points.Select(p => (Vector2)(p - this.center)).Select(d => d.Length()).Max(); } @@ -232,7 +235,7 @@ namespace SixLabors.ImageSharp.Processing return new Color(this.centerColor).ToPixel(); } - Vector2 direction = Vector2.Normalize(point - this.center); + var direction = Vector2.Normalize(point - this.center); PointF end = point + (PointF)(direction * this.maxDistance); @@ -250,7 +253,7 @@ namespace SixLabors.ImageSharp.Processing float length = DistanceBetween(intersection, this.center); float ratio = length > 0 ? DistanceBetween(intersection, point) / length : 0; - Vector4 color = Vector4.Lerp(edgeColor, this.centerColor, ratio); + var color = Vector4.Lerp(edgeColor, this.centerColor, ratio); return new Color(color).ToPixel(); } @@ -277,11 +280,6 @@ namespace SixLabors.ImageSharp.Processing return closest; } - - /// - public override void Dispose() - { - } } } } diff --git a/src/ImageSharp.Drawing/Processing/PatternBrush.cs b/src/ImageSharp.Drawing/Processing/PatternBrush.cs index 1999af8a3..726df5a79 100644 --- a/src/ImageSharp.Drawing/Processing/PatternBrush.cs +++ b/src/ImageSharp.Drawing/Processing/PatternBrush.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -61,8 +61,8 @@ namespace SixLabors.ImageSharp.Processing /// The pattern. internal PatternBrush(Color foreColor, Color backColor, in DenseMatrix pattern) { - var foreColorVector = foreColor.ToVector4(); - var backColorVector = backColor.ToVector4(); + var foreColorVector = (Vector4)foreColor; + var backColorVector = (Vector4)backColor; this.pattern = new DenseMatrix(pattern.Columns, pattern.Rows); this.patternVector = new DenseMatrix(pattern.Columns, pattern.Rows); for (int i = 0; i < pattern.Data.Length; i++) @@ -92,14 +92,16 @@ namespace SixLabors.ImageSharp.Processing /// public BrushApplicator CreateApplicator( + Configuration configuration, + GraphicsOptions options, ImageFrame source, - RectangleF region, - GraphicsOptions options) + RectangleF region) where TPixel : struct, IPixel => new PatternBrushApplicator( + configuration, + options, source, - this.pattern.ToPixelMatrix(source.Configuration), - options); + this.pattern.ToPixelMatrix(configuration)); /// /// The pattern brush applicator. @@ -115,41 +117,33 @@ namespace SixLabors.ImageSharp.Processing /// /// Initializes a new instance of the class. /// + /// The configuration instance to use when performing operations. + /// The graphics options. /// The source image. /// The pattern. - /// The options - public PatternBrushApplicator(ImageFrame source, in DenseMatrix pattern, GraphicsOptions options) - : base(source, options) + public PatternBrushApplicator( + Configuration configuration, + GraphicsOptions options, + ImageFrame source, + in DenseMatrix pattern) + : base(configuration, options, source) { this.pattern = pattern; } - /// - /// Gets the color for a single pixel. - /// # - /// The x. - /// The y. - /// - /// The Color. - /// + /// internal override TPixel this[int x, int y] { get { - x = x % this.pattern.Columns; - y = y % this.pattern.Rows; + x %= this.pattern.Columns; + y %= this.pattern.Rows; // 2d array index at row/column return this.pattern[y, x]; } } - /// - public override void Dispose() - { - // noop - } - /// internal override void Apply(Span scanline, int x, int y) { @@ -159,12 +153,12 @@ namespace SixLabors.ImageSharp.Processing using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) using (IMemoryOwner overlay = memoryAllocator.Allocate(scanline.Length)) { - Span amountSpan = amountBuffer.GetSpan(); - Span overlaySpan = overlay.GetSpan(); + Span amountSpan = amountBuffer.Memory.Span; + Span overlaySpan = overlay.Memory.Span; for (int i = 0; i < scanline.Length; i++) { - amountSpan[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1); + amountSpan[i] = NumberUtils.ClampFloat(scanline[i] * this.Options.BlendPercentage, 0, 1F); int patternX = (x + i) % this.pattern.Columns; overlaySpan[i] = this.pattern[patternY, patternX]; @@ -172,7 +166,7 @@ namespace SixLabors.ImageSharp.Processing Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend( - this.Target.Configuration, + this.Configuration, destinationRow, destinationRow, overlaySpan, @@ -181,4 +175,4 @@ namespace SixLabors.ImageSharp.Processing } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs index 1d3cf3557..3963f99a5 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs @@ -15,9 +15,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// /// Initializes a new instance of the class. /// - /// The brush to use for filling. /// The defining how to blend the brush pixels over the image pixels. - public FillProcessor(IBrush brush, GraphicsOptions options) + /// The brush to use for filling. + public FillProcessor(GraphicsOptions options, IBrush brush) { this.Brush = brush; this.Options = options; diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs index 4e052818d..fc9482618 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing int width = maxX - minX; - Rectangle workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); IBrush brush = this.definition.Brush; GraphicsOptions options = this.definition.Options; @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration) .MultiplyMinimumPixelsPerTask(4); - var colorPixel = solidBrush.Color.ToPixel(); + TPixel colorPixel = solidBrush.Color.ToPixel(); ParallelHelper.IterateRows( workingRect, @@ -84,11 +84,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing using (IMemoryOwner amount = source.MemoryAllocator.Allocate(width)) using (BrushApplicator applicator = brush.CreateApplicator( + configuration, + options, source, - sourceRectangle, - options)) + sourceRectangle)) { - amount.GetSpan().Fill(1f); + amount.Memory.Span.Fill(1f); ParallelHelper.IterateRows( workingRect, @@ -100,7 +101,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing int offsetY = y - startY; int offsetX = minX - startX; - applicator.Apply(amount.GetSpan(), offsetX, offsetY); + applicator.Apply(amount.Memory.Span, offsetX, offsetY); } }); } diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs index 2318f3168..7d51be1c5 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs @@ -16,10 +16,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing /// /// Initializes a new instance of the class. /// + /// The graphics options. /// The details how to fill the region of interest. /// The region of interest to be filled. - /// The configuration options. - public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options) + public FillRegionProcessor(GraphicsOptions options, IBrush brush, Region region) { this.Region = region; this.Brush = brush; diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs index 45d5015ae..4744a4e92 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing } } - using (BrushApplicator applicator = brush.CreateApplicator(source, rect, options)) + using (BrushApplicator applicator = brush.CreateApplicator(configuration, options, source, rect)) { int scanlineWidth = maxX - minX; using (IMemoryOwner bBuffer = source.MemoryAllocator.Allocate(maxIntersections)) @@ -81,8 +81,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing float subpixelFraction = 1f / subpixelCount; float subpixelFractionPoint = subpixelFraction / subpixelCount; - Span buffer = bBuffer.GetSpan(); - Span scanline = bScanline.GetSpan(); + Span buffer = bBuffer.Memory.Span; + Span scanline = bScanline.Memory.Span; bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush); TPixel solidBrushColor = isSolidBrushWithoutBlending ? solidBrush.Color.ToPixel() : default; diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs index ea042635d..64d32efb8 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text VerticalAlignment = this.Options.VerticalAlignment }; - this.textRenderer = new CachingGlyphRenderer(this.Source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null); + this.textRenderer = new CachingGlyphRenderer(this.Configuration.MemoryAllocator, this.Text.Length, this.Pen, this.Brush != null); this.textRenderer.Options = (GraphicsOptions)this.Options; var renderer = new TextRenderer(this.textRenderer); renderer.RenderText(this.Text, style); @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text { if (operations?.Count > 0) { - using (BrushApplicator app = brush.CreateApplicator(source, this.SourceRectangle, this.textRenderer.Options)) + using (BrushApplicator app = brush.CreateApplicator(this.Configuration, this.textRenderer.Options, source, this.SourceRectangle)) { foreach (DrawingOperation operation in operations) { @@ -326,6 +326,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text { float subpixelFraction = 1f / subpixelCount; float subpixelFractionPoint = subpixelFraction / subpixelCount; + Span intersectionSpan = rowIntersectionBuffer.Memory.Span; + Span buffer = bufferBacking.Memory.Span; for (int y = 0; y <= size.Height; y++) { @@ -337,8 +339,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text { var start = new PointF(path.Bounds.Left - 1, subPixel); var end = new PointF(path.Bounds.Right + 1, subPixel); - Span intersectionSpan = rowIntersectionBuffer.GetSpan(); - Span buffer = bufferBacking.GetSpan(); int pointsFound = path.FindIntersections(start, end, intersectionSpan); if (pointsFound == 0) diff --git a/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs b/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs index f4d2dd81f..2b1b6913f 100644 --- a/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/RadialGradientBrush.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -9,7 +9,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// A Circular Gradient Brush, defined by center point and radius. + /// A radial gradient brush, defined by center point and radius. /// public sealed class RadialGradientBrush : GradientBrush { @@ -35,12 +35,14 @@ namespace SixLabors.ImageSharp.Processing /// public override BrushApplicator CreateApplicator( + Configuration configuration, + GraphicsOptions options, ImageFrame source, - RectangleF region, - GraphicsOptions options) => + RectangleF region) => new RadialGradientBrushApplicator( - source, + configuration, options, + source, this.center, this.radius, this.ColorStops, @@ -57,30 +59,27 @@ namespace SixLabors.ImageSharp.Processing /// /// Initializes a new instance of the class. /// - /// The target image - /// The options. + /// The configuration instance to use when performing operations. + /// The graphics options. + /// The target image. /// Center point of the gradient. /// Radius of the gradient. /// Definition of colors. /// How the colors are repeated beyond the first gradient. public RadialGradientBrushApplicator( - ImageFrame target, + Configuration configuration, GraphicsOptions options, + ImageFrame target, PointF center, float radius, ColorStop[] colorStops, GradientRepetitionMode repetitionMode) - : base(target, options, colorStops, repetitionMode) + : base(configuration, options, target, colorStops, repetitionMode) { this.center = center; this.radius = radius; } - /// - public override void Dispose() - { - } - /// /// As this is a circular gradient, the position on the gradient is based on /// the distance of the point to the center. @@ -90,6 +89,7 @@ namespace SixLabors.ImageSharp.Processing /// the position on the color gradient. protected override float PositionOnGradient(float x, float y) { + // TODO: Can this not use Vector2 distance? float distance = MathF.Sqrt(MathF.Pow(this.center.X - x, 2) + MathF.Pow(this.center.Y - y, 2)); return distance / this.radius; } @@ -101,4 +101,4 @@ namespace SixLabors.ImageSharp.Processing } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/RecolorBrush.cs b/src/ImageSharp.Drawing/Processing/RecolorBrush.cs index fca95be32..e0e43cf78 100644 --- a/src/ImageSharp.Drawing/Processing/RecolorBrush.cs +++ b/src/ImageSharp.Drawing/Processing/RecolorBrush.cs @@ -1,11 +1,10 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Buffers; using System.Numerics; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -38,9 +37,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Gets the source color. /// - /// - /// The color of the source. - /// public Color SourceColor { get; } /// @@ -50,17 +46,19 @@ namespace SixLabors.ImageSharp.Processing /// public BrushApplicator CreateApplicator( + Configuration configuration, + GraphicsOptions options, ImageFrame source, - RectangleF region, - GraphicsOptions options) + RectangleF region) where TPixel : struct, IPixel { return new RecolorBrushApplicator( + configuration, + options, source, this.SourceColor.ToPixel(), this.TargetColor.ToPixel(), - this.Threshold, - options); + this.Threshold); } /// @@ -74,11 +72,6 @@ namespace SixLabors.ImageSharp.Processing /// private readonly Vector4 sourceColor; - /// - /// The target color. - /// - private readonly Vector4 targetColor; - /// /// The threshold. /// @@ -89,16 +82,22 @@ namespace SixLabors.ImageSharp.Processing /// /// Initializes a new instance of the class. /// + /// The configuration instance to use when performing operations. + /// The options /// The source image. /// Color of the source. /// Color of the target. /// The threshold . - /// The options - public RecolorBrushApplicator(ImageFrame source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options) - : base(source, options) + public RecolorBrushApplicator( + Configuration configuration, + GraphicsOptions options, + ImageFrame source, + TPixel sourceColor, + TPixel targetColor, + float threshold) + : base(configuration, options, source) { this.sourceColor = sourceColor.ToVector4(); - this.targetColor = targetColor.ToVector4(); this.targetColorPixel = targetColor; // Lets hack a min max extremes for a color space by letting the IPackedPixel clamp our values to something in the correct spaces :) @@ -109,14 +108,7 @@ namespace SixLabors.ImageSharp.Processing this.threshold = Vector4.DistanceSquared(maxColor.ToVector4(), minColor.ToVector4()) * threshold; } - /// - /// Gets the color for a single pixel. - /// - /// The x. - /// The y. - /// - /// The color - /// + /// internal override TPixel this[int x, int y] { get @@ -138,11 +130,6 @@ namespace SixLabors.ImageSharp.Processing } } - /// - public override void Dispose() - { - } - /// internal override void Apply(Span scanline, int x, int y) { @@ -151,8 +138,8 @@ namespace SixLabors.ImageSharp.Processing using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) using (IMemoryOwner overlay = memoryAllocator.Allocate(scanline.Length)) { - Span amountSpan = amountBuffer.GetSpan(); - Span overlaySpan = overlay.GetSpan(); + Span amountSpan = amountBuffer.Memory.Span; + Span overlaySpan = overlay.Memory.Span; for (int i = 0; i < scanline.Length; i++) { @@ -167,7 +154,7 @@ namespace SixLabors.ImageSharp.Processing Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend( - this.Target.Configuration, + this.Configuration, destinationRow, destinationRow, overlaySpan, @@ -176,4 +163,4 @@ namespace SixLabors.ImageSharp.Processing } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/SolidBrush.cs b/src/ImageSharp.Drawing/Processing/SolidBrush.cs index c62566f6b..c297ede21 100644 --- a/src/ImageSharp.Drawing/Processing/SolidBrush.cs +++ b/src/ImageSharp.Drawing/Processing/SolidBrush.cs @@ -1,11 +1,10 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Buffers; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -17,33 +16,29 @@ namespace SixLabors.ImageSharp.Processing /// public class SolidBrush : IBrush { - /// - /// The color to paint. - /// - private readonly Color color; - /// /// Initializes a new instance of the class. /// /// The color. public SolidBrush(Color color) { - this.color = color; + this.Color = color; } /// /// Gets the color. /// - /// - /// The color. - /// - public Color Color => this.color; + public Color Color { get; } /// - public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator( + Configuration configuration, + GraphicsOptions options, + ImageFrame source, + RectangleF region) where TPixel : struct, IPixel { - return new SolidBrushApplicator(source, this.color.ToPixel(), options); + return new SolidBrushApplicator(configuration, options, source, this.Color.ToPixel()); } /// @@ -52,38 +47,49 @@ namespace SixLabors.ImageSharp.Processing private class SolidBrushApplicator : BrushApplicator where TPixel : struct, IPixel { + private bool isDisposed; + /// /// Initializes a new instance of the class. /// + /// The configuration instance to use when performing operations. + /// The graphics options. /// The source image. /// The color. - /// The options - public SolidBrushApplicator(ImageFrame source, TPixel color, GraphicsOptions options) - : base(source, options) + public SolidBrushApplicator( + Configuration configuration, + GraphicsOptions options, + ImageFrame source, + TPixel color) + : base(configuration, options, source) { this.Colors = source.MemoryAllocator.Allocate(source.Width); - this.Colors.GetSpan().Fill(color); + this.Colors.Memory.Span.Fill(color); } /// /// Gets the colors. /// - protected IMemoryOwner Colors { get; } + protected IMemoryOwner Colors { get; private set; } - /// - /// Gets the color for a single pixel. - /// - /// The x. - /// The y. - /// - /// The color - /// - internal override TPixel this[int x, int y] => this.Colors.GetSpan()[x]; + /// + internal override TPixel this[int x, int y] => this.Colors.Memory.Span[x]; /// - public override void Dispose() + protected override void Dispose(bool disposing) { - this.Colors.Dispose(); + if (this.isDisposed) + { + return; + } + + if (disposing) + { + this.Colors.Dispose(); + } + + this.Colors = null; + this.isDisposed = true; } /// @@ -102,17 +108,17 @@ namespace SixLabors.ImageSharp.Processing } MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; - Configuration configuration = this.Target.Configuration; + Configuration configuration = this.Configuration; if (this.Options.BlendPercentage == 1f) { - this.Blender.Blend(configuration, destinationRow, destinationRow, this.Colors.GetSpan(), scanline); + this.Blender.Blend(configuration, destinationRow, destinationRow, this.Colors.Memory.Span, scanline); } else { using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) { - Span amountSpan = amountBuffer.GetSpan(); + Span amountSpan = amountBuffer.Memory.Span; for (int i = 0; i < scanline.Length; i++) { @@ -123,11 +129,11 @@ namespace SixLabors.ImageSharp.Processing configuration, destinationRow, destinationRow, - this.Colors.GetSpan(), + this.Colors.Memory.Span, amountSpan); } } } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs index 6c140be72..63730d1bf 100644 --- a/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs +++ b/src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.Fonts; @@ -9,120 +9,169 @@ namespace SixLabors.ImageSharp.Processing /// /// Options for influencing the drawing functions. /// - public struct TextGraphicsOptions + public class TextGraphicsOptions : IDeepCloneable { - private const int DefaultTextDpi = 72; + private int antialiasSubpixelDepth = 16; + private float blendPercentage = 1F; + private float tabWidth = 4F; + private float dpiX = 72F; + private float dpiY = 72F; /// - /// Represents the default . + /// Initializes a new instance of the class. /// - public static readonly TextGraphicsOptions Default = new TextGraphicsOptions(true); - - private float? blendPercentage; - - private int? antialiasSubpixelDepth; - - private bool? antialias; - - private bool? applyKerning; - - private float? tabWidth; - - private float? dpiX; - - private float? dpiY; - - private HorizontalAlignment? horizontalAlignment; - - private VerticalAlignment? verticalAlignment; + public TextGraphicsOptions() + { + } - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - public TextGraphicsOptions(bool enableAntialiasing) + private TextGraphicsOptions(TextGraphicsOptions source) { - this.applyKerning = true; - this.tabWidth = 4; - this.WrapTextWidth = 0; - this.horizontalAlignment = HorizontalAlignment.Left; - this.verticalAlignment = VerticalAlignment.Top; - - this.antialiasSubpixelDepth = 16; - this.ColorBlendingMode = PixelColorBlendingMode.Normal; - this.AlphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = 1; - this.antialias = enableAntialiasing; - this.dpiX = DefaultTextDpi; - this.dpiY = DefaultTextDpi; + this.AlphaCompositionMode = source.AlphaCompositionMode; + this.Antialias = source.Antialias; + this.AntialiasSubpixelDepth = source.AntialiasSubpixelDepth; + this.ApplyKerning = source.ApplyKerning; + this.BlendPercentage = source.BlendPercentage; + this.ColorBlendingMode = source.ColorBlendingMode; + this.DpiX = source.DpiX; + this.DpiY = source.DpiY; + this.HorizontalAlignment = source.HorizontalAlignment; + this.TabWidth = source.TabWidth; + this.WrapTextWidth = source.WrapTextWidth; + this.VerticalAlignment = source.VerticalAlignment; } /// /// Gets or sets a value indicating whether antialiasing should be applied. + /// Defaults to true. /// - public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; } + public bool Antialias { get; set; } = true; /// /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. /// - public int AntialiasSubpixelDepth { get => this.antialiasSubpixelDepth ?? 16; set => this.antialiasSubpixelDepth = value; } + public int AntialiasSubpixelDepth + { + get + { + return this.antialiasSubpixelDepth; + } + + set + { + Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.AntialiasSubpixelDepth)); + this.antialiasSubpixelDepth = value; + } + } /// - /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// Gets or sets a value indicating the blending percentage to apply to the drawing operation. /// - public float BlendPercentage { get => (this.blendPercentage ?? 1).Clamp(0, 1); set => this.blendPercentage = value; } + public float BlendPercentage + { + get + { + return this.blendPercentage; + } - // In the future we could expose a PixelBlender directly on here - // or some forms of PixelBlender factory for each pixel type. Will need - // some API thought post V1. + set + { + Guard.MustBeBetweenOrEqualTo(value, 0, 1F, nameof(this.BlendPercentage)); + this.blendPercentage = value; + } + } /// - /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation + /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation. + /// Defaults to . /// - public PixelColorBlendingMode ColorBlendingMode { get; set; } + public PixelColorBlendingMode ColorBlendingMode { get; set; } = PixelColorBlendingMode.Normal; /// /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation + /// Defaults to . /// - public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } + public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } = PixelAlphaCompositionMode.SrcOver; /// /// Gets or sets a value indicating whether the text should be drawing with kerning enabled. + /// Defaults to true; /// - public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; } + public bool ApplyKerning { get; set; } = true; /// /// Gets or sets a value indicating the number of space widths a tab should lock to. + /// Defaults to 4. /// - public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; } + public float TabWidth + { + get + { + return this.tabWidth; + } + + set + { + Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.TabWidth)); + this.tabWidth = value; + } + } /// - /// Gets or sets a value indicating if greater than zero determine the width at which text should wrap. + /// Gets or sets a value, if greater than 0, indicating the width at which text should wrap. + /// Defaults to 0. /// public float WrapTextWidth { get; set; } /// - /// Gets or sets a value indicating the DPI to render text along the X axis. + /// Gets or sets a value indicating the DPI (Dots Per Inch) to render text along the X axis. + /// Defaults to 72. /// - public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; } + public float DpiX + { + get + { + return this.dpiX; + } + + set + { + Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.DpiX)); + this.dpiX = value; + } + } /// - /// Gets or sets a value indicating the DPI to render text along the Y axis. + /// Gets or sets a value indicating the DPI (Dots Per Inch) to render text along the Y axis. + /// Defaults to 72. /// - public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => this.dpiY = value; } + public float DpiY + { + get + { + return this.dpiY; + } + + set + { + Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.DpiY)); + this.dpiY = value; + } + } /// /// Gets or sets a value indicating how to align the text relative to the rendering space. /// If is greater than zero it will align relative to the space /// defined by the location and width, if equals zero, and thus /// wrapping disabled, then the alignment is relative to the drawing location. + /// Defaults to . /// - public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; } + public HorizontalAlignment HorizontalAlignment { get; set; } = HorizontalAlignment.Left; /// /// Gets or sets a value indicating how to align the text relative to the rendering space. + /// Defaults to . /// - public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; } + public VerticalAlignment VerticalAlignment { get; set; } = VerticalAlignment.Top; /// /// Performs an implicit conversion from to . @@ -133,8 +182,9 @@ namespace SixLabors.ImageSharp.Processing /// public static implicit operator TextGraphicsOptions(GraphicsOptions options) { - return new TextGraphicsOptions(options.Antialias) + return new TextGraphicsOptions() { + Antialias = options.Antialias, AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, blendPercentage = options.BlendPercentage, ColorBlendingMode = options.ColorBlendingMode, @@ -151,13 +201,17 @@ namespace SixLabors.ImageSharp.Processing /// public static explicit operator GraphicsOptions(TextGraphicsOptions options) { - return new GraphicsOptions(options.Antialias) + return new GraphicsOptions() { + Antialias = options.Antialias, AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, ColorBlendingMode = options.ColorBlendingMode, AlphaCompositionMode = options.AlphaCompositionMode, BlendPercentage = options.BlendPercentage }; } + + /// + public TextGraphicsOptions DeepClone() => new TextGraphicsOptions(this); } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Utils/NumberUtils.cs b/src/ImageSharp.Drawing/Utils/NumberUtils.cs new file mode 100644 index 000000000..d034c5d7e --- /dev/null +++ b/src/ImageSharp.Drawing/Utils/NumberUtils.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Utility methods for numeric primitives. + /// + internal static class NumberUtils + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float ClampFloat(float value, float min, float max) + { + if (value >= max) + { + return max; + } + + if (value <= min) + { + return min; + } + + return value; + } + } +} diff --git a/src/ImageSharp.Drawing/Utils/QuickSort.cs b/src/ImageSharp.Drawing/Utils/QuickSort.cs index ca1da5505..14e3146a0 100644 --- a/src/ImageSharp.Drawing/Utils/QuickSort.cs +++ b/src/ImageSharp.Drawing/Utils/QuickSort.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 76f399517..5fad7a8e3 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -133,8 +133,7 @@ namespace SixLabors.ImageSharp public override string ToString() => this.ToHex(); /// - /// Converts the color instance to an - /// implementation defined by . + /// Converts the color instance to a specified type. /// /// The pixel type to convert to. /// The pixel value. @@ -147,6 +146,24 @@ namespace SixLabors.ImageSharp return pixel; } + /// + /// Bulk converts a span of to a span of a specified type. + /// + /// The pixel type to convert to. + /// The configuration. + /// The source color span. + /// The destination pixel span. + [MethodImpl(InliningOptions.ShortMethod)] + public static void ToPixel( + Configuration configuration, + ReadOnlySpan source, + Span destination) + where TPixel : struct, IPixel + { + ReadOnlySpan rgba64Span = MemoryMarshal.Cast(source); + PixelOperations.Instance.FromRgba64(configuration, rgba64Span, destination); + } + /// [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(Color other) @@ -166,19 +183,5 @@ namespace SixLabors.ImageSharp { return this.data.PackedValue.GetHashCode(); } - - /// - /// Bulk convert a span of to a span of a specified pixel type. - /// - [MethodImpl(InliningOptions.ShortMethod)] - internal static void ToPixel( - Configuration configuration, - ReadOnlySpan source, - Span destination) - where TPixel : struct, IPixel - { - ReadOnlySpan rgba64Span = MemoryMarshal.Cast(source); - PixelOperations.Instance.FromRgba64(configuration, rgba64Span, destination); - } } } diff --git a/src/ImageSharp/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs index 214b10810..47b930e65 100644 --- a/src/ImageSharp/GraphicsOptions.cs +++ b/src/ImageSharp/GraphicsOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -8,170 +8,82 @@ namespace SixLabors.ImageSharp /// /// Options for influencing the drawing functions. /// - public struct GraphicsOptions + public class GraphicsOptions : IDeepCloneable { - /// - /// Represents the default . - /// - public static readonly GraphicsOptions Default = new GraphicsOptions(true); - - private float? blendPercentage; - - private int? antialiasSubpixelDepth; - - private bool? antialias; - - private PixelColorBlendingMode colorBlendingMode; - - private PixelAlphaCompositionMode alphaCompositionMode; - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - public GraphicsOptions(bool enableAntialiasing) - { - this.colorBlendingMode = PixelColorBlendingMode.Normal; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = 1; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; - } - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - /// blending percentage to apply to the drawing operation - public GraphicsOptions(bool enableAntialiasing, float opacity) - { - Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - - this.colorBlendingMode = PixelColorBlendingMode.Normal; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = opacity; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; - } + private int antialiasSubpixelDepth = 16; + private float blendPercentage = 1F; /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the class. /// - /// If set to true [enable antialiasing]. - /// blending percentage to apply to the drawing operation - /// color blending mode to apply to the drawing operation - public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity) + public GraphicsOptions() { - Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - - this.colorBlendingMode = blending; - this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; - this.blendPercentage = opacity; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; } - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - /// blending percentage to apply to the drawing operation - /// color blending mode to apply to the drawing operation - /// alpha composition mode to apply to the drawing operation - public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, float opacity) + private GraphicsOptions(GraphicsOptions source) { - Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - - this.colorBlendingMode = blending; - this.alphaCompositionMode = composition; - this.blendPercentage = opacity; - this.antialiasSubpixelDepth = 16; - this.antialias = enableAntialiasing; + this.AlphaCompositionMode = source.AlphaCompositionMode; + this.Antialias = source.Antialias; + this.AntialiasSubpixelDepth = source.AntialiasSubpixelDepth; + this.BlendPercentage = source.BlendPercentage; + this.ColorBlendingMode = source.ColorBlendingMode; } /// /// Gets or sets a value indicating whether antialiasing should be applied. + /// Defaults to true. /// - public bool Antialias - { - get => this.antialias ?? true; - set => this.antialias = value; - } + public bool Antialias { get; set; } = true; /// /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. + /// Defaults to 16. /// public int AntialiasSubpixelDepth { - get => this.antialiasSubpixelDepth ?? 16; - set => this.antialiasSubpixelDepth = value; + get + { + return this.antialiasSubpixelDepth; + } + + set + { + Guard.MustBeGreaterThanOrEqualTo(value, 0, nameof(this.AntialiasSubpixelDepth)); + this.antialiasSubpixelDepth = value; + } } /// - /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// Gets or sets a value between indicating the blending percentage to apply to the drawing operation. + /// Range 0..1; Defaults to 1. /// public float BlendPercentage { - get => (this.blendPercentage ?? 1).Clamp(0, 1); - set => this.blendPercentage = value; - } + get + { + return this.blendPercentage; + } - // In the future we could expose a PixelBlender directly on here - // or some forms of PixelBlender factory for each pixel type. Will need - // some API thought post V1. + set + { + Guard.MustBeBetweenOrEqualTo(value, 0, 1F, nameof(this.BlendPercentage)); + this.blendPercentage = value; + } + } /// - /// Gets or sets a value indicating the color blending mode to apply to the drawing operation + /// Gets or sets a value indicating the color blending mode to apply to the drawing operation. + /// Defaults to . /// - public PixelColorBlendingMode ColorBlendingMode - { - get => this.colorBlendingMode; - set => this.colorBlendingMode = value; - } + public PixelColorBlendingMode ColorBlendingMode { get; set; } = PixelColorBlendingMode.Normal; /// /// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation + /// Defaults to . /// - public PixelAlphaCompositionMode AlphaCompositionMode - { - get => this.alphaCompositionMode; - set => this.alphaCompositionMode = value; - } + public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } = PixelAlphaCompositionMode.SrcOver; - /// - /// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings. - /// - /// the color - /// true if the color can be considered opaque - /// - /// Blending and composition is an expensive operation, in some cases, like - /// filling with a solid color, the blending can be avoided by a plain color replacement. - /// This method can be useful for such processors to select the fast path. - /// - internal bool IsOpaqueColorWithoutBlending(Color color) - { - if (this.ColorBlendingMode != PixelColorBlendingMode.Normal) - { - return false; - } - - if (this.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver && - this.AlphaCompositionMode != PixelAlphaCompositionMode.Src) - { - return false; - } - - if (this.BlendPercentage != 1f) - { - return false; - } - - if (color.ToVector4().W != 1f) - { - return false; - } - - return true; - } + /// + public GraphicsOptions DeepClone() => new GraphicsOptions(this); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs index b596351b5..a3fa0e1ff 100644 --- a/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs +++ b/src/ImageSharp/Memory/MemoryAllocatorExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Buffers; @@ -11,8 +11,18 @@ namespace SixLabors.ImageSharp.Memory /// /// Extension methods for . /// - internal static class MemoryAllocatorExtensions + public static class MemoryAllocatorExtensions { + /// + /// Allocates a buffer of value type objects interpreted as a 2D region + /// of x elements. + /// + /// The type of buffer items to allocate. + /// The memory allocator. + /// The buffer width. + /// The buffer heght. + /// The allocation options. + /// The . public static Buffer2D Allocate2D( this MemoryAllocator memoryAllocator, int width, @@ -26,6 +36,15 @@ namespace SixLabors.ImageSharp.Memory return new Buffer2D(memorySource, width, height); } + /// + /// Allocates a buffer of value type objects interpreted as a 2D region + /// of width x height elements. + /// + /// The type of buffer items to allocate. + /// The memory allocator. + /// The buffer size. + /// The allocation options. + /// The . public static Buffer2D Allocate2D( this MemoryAllocator memoryAllocator, Size size, @@ -41,7 +60,7 @@ namespace SixLabors.ImageSharp.Memory /// The pixel size in bytes, eg. 3 for RGB /// The padding /// A - public static IManagedByteBuffer AllocatePaddedPixelRowBuffer( + internal static IManagedByteBuffer AllocatePaddedPixelRowBuffer( this MemoryAllocator memoryAllocator, int width, int pixelSizeInBytes, @@ -51,4 +70,4 @@ namespace SixLabors.ImageSharp.Memory return memoryAllocator.AllocateManagedByteBuffer(length); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Primitives/DenseMatrix{T}.cs b/src/ImageSharp/Primitives/DenseMatrix{T}.cs index 170292e29..cc5e4a90a 100644 --- a/src/ImageSharp/Primitives/DenseMatrix{T}.cs +++ b/src/ImageSharp/Primitives/DenseMatrix{T}.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -98,9 +98,9 @@ namespace SixLabors.ImageSharp.Primitives } /// - /// Gets a Span wrapping the Data. + /// Gets a span wrapping the . /// - internal Span Span => new Span(this.Data); + public Span Span => new Span(this.Data); /// /// Gets or sets the item at the specified position. @@ -222,4 +222,4 @@ namespace SixLabors.ImageSharp.Primitives /// public override int GetHashCode() => this.Data.GetHashCode(); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs b/src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs index dd1cc1ed2..4241721f4 100644 --- a/src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/BackgroundColorExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing.Processors.Overlays; @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// The color to set as the background. /// The to allow chaining of operations. public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, Color color) => - BackgroundColor(source, GraphicsOptions.Default, color); + BackgroundColor(source, new GraphicsOptions(), color); /// /// Replaces the background color of image with the given one. @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing this IImageProcessingContext source, Color color, Rectangle rectangle) => - BackgroundColor(source, GraphicsOptions.Default, color, rectangle); + BackgroundColor(source, new GraphicsOptions(), color, rectangle); /// /// Replaces the background color of image with the given one. @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing this IImageProcessingContext source, GraphicsOptions options, Color color) => - source.ApplyProcessor(new BackgroundColorProcessor(color, options)); + source.ApplyProcessor(new BackgroundColorProcessor(options, color)); /// /// Replaces the background color of image with the given one. @@ -64,6 +64,6 @@ namespace SixLabors.ImageSharp.Processing GraphicsOptions options, Color color, Rectangle rectangle) => - source.ApplyProcessor(new BackgroundColorProcessor(color, options), rectangle); + source.ApplyProcessor(new BackgroundColorProcessor(options, color), rectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Extensions/GlowExtensions.cs b/src/ImageSharp/Processing/Extensions/GlowExtensions.cs index 39734882b..48ecb5108 100644 --- a/src/ImageSharp/Processing/Extensions/GlowExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/GlowExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Primitives; @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source) => - Glow(source, GraphicsOptions.Default); + Glow(source, new GraphicsOptions()); /// /// Applies a radial glow effect to an image. @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, Color color) { - return Glow(source, GraphicsOptions.Default, color); + return Glow(source, new GraphicsOptions(), color); } /// @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing /// The the radius. /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) => - Glow(source, GraphicsOptions.Default, radius); + Glow(source, new GraphicsOptions(), radius); /// /// Applies a radial glow effect to an image. @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) => - source.Glow(GraphicsOptions.Default, rectangle); + source.Glow(new GraphicsOptions(), rectangle); /// /// Applies a radial glow effect to an image. @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing Color color, float radius, Rectangle rectangle) => - source.Glow(GraphicsOptions.Default, color, ValueSize.Absolute(radius), rectangle); + source.Glow(new GraphicsOptions(), color, ValueSize.Absolute(radius), rectangle); /// /// Applies a radial glow effect to an image. @@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Processing Color color, ValueSize radius, Rectangle rectangle) => - source.ApplyProcessor(new GlowProcessor(color, radius, options), rectangle); + source.ApplyProcessor(new GlowProcessor(options, color, radius), rectangle); /// /// Applies a radial glow effect to an image. @@ -170,6 +170,6 @@ namespace SixLabors.ImageSharp.Processing GraphicsOptions options, Color color, ValueSize radius) => - source.ApplyProcessor(new GlowProcessor(color, radius, options)); + source.ApplyProcessor(new GlowProcessor(options, color, radius)); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Extensions/VignetteExtensions.cs b/src/ImageSharp/Processing/Extensions/VignetteExtensions.cs index 74a59d3e1..a1f3a6e8a 100644 --- a/src/ImageSharp/Processing/Extensions/VignetteExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/VignetteExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Primitives; @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source) => - Vignette(source, GraphicsOptions.Default); + Vignette(source, new GraphicsOptions()); /// /// Applies a radial vignette effect to an image. @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// The color to set as the vignette. /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, Color color) => - Vignette(source, GraphicsOptions.Default, color); + Vignette(source, new GraphicsOptions(), color); /// /// Applies a radial vignette effect to an image. @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing this IImageProcessingContext source, float radiusX, float radiusY) => - Vignette(source, GraphicsOptions.Default, radiusX, radiusY); + Vignette(source, new GraphicsOptions(), radiusX, radiusY); /// /// Applies a radial vignette effect to an image. @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) => - Vignette(source, GraphicsOptions.Default, rectangle); + Vignette(source, new GraphicsOptions(), rectangle); /// /// Applies a radial vignette effect to an image. @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing float radiusX, float radiusY, Rectangle rectangle) => - source.Vignette(GraphicsOptions.Default, color, radiusX, radiusY, rectangle); + source.Vignette(new GraphicsOptions(), color, radiusX, radiusY, rectangle); /// /// Applies a radial vignette effect to an image. @@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.Processing ValueSize radiusX, ValueSize radiusY, Rectangle rectangle) => - source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options), rectangle); + source.ApplyProcessor(new VignetteProcessor(options, color, radiusX, radiusY), rectangle); private static IImageProcessingContext VignetteInternal( this IImageProcessingContext source, @@ -174,6 +174,6 @@ namespace SixLabors.ImageSharp.Processing Color color, ValueSize radiusX, ValueSize radiusY) => - source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options)); + source.ApplyProcessor(new VignetteProcessor(options, color, radiusX, radiusY)); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs index 4b4c53727..e78f7e5e7 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs @@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// Initializes a new instance of the class. /// - /// The to set the background color to. /// The options defining blending algorithm and amount. - public BackgroundColorProcessor(Color color, GraphicsOptions options) + /// The to set the background color to. + public BackgroundColorProcessor(GraphicsOptions options, Color color) { this.Color = color; this.GraphicsOptions = options; diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 0958e3aa9..4b9a23eff 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -24,10 +24,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// Initializes a new instance of the class. /// - /// The color or the glow. /// The options effecting blending and composition. - public GlowProcessor(Color color, GraphicsOptions options) - : this(color, 0, options) + /// The color or the glow. + public GlowProcessor(GraphicsOptions options, Color color) + : this(options, color, 0) { } @@ -37,17 +37,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// The color or the glow. /// The radius of the glow. internal GlowProcessor(Color color, ValueSize radius) - : this(color, radius, GraphicsOptions.Default) + : this(new GraphicsOptions(), color, radius) { } /// /// Initializes a new instance of the class. /// + /// The options effecting blending and composition. /// The color or the glow. /// The radius of the glow. - /// The options effecting blending and composition. - internal GlowProcessor(Color color, ValueSize radius, GraphicsOptions options) + internal GlowProcessor(GraphicsOptions options, Color color, ValueSize radius) { this.GlowColor = color; this.Radius = radius; @@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// Gets the the radius. /// - internal ValueSize Radius { get; } + internal ValueSize Radius { get; } /// public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 2365318f3..3cf48e5a4 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -17,16 +17,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// The color of the vignette. public VignetteProcessor(Color color) - : this(color, GraphicsOptions.Default) + : this(new GraphicsOptions(), color) { } /// /// Initializes a new instance of the class. /// - /// The color of the vignette. /// The options effecting blending and composition. - public VignetteProcessor(Color color, GraphicsOptions options) + /// The color of the vignette. + public VignetteProcessor(GraphicsOptions options, Color color) { this.VignetteColor = color; this.GraphicsOptions = options; @@ -35,11 +35,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// Initializes a new instance of the class. /// + /// The options effecting blending and composition. /// The color of the vignette. /// The x-radius. /// The y-radius. - /// The options effecting blending and composition. - internal VignetteProcessor(Color color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options) + internal VignetteProcessor(GraphicsOptions options, Color color, ValueSize radiusX, ValueSize radiusY) { this.VignetteColor = color; this.RadiusX = radiusX; diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs index 0982db334..c19961390 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Drawing; @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Benchmarks graphics.SmoothingMode = SmoothingMode.AntiAlias; using (var font = new Font("Arial", 12, GraphicsUnit.Point)) { - graphics.DrawString(TextToRender, font, System.Drawing.Brushes.HotPink, new RectangleF(10, 10, 780, 780)); + graphics.DrawString(this.TextToRender, font, System.Drawing.Brushes.HotPink, new RectangleF(10, 10, 780, 780)); } } } @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Benchmarks using (var image = new Image(800, 800)) { var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); - image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)))); + image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions { Antialias = true, WrapTextWidth = 780 }, this.TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)))); } } @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Benchmarks using (var image = new Image(800, 800)) { var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); - image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))); + image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions { Antialias = true, WrapTextWidth = 780 }, this.TextToRender, font, Processing.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))); } IImageProcessingContext DrawTextOldVersion( @@ -93,4 +93,4 @@ namespace SixLabors.ImageSharp.Benchmarks } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs index c5c1ba5ac..7d8b77659 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawTextOutline.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Drawing; @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Benchmarks [Params(10, 100)] public int TextIterations { get; set; } public string TextPhrase { get; set; } = "Hello World"; - public string TextToRender => string.Join(" ", Enumerable.Repeat(TextPhrase, TextIterations)); + public string TextToRender => string.Join(" ", Enumerable.Repeat(this.TextPhrase, this.TextIterations)); [Benchmark(Baseline = true, Description = "System.Drawing Draw Text Outline")] public void DrawTextSystemDrawing() @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Benchmarks using (var font = new Font("Arial", 12, GraphicsUnit.Point)) using (var gp = new GraphicsPath()) { - gp.AddString(TextToRender, font.FontFamily, (int)font.Style, font.Size, new RectangleF(10, 10, 780, 780), new StringFormat()); + gp.AddString(this.TextToRender, font.FontFamily, (int)font.Style, font.Size, new RectangleF(10, 10, 780, 780), new StringFormat()); graphics.DrawPath(pen, gp); } } @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Benchmarks using (var image = new Image(800, 800)) { var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12); - image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10)))); + image.Mutate(x => x.ApplyProcessor(new DrawTextProcessor(new TextGraphicsOptions { Antialias = true, WrapTextWidth = 780 }, this.TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), new SixLabors.Primitives.PointF(10, 10)))); } } @@ -56,8 +56,8 @@ namespace SixLabors.ImageSharp.Benchmarks image.Mutate( x => DrawTextOldVersion( x, - new TextGraphicsOptions(true) { WrapTextWidth = 780 }, - TextToRender, + new TextGraphicsOptions { Antialias = true, WrapTextWidth = 780 }, + this.TextToRender, font, null, Processing.Pens.Solid(Rgba32.HotPink, 10), @@ -99,4 +99,4 @@ namespace SixLabors.ImageSharp.Benchmarks } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs index 86c1c2850..61b45729d 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTests.cs @@ -190,7 +190,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing void Test() { - background.Mutate(context => context.DrawImage(overlay, new Point(x, y), GraphicsOptions.Default)); + background.Mutate(context => context.DrawImage(overlay, new Point(x, y), new GraphicsOptions())); } } } diff --git a/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs b/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs index 2836f8a38..b45fc620b 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawLinesTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -24,10 +24,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing { Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); Pen pen = new Pen(color, thickness); - + DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); } - + [Theory] [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "White", 1f, 5, false)] public void DrawLines_Dash(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) @@ -35,10 +35,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing { Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); Pen pen = Pens.Dash(color, thickness); - + DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); } - + [Theory] [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "LightGreen", 1f, 5, false)] public void DrawLines_Dot(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) @@ -46,10 +46,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing { Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); Pen pen = Pens.Dot(color, thickness); - + DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); } - + [Theory] [WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32, "Yellow", 1f, 5, false)] public void DrawLines_DashDot(TestImageProvider provider, string colorName, float alpha, float thickness, bool antialias) @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing { Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); Pen pen = Pens.DashDot(color, thickness); - + DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); } @@ -68,11 +68,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing { Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); Pen pen = Pens.DashDotDot(color, thickness); - + DrawLinesImpl(provider, colorName, alpha, thickness, antialias, pen); } - + private static void DrawLinesImpl( TestImageProvider provider, string colorName, @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing { SixLabors.Primitives.PointF[] simplePath = { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) }; - GraphicsOptions options = new GraphicsOptions(antialias); + GraphicsOptions options = new GraphicsOptions { Antialias = antialias }; string aa = antialias ? "" : "_NoAntialias"; FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}"; diff --git a/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs index 18fde6ad8..4a6cb430a 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPolygonTests.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing }; Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - GraphicsOptions options = new GraphicsOptions(antialias); + GraphicsOptions options = new GraphicsOptions { Antialias = antialias }; string aa = antialias ? "" : "_NoAntialias"; FormattableString outputDetails = $"{colorName}_A({alpha})_T({thickness}){aa}"; diff --git a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs index 361e7e70d..031e732ea 100644 --- a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -16,6 +16,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing { using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; + using SixLabors.Shapes; [GroupOutput("Drawing/GradientBrushes")] public class FillLinearGradientBrushTests @@ -392,5 +394,44 @@ namespace SixLabors.ImageSharp.Tests.Drawing false, false); } + + [Theory] + [WithBlankImages(200, 200, PixelTypes.Rgba32)] + public void GradientsWithTransparencyOnExistingBackground(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.VerifyOperation( + image => + { + image.Mutate(i => i.Fill(Color.Red)); + image.Mutate(ApplyGloss); + + }); + + void ApplyGloss(IImageProcessingContext ctx) + { + Size size = ctx.GetCurrentSize(); + IPathCollection glossPath = BuildGloss(size.Width, size.Height); + var graphicsOptions = new GraphicsOptions + { + Antialias = true, + ColorBlendingMode = PixelColorBlendingMode.Normal, + AlphaCompositionMode = PixelAlphaCompositionMode.SrcAtop + }; + var linearGradientBrush = new LinearGradientBrush(new Point(0, 0), new Point(0, size.Height / 2), GradientRepetitionMode.Repeat, new ColorStop(0, Color.White.WithAlpha(0.5f)), new ColorStop(1, Color.White.WithAlpha(0.25f))); + ctx.Fill(graphicsOptions, linearGradientBrush, glossPath); + } + + IPathCollection BuildGloss(int imageWidth, int imageHeight) + { + var pathBuilder = new PathBuilder(); + pathBuilder.AddLine(new PointF(0, 0), new PointF(imageWidth, 0)); + pathBuilder.AddLine(new PointF(imageWidth, 0), new PointF(imageWidth, imageHeight * 0.4f)); + pathBuilder.AddBezier(new PointF(imageWidth, imageHeight * 0.4f), new PointF(imageWidth / 2, imageHeight * 0.6f), new PointF(0, imageHeight * 0.4f)); + pathBuilder.CloseFigure(); + return new PathCollection(pathBuilder.Build()); + } + } + } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs index 104237ec3..22294e76d 100644 --- a/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillPolygonTests.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing }; Color color = TestUtils.GetColorByName(colorName).WithAlpha(alpha); - var options = new GraphicsOptions(antialias); + var options = new GraphicsOptions { Antialias = antialias }; string aa = antialias ? "" : "_NoAntialias"; FormattableString outputDetails = $"{colorName}_A{alpha}{aa}"; diff --git a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs index c0388ea2d..e259d29d9 100644 --- a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; @@ -16,8 +16,6 @@ using SixLabors.Shapes; namespace SixLabors.ImageSharp.Tests.Drawing { - - public class FillRegionProcessorTests { @@ -35,11 +33,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing var brush = new Mock(); var region = new MockRegion2(bounds); - var options = new GraphicsOptions(antialias) + var options = new GraphicsOptions { + Antialias = antialias, AntialiasSubpixelDepth = 1 }; - var processor = new FillRegionProcessor(brush.Object, region, options); + var processor = new FillRegionProcessor(options, brush.Object, region); var img = new Image(1, 1); processor.Execute(img, bounds); @@ -51,8 +50,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing { var bounds = new Rectangle(-100, -10, 10, 10); var brush = new Mock(); - var options = new GraphicsOptions(true); - var processor = new FillRegionProcessor(brush.Object, new MockRegion1(), options); + var options = new GraphicsOptions { Antialias = true }; + var processor = new FillRegionProcessor(options, brush.Object, new MockRegion1()); var img = new Image(10, 10); processor.Execute(img, bounds); } @@ -73,11 +72,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing public void DoesNotThrowForIssue928() { var rectText = new RectangleF(0, 0, 2000, 2000); - using (Image img = new Image((int)rectText.Width, (int)rectText.Height)) + using (var img = new Image((int)rectText.Width, (int)rectText.Height)) { img.Mutate(x => x.Fill(Rgba32.Transparent)); - img.Mutate(ctx => { + img.Mutate(ctx => + { ctx.DrawLines( Rgba32.Red, 0.984252f, @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing new PointF(104.782608f, 1075.13245f), new PointF(104.782608f, 1075.13245f) ); - } + } ); } } @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing [Fact] public void DoesNotThrowFillingTriangle() { - using(var image = new Image(28, 28)) + using (var image = new Image(28, 28)) { var path = new Polygon( new LinearLineSegment(new PointF(17.11f, 13.99659f), new PointF(14.01433f, 27.06201f)), diff --git a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs index a5e745083..1e3688fea 100644 --- a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs @@ -156,10 +156,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing { TPixel bgColor = image[0, 0]; - var options = new GraphicsOptions(false) - { - ColorBlendingMode = blenderMode, BlendPercentage = blendPercentage - }; + var options = new GraphicsOptions + { + Antialias = false, + ColorBlendingMode = blenderMode, + BlendPercentage = blendPercentage + }; if (triggerFillRegion) { @@ -173,13 +175,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing } var testOutputDetails = new - { - triggerFillRegion = triggerFillRegion, - newColorName = newColorName, - alpha = alpha, - blenderMode = blenderMode, - blendPercentage = blendPercentage - }; + { + triggerFillRegion = triggerFillRegion, + newColorName = newColorName, + alpha = alpha, + blenderMode = blenderMode, + blendPercentage = blendPercentage + }; image.DebugSave( provider, diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs index 3691b54ce..36c11035c 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Drawing; using SixLabors.ImageSharp.Tests.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.Shapes; using Xunit; @@ -14,7 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths { public class DrawPathCollection : BaseImageOperationsExtensionTest { - GraphicsOptions noneDefault = new GraphicsOptions(); + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + + GraphicsOptions nonDefault = new GraphicsOptions { Antialias = false }; Color color = Color.HotPink; Pen pen = Pens.Solid(Rgba32.HotPink, 1); IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { @@ -46,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths { FillRegionProcessor processor = this.Verify(i); - Assert.Equal(GraphicsOptions.Default, processor.Options); + Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer); ShapePath region = Assert.IsType(processor.Region); @@ -60,13 +63,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushPathOptions() { - this.operations.Draw(this.noneDefault, this.pen, this.pathCollection); + this.operations.Draw(this.nonDefault, this.pen, this.pathCollection); for (int i = 0; i < 2; i++) { FillRegionProcessor processor = this.Verify(i); - Assert.Equal(this.noneDefault, processor.Options); + Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer); ShapePath region = Assert.IsType(processor.Region); Assert.IsType(region.Shape); @@ -84,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths { FillRegionProcessor processor = this.Verify(i); - Assert.Equal(GraphicsOptions.Default, processor.Options); + Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer); ShapePath region = Assert.IsType(processor.Region); Assert.IsType(region.Shape); @@ -97,13 +100,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorPathAndOptions() { - this.operations.Draw(this.noneDefault, this.color, 1, this.pathCollection); + this.operations.Draw(this.nonDefault, this.color, 1, this.pathCollection); for (int i = 0; i < 2; i++) { FillRegionProcessor processor = this.Verify(i); - Assert.Equal(this.noneDefault, processor.Options); + Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer); ShapePath region = Assert.IsType(processor.Region); Assert.IsType(region.Shape); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs index 160ff22a3..cea59e15e 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Drawing; using SixLabors.ImageSharp.Tests.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.Shapes; using Xunit; @@ -14,7 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths { public class FillPath : BaseImageOperationsExtensionTest { - GraphicsOptions noneDefault = new GraphicsOptions(); + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + + GraphicsOptions nonDefault = new GraphicsOptions { Antialias = false }; Color color = Color.HotPink; SolidBrush brush = Brushes.Solid(Rgba32.HotPink); IPath path = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { @@ -30,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths this.operations.Fill(this.brush, this.path); var processor = this.Verify(); - Assert.Equal(GraphicsOptions.Default, processor.Options); + Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); @@ -44,10 +47,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushPathOptions() { - this.operations.Fill(this.noneDefault, this.brush, this.path); + this.operations.Fill(this.nonDefault, this.brush, this.path); var processor = this.Verify(); - Assert.Equal(this.noneDefault, processor.Options); + Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Polygon polygon = Assert.IsType(region.Shape); @@ -62,7 +65,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths this.operations.Fill(this.color, this.path); var processor = this.Verify(); - Assert.Equal(GraphicsOptions.Default, processor.Options); + Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Polygon polygon = Assert.IsType(region.Shape); @@ -75,10 +78,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorPathAndOptions() { - this.operations.Fill(this.noneDefault, this.color, this.path); + this.operations.Fill(this.nonDefault, this.color, this.path); var processor = this.Verify(); - Assert.Equal(this.noneDefault, processor.Options); + Assert.Equal(this.nonDefault, processor.Options); ShapeRegion region = Assert.IsType(processor.Region); Polygon polygon = Assert.IsType(region.Shape); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs index b76ee8ffc..2a9c04a89 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Drawing; using SixLabors.ImageSharp.Tests.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.Shapes; using Xunit; @@ -14,7 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths { public class FillPathCollection : BaseImageOperationsExtensionTest { - GraphicsOptions noneDefault = new GraphicsOptions(); + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + + GraphicsOptions nonDefault = new GraphicsOptions { Antialias = false }; Color color = Color.HotPink; SolidBrush brush = Brushes.Solid(Rgba32.HotPink); IPath path1 = new Path(new LinearLineSegment(new SixLabors.Primitives.PointF[] { @@ -46,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths { FillRegionProcessor processor = this.Verify(i); - Assert.Equal(GraphicsOptions.Default, processor.Options); + Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); @@ -61,13 +64,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushPathOptions() { - this.operations.Fill(this.noneDefault, this.brush, this.pathCollection); + this.operations.Fill(this.nonDefault, this.brush, this.pathCollection); for (int i = 0; i < 2; i++) { FillRegionProcessor processor = this.Verify(i); - Assert.Equal(this.noneDefault, processor.Options); + Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Polygon polygon = Assert.IsType(region.Shape); @@ -86,7 +89,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths { FillRegionProcessor processor = this.Verify(i); - Assert.Equal(GraphicsOptions.Default, processor.Options); + Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Polygon polygon = Assert.IsType(region.Shape); @@ -100,13 +103,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorPathAndOptions() { - this.operations.Fill(this.noneDefault, this.color, this.pathCollection); + this.operations.Fill(this.nonDefault, this.color, this.pathCollection); for (int i = 0; i < 2; i++) { FillRegionProcessor processor = this.Verify(i); - Assert.Equal(this.noneDefault, processor.Options); + Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Polygon polygon = Assert.IsType(region.Shape); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs index c62a87148..8dacd1e7f 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Drawing; using SixLabors.ImageSharp.Tests.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.Shapes; using Xunit; @@ -14,7 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths { public class FillPolygon : BaseImageOperationsExtensionTest { - GraphicsOptions noneDefault = new GraphicsOptions(); + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + + GraphicsOptions nonDefault = new GraphicsOptions { Antialias = false }; Color color = Color.HotPink; SolidBrush brush = Brushes.Solid(Rgba32.HotPink); SixLabors.Primitives.PointF[] path = { @@ -32,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths FillRegionProcessor processor = this.Verify(); - Assert.Equal(GraphicsOptions.Default, processor.Options); + Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Polygon polygon = Assert.IsType(region.Shape); @@ -44,10 +47,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushPathAndOptions() { - this.operations.FillPolygon(this.noneDefault, this.brush, this.path); + this.operations.FillPolygon(this.nonDefault, this.brush, this.path); FillRegionProcessor processor = this.Verify(); - Assert.Equal(this.noneDefault, processor.Options); + Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Polygon polygon = Assert.IsType(region.Shape); @@ -63,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths FillRegionProcessor processor = this.Verify(); - Assert.Equal(GraphicsOptions.Default, processor.Options); + Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Polygon polygon = Assert.IsType(region.Shape); @@ -76,10 +79,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorPathAndOptions() { - this.operations.FillPolygon(this.noneDefault, this.color, this.path); + this.operations.FillPolygon(this.nonDefault, this.color, this.path); FillRegionProcessor processor = this.Verify(); - Assert.Equal(this.noneDefault, processor.Options); + Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Polygon polygon = Assert.IsType(region.Shape); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs index 17a2b87c0..6b08323b6 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -6,17 +6,19 @@ using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Drawing; using SixLabors.ImageSharp.Tests.Processing; - +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; namespace SixLabors.ImageSharp.Tests.Drawing.Paths { public class FillRectangle : BaseImageOperationsExtensionTest { - GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Rgba32.HotPink); - SixLabors.Primitives.Rectangle rectangle = new SixLabors.Primitives.Rectangle(10, 10, 77, 76); + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + + private GraphicsOptions nonDefault = new GraphicsOptions { Antialias = false }; + private Color color = Color.HotPink; + private SolidBrush brush = Brushes.Solid(Rgba32.HotPink); + private SixLabors.Primitives.Rectangle rectangle = new SixLabors.Primitives.Rectangle(10, 10, 77, 76); [Fact] public void CorrectlySetsBrushAndRectangle() @@ -24,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths this.operations.Fill(this.brush, this.rectangle); FillRegionProcessor processor = this.Verify(); - Assert.Equal(GraphicsOptions.Default, processor.Options); + Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Shapes.RectangularPolygon rect = Assert.IsType(region.Shape); @@ -39,10 +41,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushRectangleAndOptions() { - this.operations.Fill(this.noneDefault, this.brush, this.rectangle); + this.operations.Fill(this.nonDefault, this.brush, this.rectangle); FillRegionProcessor processor = this.Verify(); - Assert.Equal(this.noneDefault, processor.Options); + Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Shapes.RectangularPolygon rect = Assert.IsType(region.Shape); @@ -60,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths this.operations.Fill(this.color, this.rectangle); FillRegionProcessor processor = this.Verify(); - Assert.Equal(GraphicsOptions.Default, processor.Options); + Assert.Equal(new GraphicsOptions(), processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Shapes.RectangularPolygon rect = Assert.IsType(region.Shape); @@ -76,10 +78,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorRectangleAndOptions() { - this.operations.Fill(this.noneDefault, this.color, this.rectangle); + this.operations.Fill(this.nonDefault, this.color, this.rectangle); FillRegionProcessor processor = this.Verify(); - Assert.Equal(this.noneDefault, processor.Options); + Assert.Equal(this.nonDefault, processor.Options, graphicsOptionsComparer); ShapeRegion region = Assert.IsType(processor.Region); Shapes.RectangularPolygon rect = Assert.IsType(region.Shape); diff --git a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs index da7c865b9..f1a62cf29 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Collections.Generic; @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing } } } - + [Theory] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] @@ -46,7 +46,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing Color.DarkBlue, new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY) ) - .Fill(new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode=composition }, + .Fill( + new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition }, Color.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)) ); @@ -73,12 +74,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); img.Mutate( x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, + new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition }, Color.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))); img.Mutate( x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, + new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition }, Color.Transparent, new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) ); @@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY))); img.Mutate( x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, + new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition }, Color.HotPink, new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY))); @@ -113,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing img.Mutate( x => x.Fill( - new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, + new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition }, transparentRed, new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) ); @@ -130,7 +131,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing PixelAlphaCompositionMode composition) where TPixel : struct, IPixel { - using(Image dstImg = provider.GetImage(), srcImg = provider.GetImage()) + using (Image dstImg = provider.GetImage(), srcImg = provider.GetImage()) { int scaleX = (dstImg.Width / 100); int scaleY = (dstImg.Height / 100); @@ -146,13 +147,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))); dstImg.Mutate( - x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }) - ); + x => x.DrawImage(srcImg, new GraphicsOptions { Antialias = true, ColorBlendingMode = blending, AlphaCompositionMode = composition }) + ); VerifyImage(provider, blending, composition, dstImg); } } - + private static void VerifyImage( TestImageProvider provider, PixelColorBlendingMode blending, @@ -165,13 +166,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing new { composition, blending }, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); - + var comparer = ImageComparer.TolerantPercentage(0.01f, 3); img.CompareFirstFrameToReferenceOutput(comparer, provider, new { composition, blending }, appendPixelTypeToFileName: false, - appendSourceFileOrDescription: false); + appendSourceFileOrDescription: false); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs index e6866c657..2a39e18cb 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text public void FillsForEachACharacterWhenBrushSetAndNotPen() { this.operations.DrawText( - new TextGraphicsOptions(true), + new TextGraphicsOptions { Antialias = true }, "123", this.Font, Brushes.Solid(Color.Red), @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text [Fact] public void FillsForEachACharacterWhenBrushSet() { - this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero); + this.operations.DrawText(new TextGraphicsOptions { Antialias = true }, "123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero); this.Verify(0); } @@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text [Fact] public void FillsForEachACharacterWhenColorSet() { - this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Color.Red, Vector2.Zero); + this.operations.DrawText(new TextGraphicsOptions { Antialias = true }, "123", this.Font, Color.Red, Vector2.Zero); var processor = this.Verify(0); @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text public void DrawForEachACharacterWhenPenSetAndNotBrush() { this.operations.DrawText( - new TextGraphicsOptions(true), + new TextGraphicsOptions { Antialias = true }, "123", this.Font, null, @@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text [Fact] public void DrawForEachACharacterWhenPenSet() { - this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero); + this.operations.DrawText(new TextGraphicsOptions { Antialias = true }, "123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero); this.Verify(0); } @@ -132,7 +132,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text public void DrawForEachACharacterWhenPenSetAndFillFroEachWhenBrushSet() { this.operations.DrawText( - new TextGraphicsOptions(true), + new TextGraphicsOptions { Antialias = true }, "123", this.Font, Brushes.Solid(Color.Red), diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs index a767a686e..281a51650 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs @@ -55,8 +55,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text var scaledFont = new Font(font, scalingFactor * font.Size); var center = new PointF(img.Width / 2, img.Height / 2); - var textGraphicOptions = new TextGraphicsOptions(true) + var textGraphicOptions = new TextGraphicsOptions { + Antialias = true, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center }; @@ -222,7 +223,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text string text = Repeat("Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch!\n", 20); - var textOptions = new TextGraphicsOptions(true) { WrapTextWidth = 1000 }; + var textOptions = new TextGraphicsOptions + { + Antialias = true, + WrapTextWidth = 1000 + }; string details = fontName.Replace(" ", ""); diff --git a/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs b/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs index 0885611c6..a59afb271 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs @@ -1,6 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.Fonts; +using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using Xunit; @@ -9,16 +11,184 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text { public class TextGraphicsOptionsTests { + private readonly TextGraphicsOptions newTextGraphicsOptions = new TextGraphicsOptions(); + private readonly TextGraphicsOptions cloneTextGraphicsOptions = new TextGraphicsOptions().DeepClone(); + + [Fact] + public void CloneTextGraphicsOptionsIsNotNull() => Assert.True(this.cloneTextGraphicsOptions != null); + + [Fact] + public void DefaultTextGraphicsOptionsAntialias() + { + Assert.True(this.newTextGraphicsOptions.Antialias); + Assert.True(this.cloneTextGraphicsOptions.Antialias); + } + + [Fact] + public void DefaultTextGraphicsOptionsAntialiasSuppixelDepth() + { + const int Expected = 16; + Assert.Equal(Expected, this.newTextGraphicsOptions.AntialiasSubpixelDepth); + Assert.Equal(Expected, this.cloneTextGraphicsOptions.AntialiasSubpixelDepth); + } + + [Fact] + public void DefaultTextGraphicsOptionsBlendPercentage() + { + const float Expected = 1F; + Assert.Equal(Expected, this.newTextGraphicsOptions.BlendPercentage); + Assert.Equal(Expected, this.cloneTextGraphicsOptions.BlendPercentage); + } + + [Fact] + public void DefaultTextGraphicsOptionsColorBlendingMode() + { + const PixelColorBlendingMode Expected = PixelColorBlendingMode.Normal; + Assert.Equal(Expected, this.newTextGraphicsOptions.ColorBlendingMode); + Assert.Equal(Expected, this.cloneTextGraphicsOptions.ColorBlendingMode); + } + + [Fact] + public void DefaultTextGraphicsOptionsAlphaCompositionMode() + { + const PixelAlphaCompositionMode Expected = PixelAlphaCompositionMode.SrcOver; + Assert.Equal(Expected, this.newTextGraphicsOptions.AlphaCompositionMode); + Assert.Equal(Expected, this.cloneTextGraphicsOptions.AlphaCompositionMode); + } + + [Fact] + public void DefaultTextGraphicsOptionsApplyKerning() + { + const bool Expected = true; + Assert.Equal(Expected, this.newTextGraphicsOptions.ApplyKerning); + Assert.Equal(Expected, this.cloneTextGraphicsOptions.ApplyKerning); + } + + [Fact] + public void DefaultTextGraphicsOptionsHorizontalAlignment() + { + const HorizontalAlignment Expected = HorizontalAlignment.Left; + Assert.Equal(Expected, this.newTextGraphicsOptions.HorizontalAlignment); + Assert.Equal(Expected, this.cloneTextGraphicsOptions.HorizontalAlignment); + } + + [Fact] + public void DefaultTextGraphicsOptionsVerticalAlignment() + { + const VerticalAlignment Expected = VerticalAlignment.Top; + Assert.Equal(Expected, this.newTextGraphicsOptions.VerticalAlignment); + Assert.Equal(Expected, this.cloneTextGraphicsOptions.VerticalAlignment); + } + + [Fact] + public void DefaultTextGraphicsOptionsDpiX() + { + const float Expected = 72F; + Assert.Equal(Expected, this.newTextGraphicsOptions.DpiX); + Assert.Equal(Expected, this.cloneTextGraphicsOptions.DpiX); + } + + [Fact] + public void DefaultTextGraphicsOptionsDpiY() + { + const float Expected = 72F; + Assert.Equal(Expected, this.newTextGraphicsOptions.DpiY); + Assert.Equal(Expected, this.cloneTextGraphicsOptions.DpiY); + } + + [Fact] + public void DefaultTextGraphicsOptionsTabWidth() + { + const float Expected = 4F; + Assert.Equal(Expected, this.newTextGraphicsOptions.TabWidth); + Assert.Equal(Expected, this.cloneTextGraphicsOptions.TabWidth); + } + + [Fact] + public void DefaultTextGraphicsOptionsWrapTextWidth() + { + const float Expected = 0F; + Assert.Equal(Expected, this.newTextGraphicsOptions.WrapTextWidth); + Assert.Equal(Expected, this.cloneTextGraphicsOptions.WrapTextWidth); + } + + [Fact] + public void NonDefaultClone() + { + var expected = new TextGraphicsOptions + { + AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop, + Antialias = false, + AntialiasSubpixelDepth = 23, + ApplyKerning = false, + BlendPercentage = .25F, + ColorBlendingMode = PixelColorBlendingMode.HardLight, + DpiX = 46F, + DpiY = 52F, + HorizontalAlignment = HorizontalAlignment.Center, + TabWidth = 3F, + VerticalAlignment = VerticalAlignment.Bottom, + WrapTextWidth = 42F + }; + + TextGraphicsOptions actual = expected.DeepClone(); + + Assert.Equal(expected.AlphaCompositionMode, actual.AlphaCompositionMode); + Assert.Equal(expected.Antialias, actual.Antialias); + Assert.Equal(expected.AntialiasSubpixelDepth, actual.AntialiasSubpixelDepth); + Assert.Equal(expected.ApplyKerning, actual.ApplyKerning); + Assert.Equal(expected.BlendPercentage, actual.BlendPercentage); + Assert.Equal(expected.ColorBlendingMode, actual.ColorBlendingMode); + Assert.Equal(expected.DpiX, actual.DpiX); + Assert.Equal(expected.DpiY, actual.DpiY); + Assert.Equal(expected.HorizontalAlignment, actual.HorizontalAlignment); + Assert.Equal(expected.TabWidth, actual.TabWidth); + Assert.Equal(expected.VerticalAlignment, actual.VerticalAlignment); + Assert.Equal(expected.WrapTextWidth, actual.WrapTextWidth); + } + + [Fact] + public void CloneIsDeep() + { + var expected = new TextGraphicsOptions(); + TextGraphicsOptions actual = expected.DeepClone(); + + actual.AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop; + actual.Antialias = false; + actual.AntialiasSubpixelDepth = 23; + actual.ApplyKerning = false; + actual.BlendPercentage = .25F; + actual.ColorBlendingMode = PixelColorBlendingMode.HardLight; + actual.DpiX = 46F; + actual.DpiY = 52F; + actual.HorizontalAlignment = HorizontalAlignment.Center; + actual.TabWidth = 3F; + actual.VerticalAlignment = VerticalAlignment.Bottom; + actual.WrapTextWidth = 42F; + + Assert.NotEqual(expected.AlphaCompositionMode, actual.AlphaCompositionMode); + Assert.NotEqual(expected.Antialias, actual.Antialias); + Assert.NotEqual(expected.AntialiasSubpixelDepth, actual.AntialiasSubpixelDepth); + Assert.NotEqual(expected.ApplyKerning, actual.ApplyKerning); + Assert.NotEqual(expected.BlendPercentage, actual.BlendPercentage); + Assert.NotEqual(expected.ColorBlendingMode, actual.ColorBlendingMode); + Assert.NotEqual(expected.DpiX, actual.DpiX); + Assert.NotEqual(expected.DpiY, actual.DpiY); + Assert.NotEqual(expected.HorizontalAlignment, actual.HorizontalAlignment); + Assert.NotEqual(expected.TabWidth, actual.TabWidth); + Assert.NotEqual(expected.VerticalAlignment, actual.VerticalAlignment); + Assert.NotEqual(expected.WrapTextWidth, actual.WrapTextWidth); + } + [Fact] public void ExplicitCastOfGraphicsOptions() { - var opt = new GraphicsOptions(false) + TextGraphicsOptions textOptions = new GraphicsOptions { + Antialias = false, AntialiasSubpixelDepth = 99 }; - TextGraphicsOptions textOptions = opt; - Assert.False(textOptions.Antialias); Assert.Equal(99, textOptions.AntialiasSubpixelDepth); } @@ -26,8 +196,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text [Fact] public void ImplicitCastToGraphicsOptions() { - var textOptions = new TextGraphicsOptions(false) + var textOptions = new TextGraphicsOptions { + Antialias = false, AntialiasSubpixelDepth = 99 }; @@ -37,4 +208,4 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text Assert.Equal(99, opt.AntialiasSubpixelDepth); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/GraphicsOptionsTests.cs b/tests/ImageSharp.Tests/GraphicsOptionsTests.cs index 6ff38626d..69f904f1c 100644 --- a/tests/ImageSharp.Tests/GraphicsOptionsTests.cs +++ b/tests/ImageSharp.Tests/GraphicsOptionsTests.cs @@ -1,21 +1,100 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; namespace SixLabors.ImageSharp.Tests { public class GraphicsOptionsTests { + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + private readonly GraphicsOptions newGraphicsOptions = new GraphicsOptions(); + private readonly GraphicsOptions cloneGraphicsOptions = new GraphicsOptions().DeepClone(); + + [Fact] + public void CloneGraphicsOptionsIsNotNull() => Assert.True(this.cloneGraphicsOptions != null); + + [Fact] + public void DefaultGraphicsOptionsAntialias() + { + Assert.True(this.newGraphicsOptions.Antialias); + Assert.True(this.cloneGraphicsOptions.Antialias); + } + + [Fact] + public void DefaultGraphicsOptionsAntialiasSuppixelDepth() + { + const int Expected = 16; + Assert.Equal(Expected, this.newGraphicsOptions.AntialiasSubpixelDepth); + Assert.Equal(Expected, this.cloneGraphicsOptions.AntialiasSubpixelDepth); + } + + [Fact] + public void DefaultGraphicsOptionsBlendPercentage() + { + const float Expected = 1F; + Assert.Equal(Expected, this.newGraphicsOptions.BlendPercentage); + Assert.Equal(Expected, this.cloneGraphicsOptions.BlendPercentage); + } + + [Fact] + public void DefaultGraphicsOptionsColorBlendingMode() + { + const PixelColorBlendingMode Expected = PixelColorBlendingMode.Normal; + Assert.Equal(Expected, this.newGraphicsOptions.ColorBlendingMode); + Assert.Equal(Expected, this.cloneGraphicsOptions.ColorBlendingMode); + } + + [Fact] + public void DefaultGraphicsOptionsAlphaCompositionMode() + { + const PixelAlphaCompositionMode Expected = PixelAlphaCompositionMode.SrcOver; + Assert.Equal(Expected, this.newGraphicsOptions.AlphaCompositionMode); + Assert.Equal(Expected, this.cloneGraphicsOptions.AlphaCompositionMode); + } + + [Fact] + public void NonDefaultClone() + { + var expected = new GraphicsOptions + { + AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop, + Antialias = false, + AntialiasSubpixelDepth = 23, + BlendPercentage = .25F, + ColorBlendingMode = PixelColorBlendingMode.HardLight, + }; + + GraphicsOptions actual = expected.DeepClone(); + + Assert.Equal(expected, actual, graphicsOptionsComparer); + } + + [Fact] + public void CloneIsDeep() + { + var expected = new GraphicsOptions(); + GraphicsOptions actual = expected.DeepClone(); + + actual.AlphaCompositionMode = PixelAlphaCompositionMode.DestAtop; + actual.Antialias = false; + actual.AntialiasSubpixelDepth = 23; + actual.BlendPercentage = .25F; + actual.ColorBlendingMode = PixelColorBlendingMode.HardLight; + + Assert.NotEqual(expected, actual, graphicsOptionsComparer); + } + [Fact] public void IsOpaqueColor() { - Assert.True(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(Rgba32.Red)); - Assert.False(new GraphicsOptions(true, 0.5f).IsOpaqueColorWithoutBlending(Rgba32.Red)); - Assert.False(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(Rgba32.Transparent)); - Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Lighten, 1).IsOpaqueColorWithoutBlending(Rgba32.Red)); - Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.DestOver, 1).IsOpaqueColorWithoutBlending(Rgba32.Red)); + Assert.True(new GraphicsOptions().IsOpaqueColorWithoutBlending(Rgba32.Red)); + Assert.False(new GraphicsOptions { BlendPercentage = .5F }.IsOpaqueColorWithoutBlending(Rgba32.Red)); + Assert.False(new GraphicsOptions().IsOpaqueColorWithoutBlending(Rgba32.Transparent)); + Assert.False(new GraphicsOptions { ColorBlendingMode = PixelColorBlendingMode.Lighten, BlendPercentage = 1F }.IsOpaqueColorWithoutBlending(Rgba32.Red)); + Assert.False(new GraphicsOptions { ColorBlendingMode = PixelColorBlendingMode.Normal, AlphaCompositionMode = PixelAlphaCompositionMode.DestOver, BlendPercentage = 1f }.IsOpaqueColorWithoutBlending(Rgba32.Red)); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Issues/Issue412.cs b/tests/ImageSharp.Tests/Issues/Issue412.cs index 5a590018e..53c65b643 100644 --- a/tests/ImageSharp.Tests/Issues/Issue412.cs +++ b/tests/ImageSharp.Tests/Issues/Issue412.cs @@ -20,14 +20,14 @@ namespace SixLabors.ImageSharp.Tests.Issues for (var i = 0; i < 40; ++i) { context.DrawLines( - new GraphicsOptions(false), + new GraphicsOptions { Antialias = false }, Color.Black, 1, new PointF(i, 0.1066f), new PointF(i, 10.1066f)); context.DrawLines( - new GraphicsOptions(false), + new GraphicsOptions { Antialias = false }, Color.Red, 1, new PointF(i, 15.1066f), diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs index 09a78a6aa..693dd6bd8 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders @@ -36,9 +36,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders using (Image src = srcFile.CreateRgba32Image()) using (Image dest = provider.GetImage()) { - GraphicsOptions options = new GraphicsOptions - { - AlphaCompositionMode = mode + var options = new GraphicsOptions + { + Antialias = false, + AlphaCompositionMode = mode }; using (Image res = dest.Clone(x => x.DrawImage(src, options))) @@ -53,4 +54,4 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs index 140af9563..cfac8645f 100644 --- a/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs +++ b/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Processing public BaseImageOperationsExtensionTest() { - this.options = new GraphicsOptions(false); + this.options = new GraphicsOptions { Antialias = false }; this.source = new Image(91 + 324, 123 + 56); this.rect = new Rectangle(91, 123, 324, 56); // make this random? this.internalOperations = new FakeImageOperationsProvider.FakeImageOperations(this.source, false); diff --git a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs index 1b5bd656d..a137a9f43 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs @@ -1,22 +1,24 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Overlays; - +using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Effects { public class BackgroundColorTest : BaseImageOperationsExtensionTest { + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + [Fact] public void BackgroundColor_amount_BackgroundColorProcessorDefaultsSet() { this.operations.BackgroundColor(Color.BlanchedAlmond); - var processor = this.Verify(); + BackgroundColorProcessor processor = this.Verify(); - Assert.Equal(GraphicsOptions.Default, processor.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), processor.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.BlanchedAlmond, processor.Color); } @@ -24,9 +26,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void BackgroundColor_amount_rect_BackgroundColorProcessorDefaultsSet() { this.operations.BackgroundColor(Color.BlanchedAlmond, this.rect); - var processor = this.Verify(this.rect); + BackgroundColorProcessor processor = this.Verify(this.rect); - Assert.Equal(GraphicsOptions.Default, processor.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), processor.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.BlanchedAlmond, processor.Color); } @@ -34,9 +36,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void BackgroundColor_amount_options_BackgroundColorProcessorDefaultsSet() { this.operations.BackgroundColor(this.options, Color.BlanchedAlmond); - var processor = this.Verify(); + BackgroundColorProcessor processor = this.Verify(); - Assert.Equal(this.options, processor.GraphicsOptions); + Assert.Equal(this.options, processor.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.BlanchedAlmond, processor.Color); } @@ -44,10 +46,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void BackgroundColor_amount_rect_options_BackgroundColorProcessorDefaultsSet() { this.operations.BackgroundColor(this.options, Color.BlanchedAlmond, this.rect); - var processor = this.Verify(this.rect); + BackgroundColorProcessor processor = this.Verify(this.rect); - Assert.Equal(this.options, processor.GraphicsOptions); + Assert.Equal(this.options, processor.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.BlanchedAlmond, processor.Color); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs index 978fd416b..32c4c6fe7 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs @@ -1,10 +1,11 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Overlays; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.Primitives; using Xunit; @@ -12,13 +13,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays { public class GlowTest : BaseImageOperationsExtensionTest { + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + [Fact] public void Glow_GlowProcessorWithDefaultValues() { this.operations.Glow(); - var p = this.Verify(); + GlowProcessor p = this.Verify(); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Black, p.GlowColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } @@ -27,9 +30,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Glow_Color_GlowProcessorWithDefaultValues() { this.operations.Glow(Rgba32.Aquamarine); - var p = this.Verify(); + GlowProcessor p = this.Verify(); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Aquamarine, p.GlowColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } @@ -38,9 +41,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Glow_Radux_GlowProcessorWithDefaultValues() { this.operations.Glow(3.5f); - var p = this.Verify(); + GlowProcessor p = this.Verify(); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Black, p.GlowColor); Assert.Equal(ValueSize.Absolute(3.5f), p.Radius); } @@ -50,11 +53,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays { var rect = new Rectangle(12, 123, 43, 65); this.operations.Glow(rect); - var p = this.Verify(rect); + GlowProcessor p = this.Verify(rect); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Black, p.GlowColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs index 2484cf0cb..ebf4fee31 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs @@ -1,9 +1,10 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Overlays; +using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.Primitives; using Xunit; @@ -11,13 +12,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays { public class VignetteTest : BaseImageOperationsExtensionTest { + private static readonly GraphicsOptionsComparer graphicsOptionsComparer = new GraphicsOptionsComparer(); + [Fact] public void Vignette_VignetteProcessorWithDefaultValues() { this.operations.Vignette(); - var p = this.Verify(); + VignetteProcessor p = this.Verify(); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Black, p.VignetteColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); @@ -27,9 +30,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Vignette_Color_VignetteProcessorWithDefaultValues() { this.operations.Vignette(Color.Aquamarine); - var p = this.Verify(); + VignetteProcessor p = this.Verify(); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Aquamarine, p.VignetteColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); @@ -39,9 +42,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays public void Vignette_Radux_VignetteProcessorWithDefaultValues() { this.operations.Vignette(3.5f, 12123f); - var p = this.Verify(); + VignetteProcessor p = this.Verify(); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Black, p.VignetteColor); Assert.Equal(ValueSize.Absolute(3.5f), p.RadiusX); Assert.Equal(ValueSize.Absolute(12123f), p.RadiusY); @@ -52,12 +55,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Overlays { var rect = new Rectangle(12, 123, 43, 65); this.operations.Vignette(rect); - var p = this.Verify(rect); + VignetteProcessor p = this.Verify(rect); - Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(new GraphicsOptions(), p.GraphicsOptions, graphicsOptionsComparer); Assert.Equal(Color.Black, p.VignetteColor); Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs b/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs new file mode 100644 index 000000000..248755ea3 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/GraphicsOptionsComparer.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; + +namespace SixLabors.ImageSharp.Tests.TestUtilities +{ + public class GraphicsOptionsComparer : IEqualityComparer + { + public bool Equals(GraphicsOptions x, GraphicsOptions y) + { + return x.AlphaCompositionMode == y.AlphaCompositionMode + && x.Antialias == y.Antialias + && x.AntialiasSubpixelDepth == y.AntialiasSubpixelDepth + && x.BlendPercentage == y.BlendPercentage + && x.ColorBlendingMode == y.ColorBlendingMode; + } + + public int GetHashCode(GraphicsOptions obj) => obj.GetHashCode(); + } +}