diff --git a/.editorconfig b/.editorconfig index 8f0e28eec6..b0d0662bf8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -79,6 +79,7 @@ dotnet_naming_rule.private_and_internal_fields_should_be_camel_case.symbols = pr dotnet_naming_rule.public_and_protected_declarations_should_be_pascal_case.severity = suggestion dotnet_naming_rule.public_and_protected_declarations_should_be_pascal_case.style = pascal_case dotnet_naming_rule.public_and_protected_declarations_should_be_pascal_case.symbols = public_and_protected_declarations +dotnet_naming_symbols.public_and_protected_declarations.applicable_kinds = method, field, event, property dotnet_naming_rule.static_readonly_declarations_should_be_pascal_case.severity = suggestion dotnet_naming_rule.static_readonly_declarations_should_be_pascal_case.style = pascal_case @@ -322,11 +323,11 @@ csharp_space_between_square_brackets = false # suggest conditional delegate calls, # suggest deconstructed variable declarations, # generate expression-bodied accessors, -# don't generate expression-bodied constructors, +# generate expression-bodied constructors, # generate expression-bodied indexers, # generate expression-bodied lambdas, -# don't generate expression-bodied methods, -# don't generate expression-bodied operators, +# generate expression-bodied methods, +# generate expression-bodied operators, # generate expression-bodied properties, # suggest inlined variable declarations, # suggest local over anonymous functions, @@ -348,11 +349,11 @@ csharp_style_conditional_delegate_call = true:suggestion csharp_style_deconstructed_variable_declaration = true:suggestion csharp_style_expression_bodied_accessors = true:silent -csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_constructors = true:silent csharp_style_expression_bodied_indexers = true:silent csharp_style_expression_bodied_lambdas = true:silent -csharp_style_expression_bodied_methods = false:silent -csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_methods = true:silent +csharp_style_expression_bodied_operators = true:silent csharp_style_expression_bodied_properties = true:silent csharp_style_inlined_variable_declaration = true:suggestion diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 01c09d2231..d45d98b393 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -29,6 +29,10 @@ * Ask any question about how to use ImageSharp in the [ImageSharp Gitter Chat Room](https://gitter.im/ImageSharp/General). +#### Code of Conduct +This project has adopted the code of conduct defined by the [Contributor Covenant](https://contributor-covenant.org/) to clarify expected behavior in our community. +For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). + And please remember. ImageSharp is the work of a very, very, small number of developers who struggle balancing time to contribute to the project with family time and work commitments. We encourage you to pitch in and help make our vision of simple accessible imageprocessing available to all. Open Source can only exist with your help. Thanks for reading! diff --git a/.gitmodules b/.gitmodules index 37ef701cdf..55389121f2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,6 +2,6 @@ path = tests/Images/External url = https://github.com/SixLabors/Imagesharp.Tests.Images.git branch = master -[submodule "standards"] - path = standards - url = https://github.com/SixLabors/Standards +[submodule "shared-infrastructure"] + path = shared-infrastructure + url = https://github.com/SixLabors/SharedInfrastructure diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..b34bbb41a3 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Code of Conduct +This project has adopted the code of conduct defined by the [Contributor Covenant](https://contributor-covenant.org/) to clarify expected behavior in our community. +For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index bf004921ea..efe4cc9665 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -52,7 +52,7 @@ - $(MSBuildThisFileDirectory)standards/SixLabors.snk + $(MSBuildThisFileDirectory)shared-infrastructure/SixLabors.snk Copyright © Six Labors and Contributors strict;IOperation true diff --git a/Directory.Build.targets b/Directory.Build.targets index d1183e5d46..1dc081782a 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -25,13 +25,13 @@ - + - - - - + + + + diff --git a/ImageSharp.sln b/ImageSharp.sln index 1fd5e2d8b4..d4a0419eed 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -21,7 +21,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt LICENSE = LICENSE README.md = README.md run-tests.ps1 = run-tests.ps1 - stylecop.json = stylecop.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{1799C43E-5C54-4A8F-8D64-B1475241DB0D}" diff --git a/README.md b/README.md index 078219183e..28c3770373 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,10 @@ The **ImageSharp** library is made up of multiple packages: - Do you have questions? We are happy to help! Please [join our gitter channel](https://gitter.im/ImageSharp/General), or ask them on [stackoverflow](https://stackoverflow.com) using the `ImageSharp` tag. **Do not** open issues for questions! - Please read our [Contribution Guide](https://github.com/SixLabors/ImageSharp/blob/master/.github/CONTRIBUTING.md) before opening issues or pull requests! +### Code of Conduct +This project has adopted the code of conduct defined by the [Contributor Covenant](https://contributor-covenant.org/) to clarify expected behavior in our community. +For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). + ### API Our API is designed to be simple to consume. Here's an example of the code required to resize an image using the default Bicubic resampler then turn the colors into their grayscale equivalent using the BT709 standard matrix. diff --git a/shared-infrastructure b/shared-infrastructure new file mode 160000 index 0000000000..faf84e44ec --- /dev/null +++ b/shared-infrastructure @@ -0,0 +1 @@ +Subproject commit faf84e44ec90e8a42a7271bcd04fea76279efb08 diff --git a/src/Directory.Build.props b/src/Directory.Build.props index cd3d5e8cb3..6fbbb7c916 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -18,12 +18,12 @@ - $(MSBuildThisFileDirectory)..\standards\SixLabors.ruleset + $(MSBuildThisFileDirectory)..\shared-infrastructure\SixLabors.ruleset true - + diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index a092e3604b..5a53d3e78b 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -1,4 +1,4 @@ - + @@ -11,6 +11,15 @@ netcoreapp2.1;netstandard1.3;netstandard2.0 + + + $(DefineConstants);SUPPORTS_MATHF + + + + $(DefineConstants);SUPPORTS_HASHCODE + + diff --git a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs new file mode 100644 index 0000000000..7315dc5a3e --- /dev/null +++ b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs @@ -0,0 +1,287 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SixLabors.Shapes; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Provides an implementation of a brush for painting gradients between multiple color positions in 2D coordinates. + /// It works similarly with the class in System.Drawing.Drawing2D of the same name. + /// + public sealed class PathGradientBrush : IBrush + { + private readonly IList edges; + + private readonly Color centerColor; + + /// + /// Initializes a new instance of the class. + /// + /// Points that constitute a polygon that represents the gradient area. + /// Array of colors that correspond to each point in the polygon. + /// Color at the center of the gradient area to which the other colors converge. + public PathGradientBrush(PointF[] points, Color[] colors, Color centerColor) + { + if (points == null) + { + throw new ArgumentNullException(nameof(points)); + } + + if (points.Length < 3) + { + throw new ArgumentOutOfRangeException( + nameof(points), + "There must be at least 3 lines to construct a path gradient brush."); + } + + if (colors == null) + { + throw new ArgumentNullException(nameof(colors)); + } + + if (!colors.Any()) + { + throw new ArgumentOutOfRangeException( + nameof(colors), + "One or more color is needed to construct a path gradient brush."); + } + + int size = points.Length; + + var lines = new ILineSegment[size]; + + for (int i = 0; i < size; i++) + { + lines[i] = new LinearLineSegment(points[i % size], points[(i + 1) % size]); + } + + this.centerColor = centerColor; + + Color ColorAt(int index) => colors[index % colors.Length]; + + this.edges = lines.Select(s => new Path(s)) + .Select((path, i) => new Edge(path, ColorAt(i), ColorAt(i + 1))).ToList(); + } + + /// + /// Initializes a new instance of the class. + /// + /// Points that constitute a polygon that represents the gradient area. + /// Array of colors that correspond to each point in the polygon. + public PathGradientBrush(PointF[] points, Color[] colors) + : this(points, colors, CalculateCenterColor(colors)) + { + } + + /// + public BrushApplicator CreateApplicator( + ImageFrame source, + RectangleF region, + GraphicsOptions options) + where TPixel : struct, IPixel + { + return new PathGradientBrushApplicator(source, this.edges, this.centerColor, options); + } + + private static Color CalculateCenterColor(Color[] colors) + { + if (colors == null) + { + throw new ArgumentNullException(nameof(colors)); + } + + if (!colors.Any()) + { + throw new ArgumentOutOfRangeException( + nameof(colors), + "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); + } + + private static float DistanceBetween(PointF p1, PointF p2) => ((Vector2)(p2 - p1)).Length(); + + private struct Intersection + { + public Intersection(PointF point, float distance) + { + this.Point = point; + this.Distance = distance; + } + + public PointF Point { get; } + + public float Distance { get; } + } + + /// + /// An edge of the polygon that represents the gradient area. + /// + private class Edge + { + private readonly Path path; + + private readonly float length; + + private readonly PointF[] buffer; + + public Edge(Path path, Color startColor, Color endColor) + { + this.path = path; + + Vector2[] points = path.LineSegments.SelectMany(s => s.Flatten()).Select(p => (Vector2)p).ToArray(); + + this.Start = points.First(); + this.StartColor = startColor.ToVector4(); + + this.End = points.Last(); + this.EndColor = endColor.ToVector4(); + + this.length = DistanceBetween(this.End, this.Start); + this.buffer = new PointF[this.path.MaxIntersections]; + } + + public PointF Start { get; } + + public Vector4 StartColor { get; } + + public PointF End { get; } + + public Vector4 EndColor { get; } + + public Intersection? FindIntersection(PointF start, PointF end) + { + int intersections = this.path.FindIntersections(start, end, this.buffer); + + if (intersections == 0) + { + return null; + } + + return this.buffer.Take(intersections) + .Select(p => new Intersection(point: p, distance: ((Vector2)(p - start)).LengthSquared())) + .Aggregate((min, current) => min.Distance > current.Distance ? current : min); + } + + public Vector4 ColorAt(float distance) + { + float ratio = this.length > 0 ? distance / this.length : 0; + + return Vector4.Lerp(this.StartColor, this.EndColor, ratio); + } + + public Vector4 ColorAt(PointF point) => this.ColorAt(DistanceBetween(point, this.Start)); + } + + /// + /// The path gradient brush applicator. + /// + private class PathGradientBrushApplicator : BrushApplicator + where TPixel : struct, IPixel + { + private readonly PointF center; + + private readonly Vector4 centerColor; + + private readonly float maxDistance; + + private readonly IList edges; + + /// + /// Initializes a new instance of the class. + /// + /// 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( + ImageFrame source, + IList edges, + Color centerColor, + GraphicsOptions options) + : base(source, options) + { + 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.maxDistance = points.Select(p => (Vector2)(p - this.center)).Select(d => d.Length()).Max(); + } + + /// + internal override TPixel this[int x, int y] + { + get + { + var point = new PointF(x, y); + + if (point == this.center) + { + return new Color(this.centerColor).ToPixel(); + } + + Vector2 direction = Vector2.Normalize(point - this.center); + + PointF end = point + (PointF)(direction * this.maxDistance); + + (Edge edge, Intersection? info) = this.FindIntersection(point, end); + + if (!info.HasValue) + { + return Color.Transparent.ToPixel(); + } + + PointF intersection = info.Value.Point; + + Vector4 edgeColor = edge.ColorAt(intersection); + + float length = DistanceBetween(intersection, this.center); + float ratio = length > 0 ? DistanceBetween(intersection, point) / length : 0; + + Vector4 color = Vector4.Lerp(edgeColor, this.centerColor, ratio); + + return new Color(color).ToPixel(); + } + } + + private (Edge edge, Intersection? info) FindIntersection(PointF start, PointF end) + { + (Edge edge, Intersection? info) closest = default; + + foreach (Edge edge in this.edges) + { + Intersection? intersection = edge.FindIntersection(start, end); + + if (!intersection.HasValue) + { + continue; + } + + if (closest.info == null || closest.info.Value.Distance > intersection.Value.Distance) + { + closest = (edge, intersection); + } + } + + return closest; + } + + /// + public override void Dispose() + { + } + } + } +} diff --git a/src/ImageSharp.Drawing/Processing/PatternBrush.cs b/src/ImageSharp.Drawing/Processing/PatternBrush.cs index a7a6785b92..1999af8a39 100644 --- a/src/ImageSharp.Drawing/Processing/PatternBrush.cs +++ b/src/ImageSharp.Drawing/Processing/PatternBrush.cs @@ -99,7 +99,6 @@ namespace SixLabors.ImageSharp.Processing new PatternBrushApplicator( source, this.pattern.ToPixelMatrix(source.Configuration), - this.patternVector, options); /// @@ -112,20 +111,17 @@ namespace SixLabors.ImageSharp.Processing /// The pattern. /// private readonly DenseMatrix pattern; - private readonly DenseMatrix patternVector; /// /// Initializes a new instance of the class. /// /// The source image. /// The pattern. - /// The patternVector. /// The options - public PatternBrushApplicator(ImageFrame source, in DenseMatrix pattern, DenseMatrix patternVector, GraphicsOptions options) + public PatternBrushApplicator(ImageFrame source, in DenseMatrix pattern, GraphicsOptions options) : base(source, options) { this.pattern = pattern; - this.patternVector = patternVector; } /// diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs index 7ec359220e..e217fd9a6c 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -1,6 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -59,10 +60,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing public float Opacity { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixelBg : struct, IPixel { - var visitor = new ProcessorFactoryVisitor(this); + var visitor = new ProcessorFactoryVisitor(this, source, sourceRectangle); this.Image.AcceptVisitor(visitor); return visitor.Result; } @@ -71,10 +72,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing where TPixelBg : struct, IPixel { private readonly DrawImageProcessor definition; + private readonly Image source; + private readonly Rectangle sourceRectangle; - public ProcessorFactoryVisitor(DrawImageProcessor definition) + public ProcessorFactoryVisitor(DrawImageProcessor definition, Image source, Rectangle sourceRectangle) { this.definition = definition; + this.source = source; + this.sourceRectangle = sourceRectangle; } public IImageProcessor Result { get; private set; } @@ -84,6 +89,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing { this.Result = new DrawImageProcessor( image, + this.source, + this.sourceRectangle, this.definition.Location, this.definition.ColorBlendingMode, this.definition.AlphaCompositionMode, @@ -91,4 +98,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index 21599bf787..eab6b2f4a1 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -4,7 +4,7 @@ using System; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -20,19 +20,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing where TPixelFg : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The image to blend with the currently processing image. + /// The foreground to blend with the currently processing image. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. /// The location to draw the blended image. /// The blending mode to use when drawing the image. /// The Alpha blending mode to use when drawing the image. /// The opacity of the image to blend. Must be between 0 and 1. public DrawImageProcessor( Image image, + Image source, + Rectangle sourceRectangle, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity) + : base(source, sourceRectangle) { Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); @@ -63,11 +68,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing public Point Location { get; } /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { + Rectangle sourceRectangle = this.SourceRectangle; + Configuration configuration = this.Configuration; + Image targetImage = this.Image; PixelBlender blender = this.Blender; int locationY = this.Location.Y; @@ -86,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); - // not a valid operation because rectangle does not overlap with this image. + // Not a valid operation because rectangle does not overlap with this image. if (workingRect.Width <= 0 || workingRect.Height <= 0) { throw new ImageProcessingException( @@ -97,15 +102,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing workingRect, configuration, rows => + { + for (int y = rows.Min; y < rows.Max; y++) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span background = source.GetPixelRowSpan(y).Slice(minX, width); - Span foreground = - targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); - blender.Blend(configuration, background, background, foreground, this.Opacity); - } - }); + Span background = source.GetPixelRowSpan(y).Slice(minX, width); + Span foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); + blender.Blend(configuration, background, background, foreground, this.Opacity); + } + }); } } -} \ 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 81880308cf..1d3cf35576 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Drawing { @@ -33,10 +34,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing public GraphicsOptions Options { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new FillProcessor(this); + return new FillProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs index 68aef82e2d..4e052818da 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs @@ -5,8 +5,8 @@ using System; using System.Buffers; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -21,14 +21,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing { private readonly FillProcessor definition; - public FillProcessor(FillProcessor definition) + public FillProcessor(FillProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { + Rectangle sourceRectangle = this.SourceRectangle; + Configuration configuration = this.Configuration; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int startY = sourceRectangle.Y; @@ -42,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing int width = maxX - minX; - var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + Rectangle workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); IBrush brush = this.definition.Brush; GraphicsOptions options = this.definition.Options; @@ -50,9 +53,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing // If there's no reason for blending, then avoid it. if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush)) { - ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); + ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration) + .MultiplyMinimumPixelsPerTask(4); - TPixel colorPixel = solidBrush.Color.ToPixel(); + var colorPixel = solidBrush.Color.ToPixel(); ParallelHelper.IterateRows( workingRect, @@ -115,4 +119,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs index 000fa260ab..2318f3168b 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs @@ -1,8 +1,9 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Drawing { @@ -41,10 +42,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing public GraphicsOptions Options { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new FillRegionProcessor(this); + return new FillRegionProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs index 347d243ae7..45d5015ae0 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs @@ -23,14 +23,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing { private readonly FillRegionProcessor definition; - public FillRegionProcessor(FillRegionProcessor definition) + public FillRegionProcessor(FillRegionProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { + Configuration configuration = this.Configuration; GraphicsOptions options = this.definition.Options; IBrush brush = this.definition.Brush; Region region = this.definition.Region; diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs index 40621ce997..775cf55abf 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.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; @@ -72,10 +72,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text public PointF Location { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new DrawTextProcessor(this); + return new DrawTextProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs index b3c336c885..ea042635dd 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs @@ -27,7 +27,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text private readonly DrawTextProcessor definition; - public DrawTextProcessor(DrawTextProcessor definition) + public DrawTextProcessor(DrawTextProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } @@ -44,35 +45,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text private IBrush Brush => this.definition.Brush; - protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + protected override void BeforeImageApply() { - base.BeforeImageApply(source, sourceRectangle); + base.BeforeImageApply(); // do everything at the image level as we are delegating the processing down to other processors var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location) - { - ApplyKerning = this.Options.ApplyKerning, - TabWidth = this.Options.TabWidth, - WrappingWidth = this.Options.WrapTextWidth, - HorizontalAlignment = this.Options.HorizontalAlignment, - VerticalAlignment = this.Options.VerticalAlignment - }; - - this.textRenderer = new CachingGlyphRenderer(source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null); + { + ApplyKerning = this.Options.ApplyKerning, + TabWidth = this.Options.TabWidth, + WrappingWidth = this.Options.WrapTextWidth, + HorizontalAlignment = this.Options.HorizontalAlignment, + VerticalAlignment = this.Options.VerticalAlignment + }; + + this.textRenderer = new CachingGlyphRenderer(this.Source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null); this.textRenderer.Options = (GraphicsOptions)this.Options; var renderer = new TextRenderer(this.textRenderer); renderer.RenderText(this.Text, style); } - protected override void AfterImageApply(Image source, Rectangle sourceRectangle) + protected override void AfterImageApply() { - base.AfterImageApply(source, sourceRectangle); + base.AfterImageApply(); this.textRenderer?.Dispose(); this.textRenderer = null; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { // this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome Draw(this.textRenderer.FillOperations, this.Brush); @@ -82,33 +83,38 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text { if (operations?.Count > 0) { - using (BrushApplicator app = brush.CreateApplicator(source, sourceRectangle, this.textRenderer.Options)) + using (BrushApplicator app = brush.CreateApplicator(source, this.SourceRectangle, this.textRenderer.Options)) { foreach (DrawingOperation operation in operations) { Buffer2D buffer = operation.Map; int startY = operation.Location.Y; int startX = operation.Location.X; - int offSetSpan = 0; + int offsetSpan = 0; if (startX < 0) { - offSetSpan = -startX; + offsetSpan = -startX; startX = 0; } - int fistRow = 0; + if (startX >= source.Width) + { + continue; + } + + int firstRow = 0; if (startY < 0) { - fistRow = -startY; + firstRow = -startY; } int maxHeight = source.Height - startY; int end = Math.Min(operation.Map.Height, maxHeight); - for (int row = fistRow; row < end; row++) + for (int row = firstRow; row < end; row++) { int y = startY + row; - Span span = buffer.GetRowSpan(row).Slice(offSetSpan); + Span span = buffer.GetRowSpan(row).Slice(offsetSpan); app.Apply(span, startX, y); } } @@ -279,19 +285,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text if (this.renderFill) { this.FillOperations.Add(new DrawingOperation - { - Location = this.currentRenderPosition, - Map = renderData.FillMap - }); + { + Location = this.currentRenderPosition, + Map = renderData.FillMap + }); } if (this.renderOutline) { this.OutlineOperations.Add(new DrawingOperation - { - Location = this.currentRenderPosition, - Map = renderData.OutlineMap - }); + { + Location = this.currentRenderPosition, + Map = renderData.OutlineMap + }); } } diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index 4b1d4222cb..22e6d47e9a 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -15,6 +15,15 @@ namespace SixLabors.ImageSharp.Advanced /// public static class AdvancedImageExtensions { + /// + /// Accepts a to implement a double-dispatch pattern in order to + /// apply pixel-specific operations on non-generic instances + /// + /// The source. + /// The visitor. + public static void AcceptVisitor(this Image source, IImageVisitor visitor) + => source.Accept(visitor); + /// /// Gets the configuration for the image. /// diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 439ea6de0c..5d172d93f7 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -2,13 +2,12 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Processing.Processors.Quantization; -using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Advanced { @@ -81,9 +80,8 @@ namespace SixLabors.ImageSharp.Advanced AotCompileWuQuantizer(); AotCompileDithering(); AotCompilePixelOperations(); - AotCompileResizeOperations(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); + Unsafe.SizeOf(); AotCodec(new Formats.Png.PngDecoder(), new Formats.Png.PngEncoder()); AotCodec(new Formats.Bmp.BmpDecoder(), new Formats.Bmp.BmpEncoder()); @@ -107,8 +105,10 @@ namespace SixLabors.ImageSharp.Advanced private static void AotCompileOctreeQuantizer() where TPixel : struct, IPixel { - var test = new OctreeFrameQuantizer(new OctreeQuantizer(false)); - test.AotGetPalette(); + using (var test = new OctreeFrameQuantizer(new OctreeQuantizer(false))) + { + test.AotGetPalette(); + } } /// @@ -118,9 +118,11 @@ namespace SixLabors.ImageSharp.Advanced private static void AotCompileWuQuantizer() where TPixel : struct, IPixel { - var test = new WuFrameQuantizer(Configuration.Default.MemoryAllocator, new WuQuantizer(false)); - test.QuantizeFrame(new ImageFrame(Configuration.Default, 1, 1)); - test.AotGetPalette(); + using (var test = new WuFrameQuantizer(Configuration.Default.MemoryAllocator, new WuQuantizer(false))) + { + test.QuantizeFrame(new ImageFrame(Configuration.Default, 1, 1)); + test.AotGetPalette(); + } } /// @@ -132,7 +134,10 @@ namespace SixLabors.ImageSharp.Advanced { var test = new FloydSteinbergDiffuser(); TPixel pixel = default; - test.Dither(new ImageFrame(Configuration.Default, 1, 1), pixel, pixel, 0, 0, 0, 0, 0, 0); + using (var image = new ImageFrame(Configuration.Default, 1, 1)) + { + test.Dither(image, pixel, pixel, 0, 0, 0, 0, 0); + } } /// @@ -171,17 +176,5 @@ namespace SixLabors.ImageSharp.Advanced var pixelOp = new PixelOperations(); pixelOp.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Clear); } - - /// - /// This method pre-seeds the ResizeProcessor for the AoT compiler on iOS. - /// - /// The pixel format. - private static void AotCompileResizeOperations() - where TPixel : struct, IPixel - { - var resizeProcessor = new ResizeProcessor(new ResizeOptions(), default); - var genericResizeProcessor = new ResizeProcessor((ResizeProcessor)resizeProcessor.CreatePixelSpecificProcessor()); - genericResizeProcessor.AotCreateDestination(new Image(0, 0), default); - } } } diff --git a/src/ImageSharp/IImageVisitor.cs b/src/ImageSharp/Advanced/IImageVisitor.cs similarity index 64% rename from src/ImageSharp/IImageVisitor.cs rename to src/ImageSharp/Advanced/IImageVisitor.cs index 971c4d37cb..ba8b13e2e8 100644 --- a/src/ImageSharp/IImageVisitor.cs +++ b/src/ImageSharp/Advanced/IImageVisitor.cs @@ -3,13 +3,13 @@ using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp +namespace SixLabors.ImageSharp.Advanced { /// - /// A visitor to implement double-dispatch pattern in order to apply pixel-specific operations - /// on non-generic instances. The operation is dispatched by . + /// A visitor to implement a double-dispatch pattern in order to apply pixel-specific operations + /// on non-generic instances. /// - internal interface IImageVisitor + public interface IImageVisitor { /// /// Provides a pixel-specific implementation for a given operation. @@ -19,4 +19,4 @@ namespace SixLabors.ImageSharp void Visit(Image image) where TPixel : struct, IPixel; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs b/src/ImageSharp/Advanced/ParallelUtils/ParallelExecutionSettings.cs similarity index 55% rename from src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs rename to src/ImageSharp/Advanced/ParallelUtils/ParallelExecutionSettings.cs index 40163bc789..431656ef91 100644 --- a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs +++ b/src/ImageSharp/Advanced/ParallelUtils/ParallelExecutionSettings.cs @@ -1,16 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Threading.Tasks; using SixLabors.Memory; -namespace SixLabors.ImageSharp.ParallelUtils +namespace SixLabors.ImageSharp.Advanced.ParallelUtils { /// /// Defines execution settings for methods in . /// - internal readonly struct ParallelExecutionSettings + public readonly struct ParallelExecutionSettings { /// /// Default value for . @@ -20,11 +21,24 @@ namespace SixLabors.ImageSharp.ParallelUtils /// /// Initializes a new instance of the struct. /// + /// The value used for initializing when using TPL. + /// The value for . + /// The . public ParallelExecutionSettings( int maxDegreeOfParallelism, int minimumPixelsProcessedPerTask, MemoryAllocator memoryAllocator) { + // Shall be compatible with ParallelOptions.MaxDegreeOfParallelism: + // https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.paralleloptions.maxdegreeofparallelism + if (maxDegreeOfParallelism == 0 || maxDegreeOfParallelism < -1) + { + throw new ArgumentOutOfRangeException(nameof(maxDegreeOfParallelism)); + } + + Guard.MustBeGreaterThan(minimumPixelsProcessedPerTask, 0, nameof(minimumPixelsProcessedPerTask)); + Guard.NotNull(memoryAllocator, nameof(memoryAllocator)); + this.MaxDegreeOfParallelism = maxDegreeOfParallelism; this.MinimumPixelsProcessedPerTask = minimumPixelsProcessedPerTask; this.MemoryAllocator = memoryAllocator; @@ -33,13 +47,15 @@ namespace SixLabors.ImageSharp.ParallelUtils /// /// Initializes a new instance of the struct. /// + /// The value used for initializing when using TPL. + /// The . public ParallelExecutionSettings(int maxDegreeOfParallelism, MemoryAllocator memoryAllocator) : this(maxDegreeOfParallelism, DefaultMinimumPixelsProcessedPerTask, memoryAllocator) { } /// - /// Gets the MemoryAllocator + /// Gets the . /// public MemoryAllocator MemoryAllocator { get; } @@ -60,12 +76,26 @@ namespace SixLabors.ImageSharp.ParallelUtils /// Creates a new instance of /// having multiplied by /// + /// The value to multiply with. + /// The modified . public ParallelExecutionSettings MultiplyMinimumPixelsPerTask(int multiplier) { + Guard.MustBeGreaterThan(multiplier, 0, nameof(multiplier)); + return new ParallelExecutionSettings( this.MaxDegreeOfParallelism, this.MinimumPixelsProcessedPerTask * multiplier, this.MemoryAllocator); } + + /// + /// Get the default for a + /// + /// The . + /// The . + public static ParallelExecutionSettings FromConfiguration(Configuration configuration) + { + return new ParallelExecutionSettings(configuration.MaxDegreeOfParallelism, configuration.MemoryAllocator); + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs b/src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs similarity index 77% rename from src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs rename to src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs index 1e7299720d..c56337bffe 100644 --- a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs +++ b/src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs @@ -10,29 +10,25 @@ using SixLabors.ImageSharp.Memory; using SixLabors.Memory; using SixLabors.Primitives; -namespace SixLabors.ImageSharp.ParallelUtils +namespace SixLabors.ImageSharp.Advanced.ParallelUtils { /// /// Utility methods for batched processing of pixel row intervals. - /// Parallel execution is optimized for image processing. - /// Use this instead of direct calls! + /// Parallel execution is optimized for image processing based on values defined + /// or . + /// Using this class is preferred over direct usage of utility methods. /// - internal static class ParallelHelper + public static class ParallelHelper { - /// - /// Get the default for a - /// - public static ParallelExecutionSettings GetParallelSettings(this Configuration configuration) - { - return new ParallelExecutionSettings(configuration.MaxDegreeOfParallelism, configuration.MemoryAllocator); - } - /// /// Iterate through the rows of a rectangle in optimized batches defined by -s. /// + /// The . + /// The to get the parallel settings from. + /// The method body defining the iteration logic on a single . public static void IterateRows(Rectangle rectangle, Configuration configuration, Action body) { - ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings(); + ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration); IterateRows(rectangle, parallelSettings, body); } @@ -40,6 +36,9 @@ namespace SixLabors.ImageSharp.ParallelUtils /// /// Iterate through the rows of a rectangle in optimized batches defined by -s. /// + /// The . + /// The . + /// The method body defining the iteration logic on a single . public static void IterateRows( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, @@ -47,7 +46,9 @@ namespace SixLabors.ImageSharp.ParallelUtils { ValidateRectangle(rectangle); - int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask); + int maxSteps = DivideCeil( + rectangle.Width * rectangle.Height, + parallelSettings.MinimumPixelsProcessedPerTask); int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); @@ -81,7 +82,7 @@ namespace SixLabors.ImageSharp.ParallelUtils /// Iterate through the rows of a rectangle in optimized batches defined by -s /// instantiating a temporary buffer for each invocation. /// - public static void IterateRowsWithTempBuffer( + internal static void IterateRowsWithTempBuffer( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, Action> body) @@ -133,13 +134,13 @@ namespace SixLabors.ImageSharp.ParallelUtils /// Iterate through the rows of a rectangle in optimized batches defined by -s /// instantiating a temporary buffer for each invocation. /// - public static void IterateRowsWithTempBuffer( + internal static void IterateRowsWithTempBuffer( Rectangle rectangle, Configuration configuration, Action> body) where T : unmanaged { - IterateRowsWithTempBuffer(rectangle, configuration.GetParallelSettings(), body); + IterateRowsWithTempBuffer(rectangle, ParallelExecutionSettings.FromConfiguration(configuration), body); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Color/Color.NamedColors.cs b/src/ImageSharp/Color/Color.NamedColors.cs index 8811516c1c..0575a3e99e 100644 --- a/src/ImageSharp/Color/Color.NamedColors.cs +++ b/src/ImageSharp/Color/Color.NamedColors.cs @@ -174,9 +174,9 @@ namespace SixLabors.ImageSharp public static readonly Color DarkSalmon = FromRgba(233, 150, 122, 255); /// - /// Represents a matching the W3C definition that has an hex value of #8FBC8B. + /// Represents a matching the W3C definition that has an hex value of #8FBC8F. /// - public static readonly Color DarkSeaGreen = FromRgba(143, 188, 139, 255); + public static readonly Color DarkSeaGreen = FromRgba(143, 188, 143, 255); /// /// Represents a matching the W3C definition that has an hex value of #483D8B. @@ -718,4 +718,4 @@ namespace SixLabors.ImageSharp /// public static readonly Color YellowGreen = FromRgba(154, 205, 50, 255); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 4bdbe088ca..76f3995171 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp [MethodImpl(InliningOptions.ShortMethod)] public static Color FromHex(string hex) { - Rgba32 rgba = Rgba32.FromHex(hex); + var rgba = Rgba32.FromHex(hex); return new Color(rgba); } @@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp where TPixel : struct, IPixel { ReadOnlySpan rgba64Span = MemoryMarshal.Cast(source); - PixelOperations.Instance.FromRgba64(Configuration.Default, rgba64Span, destination); + PixelOperations.Instance.FromRgba64(configuration, rgba64Span, destination); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs index 761313b7e0..97465e526a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs @@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation } else { - s = chroma / (2F - chroma); + s = chroma / (2F - max - min); } return new Hsl(h, s, l); @@ -157,4 +157,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation return value; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs b/src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs index fa13700787..82aa8cf09f 100644 --- a/src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs +++ b/src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; - namespace SixLabors.ImageSharp { /// @@ -20,17 +18,5 @@ namespace SixLabors.ImageSharp : base(errorMessage) { } - - /// - /// Initializes a new instance of the class with a specified - /// error message and the exception that is the cause of this exception. - /// - /// The error message that explains the reason for this exception. - /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) - /// if no inner exception is specified. - public UnknownImageFormatException(string errorMessage, Exception innerException) - : base(errorMessage, innerException) - { - } } } diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs index 43eebeac87..356dd419b5 100644 --- a/src/ImageSharp/Common/Helpers/DebugGuard.cs +++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs @@ -1,168 +1,17 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Diagnostics; // TODO: These should just call the guard equivalents -namespace SixLabors.ImageSharp +namespace SixLabors { /// /// Provides methods to protect against invalid parameters for a DEBUG build. /// - [DebuggerStepThrough] - internal static class DebugGuard + internal static partial class DebugGuard { - /// - /// Verifies, that the method parameter with specified object value is not null - /// and throws an exception if it is found to be so. - /// - /// The target object, which cannot be null. - /// The name of the parameter that is to be checked. - /// is null - [Conditional("DEBUG")] - public static void NotNull(T value, string parameterName) - where T : class - { - if (value is null) - { - throw new ArgumentNullException(parameterName); - } - } - - /// - /// Verifies that the specified value is less than a maximum value - /// and throws an exception if it is not. - /// - /// The target value, which should be validated. - /// The maximum value. - /// The name of the parameter that is to be checked. - /// The type of the value. - /// - /// is greater than the maximum value. - /// - [Conditional("DEBUG")] - public static void MustBeLessThan(TValue value, TValue max, string parameterName) - where TValue : IComparable - { - if (value.CompareTo(max) >= 0) - { - throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than {max}."); - } - } - - /// - /// Verifies that the specified value is less than or equal to a maximum value - /// and throws an exception if it is not. - /// - /// The target value, which should be validated. - /// The maximum value. - /// The name of the parameter that is to be checked. - /// The type of the value. - /// - /// is greater than the maximum value. - /// - [Conditional("DEBUG")] - public static void MustBeLessThanOrEqualTo(TValue value, TValue max, string parameterName) - where TValue : IComparable - { - if (value.CompareTo(max) > 0) - { - throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than or equal to {max}."); - } - } - - /// - /// Verifies that the specified value is greater than a minimum value - /// and throws an exception if it is not. - /// - /// The target value, which should be validated. - /// The minimum value. - /// The name of the parameter that is to be checked. - /// The type of the value. - /// - /// is less than the minimum value. - /// - [Conditional("DEBUG")] - public static void MustBeGreaterThan(TValue value, TValue min, string parameterName) - where TValue : IComparable - { - if (value.CompareTo(min) <= 0) - { - throw new ArgumentOutOfRangeException( - parameterName, - $"Value must be greater than {min}."); - } - } - - /// - /// Verifies that the specified value is greater than or equal to a minimum value - /// and throws an exception if it is not. - /// - /// The target value, which should be validated. - /// The minimum value. - /// The name of the parameter that is to be checked. - /// The type of the value. - /// - /// is less than the minimum value. - /// - [Conditional("DEBUG")] - public static void MustBeGreaterThanOrEqualTo(TValue value, TValue min, string parameterName) - where TValue : IComparable - { - if (value.CompareTo(min) < 0) - { - throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min}."); - } - } - - /// - /// Verifies that the specified value is greater than or equal to a minimum value and less than - /// or equal to a maximum value and throws an exception if it is not. - /// - /// The target value, which should be validated. - /// The minimum value. - /// The maximum value. - /// The name of the parameter that is to be checked. - /// The type of the value. - /// - /// is less than the minimum value of greater than the maximum value. - /// - [Conditional("DEBUG")] - public static void MustBeBetweenOrEqualTo(TValue value, TValue min, TValue max, string parameterName) - where TValue : IComparable - { - if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0) - { - throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min} and less than or equal to {max}."); - } - } - - /// - /// Verifies, that the method parameter with specified target value is true - /// and throws an exception if it is found to be so. - /// - /// - /// The target value, which cannot be false. - /// - /// - /// The name of the parameter that is to be checked. - /// - /// - /// The error message, if any to add to the exception. - /// - /// - /// is false - /// - [Conditional("DEBUG")] - public static void IsTrue(bool target, string parameterName, string message) - { - if (!target) - { - throw new ArgumentException(message, parameterName); - } - } - /// /// Verifies whether a specific condition is met, throwing an exception if it's false. /// @@ -177,25 +26,6 @@ namespace SixLabors.ImageSharp } } - /// - /// Verifies, that the method parameter with specified target value is false - /// and throws an exception if it is found to be so. - /// - /// The target value, which cannot be true. - /// The name of the parameter that is to be checked. - /// The error message, if any to add to the exception. - /// - /// is true - /// - [Conditional("DEBUG")] - public static void IsFalse(bool target, string parameterName, string message) - { - if (target) - { - throw new ArgumentException(message, parameterName); - } - } - /// /// Verifies, that the target span is of same size than the 'other' span. /// @@ -236,4 +66,4 @@ namespace SixLabors.ImageSharp } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs deleted file mode 100644 index 7dc683c37f..0000000000 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp -{ - /// - /// Provides methods to protect against invalid parameters. - /// - [DebuggerStepThrough] - internal static class Guard - { - /// - /// Ensures that the value is not null. - /// - /// The target object, which cannot be null. - /// The name of the parameter that is to be checked. - /// is null - [MethodImpl(InliningOptions.ShortMethod)] - public static void NotNull(T value, string parameterName) - where T : class - { - if (value is null) - { - ThrowArgumentNullException(parameterName); - } - } - - /// - /// Ensures that the target value is not null, empty, or whitespace. - /// - /// The target string, which should be checked against being null or empty. - /// Name of the parameter. - /// is null. - /// is empty or contains only blanks. - [MethodImpl(InliningOptions.ShortMethod)] - public static void NotNullOrWhiteSpace(string value, string parameterName) - { - if (value is null) - { - ThrowArgumentNullException(parameterName); - } - - if (string.IsNullOrWhiteSpace(value)) - { - ThrowArgumentException("Must not be empty or whitespace.", parameterName); - } - } - - /// - /// Ensures that the specified value is less than a maximum value. - /// - /// The target value, which should be validated. - /// The maximum value. - /// The name of the parameter that is to be checked. - /// The type of the value. - /// - /// is greater than the maximum value. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static void MustBeLessThan(TValue value, TValue max, string parameterName) - where TValue : IComparable - { - if (value.CompareTo(max) >= 0) - { - ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be less than {max}."); - } - } - - /// - /// Verifies that the specified value is less than or equal to a maximum value - /// and throws an exception if it is not. - /// - /// The target value, which should be validated. - /// The maximum value. - /// The name of the parameter that is to be checked. - /// The type of the value. - /// - /// is greater than the maximum value. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static void MustBeLessThanOrEqualTo(TValue value, TValue max, string parameterName) - where TValue : IComparable - { - if (value.CompareTo(max) > 0) - { - ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be less than or equal to {max}."); - } - } - - /// - /// Verifies that the specified value is greater than a minimum value - /// and throws an exception if it is not. - /// - /// The target value, which should be validated. - /// The minimum value. - /// The name of the parameter that is to be checked. - /// The type of the value. - /// - /// is less than the minimum value. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static void MustBeGreaterThan(TValue value, TValue min, string parameterName) - where TValue : IComparable - { - if (value.CompareTo(min) <= 0) - { - ThrowArgumentOutOfRangeException( - parameterName, - $"Value {value} must be greater than {min}."); - } - } - - /// - /// Verifies that the specified value is greater than or equal to a minimum value - /// and throws an exception if it is not. - /// - /// The target value, which should be validated. - /// The minimum value. - /// The name of the parameter that is to be checked. - /// The type of the value. - /// - /// is less than the minimum value. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static void MustBeGreaterThanOrEqualTo(TValue value, TValue min, string parameterName) - where TValue : IComparable - { - if (value.CompareTo(min) < 0) - { - ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min}."); - } - } - - /// - /// Verifies that the specified value is greater than or equal to a minimum value and less than - /// or equal to a maximum value and throws an exception if it is not. - /// - /// The target value, which should be validated. - /// The minimum value. - /// The maximum value. - /// The name of the parameter that is to be checked. - /// The type of the value. - /// - /// is less than the minimum value of greater than the maximum value. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static void MustBeBetweenOrEqualTo(TValue value, TValue min, TValue max, string parameterName) - where TValue : IComparable - { - if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0) - { - ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min} and less than or equal to {max}."); - } - } - - /// - /// Verifies, that the method parameter with specified target value is true - /// and throws an exception if it is found to be so. - /// - /// The target value, which cannot be false. - /// The name of the parameter that is to be checked. - /// The error message, if any to add to the exception. - /// - /// is false - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static void IsTrue(bool target, string parameterName, string message) - { - if (!target) - { - ThrowArgumentException(message, parameterName); - } - } - - /// - /// Verifies, that the method parameter with specified target value is false - /// and throws an exception if it is found to be so. - /// - /// The target value, which cannot be true. - /// The name of the parameter that is to be checked. - /// The error message, if any to add to the exception. - /// - /// is true - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static void IsFalse(bool target, string parameterName, string message) - { - if (target) - { - ThrowArgumentException(message, parameterName); - } - } - - /// - /// Verifies, that the `source` span has the length of 'minLength', or longer. - /// - /// The element type of the spans - /// The source span. - /// The minimum length. - /// The name of the parameter that is to be checked. - /// - /// has less than items - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static void MustBeSizedAtLeast(ReadOnlySpan source, int minLength, string parameterName) - { - if (source.Length < minLength) - { - ThrowArgumentException($"Span-s must be at least of length {minLength}!", parameterName); - } - } - - /// - /// Verifies that the 'destination' span is not shorter than 'source'. - /// - /// The source element type - /// The destination element type - /// The source span - /// The destination span - /// The name of the argument for 'destination' - [MethodImpl(InliningOptions.ShortMethod)] - public static void DestinationShouldNotBeTooShort( - ReadOnlySpan source, - Span destination, - string destinationParamName) - { - if (destination.Length < source.Length) - { - ThrowArgumentException("Destination span is too short!", destinationParamName); - } - } - - /// - /// Verifies that the 'destination' span is not shorter than 'source'. - /// - /// The source element type - /// The destination element type - /// The source span - /// The destination span - /// The name of the argument for 'destination' - [MethodImpl(InliningOptions.ShortMethod)] - public static void DestinationShouldNotBeTooShort( - Span source, - Span destination, - string destinationParamName) - { - if (destination.Length < source.Length) - { - ThrowArgumentException("Destination span is too short!", destinationParamName); - } - } - - /// - /// Verifies, that the `source` span has the length of 'minLength', or longer. - /// - /// The element type of the spans - /// The target span. - /// The minimum length. - /// The name of the parameter that is to be checked. - /// - /// has less than items - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static void MustBeSizedAtLeast(Span source, int minLength, string parameterName) - { - if (source.Length < minLength) - { - ThrowArgumentException($"Span-s must be at least of length {minLength}!", parameterName); - } - } - - [MethodImpl(InliningOptions.ColdPath)] - private static void ThrowArgumentException(string message, string parameterName) - { - throw new ArgumentException(message, parameterName); - } - - [MethodImpl(InliningOptions.ColdPath)] - private static void ThrowArgumentOutOfRangeException(string parameterName, string message) - { - throw new ArgumentOutOfRangeException(parameterName, message); - } - - [MethodImpl(InliningOptions.ColdPath)] - private static void ThrowArgumentNullException(string parameterName) - { - throw new ArgumentNullException(parameterName); - } - } -} diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs index 5aa0b21ec1..bc07fbf317 100644 --- a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs +++ b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs @@ -19,6 +19,7 @@ namespace SixLabors.ImageSharp { public static bool IsAvailable { get; } = IsAvx2CompatibleArchitecture; +#if !SUPPORTS_EXTENDED_INTRINSICS /// /// as many elements as possible, slicing them down (keeping the remainder). /// @@ -74,6 +75,7 @@ namespace SixLabors.ImageSharp dest = dest.Slice(adjustedCount); } } +#endif /// /// SIMD optimized implementation for . diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 0d44db8d87..ae20490c77 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp get => this.maxDegreeOfParallelism; set { - if (value <= 0) + if (value == 0 || value < -1) { throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism)); } @@ -161,4 +161,4 @@ namespace SixLabors.ImageSharp new BmpConfigurationModule()); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs index 13c89c82cf..34fe1aecbd 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder [MethodImpl(InliningOptions.ShortMethod)] public void CheckBits() { - if (this.remainingBits < 16) + if (this.remainingBits < JpegConstants.Huffman.MinBits) { this.FillBuffer(); } @@ -85,8 +85,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { // Attempt to load at least the minimum number of required bits into the buffer. // We fail to do so only if we hit a marker or reach the end of the input stream. - this.remainingBits += 48; - this.data = (this.data << 48) | this.GetBytes(); + this.remainingBits += JpegConstants.Huffman.FetchBits; + this.data = (this.data << JpegConstants.Huffman.FetchBits) | this.GetBytes(); } [MethodImpl(InliningOptions.ShortMethod)] @@ -141,7 +141,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private ulong GetBytes() { ulong temp = 0; - for (int i = 0; i < 6; i++) + for (int i = 0; i < JpegConstants.Huffman.FetchLoop; i++) { int b = this.ReadStream(); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index 4685ba2895..6025930169 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -82,12 +82,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // Figure C.1: make table of Huffman code length for each symbol int p = 0; - for (int l = 1; l <= 16; l++) + for (int j = 1; j <= 16; j++) { - int i = this.Sizes[l]; + int i = this.Sizes[j]; while (i-- != 0) { - huffSize[p++] = (char)l; + huffSize[p++] = (char)j; } } @@ -111,20 +111,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // Figure F.15: generate decoding tables for bit-sequential decoding p = 0; - for (int l = 1; l <= 16; l++) + for (int j = 1; j <= 16; j++) { - if (this.Sizes[l] != 0) + if (this.Sizes[j] != 0) { - int offset = p - (int)huffCode[p]; - this.ValOffset[l] = offset; - p += this.Sizes[l]; - this.MaxCode[l] = huffCode[p - 1]; // Maximum code of length l - this.MaxCode[l] <<= 64 - l; // Left justify - this.MaxCode[l] |= (1ul << (64 - l)) - 1; + this.ValOffset[j] = p - (int)huffCode[p]; + p += this.Sizes[j]; + this.MaxCode[j] = huffCode[p - 1]; // Maximum code of length l + this.MaxCode[j] <<= JpegConstants.Huffman.RegisterSize - j; // Left justify + this.MaxCode[j] |= (1ul << (JpegConstants.Huffman.RegisterSize - j)) - 1; } else { - this.MaxCode[l] = 0; + this.MaxCode[j] = 0; } } @@ -142,11 +141,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder p = 0; for (int length = 1; length <= JpegConstants.Huffman.LookupBits; length++) { + int jShift = JpegConstants.Huffman.LookupBits - length; for (int i = 1; i <= this.Sizes[length]; i++, p++) { // length = current code's length, p = its index in huffCode[] & Values[]. // Generate left-justified code followed by all possible bit sequences - int lookBits = (int)(huffCode[p] << (JpegConstants.Huffman.LookupBits - length)); + int lookBits = (int)(huffCode[p] << jShift); for (int ctr = 1 << (JpegConstants.Huffman.LookupBits - length); ctr > 0; ctr--) { this.LookaheadSize[lookBits] = (byte)length; diff --git a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs b/src/ImageSharp/Formats/Jpeg/JpegConstants.cs index a39480e126..9f50e2cab1 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegConstants.cs @@ -1,7 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; namespace SixLabors.ImageSharp.Formats.Jpeg { @@ -249,6 +250,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public const int RegisterSize = 64; + /// + /// The number of bits to fetch when filling the buffer. + /// + public const int FetchBits = 48; + + /// + /// The number of times to read the input stream when filling the buffer. + /// + public const int FetchLoop = FetchBits / 8; + + /// + /// The minimum number of bits allowed before by the before fetching. + /// + public const int MinBits = RegisterSize - FetchBits; + /// /// If the next Huffman code is no more than this number of bits, we can obtain its length /// and the corresponding symbol directly from this tables. @@ -266,4 +282,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public const int LookupSize = 1 << LookupBits; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 9bc5a5079d..037f648f0a 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -5,6 +5,7 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -175,11 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Png this.InitializeImage(metadata, out image); } - using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk)) - { - deframeStream.AllocateNewBytes(chunk.Length); - this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetadata); - } + this.ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata); break; case PngChunkType.Palette: @@ -465,19 +462,25 @@ namespace SixLabors.ImageSharp.Formats.Png /// Reads the scanlines within the image. /// /// The pixel format. - /// The containing data. + /// The png chunk containing the compressed scanline data. /// The pixel data. /// The png metadata - private void ReadScanlines(Stream dataStream, ImageFrame image, PngMetadata pngMetadata) + private void ReadScanlines(PngChunk chunk, ImageFrame image, PngMetadata pngMetadata) where TPixel : struct, IPixel { - if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) - { - this.DecodeInterlacedPixelData(dataStream, image, pngMetadata); - } - else + using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk)) { - this.DecodePixelData(dataStream, image, pngMetadata); + deframeStream.AllocateNewBytes(chunk.Length, true); + DeflateStream dataStream = deframeStream.CompressedStream; + + if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) + { + this.DecodeInterlacedPixelData(dataStream, image, pngMetadata); + } + else + { + this.DecodePixelData(dataStream, image, pngMetadata); + } } } @@ -924,7 +927,11 @@ namespace SixLabors.ImageSharp.Formats.Png } ReadOnlySpan compressedData = data.Slice(zeroIndex + 2); - metadata.TextData.Add(new PngTextData(name, this.UncompressTextData(compressedData, PngConstants.Encoding), string.Empty, string.Empty)); + + if (this.TryUncompressTextData(compressedData, PngConstants.Encoding, out string uncompressed)) + { + metadata.TextData.Add(new PngTextData(name, uncompressed, string.Empty, string.Empty)); + } } /// @@ -987,7 +994,11 @@ namespace SixLabors.ImageSharp.Formats.Png if (compressionFlag == 1) { ReadOnlySpan compressedData = data.Slice(dataStartIdx); - metadata.TextData.Add(new PngTextData(keyword, this.UncompressTextData(compressedData, PngConstants.TranslatedEncoding), language, translatedKeyword)); + + if (this.TryUncompressTextData(compressedData, PngConstants.TranslatedEncoding, out string uncompressed)) + { + metadata.TextData.Add(new PngTextData(keyword, uncompressed, language, translatedKeyword)); + } } else { @@ -1001,13 +1012,19 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Compressed text data bytes. /// The string encoding to use. - /// A string. - private string UncompressTextData(ReadOnlySpan compressedData, Encoding encoding) + /// The uncompressed value. + /// The . + private bool TryUncompressTextData(ReadOnlySpan compressedData, Encoding encoding, out string value) { using (var memoryStream = new MemoryStream(compressedData.ToArray())) - using (var inflateStream = new ZlibInflateStream(memoryStream, () => 0)) + using (var inflateStream = new ZlibInflateStream(memoryStream)) { - inflateStream.AllocateNewBytes(compressedData.Length); + if (!inflateStream.AllocateNewBytes(compressedData.Length, false)) + { + value = null; + return false; + } + var uncompressedBytes = new List(); // Note: this uses the a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here. @@ -1018,7 +1035,8 @@ namespace SixLabors.ImageSharp.Formats.Png bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length); } - return encoding.GetString(uncompressedBytes.ToArray()); + value = encoding.GetString(uncompressedBytes.ToArray()); + return true; } } diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs index 405eeafeb9..3eb34b8617 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs @@ -13,21 +13,21 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib internal sealed class ZlibInflateStream : Stream { /// - /// Used to read the Adler-32 and Crc-32 checksums + /// Used to read the Adler-32 and Crc-32 checksums. /// We don't actually use this for anything so it doesn't /// have to be threadsafe. /// private static readonly byte[] ChecksumBuffer = new byte[4]; /// - /// The inner raw memory stream + /// A default delegate to get more data from the inner stream. /// - private readonly Stream innerStream; + private static readonly Func GetDataNoOp = () => 0; /// - /// The compressed stream sitting over the top of the deframer + /// The inner raw memory stream. /// - private DeflateStream compressedStream; + private readonly Stream innerStream; /// /// A value indicating whether this instance of the given entity has been disposed. @@ -43,20 +43,29 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib private bool isDisposed; /// - /// The current data remaining to be read + /// The current data remaining to be read. /// private int currentDataRemaining; /// - /// Delegate to get more data once we've exhausted the current data remaining + /// Delegate to get more data once we've exhausted the current data remaining. /// private readonly Func getData; /// /// Initializes a new instance of the class. /// - /// The inner raw stream - /// A delegate to get more data from the inner stream + /// The inner raw stream. + public ZlibInflateStream(Stream innerStream) + : this(innerStream, GetDataNoOp) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The inner raw stream. + /// A delegate to get more data from the inner stream. public ZlibInflateStream(Stream innerStream, Func getData) { this.innerStream = innerStream; @@ -76,31 +85,32 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib public override long Length => throw new NotSupportedException(); /// - public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } /// - /// Gets the compressed stream over the deframed inner stream + /// Gets the compressed stream over the deframed inner stream. /// - public DeflateStream CompressedStream => this.compressedStream; + public DeflateStream CompressedStream { get; private set; } /// - /// Adds new bytes from a frame found in the original stream + /// Adds new bytes from a frame found in the original stream. /// - /// blabla - public void AllocateNewBytes(int bytes) + /// The current remaining data according to the chunk length. + /// Whether the chunk to be inflated is a critical chunk. + /// The . + public bool AllocateNewBytes(int bytes, bool isCriticalChunk) { this.currentDataRemaining = bytes; - if (this.compressedStream is null) + if (this.CompressedStream is null) { - this.InitializeInflateStream(); + return this.InitializeInflateStream(isCriticalChunk); } + + return true; } /// - public override void Flush() - { - throw new NotSupportedException(); - } + public override void Flush() => throw new NotSupportedException(); /// public override int ReadByte() @@ -114,7 +124,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib { if (this.currentDataRemaining == 0) { - // last buffer was read in its entirety, let's make sure we don't actually have more + // Last buffer was read in its entirety, let's make sure we don't actually have more in additional IDAT chunks. this.currentDataRemaining = this.getData(); if (this.currentDataRemaining == 0) @@ -125,32 +135,35 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib int bytesToRead = Math.Min(count, this.currentDataRemaining); this.currentDataRemaining -= bytesToRead; - int bytesRead = this.innerStream.Read(buffer, offset, bytesToRead); - long length = this.innerStream.Length; + int totalBytesRead = this.innerStream.Read(buffer, offset, bytesToRead); + long innerStreamLength = this.innerStream.Length; - // Keep reading data until we've reached the end of the stream or filled the buffer - while (this.currentDataRemaining == 0 && bytesRead < count) + // Keep reading data until we've reached the end of the stream or filled the buffer. + int bytesRead = 0; + offset += totalBytesRead; + while (this.currentDataRemaining == 0 && totalBytesRead < count) { this.currentDataRemaining = this.getData(); if (this.currentDataRemaining == 0) { - return bytesRead; + return totalBytesRead; } offset += bytesRead; - if (offset >= length || offset >= count) + if (offset >= innerStreamLength || offset >= count) { - return bytesRead; + return totalBytesRead; } - bytesToRead = Math.Min(count - bytesRead, this.currentDataRemaining); + bytesToRead = Math.Min(count - totalBytesRead, this.currentDataRemaining); this.currentDataRemaining -= bytesToRead; - bytesRead += this.innerStream.Read(buffer, offset, bytesToRead); + bytesRead = this.innerStream.Read(buffer, offset, bytesToRead); + totalBytesRead += bytesRead; } - return bytesRead; + return totalBytesRead; } /// @@ -181,11 +194,11 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib if (disposing) { - // dispose managed resources - if (this.compressedStream != null) + // Dispose managed resources. + if (this.CompressedStream != null) { - this.compressedStream.Dispose(); - this.compressedStream = null; + this.CompressedStream.Dispose(); + this.CompressedStream = null; } } @@ -197,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib this.isDisposed = true; } - private void InitializeInflateStream() + private bool InitializeInflateStream(bool isCriticalChunk) { // Read the zlib header : http://tools.ietf.org/html/rfc1950 // CMF(Compression Method and flags) @@ -215,7 +228,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib this.currentDataRemaining -= 2; if (cmf == -1 || flag == -1) { - return; + return false; } if ((cmf & 0x0F) == 8) @@ -225,14 +238,28 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib if (cinfo > 7) { - // Values of CINFO above 7 are not allowed in RFC1950. - // CINFO is not defined in this specification for CM not equal to 8. - throw new ImageFormatException($"Invalid window size for ZLIB header: cinfo={cinfo}"); + if (isCriticalChunk) + { + // Values of CINFO above 7 are not allowed in RFC1950. + // CINFO is not defined in this specification for CM not equal to 8. + throw new ImageFormatException($"Invalid window size for ZLIB header: cinfo={cinfo}"); + } + else + { + return false; + } } } else { - throw new ImageFormatException($"Bad method for ZLIB header: cmf={cmf}"); + if (isCriticalChunk) + { + throw new ImageFormatException($"Bad method for ZLIB header: cmf={cmf}"); + } + else + { + return false; + } } // The preset dictionary. @@ -246,7 +273,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib } // Initialize the deflate Stream. - this.compressedStream = new DeflateStream(this, CompressionMode.Decompress, true); + this.CompressedStream = new DeflateStream(this, CompressionMode.Decompress, true); + + return true; } } } diff --git a/src/ImageSharp/IImage.cs b/src/ImageSharp/IImage.cs index b9e2cee616..0d4dc3c9d9 100644 --- a/src/ImageSharp/IImage.cs +++ b/src/ImageSharp/IImage.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; @@ -11,4 +11,4 @@ namespace SixLabors.ImageSharp public interface IImage : IImageInfo, IDisposable { } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 57f60f2e75..d7fed90164 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; using SixLabors.ImageSharp.Advanced; @@ -80,21 +81,11 @@ namespace SixLabors.ImageSharp /// Configuration IConfigurable.Configuration => this.Configuration; - /// - /// Gets a value indicating whether the image instance is disposed. - /// - public bool IsDisposed { get; private set; } - /// public void Dispose() { - if (this.IsDisposed) - { - return; - } - - this.IsDisposed = true; - this.DisposeImpl(); + this.Dispose(true); + GC.SuppressFinalize(this); } /// @@ -109,8 +100,7 @@ namespace SixLabors.ImageSharp Guard.NotNull(encoder, nameof(encoder)); this.EnsureNotDisposed(); - EncodeVisitor visitor = new EncodeVisitor(encoder, stream); - this.AcceptVisitor(visitor); + this.AcceptVisitor(new EncodeVisitor(encoder, stream)); } /// @@ -130,13 +120,6 @@ namespace SixLabors.ImageSharp public abstract Image CloneAs(Configuration configuration) where TPixel2 : struct, IPixel; - /// - /// Accept a . - /// Implemented by invoking - /// with the pixel type of the image. - /// - internal abstract void AcceptVisitor(IImageVisitor visitor); - /// /// Update the size of the image after mutation. /// @@ -144,9 +127,23 @@ namespace SixLabors.ImageSharp protected void UpdateSize(Size size) => this.size = size; /// - /// Implements the Dispose logic. + /// Disposes the object and frees resources for the Garbage Collector. + /// + /// Whether to dispose of managed and unmanaged objects. + protected abstract void Dispose(bool disposing); + + /// + /// Throws if the image is disposed. + /// + internal abstract void EnsureNotDisposed(); + + /// + /// Accepts a . + /// Implemented by invoking + /// with the pixel type of the image. /// - protected abstract void DisposeImpl(); + /// The visitor. + internal abstract void Accept(IImageVisitor visitor); private class EncodeVisitor : IImageVisitor { diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index 6ea2b234c5..6cdc948d40 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.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; @@ -119,16 +119,5 @@ namespace SixLabors.ImageSharp return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(stream.ToArray())}"; } } - - /// - /// Throws if the image is disposed. - /// - internal static void EnsureNotDisposed(this Image image) - { - if (image.IsDisposed) - { - throw new ObjectDisposedException(nameof(image), "Trying to execute an operation on a disposed image."); - } - } } } diff --git a/src/ImageSharp/ImageFrame.cs b/src/ImageSharp/ImageFrame.cs index f3fe1ed8d4..91872b21d6 100644 --- a/src/ImageSharp/ImageFrame.cs +++ b/src/ImageSharp/ImageFrame.cs @@ -74,7 +74,17 @@ namespace SixLabors.ImageSharp public Rectangle Bounds() => new Rectangle(0, 0, this.Width, this.Height); /// - 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 of managed and unmanaged objects. + protected abstract void Dispose(bool disposing); internal abstract void CopyPixelsTo(Span destination) where TDestinationPixel : struct, IPixel; diff --git a/src/ImageSharp/ImageFrameCollection{TPixel}.cs b/src/ImageSharp/ImageFrameCollection{TPixel}.cs index 893a7a4a80..722a4ddea8 100644 --- a/src/ImageSharp/ImageFrameCollection{TPixel}.cs +++ b/src/ImageSharp/ImageFrameCollection{TPixel}.cs @@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp this.parent.GetConfiguration(), source.Size(), source.Metadata.DeepClone()); - source.CopyPixelsTo(result.PixelBuffer.Span); + source.CopyPixelsTo(result.PixelBuffer.GetSpan()); return result; } } diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 5c9ff489e1..64f37a3407 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -6,9 +6,9 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp /// In all other cases it is the only frame of the image. /// /// The pixel format. - public sealed class ImageFrame : ImageFrame, IPixelSource, IDisposable + public sealed class ImageFrame : ImageFrame, IPixelSource where TPixel : struct, IPixel { private bool isDisposed; @@ -196,20 +196,20 @@ namespace SixLabors.ImageSharp this.UpdateSize(this.PixelBuffer.Size()); } - /// - /// Disposes the object and frees resources for the Garbage Collector. - /// - public override void Dispose() + /// + protected override void Dispose(bool disposing) { if (this.isDisposed) { return; } - this.PixelBuffer?.Dispose(); - this.PixelBuffer = null; + if (disposing) + { + this.PixelBuffer?.Dispose(); + this.PixelBuffer = null; + } - // Note disposing is done. this.isDisposed = true; } @@ -218,10 +218,10 @@ namespace SixLabors.ImageSharp if (typeof(TPixel) == typeof(TDestinationPixel)) { Span dest1 = MemoryMarshal.Cast(destination); - this.PixelBuffer.Span.CopyTo(dest1); + this.PixelBuffer.GetSpan().CopyTo(dest1); } - PixelOperations.Instance.To(this.Configuration, this.PixelBuffer.Span, destination); + PixelOperations.Instance.To(this.Configuration, this.PixelBuffer.GetSpan(), destination); } /// diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 8dff3b9779..86b0848663 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -19,6 +19,15 @@ SixLabors.ImageSharp + + + $(DefineConstants);SUPPORTS_MATHF + + + + $(DefineConstants);SUPPORTS_HASHCODE + + $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS @@ -27,6 +36,10 @@ + + + + True diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index a7ea58652c..3f733479dc 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.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; @@ -21,6 +21,8 @@ namespace SixLabors.ImageSharp public sealed class Image : Image where TPixel : struct, IPixel { + private bool isDisposed; + /// /// Initializes a new instance of the class /// with the height and the width of the image. @@ -185,19 +187,41 @@ namespace SixLabors.ImageSharp } /// - protected override void DisposeImpl() => this.Frames.Dispose(); + protected override void Dispose(bool disposing) + { + if (this.isDisposed) + { + return; + } + + if (disposing) + { + this.Frames.Dispose(); + } + + this.isDisposed = true; + } + + /// + internal override void EnsureNotDisposed() + { + if (this.isDisposed) + { + throw new ObjectDisposedException("Trying to execute an operation on a disposed image."); + } + } + + /// + public override string ToString() => $"Image<{typeof(TPixel).Name}>: {this.Width}x{this.Height}"; /// - internal override void AcceptVisitor(IImageVisitor visitor) + internal override void Accept(IImageVisitor visitor) { this.EnsureNotDisposed(); visitor.Visit(this); } - /// - public override string ToString() => $"Image<{typeof(TPixel).Name}>: {this.Width}x{this.Height}"; - /// /// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer. /// diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 59247aa2d2..35d55ba590 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -13,13 +13,69 @@ namespace SixLabors.ImageSharp.Memory /// /// Defines extension methods for . /// - internal static class Buffer2DExtensions + public static class Buffer2DExtensions { + /// + /// Gets a to the backing buffer of . + /// + /// The . + /// The value type. + /// The referencing the memory area. + public static Span GetSpan(this Buffer2D buffer) + where T : struct + { + Guard.NotNull(buffer, nameof(buffer)); + return buffer.MemorySource.GetSpan(); + } + + /// + /// Gets the holding the backing buffer of . + /// + /// The . + /// The value type. + /// The . + public static Memory GetMemory(this Buffer2D buffer) + where T : struct + { + Guard.NotNull(buffer, nameof(buffer)); + return buffer.MemorySource.Memory; + } + + /// + /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. + /// + /// The buffer + /// The y (row) coordinate + /// The element type + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span GetRowSpan(this Buffer2D buffer, int y) + where T : struct + { + Guard.NotNull(buffer, nameof(buffer)); + return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width); + } + + /// + /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. + /// + /// The buffer + /// The y (row) coordinate + /// The element type + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Memory GetRowMemory(this Buffer2D buffer, int y) + where T : struct + { + Guard.NotNull(buffer, nameof(buffer)); + return buffer.MemorySource.Memory.Slice(y * buffer.Width, buffer.Width); + } + /// /// Copy columns of inplace, /// from positions starting at to positions at . /// - public static unsafe void CopyColumns( + internal static unsafe void CopyColumns( this Buffer2D buffer, int sourceIndex, int destIndex, @@ -37,7 +93,7 @@ namespace SixLabors.ImageSharp.Memory int dOffset = destIndex * elementSize; long count = columnCount * elementSize; - Span span = MemoryMarshal.AsBytes(buffer.Memory.Span); + Span span = MemoryMarshal.AsBytes(buffer.GetMemory().Span); fixed (byte* ptr = span) { @@ -60,7 +116,7 @@ namespace SixLabors.ImageSharp.Memory /// The element type /// The /// The - public static Rectangle FullRectangle(this Buffer2D buffer) + internal static Rectangle FullRectangle(this Buffer2D buffer) where T : struct { return new Rectangle(0, 0, buffer.Width, buffer.Height); @@ -73,11 +129,11 @@ namespace SixLabors.ImageSharp.Memory /// The /// The rectangle subarea /// The - public static BufferArea GetArea(this Buffer2D buffer, in Rectangle rectangle) + internal static BufferArea GetArea(this Buffer2D buffer, in Rectangle rectangle) where T : struct => new BufferArea(buffer, rectangle); - public static BufferArea GetArea(this Buffer2D buffer, int x, int y, int width, int height) + internal static BufferArea GetArea(this Buffer2D buffer, int x, int y, int width, int height) where T : struct => new BufferArea(buffer, new Rectangle(x, y, width, height)); @@ -87,64 +143,17 @@ namespace SixLabors.ImageSharp.Memory /// The element type /// The /// The - public static BufferArea GetArea(this Buffer2D buffer) + internal static BufferArea GetArea(this Buffer2D buffer) where T : struct => new BufferArea(buffer); - public static BufferArea GetAreaBetweenRows(this Buffer2D buffer, int minY, int maxY) - where T : struct => - new BufferArea(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY)); - /// /// Gets a span for all the pixels in defined by /// - public static Span GetMultiRowSpan(this Buffer2D buffer, in RowInterval rows) + internal static Span GetMultiRowSpan(this Buffer2D buffer, in RowInterval rows) where T : struct { - return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); - } - - /// - /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. - /// - /// The buffer - /// The y (row) coordinate - /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory GetRowMemory(this Buffer2D buffer, int y) - where T : struct - { - return buffer.MemorySource.Memory.Slice(y * buffer.Width, buffer.Width); - } - - /// - /// Gets a to the row 'y' beginning from the pixel at 'x'. - /// - /// The buffer - /// The x coordinate (position in the row) - /// The y (row) coordinate - /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span GetRowSpan(this Buffer2D buffer, int x, int y) - where T : struct - { - return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x); - } - - /// - /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. - /// - /// The buffer - /// The y (row) coordinate - /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span GetRowSpan(this Buffer2D buffer, int y) - where T : struct - { - return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width); + return buffer.GetSpan().Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); } /// @@ -153,21 +162,12 @@ namespace SixLabors.ImageSharp.Memory /// The element type /// The /// The of the buffer - public static Size Size(this Buffer2D buffer) + internal static Size Size(this Buffer2D buffer) where T : struct { return new Size(buffer.Width, buffer.Height); } - /// - /// Gets a to the backing buffer of . - /// - internal static Span GetSpan(this Buffer2D buffer) - where T : struct - { - return buffer.MemorySource.GetSpan(); - } - [Conditional("DEBUG")] private static void CheckColumnRegionsDoNotOverlap( Buffer2D buffer, diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 82a98bfc63..06cfdf5607 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -12,8 +12,12 @@ namespace SixLabors.ImageSharp.Memory /// Represents a buffer of value type objects /// interpreted as a 2D region of x elements. /// + /// + /// Before RC1, this class might be target of API changes, use it on your own risk! + /// /// The value type. - internal sealed class Buffer2D : IDisposable + // TODO: Consider moving this type to the SixLabors.Memory namespace (SixLabors.Core). + public sealed class Buffer2D : IDisposable where T : struct { private MemorySource memorySource; @@ -24,7 +28,7 @@ namespace SixLabors.ImageSharp.Memory /// The buffer to wrap /// The number of elements in a row /// The number of rows - public Buffer2D(MemorySource memorySource, int width, int height) + internal Buffer2D(MemorySource memorySource, int width, int height) { this.memorySource = memorySource; this.Width = width; @@ -44,11 +48,7 @@ namespace SixLabors.ImageSharp.Memory /// /// Gets the backing /// - public MemorySource MemorySource => this.memorySource; - - public Memory Memory => this.MemorySource.Memory; - - public Span Span => this.Memory.Span; + internal MemorySource MemorySource => this.memorySource; /// /// Gets a reference to the element at the specified position. @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Memory /// The x coordinate (row) /// The y coordinate (position at row) /// A reference to the element. - public ref T this[int x, int y] + internal ref T this[int x, int y] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get @@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Memory DebugGuard.MustBeLessThan(x, this.Width, nameof(x)); DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); - Span span = this.Span; + Span span = this.GetSpan(); return ref span[(this.Width * y) + x]; } } @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Memory DebugGuard.MustBeGreaterThan(h, 0, nameof(h)); DebugGuard.MustBeLessThanOrEqualTo(y + h, this.Height, nameof(h)); - Memory slice = this.Memory.Slice(y * this.Width, h * this.Width); + Memory slice = this.GetMemory().Slice(y * this.Width, h * this.Width); var memory = new MemorySource(slice); return new Buffer2D(memory, this.Width, h); } @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Memory /// Swaps the contents of 'destination' with 'source' if the buffers are owned (1), /// copies the contents of 'source' to 'destination' otherwise (2). Buffers should be of same size in case 2! /// - public static void SwapOrCopyContent(Buffer2D destination, Buffer2D source) + internal static void SwapOrCopyContent(Buffer2D destination, Buffer2D source) { MemorySource.SwapOrCopyContent(ref destination.memorySource, ref source.memorySource); SwapDimensionData(destination, source); diff --git a/src/ImageSharp/Memory/BufferArea{T}.cs b/src/ImageSharp/Memory/BufferArea{T}.cs index f71a281390..38f0b8129d 100644 --- a/src/ImageSharp/Memory/BufferArea{T}.cs +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -23,10 +23,10 @@ namespace SixLabors.ImageSharp.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public BufferArea(Buffer2D destinationBuffer, Rectangle rectangle) { - ImageSharp.DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.X, 0, nameof(rectangle)); - ImageSharp.DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.Y, 0, nameof(rectangle)); - ImageSharp.DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, destinationBuffer.Width, nameof(rectangle)); - ImageSharp.DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, destinationBuffer.Height, nameof(rectangle)); + DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.X, 0, nameof(rectangle)); + DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.Y, 0, nameof(rectangle)); + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, destinationBuffer.Width, nameof(rectangle)); + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, destinationBuffer.Height, nameof(rectangle)); this.DestinationBuffer = destinationBuffer; this.Rectangle = rectangle; @@ -122,8 +122,8 @@ namespace SixLabors.ImageSharp.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public BufferArea GetSubArea(Rectangle rectangle) { - ImageSharp.DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, this.Rectangle.Width, nameof(rectangle)); - ImageSharp.DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, this.Rectangle.Height, nameof(rectangle)); + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, this.Rectangle.Width, nameof(rectangle)); + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, this.Rectangle.Height, nameof(rectangle)); int x = this.Rectangle.X + rectangle.X; int y = this.Rectangle.Y + rectangle.Y; @@ -161,4 +161,4 @@ namespace SixLabors.ImageSharp.Memory } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Memory/MemoryOwnerExtensions.cs b/src/ImageSharp/Memory/MemoryOwnerExtensions.cs index d82e3aaee7..6d4468f788 100644 --- a/src/ImageSharp/Memory/MemoryOwnerExtensions.cs +++ b/src/ImageSharp/Memory/MemoryOwnerExtensions.cs @@ -13,7 +13,6 @@ namespace SixLabors.ImageSharp.Memory /// internal static class MemoryOwnerExtensions { - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span GetSpan(this IMemoryOwner buffer) => buffer.Memory.Span; diff --git a/src/ImageSharp/Memory/RowInterval.cs b/src/ImageSharp/Memory/RowInterval.cs index 815918754a..13037c889f 100644 --- a/src/ImageSharp/Memory/RowInterval.cs +++ b/src/ImageSharp/Memory/RowInterval.cs @@ -10,26 +10,32 @@ namespace SixLabors.ImageSharp.Memory /// /// Represents an interval of rows in a and/or /// - internal readonly struct RowInterval : IEquatable + /// + /// Before RC1, this class might be target of API changes, use it on your own risk! + /// + // TODO: Consider moving this type to the SixLabors.Memory namespace (SixLabors.Core). + public readonly struct RowInterval : IEquatable { /// /// Initializes a new instance of the struct. /// + /// The inclusive minimum row. + /// The exclusive maximum row. public RowInterval(int min, int max) { - DebugGuard.MustBeLessThan(min, max, nameof(min)); + Guard.MustBeLessThan(min, max, nameof(min)); this.Min = min; this.Max = max; } /// - /// Gets the INCLUSIVE minimum. + /// Gets the inclusive minimum row. /// public int Min { get; } /// - /// Gets the EXCLUSIVE maximum. + /// Gets the exclusive maximum row. /// public int Max { get; } @@ -38,33 +44,48 @@ namespace SixLabors.ImageSharp.Memory /// public int Height => this.Max - this.Min; + /// + /// Returns a boolean indicating whether the given two -s are equal. + /// + /// The first to compare. + /// The second to compare. + /// True if the given -s are equal; False otherwise. public static bool operator ==(RowInterval left, RowInterval right) { return left.Equals(right); } + /// + /// Returns a boolean indicating whether the given two -s are not equal. + /// + /// The first to compare. + /// The second to compare. + /// True if the given -s are not equal; False otherwise. public static bool operator !=(RowInterval left, RowInterval right) { return !left.Equals(right); } /// - public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]"; - - public RowInterval Slice(int start) => new RowInterval(this.Min + start, this.Max); - - public RowInterval Slice(int start, int length) => new RowInterval(this.Min + start, this.Min + start + length); - public bool Equals(RowInterval other) { return this.Min == other.Min && this.Max == other.Max; } + /// public override bool Equals(object obj) { return !ReferenceEquals(null, obj) && obj is RowInterval other && this.Equals(other); } + /// public override int GetHashCode() => HashCode.Combine(this.Min, this.Max); + + /// + public override string ToString() => $"RowInterval [{this.Min}->{this.Max}]"; + + internal RowInterval Slice(int start) => new RowInterval(this.Min + start, this.Max); + + internal RowInterval Slice(int start, int length) => new RowInterval(this.Min + start, this.Min + start + length); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs index 0188c662ed..845e4ee734 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs @@ -12,9 +12,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] internal sealed class ExifTagDescriptionAttribute : Attribute { - private readonly object value; - private readonly string description; - /// /// Initializes a new instance of the class. /// @@ -22,8 +19,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// The description for the value of the exif tag. public ExifTagDescriptionAttribute(object value, string description) { - this.value = value; - this.description = description; } /// diff --git a/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs b/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs index 62dc0fcf59..b2f6261ef5 100644 --- a/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs +++ b/src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs @@ -9,17 +9,17 @@ namespace SixLabors.ImageSharp.PixelFormats public enum PixelAlphaCompositionMode { /// - /// returns the destination over the source. + /// Returns the destination over the source. /// SrcOver = 0, /// - /// returns the source colors. + /// Returns the source colors. /// Src, /// - /// returns the source over the destination. + /// Returns the source over the destination. /// SrcAtop, diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index e94ea452be..51ee5d12d9 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -26,7 +26,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders where TPixel : struct, IPixel { - internal class NormalSrc : PixelBlender + + /// + /// A pixel blender that implements the "NormalSrc" composition equation. + /// + public class NormalSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -61,7 +65,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrc : PixelBlender + + /// + /// A pixel blender that implements the "MultiplySrc" composition equation. + /// + public class MultiplySrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -96,7 +104,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrc : PixelBlender + + /// + /// A pixel blender that implements the "AddSrc" composition equation. + /// + public class AddSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -131,7 +143,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrc : PixelBlender + + /// + /// A pixel blender that implements the "SubtractSrc" composition equation. + /// + public class SubtractSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -166,7 +182,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrc : PixelBlender + + /// + /// A pixel blender that implements the "ScreenSrc" composition equation. + /// + public class ScreenSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -201,7 +221,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrc : PixelBlender + + /// + /// A pixel blender that implements the "DarkenSrc" composition equation. + /// + public class DarkenSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -236,7 +260,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrc : PixelBlender + + /// + /// A pixel blender that implements the "LightenSrc" composition equation. + /// + public class LightenSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -271,7 +299,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrc : PixelBlender + + /// + /// A pixel blender that implements the "OverlaySrc" composition equation. + /// + public class OverlaySrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -306,7 +338,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrc : PixelBlender + + /// + /// A pixel blender that implements the "HardLightSrc" composition equation. + /// + public class HardLightSrc : PixelBlender { /// /// Gets the static instance of this blender. @@ -341,7 +377,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalSrcAtop : PixelBlender + + /// + /// A pixel blender that implements the "NormalSrcAtop" composition equation. + /// + public class NormalSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -376,7 +416,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrcAtop : PixelBlender + + /// + /// A pixel blender that implements the "MultiplySrcAtop" composition equation. + /// + public class MultiplySrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -411,7 +455,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrcAtop : PixelBlender + + /// + /// A pixel blender that implements the "AddSrcAtop" composition equation. + /// + public class AddSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -446,7 +494,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrcAtop : PixelBlender + + /// + /// A pixel blender that implements the "SubtractSrcAtop" composition equation. + /// + public class SubtractSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -481,7 +533,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrcAtop : PixelBlender + + /// + /// A pixel blender that implements the "ScreenSrcAtop" composition equation. + /// + public class ScreenSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -516,7 +572,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrcAtop : PixelBlender + + /// + /// A pixel blender that implements the "DarkenSrcAtop" composition equation. + /// + public class DarkenSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -551,7 +611,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrcAtop : PixelBlender + + /// + /// A pixel blender that implements the "LightenSrcAtop" composition equation. + /// + public class LightenSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -586,7 +650,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrcAtop : PixelBlender + + /// + /// A pixel blender that implements the "OverlaySrcAtop" composition equation. + /// + public class OverlaySrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -621,7 +689,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrcAtop : PixelBlender + + /// + /// A pixel blender that implements the "HardLightSrcAtop" composition equation. + /// + public class HardLightSrcAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -656,7 +728,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalSrcOver : PixelBlender + + /// + /// A pixel blender that implements the "NormalSrcOver" composition equation. + /// + public class NormalSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -691,7 +767,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrcOver : PixelBlender + + /// + /// A pixel blender that implements the "MultiplySrcOver" composition equation. + /// + public class MultiplySrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -726,7 +806,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrcOver : PixelBlender + + /// + /// A pixel blender that implements the "AddSrcOver" composition equation. + /// + public class AddSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -761,7 +845,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrcOver : PixelBlender + + /// + /// A pixel blender that implements the "SubtractSrcOver" composition equation. + /// + public class SubtractSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -796,7 +884,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrcOver : PixelBlender + + /// + /// A pixel blender that implements the "ScreenSrcOver" composition equation. + /// + public class ScreenSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -831,7 +923,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrcOver : PixelBlender + + /// + /// A pixel blender that implements the "DarkenSrcOver" composition equation. + /// + public class DarkenSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -866,7 +962,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrcOver : PixelBlender + + /// + /// A pixel blender that implements the "LightenSrcOver" composition equation. + /// + public class LightenSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -901,7 +1001,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrcOver : PixelBlender + + /// + /// A pixel blender that implements the "OverlaySrcOver" composition equation. + /// + public class OverlaySrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -936,7 +1040,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrcOver : PixelBlender + + /// + /// A pixel blender that implements the "HardLightSrcOver" composition equation. + /// + public class HardLightSrcOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -971,7 +1079,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalSrcIn : PixelBlender + + /// + /// A pixel blender that implements the "NormalSrcIn" composition equation. + /// + public class NormalSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1006,7 +1118,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrcIn : PixelBlender + + /// + /// A pixel blender that implements the "MultiplySrcIn" composition equation. + /// + public class MultiplySrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1041,7 +1157,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrcIn : PixelBlender + + /// + /// A pixel blender that implements the "AddSrcIn" composition equation. + /// + public class AddSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1076,7 +1196,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrcIn : PixelBlender + + /// + /// A pixel blender that implements the "SubtractSrcIn" composition equation. + /// + public class SubtractSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1111,7 +1235,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrcIn : PixelBlender + + /// + /// A pixel blender that implements the "ScreenSrcIn" composition equation. + /// + public class ScreenSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1146,7 +1274,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrcIn : PixelBlender + + /// + /// A pixel blender that implements the "DarkenSrcIn" composition equation. + /// + public class DarkenSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1181,7 +1313,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrcIn : PixelBlender + + /// + /// A pixel blender that implements the "LightenSrcIn" composition equation. + /// + public class LightenSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1216,7 +1352,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrcIn : PixelBlender + + /// + /// A pixel blender that implements the "OverlaySrcIn" composition equation. + /// + public class OverlaySrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1251,7 +1391,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrcIn : PixelBlender + + /// + /// A pixel blender that implements the "HardLightSrcIn" composition equation. + /// + public class HardLightSrcIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -1286,7 +1430,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalSrcOut : PixelBlender + + /// + /// A pixel blender that implements the "NormalSrcOut" composition equation. + /// + public class NormalSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1321,7 +1469,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrcOut : PixelBlender + + /// + /// A pixel blender that implements the "MultiplySrcOut" composition equation. + /// + public class MultiplySrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1356,7 +1508,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrcOut : PixelBlender + + /// + /// A pixel blender that implements the "AddSrcOut" composition equation. + /// + public class AddSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1391,7 +1547,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrcOut : PixelBlender + + /// + /// A pixel blender that implements the "SubtractSrcOut" composition equation. + /// + public class SubtractSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1426,7 +1586,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrcOut : PixelBlender + + /// + /// A pixel blender that implements the "ScreenSrcOut" composition equation. + /// + public class ScreenSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1461,7 +1625,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrcOut : PixelBlender + + /// + /// A pixel blender that implements the "DarkenSrcOut" composition equation. + /// + public class DarkenSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1496,7 +1664,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrcOut : PixelBlender + + /// + /// A pixel blender that implements the "LightenSrcOut" composition equation. + /// + public class LightenSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1531,7 +1703,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrcOut : PixelBlender + + /// + /// A pixel blender that implements the "OverlaySrcOut" composition equation. + /// + public class OverlaySrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1566,7 +1742,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrcOut : PixelBlender + + /// + /// A pixel blender that implements the "HardLightSrcOut" composition equation. + /// + public class HardLightSrcOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -1601,7 +1781,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDest : PixelBlender + + /// + /// A pixel blender that implements the "NormalDest" composition equation. + /// + public class NormalDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1636,7 +1820,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDest : PixelBlender + + /// + /// A pixel blender that implements the "MultiplyDest" composition equation. + /// + public class MultiplyDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1671,7 +1859,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDest : PixelBlender + + /// + /// A pixel blender that implements the "AddDest" composition equation. + /// + public class AddDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1706,7 +1898,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDest : PixelBlender + + /// + /// A pixel blender that implements the "SubtractDest" composition equation. + /// + public class SubtractDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1741,7 +1937,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDest : PixelBlender + + /// + /// A pixel blender that implements the "ScreenDest" composition equation. + /// + public class ScreenDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1776,7 +1976,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDest : PixelBlender + + /// + /// A pixel blender that implements the "DarkenDest" composition equation. + /// + public class DarkenDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1811,7 +2015,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDest : PixelBlender + + /// + /// A pixel blender that implements the "LightenDest" composition equation. + /// + public class LightenDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1846,7 +2054,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDest : PixelBlender + + /// + /// A pixel blender that implements the "OverlayDest" composition equation. + /// + public class OverlayDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1881,7 +2093,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDest : PixelBlender + + /// + /// A pixel blender that implements the "HardLightDest" composition equation. + /// + public class HardLightDest : PixelBlender { /// /// Gets the static instance of this blender. @@ -1916,7 +2132,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDestAtop : PixelBlender + + /// + /// A pixel blender that implements the "NormalDestAtop" composition equation. + /// + public class NormalDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -1951,7 +2171,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDestAtop : PixelBlender + + /// + /// A pixel blender that implements the "MultiplyDestAtop" composition equation. + /// + public class MultiplyDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -1986,7 +2210,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDestAtop : PixelBlender + + /// + /// A pixel blender that implements the "AddDestAtop" composition equation. + /// + public class AddDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2021,7 +2249,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDestAtop : PixelBlender + + /// + /// A pixel blender that implements the "SubtractDestAtop" composition equation. + /// + public class SubtractDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2056,7 +2288,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDestAtop : PixelBlender + + /// + /// A pixel blender that implements the "ScreenDestAtop" composition equation. + /// + public class ScreenDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2091,7 +2327,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDestAtop : PixelBlender + + /// + /// A pixel blender that implements the "DarkenDestAtop" composition equation. + /// + public class DarkenDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2126,7 +2366,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDestAtop : PixelBlender + + /// + /// A pixel blender that implements the "LightenDestAtop" composition equation. + /// + public class LightenDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2161,7 +2405,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDestAtop : PixelBlender + + /// + /// A pixel blender that implements the "OverlayDestAtop" composition equation. + /// + public class OverlayDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2196,7 +2444,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDestAtop : PixelBlender + + /// + /// A pixel blender that implements the "HardLightDestAtop" composition equation. + /// + public class HardLightDestAtop : PixelBlender { /// /// Gets the static instance of this blender. @@ -2231,7 +2483,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDestOver : PixelBlender + + /// + /// A pixel blender that implements the "NormalDestOver" composition equation. + /// + public class NormalDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2266,7 +2522,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDestOver : PixelBlender + + /// + /// A pixel blender that implements the "MultiplyDestOver" composition equation. + /// + public class MultiplyDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2301,7 +2561,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDestOver : PixelBlender + + /// + /// A pixel blender that implements the "AddDestOver" composition equation. + /// + public class AddDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2336,7 +2600,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDestOver : PixelBlender + + /// + /// A pixel blender that implements the "SubtractDestOver" composition equation. + /// + public class SubtractDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2371,7 +2639,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDestOver : PixelBlender + + /// + /// A pixel blender that implements the "ScreenDestOver" composition equation. + /// + public class ScreenDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2406,7 +2678,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDestOver : PixelBlender + + /// + /// A pixel blender that implements the "DarkenDestOver" composition equation. + /// + public class DarkenDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2441,7 +2717,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDestOver : PixelBlender + + /// + /// A pixel blender that implements the "LightenDestOver" composition equation. + /// + public class LightenDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2476,7 +2756,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDestOver : PixelBlender + + /// + /// A pixel blender that implements the "OverlayDestOver" composition equation. + /// + public class OverlayDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2511,7 +2795,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDestOver : PixelBlender + + /// + /// A pixel blender that implements the "HardLightDestOver" composition equation. + /// + public class HardLightDestOver : PixelBlender { /// /// Gets the static instance of this blender. @@ -2546,7 +2834,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDestIn : PixelBlender + + /// + /// A pixel blender that implements the "NormalDestIn" composition equation. + /// + public class NormalDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2581,7 +2873,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDestIn : PixelBlender + + /// + /// A pixel blender that implements the "MultiplyDestIn" composition equation. + /// + public class MultiplyDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2616,7 +2912,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDestIn : PixelBlender + + /// + /// A pixel blender that implements the "AddDestIn" composition equation. + /// + public class AddDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2651,7 +2951,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDestIn : PixelBlender + + /// + /// A pixel blender that implements the "SubtractDestIn" composition equation. + /// + public class SubtractDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2686,7 +2990,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDestIn : PixelBlender + + /// + /// A pixel blender that implements the "ScreenDestIn" composition equation. + /// + public class ScreenDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2721,7 +3029,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDestIn : PixelBlender + + /// + /// A pixel blender that implements the "DarkenDestIn" composition equation. + /// + public class DarkenDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2756,7 +3068,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDestIn : PixelBlender + + /// + /// A pixel blender that implements the "LightenDestIn" composition equation. + /// + public class LightenDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2791,7 +3107,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDestIn : PixelBlender + + /// + /// A pixel blender that implements the "OverlayDestIn" composition equation. + /// + public class OverlayDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2826,7 +3146,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDestIn : PixelBlender + + /// + /// A pixel blender that implements the "HardLightDestIn" composition equation. + /// + public class HardLightDestIn : PixelBlender { /// /// Gets the static instance of this blender. @@ -2861,7 +3185,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDestOut : PixelBlender + + /// + /// A pixel blender that implements the "NormalDestOut" composition equation. + /// + public class NormalDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -2896,7 +3224,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDestOut : PixelBlender + + /// + /// A pixel blender that implements the "MultiplyDestOut" composition equation. + /// + public class MultiplyDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -2931,7 +3263,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDestOut : PixelBlender + + /// + /// A pixel blender that implements the "AddDestOut" composition equation. + /// + public class AddDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -2966,7 +3302,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDestOut : PixelBlender + + /// + /// A pixel blender that implements the "SubtractDestOut" composition equation. + /// + public class SubtractDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -3001,7 +3341,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDestOut : PixelBlender + + /// + /// A pixel blender that implements the "ScreenDestOut" composition equation. + /// + public class ScreenDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -3036,7 +3380,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDestOut : PixelBlender + + /// + /// A pixel blender that implements the "DarkenDestOut" composition equation. + /// + public class DarkenDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -3071,7 +3419,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDestOut : PixelBlender + + /// + /// A pixel blender that implements the "LightenDestOut" composition equation. + /// + public class LightenDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -3106,7 +3458,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDestOut : PixelBlender + + /// + /// A pixel blender that implements the "OverlayDestOut" composition equation. + /// + public class OverlayDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -3141,7 +3497,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDestOut : PixelBlender + + /// + /// A pixel blender that implements the "HardLightDestOut" composition equation. + /// + public class HardLightDestOut : PixelBlender { /// /// Gets the static instance of this blender. @@ -3176,7 +3536,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalClear : PixelBlender + + /// + /// A pixel blender that implements the "NormalClear" composition equation. + /// + public class NormalClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3211,7 +3575,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyClear : PixelBlender + + /// + /// A pixel blender that implements the "MultiplyClear" composition equation. + /// + public class MultiplyClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3246,7 +3614,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddClear : PixelBlender + + /// + /// A pixel blender that implements the "AddClear" composition equation. + /// + public class AddClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3281,7 +3653,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractClear : PixelBlender + + /// + /// A pixel blender that implements the "SubtractClear" composition equation. + /// + public class SubtractClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3316,7 +3692,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenClear : PixelBlender + + /// + /// A pixel blender that implements the "ScreenClear" composition equation. + /// + public class ScreenClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3351,7 +3731,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenClear : PixelBlender + + /// + /// A pixel blender that implements the "DarkenClear" composition equation. + /// + public class DarkenClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3386,7 +3770,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenClear : PixelBlender + + /// + /// A pixel blender that implements the "LightenClear" composition equation. + /// + public class LightenClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3421,7 +3809,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayClear : PixelBlender + + /// + /// A pixel blender that implements the "OverlayClear" composition equation. + /// + public class OverlayClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3456,7 +3848,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightClear : PixelBlender + + /// + /// A pixel blender that implements the "HardLightClear" composition equation. + /// + public class HardLightClear : PixelBlender { /// /// Gets the static instance of this blender. @@ -3491,7 +3887,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalXor : PixelBlender + + /// + /// A pixel blender that implements the "NormalXor" composition equation. + /// + public class NormalXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3526,7 +3926,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyXor : PixelBlender + + /// + /// A pixel blender that implements the "MultiplyXor" composition equation. + /// + public class MultiplyXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3561,7 +3965,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddXor : PixelBlender + + /// + /// A pixel blender that implements the "AddXor" composition equation. + /// + public class AddXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3596,7 +4004,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractXor : PixelBlender + + /// + /// A pixel blender that implements the "SubtractXor" composition equation. + /// + public class SubtractXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3631,7 +4043,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenXor : PixelBlender + + /// + /// A pixel blender that implements the "ScreenXor" composition equation. + /// + public class ScreenXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3666,7 +4082,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenXor : PixelBlender + + /// + /// A pixel blender that implements the "DarkenXor" composition equation. + /// + public class DarkenXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3701,7 +4121,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenXor : PixelBlender + + /// + /// A pixel blender that implements the "LightenXor" composition equation. + /// + public class LightenXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3736,7 +4160,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayXor : PixelBlender + + /// + /// A pixel blender that implements the "OverlayXor" composition equation. + /// + public class OverlayXor : PixelBlender { /// /// Gets the static instance of this blender. @@ -3771,7 +4199,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightXor : PixelBlender + + /// + /// A pixel blender that implements the "HardLightXor" composition equation. + /// + public class HardLightXor : PixelBlender { /// /// Gets the static instance of this blender. diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index 66a00975e1..55eb01df31 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -68,9 +68,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders foreach(var blender in blenders) { var blender_composer= $"{blender}{composer}"; - #> - internal class <#= blender_composer#> : PixelBlender + /// + /// A pixel blender that implements the "<#= blender_composer#>" composition equation. + /// + public class <#= blender_composer#> : PixelBlender { /// /// Gets the static instance of this blender. diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index 8b92f95c36..4616ce36c6 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -3,8 +3,6 @@ // - -using System; using System.Numerics; using System.Runtime.CompilerServices; @@ -16,7 +14,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders - + /// + /// Returns the result of the "NormalSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -25,6 +29,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "NormalSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -33,6 +44,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Normal(backdrop, source)); } + /// + /// Returns the result of the "NormalSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -41,14 +59,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Normal(backdrop, source)); } + /// + /// Returns the result of the "NormalSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Normal(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "NormalSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -57,12 +89,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "NormalDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "NormalDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -71,6 +117,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Normal(source, backdrop)); } + /// + /// Returns the result of the "NormalDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -79,14 +132,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Normal(source, backdrop)); } + /// + /// Returns the result of the "NormalDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Normal(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "NormalDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -95,6 +162,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "NormalXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -103,6 +177,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "NormalClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 NormalClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -111,6 +192,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "NormalSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -122,6 +211,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -133,6 +230,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -144,6 +249,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -155,6 +268,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -166,6 +287,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -177,6 +306,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -188,6 +325,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -199,6 +344,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -210,6 +363,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -221,6 +382,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -232,6 +401,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "NormalXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel NormalXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -243,6 +420,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplySrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -251,6 +435,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "MultiplySrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -259,6 +450,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Multiply(backdrop, source)); } + /// + /// Returns the result of the "MultiplySrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -267,14 +465,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Multiply(backdrop, source)); } + /// + /// Returns the result of the "MultiplySrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Multiply(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "MultiplySrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplySrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -283,12 +495,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "MultiplyDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "MultiplyDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -297,6 +523,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Multiply(source, backdrop)); } + /// + /// Returns the result of the "MultiplyDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -305,14 +538,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Multiply(source, backdrop)); } + /// + /// Returns the result of the "MultiplyDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Multiply(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "MultiplyDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -321,6 +568,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "MultiplyXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -329,6 +583,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "MultiplyClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 MultiplyClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -337,6 +598,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "MultiplySrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -348,6 +617,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplySrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -359,6 +636,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplySrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -370,6 +655,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplySrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -381,6 +674,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplySrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplySrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -392,6 +693,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -403,6 +712,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -414,6 +731,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -425,6 +750,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -436,6 +769,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -447,6 +788,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -458,6 +807,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "MultiplyXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel MultiplyXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -469,6 +826,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -477,6 +841,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "AddSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -485,6 +856,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Add(backdrop, source)); } + /// + /// Returns the result of the "AddSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -493,14 +871,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Add(backdrop, source)); } + /// + /// Returns the result of the "AddSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Add(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "AddSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -509,12 +901,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "AddDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "AddDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -523,6 +929,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Add(source, backdrop)); } + /// + /// Returns the result of the "AddDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -531,14 +944,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Add(source, backdrop)); } + /// + /// Returns the result of the "AddDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Add(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "AddDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -547,6 +974,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "AddXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -555,6 +989,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "AddClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 AddClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -563,6 +1004,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "AddSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -574,6 +1023,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -585,6 +1042,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -596,6 +1061,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -607,6 +1080,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -618,6 +1099,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -629,6 +1118,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -640,6 +1137,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -651,6 +1156,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -662,6 +1175,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -673,6 +1194,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -684,6 +1213,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "AddXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel AddXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -695,6 +1232,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -703,6 +1247,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "SubtractSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -711,6 +1262,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Subtract(backdrop, source)); } + /// + /// Returns the result of the "SubtractSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -719,14 +1277,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Subtract(backdrop, source)); } + /// + /// Returns the result of the "SubtractSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Subtract(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "SubtractSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -735,12 +1307,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "SubtractDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "SubtractDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -749,6 +1335,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Subtract(source, backdrop)); } + /// + /// Returns the result of the "SubtractDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -757,14 +1350,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Subtract(source, backdrop)); } + /// + /// Returns the result of the "SubtractDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Subtract(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "SubtractDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -773,6 +1380,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "SubtractXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -781,6 +1395,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "SubtractClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 SubtractClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -789,6 +1410,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "SubtractSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -800,6 +1429,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -811,6 +1448,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -822,6 +1467,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -833,6 +1486,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -844,6 +1505,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -855,6 +1524,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -866,6 +1543,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -877,6 +1562,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -888,6 +1581,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -899,6 +1600,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -910,6 +1619,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "SubtractXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel SubtractXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -921,6 +1638,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -929,6 +1653,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "ScreenSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -937,6 +1668,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Screen(backdrop, source)); } + /// + /// Returns the result of the "ScreenSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -945,14 +1683,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Screen(backdrop, source)); } + /// + /// Returns the result of the "ScreenSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Screen(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "ScreenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -961,12 +1713,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "ScreenDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "ScreenDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -975,6 +1741,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Screen(source, backdrop)); } + /// + /// Returns the result of the "ScreenDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -983,14 +1756,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Screen(source, backdrop)); } + /// + /// Returns the result of the "ScreenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Screen(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "ScreenDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -999,6 +1786,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "ScreenXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -1007,6 +1801,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "ScreenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 ScreenClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -1015,6 +1816,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "ScreenSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1026,6 +1835,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1037,6 +1854,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1048,6 +1873,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1059,6 +1892,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1070,6 +1911,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1081,6 +1930,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1092,6 +1949,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1103,6 +1968,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1114,6 +1987,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1125,6 +2006,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1136,6 +2025,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "ScreenXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel ScreenXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1147,6 +2044,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -1155,6 +2059,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "DarkenSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1163,6 +2074,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Darken(backdrop, source)); } + /// + /// Returns the result of the "DarkenSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1171,14 +2089,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Darken(backdrop, source)); } + /// + /// Returns the result of the "DarkenSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Darken(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "DarkenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1187,12 +2119,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "DarkenDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "DarkenDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1201,6 +2147,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Darken(source, backdrop)); } + /// + /// Returns the result of the "DarkenDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1209,14 +2162,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Darken(source, backdrop)); } + /// + /// Returns the result of the "DarkenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Darken(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "DarkenDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1225,6 +2192,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "DarkenXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -1233,6 +2207,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "DarkenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 DarkenClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -1241,6 +2222,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "DarkenSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1252,6 +2241,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1263,6 +2260,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1274,6 +2279,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1285,6 +2298,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1296,6 +2317,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1307,6 +2336,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1318,6 +2355,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1329,6 +2374,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1340,6 +2393,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1351,6 +2412,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1362,6 +2431,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "DarkenXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel DarkenXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1373,6 +2450,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -1381,6 +2465,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "LightenSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1389,6 +2480,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Lighten(backdrop, source)); } + /// + /// Returns the result of the "LightenSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1397,14 +2495,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Lighten(backdrop, source)); } + /// + /// Returns the result of the "LightenSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Lighten(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "LightenSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1413,12 +2525,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "LightenDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "LightenDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1427,6 +2553,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Lighten(source, backdrop)); } + /// + /// Returns the result of the "LightenDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1435,14 +2568,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Lighten(source, backdrop)); } + /// + /// Returns the result of the "LightenDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Lighten(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "LightenDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1451,6 +2598,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "LightenXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -1459,6 +2613,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "LightenClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 LightenClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -1467,6 +2628,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "LightenSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1478,6 +2647,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1489,6 +2666,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1500,6 +2685,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1511,6 +2704,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1522,6 +2723,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1533,6 +2742,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1544,6 +2761,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1555,6 +2780,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1566,6 +2799,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1577,6 +2818,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1588,6 +2837,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "LightenXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel LightenXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1599,6 +2856,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlaySrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -1607,6 +2871,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "OverlaySrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1615,6 +2886,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, Overlay(backdrop, source)); } + /// + /// Returns the result of the "OverlaySrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1623,14 +2901,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, Overlay(backdrop, source)); } + /// + /// Returns the result of the "OverlaySrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, Overlay(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "OverlaySrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlaySrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1639,12 +2931,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "OverlayDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "OverlayDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1653,6 +2959,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, Overlay(source, backdrop)); } + /// + /// Returns the result of the "OverlayDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1661,14 +2974,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, Overlay(source, backdrop)); } + /// + /// Returns the result of the "OverlayDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, Overlay(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "OverlayDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1677,6 +3004,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "OverlayXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -1685,6 +3019,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "OverlayClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 OverlayClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -1693,6 +3034,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "OverlaySrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1704,6 +3053,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlaySrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1715,6 +3072,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlaySrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1726,6 +3091,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlaySrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1737,6 +3110,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlaySrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlaySrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1748,6 +3129,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1759,6 +3148,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1770,6 +3167,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1781,6 +3186,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1792,6 +3205,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1803,6 +3224,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1814,6 +3243,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "OverlayXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel OverlayXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1825,6 +3262,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightSrc" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrc(Vector4 backdrop, Vector4 source, float opacity) { @@ -1833,6 +3277,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "HardLightSrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1841,6 +3292,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, HardLight(backdrop, source)); } + /// + /// Returns the result of the "HardLightSrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1849,14 +3307,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, HardLight(backdrop, source)); } + /// + /// Returns the result of the "HardLightSrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, HardLight(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "HardLightSrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightSrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1865,12 +3337,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "HardLightDest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "HardLightDestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -1879,6 +3365,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, HardLight(source, backdrop)); } + /// + /// Returns the result of the "HardLightDestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -1887,14 +3380,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, HardLight(source, backdrop)); } + /// + /// Returns the result of the "HardLightDestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, HardLight(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "HardLightDestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightDestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -1903,6 +3410,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "HardLightXor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightXor(Vector4 backdrop, Vector4 source, float opacity) { @@ -1911,6 +3425,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "HardLightClear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 HardLightClear(Vector4 backdrop, Vector4 source, float opacity) { @@ -1919,6 +3440,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Clear(backdrop, source); } + /// + /// Returns the result of the "HardLightSrc" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrc(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1930,6 +3459,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightSrcAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrcAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1941,6 +3478,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightSrcOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrcOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1952,6 +3497,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightSrcIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrcIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1963,6 +3516,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightSrcOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightSrcOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1974,6 +3535,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightDest" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDest(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1985,6 +3554,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightDestAtop" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDestAtop(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -1996,6 +3573,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightDestOver" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDestOver(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -2007,6 +3592,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightDestIn" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDestIn(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -2018,6 +3611,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightDestOut" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightDestOut(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -2029,6 +3630,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightClear" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightClear(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel @@ -2040,6 +3649,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } + /// + /// Returns the result of the "HardLightXor" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel HardLightXor(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index f591ee873d..bc4fa17c27 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -17,8 +17,6 @@ // Note use of MethodImplOptions.NoInlining. We have tests that are failing on certain architectures when // AggresiveInlining is used. Confirmed on Intel i7-6600U in 64bit. #> - -using System; using System.Numerics; using System.Runtime.CompilerServices; @@ -26,9 +24,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { internal static partial class PorterDuffFunctions { - <# void GeneratePixelBlenders(string blender) { #> - + /// + /// Returns the result of the "<#=blender#>Src" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity) { @@ -37,6 +40,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return source; } + /// + /// Returns the result of the "<#=blender#>SrcAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -45,6 +55,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(backdrop, source, <#=blender#>(backdrop, source)); } + /// + /// Returns the result of the "<#=blender#>SrcOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -53,14 +70,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(backdrop, source, <#=blender#>(backdrop, source)); } + /// + /// Returns the result of the "<#=blender#>SrcIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(backdrop, source, <#=blender#>(backdrop, source)); + return In(backdrop, source); } + /// + /// Returns the result of the "<#=blender#>SrcOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -69,12 +100,26 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(backdrop, source); } + /// + /// Returns the result of the "<#=blender#>Dest" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>Dest(Vector4 backdrop, Vector4 source, float opacity) { return backdrop; } + /// + /// Returns the result of the "<#=blender#>DestAtop" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity) { @@ -83,6 +128,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Atop(source, backdrop, <#=blender#>(source, backdrop)); } + /// + /// Returns the result of the "<#=blender#>DestOver" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity) { @@ -91,14 +143,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Over(source, backdrop, <#=blender#>(source, backdrop)); } + /// + /// Returns the result of the "<#=blender#>DestIn" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - return In(source, backdrop, <#=blender#>(source, backdrop)); + return In(source, backdrop); } + /// + /// Returns the result of the "<#=blender#>DestOut" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity) { @@ -107,6 +173,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Out(source, backdrop); } + /// + /// Returns the result of the "<#=blender#>Xor" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity) { @@ -115,6 +188,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Xor(backdrop, source); } + /// + /// Returns the result of the "<#=blender#>Clear" compositing equation. + /// + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.NoInlining)] public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity) { @@ -127,6 +207,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders <# void GenerateGenericPixelBlender(string blender, string composer) { #> + /// + /// Returns the result of the "<#=blender#><#=composer#>" compositing equation. + /// + /// The pixel format. + /// The backdrop vector. + /// The source vector. + /// The source opacity. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TPixel <#=blender#><#=composer#>(TPixel backdrop, TPixel source, float opacity) where TPixel : struct, IPixel diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index 9111520a02..97b4458aff 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.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; @@ -21,11 +21,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders internal static partial class PorterDuffFunctions { /// - /// Source over backdrop + /// Returns the result of the "Normal" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Normal(Vector4 backdrop, Vector4 source) { @@ -33,11 +33,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Source multiplied by backdrop + /// Returns the result of the "Multiply" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Multiply(Vector4 backdrop, Vector4 source) { @@ -45,11 +45,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Source added to backdrop + /// Returns the result of the "Add" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Add(Vector4 backdrop, Vector4 source) { @@ -57,11 +57,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Source subtracted from backdrop + /// Returns the result of the "Subtract" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Subtract(Vector4 backdrop, Vector4 source) { @@ -69,11 +69,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Complement of source multiplied by the complement of backdrop + /// Returns the result of the "Screen" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Screen(Vector4 backdrop, Vector4 source) { @@ -81,11 +81,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Per element, chooses the smallest value of source and backdrop + /// Returns the result of the "Darken" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Darken(Vector4 backdrop, Vector4 source) { @@ -93,11 +93,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Per element, chooses the largest value of source and backdrop + /// Returns the result of the "Lighten" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Lighten(Vector4 backdrop, Vector4 source) { @@ -105,11 +105,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Overlays source over backdrop + /// Returns the result of the "Overlay" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Overlay(Vector4 backdrop, Vector4 source) { @@ -121,11 +121,11 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } /// - /// Hard light effect + /// Returns the result of the "HardLight" compositing equation. /// - /// Backdrop color - /// Source color - /// Output color + /// The backdrop vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 HardLight(Vector4 backdrop, Vector4 source) { @@ -145,22 +145,29 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float OverlayValueFunction(float backdrop, float source) { - return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); + return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - (2 * (1 - source) * (1 - backdrop)); } + /// + /// Returns the result of the "Over" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The amount to blend. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Over(Vector4 dst, Vector4 src, Vector4 blend) + public static Vector4 Over(Vector4 destination, Vector4 source, Vector4 blend) { // calculate weights - float blendW = dst.W * src.W; - float dstW = dst.W - blendW; - float srcW = src.W - blendW; + float blendW = destination.W * source.W; + float dstW = destination.W - blendW; + float srcW = source.W - blendW; // calculate final alpha - float alpha = dstW + srcW + blendW; + float alpha = dstW + source.W; // calculate final color - Vector4 color = (dst * dstW) + (src * srcW) + (blend * blendW); + Vector4 color = (destination * dstW) + (source * srcW) + (blend * blendW); // unpremultiply color /= MathF.Max(alpha, Constants.Epsilon); @@ -169,18 +176,25 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return color; } + /// + /// Returns the result of the "Atop" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The amount to blend. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Atop(Vector4 dst, Vector4 src, Vector4 blend) + public static Vector4 Atop(Vector4 destination, Vector4 source, Vector4 blend) { // calculate weights - float blendW = dst.W * src.W; - float dstW = dst.W - blendW; + float blendW = destination.W * source.W; + float dstW = destination.W - blendW; // calculate final alpha - float alpha = dstW + blendW; + float alpha = destination.W; // calculate final color - Vector4 color = (dst * dstW) + (blend * blendW); + Vector4 color = (destination * dstW) + (blend * blendW); // unpremultiply color /= MathF.Max(alpha, Constants.Epsilon); @@ -189,38 +203,56 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return color; } + /// + /// Returns the result of the "In" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend) + public static Vector4 In(Vector4 destination, Vector4 source) { - float alpha = dst.W * src.W; + float alpha = destination.W * source.W; - Vector4 color = src * alpha; // premultiply + Vector4 color = source * alpha; // premultiply color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply color.W = alpha; return color; } + /// + /// Returns the result of the "Out" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Out(Vector4 dst, Vector4 src) + public static Vector4 Out(Vector4 destination, Vector4 source) { - float alpha = (1 - dst.W) * src.W; + float alpha = (1 - destination.W) * source.W; - Vector4 color = src * alpha; // premultiply + Vector4 color = source * alpha; // premultiply color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply color.W = alpha; return color; } + /// + /// Returns the result of the "XOr" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Xor(Vector4 dst, Vector4 src) + public static Vector4 Xor(Vector4 destination, Vector4 source) { - float srcW = 1 - dst.W; - float dstW = 1 - src.W; + float srcW = 1 - destination.W; + float dstW = 1 - source.W; - float alpha = (src.W * srcW) + (dst.W * dstW); - Vector4 color = (src.W * src * srcW) + (dst.W * dst * dstW); + float alpha = (source.W * srcW) + (destination.W * dstW); + Vector4 color = (source.W * source * srcW) + (destination.W * destination * dstW); // unpremultiply color /= MathF.Max(alpha, Constants.Epsilon); @@ -235,4 +267,4 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return Vector4.Zero; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 77bee23937..c64fd3a2d9 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -1,10 +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.Buffers; using System.Numerics; - using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.PixelFormats @@ -13,7 +12,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Abstract base class for calling pixel composition functions /// /// The type of the pixel - internal abstract class PixelBlender + public abstract class PixelBlender where TPixel : struct, IPixel { /// @@ -23,64 +22,11 @@ namespace SixLabors.ImageSharp.PixelFormats /// The source color. /// /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. /// - /// The final pixel value after composition + /// The final pixel value after composition. public abstract TPixel Blend(TPixel background, TPixel source, float amount); - /// - /// Blend 2 rows together. - /// - /// destination span - /// the background span - /// the source span - /// - /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - protected abstract void BlendFunction( - Span destination, - ReadOnlySpan background, - ReadOnlySpan source, - float amount); - - /// - /// Blend 2 rows together. - /// - /// destination span - /// the background span - /// the source span - /// - /// A span with values between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - protected abstract void BlendFunction( - Span destination, - ReadOnlySpan background, - ReadOnlySpan source, - ReadOnlySpan amount); - - /// - /// Blends 2 rows together - /// - /// to use internally - /// the destination span - /// the background span - /// the source span - /// - /// A span with values between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - public void Blend( - Configuration configuration, - Span destination, - ReadOnlySpan background, - ReadOnlySpan source, - ReadOnlySpan amount) - { - this.Blend(configuration, destination, background, source, amount); - } - /// /// Blends 2 rows together /// @@ -90,20 +36,20 @@ namespace SixLabors.ImageSharp.PixelFormats /// the background span /// the source span /// - /// A span with values between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. /// public void Blend( Configuration configuration, Span destination, ReadOnlySpan background, ReadOnlySpan source, - ReadOnlySpan amount) + float amount) where TPixelSrc : struct, IPixel { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); using (IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(destination.Length * 3)) @@ -124,6 +70,25 @@ namespace SixLabors.ImageSharp.PixelFormats } } + /// + /// Blends 2 rows together + /// + /// to use internally + /// the destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount) + => this.Blend(configuration, destination, background, source, amount); + /// /// Blends 2 rows together /// @@ -133,20 +98,20 @@ namespace SixLabors.ImageSharp.PixelFormats /// the background span /// the source span /// - /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. /// public void Blend( Configuration configuration, Span destination, ReadOnlySpan background, ReadOnlySpan source, - float amount) + ReadOnlySpan amount) where TPixelSrc : struct, IPixel { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); using (IMemoryOwner buffer = configuration.MemoryAllocator.Allocate(destination.Length * 3)) @@ -166,5 +131,37 @@ namespace SixLabors.ImageSharp.PixelFormats PixelOperations.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); } } + + /// + /// Blend 2 rows together. + /// + /// destination span + /// the background span + /// the source span + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + protected abstract void BlendFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount); + + /// + /// Blend 2 rows together. + /// + /// destination span + /// the background span + /// the source span + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. + /// + protected abstract void BlendFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs index 445f899817..10c642300c 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -9,7 +9,7 @@ using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.PixelFormats { /// - /// Unpacked pixel type containing four 16-bit floating-point values typically ranging from 0 to 1. + /// Unpacked pixel type containing four 32-bit floating-point values typically ranging from 0 to 1. /// The color components are stored in red, green, blue, and alpha order. /// /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs index 63db674c8e..9f6fd27852 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.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.PixelBlenders; @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// the blending and composition to apply /// A . - internal PixelBlender GetPixelBlender(GraphicsOptions options) + public PixelBlender GetPixelBlender(GraphicsOptions options) { return this.GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode); } @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The color blending mode to apply /// The alpha composition mode to apply /// A . - internal virtual PixelBlender GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) + public virtual PixelBlender GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) { switch (alphaMode) { @@ -214,4 +214,4 @@ namespace SixLabors.ImageSharp.PixelFormats } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs similarity index 64% rename from src/ImageSharp/Processing/DefaultImageProcessorContext.cs rename to src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs index d0c4c076f1..328ccdf941 100644 --- a/src/ImageSharp/Processing/DefaultImageProcessorContext.cs +++ b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.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.Advanced; @@ -29,6 +29,8 @@ namespace SixLabors.ImageSharp.Processing { this.mutate = mutate; this.source = source; + + // Mutate acts upon the source image only. if (this.mutate) { this.destination = source; @@ -43,7 +45,8 @@ namespace SixLabors.ImageSharp.Processing { if (!this.mutate && this.destination is null) { - // Ensure we have cloned it if we are not mutating as we might have failed to register any processors + // Ensure we have cloned the source if we are not mutating as we might have failed + // to register any processors. this.destination = this.source.Clone(); } @@ -53,43 +56,41 @@ namespace SixLabors.ImageSharp.Processing /// public Size GetCurrentSize() => this.GetCurrentBounds().Size; - public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) - { - var processorImplementation = processor.CreatePixelSpecificProcessor(); - return this.ApplyProcessor(processorImplementation, rectangle); - } - + /// public IImageProcessingContext ApplyProcessor(IImageProcessor processor) { - var processorImplementation = processor.CreatePixelSpecificProcessor(); - return this.ApplyProcessor(processorImplementation); + return this.ApplyProcessor(processor, this.GetCurrentBounds()); } - private IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + /// + public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { if (!this.mutate && this.destination is null) { - // This will only work if the first processor applied is the cloning one thus - // realistically for this optimization to work the resize must the first processor - // applied any only up processors will take the double data path. - if (processor is ICloningImageProcessor cloningImageProcessor) + // When cloning an image we can optimize the processing pipeline by avoiding an unnecessary + // interim clone if the first processor in the pipeline is a cloning processor. + if (processor is ICloningImageProcessor cloningImageProcessor) { - this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle); - return this; + using (ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificCloningProcessor(this.source, rectangle)) + { + this.destination = pixelProcessor.CloneAndExecute(); + return this; + } } + // Not a cloning processor? We need to create a clone to operate on. this.destination = this.source.Clone(); } - processor.Apply(this.destination, rectangle); - return this; - } + // Standard processing pipeline. + using (IImageProcessor specificProcessor = processor.CreatePixelSpecificProcessor(this.destination, rectangle)) + { + specificProcessor.Execute(); + } - private IImageProcessingContext ApplyProcessor(IImageProcessor processor) - { - return this.ApplyProcessor(processor, this.GetCurrentBounds()); + return this; } private Rectangle GetCurrentBounds() => this.destination?.Bounds() ?? this.source.Bounds(); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs b/src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs index f9a1bdde0e..b721110998 100644 --- a/src/ImageSharp/Processing/Extensions/DiffuseExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/DiffuseExtensions.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; @@ -95,4 +95,4 @@ namespace SixLabors.ImageSharp.Processing.Dithering Rectangle rectangle) => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette), rectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs b/src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs index a4bfaa516c..d87c40226c 100644 --- a/src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/GrayscaleExtensions.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; @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing { IImageProcessor processor = mode == GrayscaleMode.Bt709 ? (IImageProcessor)new GrayscaleBt709Processor(amount) - : new GrayscaleBt601Processor(1F); + : new GrayscaleBt601Processor(amount); source.ApplyProcessor(processor); return source; @@ -111,4 +111,4 @@ namespace SixLabors.ImageSharp.Processing return source; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Extensions/LightnessExtensions.cs b/src/ImageSharp/Processing/Extensions/LightnessExtensions.cs new file mode 100644 index 0000000000..86db9509e8 --- /dev/null +++ b/src/ImageSharp/Processing/Extensions/LightnessExtensions.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Processors.Filters; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Defines extensions that allow the alteration of the lightness component of an + /// using Mutate/Clone. + /// + public static class LightnessExtensions + { + /// + /// Alters the lightness component of the image. + /// + /// + /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing lighter results. + /// + /// The image this method extends. + /// The proportion of the conversion. Must be greater than or equal to 0. + /// The to allow chaining of operations. + public static IImageProcessingContext Lightness(this IImageProcessingContext source, float amount) + => source.ApplyProcessor(new LightnessProcessor(amount)); + + /// + /// Alters the lightness component of the image. + /// + /// + /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing lighter results. + /// + /// The image this method extends. + /// The proportion of the conversion. Must be greater than or equal to 0. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The to allow chaining of operations. + public static IImageProcessingContext Lightness(this IImageProcessingContext source, float amount, Rectangle rectangle) + => source.ApplyProcessor(new LightnessProcessor(amount), rectangle); + } +} diff --git a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs index 48876251e2..40b1c439e6 100644 --- a/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs @@ -25,8 +25,7 @@ namespace SixLabors.ImageSharp.Processing Guard.NotNull(operation, nameof(operation)); source.EnsureNotDisposed(); - var visitor = new ProcessingVisitor(operation, true); - source.AcceptVisitor(visitor); + source.AcceptVisitor(new ProcessingVisitor(operation, true)); } /// @@ -42,8 +41,10 @@ namespace SixLabors.ImageSharp.Processing Guard.NotNull(operation, nameof(operation)); source.EnsureNotDisposed(); - IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider - .CreateImageProcessingContext(source, true); + IInternalImageProcessingContext operationsRunner + = source.GetConfiguration() + .ImageOperationsProvider.CreateImageProcessingContext(source, true); + operation(operationsRunner); } @@ -60,8 +61,10 @@ namespace SixLabors.ImageSharp.Processing Guard.NotNull(operations, nameof(operations)); source.EnsureNotDisposed(); - IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider - .CreateImageProcessingContext(source, true); + IInternalImageProcessingContext operationsRunner + = source.GetConfiguration() + .ImageOperationsProvider.CreateImageProcessingContext(source, true); + operationsRunner.ApplyProcessors(operations); } @@ -96,8 +99,10 @@ namespace SixLabors.ImageSharp.Processing Guard.NotNull(operation, nameof(operation)); source.EnsureNotDisposed(); - IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider - .CreateImageProcessingContext(source, false); + IInternalImageProcessingContext operationsRunner + = source.GetConfiguration() + .ImageOperationsProvider.CreateImageProcessingContext(source, false); + operation(operationsRunner); return operationsRunner.GetResultImage(); } @@ -116,8 +121,10 @@ namespace SixLabors.ImageSharp.Processing Guard.NotNull(operations, nameof(operations)); source.EnsureNotDisposed(); - IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider - .CreateImageProcessingContext(source, false); + IInternalImageProcessingContext operationsRunner + = source.GetConfiguration() + .ImageOperationsProvider.CreateImageProcessingContext(source, false); + operationsRunner.ApplyProcessors(operations); return operationsRunner.GetResultImage(); } @@ -157,8 +164,10 @@ namespace SixLabors.ImageSharp.Processing public void Visit(Image image) where TPixel : struct, IPixel { - IInternalImageProcessingContext operationsRunner = image.GetConfiguration() - .ImageOperationsProvider.CreateImageProcessingContext(image, this.mutate); + IInternalImageProcessingContext operationsRunner = + image.GetConfiguration() + .ImageOperationsProvider.CreateImageProcessingContext(image, this.mutate); + this.operation(operationsRunner); this.ResultImage = operationsRunner.GetResultImage(); } diff --git a/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs b/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs index 81b1c2c663..f494ed9094 100644 --- a/src/ImageSharp/Processing/Extensions/ResizeExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/ResizeExtensions.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.Transforms; @@ -12,16 +12,6 @@ namespace SixLabors.ImageSharp.Processing /// public static class ResizeExtensions { - /// - /// Resizes an image in accordance with the given . - /// - /// The image to resize. - /// The resize options. - /// The to allow chaining of operations. - /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) - => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize())); - /// /// Resizes an image to the given . /// @@ -128,7 +118,18 @@ namespace SixLabors.ImageSharp.Processing Rectangle sourceRectangle, Rectangle targetRectangle, bool compand) - => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand), sourceRectangle); + { + var options = new ResizeOptions + { + Size = new Size(width, height), + Mode = ResizeMode.Manual, + Sampler = sampler, + TargetRectangle = targetRectangle, + Compand = compand + }; + + return source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize()), sourceRectangle); + } /// /// Resizes an image to the given width and height with the given sampler and source rectangle. @@ -150,6 +151,27 @@ namespace SixLabors.ImageSharp.Processing IResampler sampler, Rectangle targetRectangle, bool compand) - => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand)); + { + var options = new ResizeOptions + { + Size = new Size(width, height), + Mode = ResizeMode.Manual, + Sampler = sampler, + TargetRectangle = targetRectangle, + Compand = compand + }; + + return Resize(source, options); + } + + /// + /// Resizes an image in accordance with the given . + /// + /// The image to resize. + /// The resize options. + /// The to allow chaining of operations. + /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image or the nearest possible ratio. + public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) + => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize())); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/IInternalImageProcessingContext.cs b/src/ImageSharp/Processing/IInternalImageProcessingContext{TPixel}.cs similarity index 100% rename from src/ImageSharp/Processing/IInternalImageProcessingContext.cs rename to src/ImageSharp/Processing/IInternalImageProcessingContext{TPixel}.cs diff --git a/src/ImageSharp/Processing/KnownFilterMatrices.cs b/src/ImageSharp/Processing/KnownFilterMatrices.cs index 1f36e2593a..31b19433c7 100644 --- a/src/ImageSharp/Processing/KnownFilterMatrices.cs +++ b/src/ImageSharp/Processing/KnownFilterMatrices.cs @@ -432,6 +432,32 @@ namespace SixLabors.ImageSharp.Processing return m; } + /// + /// Create a lightness filter matrix using the given amount. + /// + /// + /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing lighter results. + /// + /// The proportion of the conversion. Must be greater than or equal to 0. + /// The + public static ColorMatrix CreateLightnessFilter(float amount) + { + Guard.MustBeGreaterThanOrEqualTo(amount, 0, nameof(amount)); + amount--; + + return new ColorMatrix + { + M11 = 1F, + M22 = 1F, + M33 = 1F, + M44 = 1F, + M51 = amount, + M52 = amount, + M53 = amount + }; + } + /// /// Create a sepia filter matrix using the given amount. /// The formula used matches the svg specification. diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs index 840d1c1f46..80164793b2 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs @@ -1,8 +1,9 @@ -// 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.Processing.Processors.Dithering; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { @@ -69,10 +70,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization public Color LowerColor { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new BinaryErrorDiffusionProcessor(this); - } + => new BinaryErrorDiffusionProcessor(this, source, sourceRectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs index e3828aeb60..0124575488 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs @@ -14,18 +14,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// Performs binary threshold filtering against an image using error diffusion. /// /// The pixel format. - internal class BinaryErrorDiffusionProcessor : ImageProcessor + internal sealed class BinaryErrorDiffusionProcessor : ImageProcessor where TPixel : struct, IPixel { private readonly BinaryErrorDiffusionProcessor definition; - public BinaryErrorDiffusionProcessor(BinaryErrorDiffusionProcessor definition) + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public BinaryErrorDiffusionProcessor(BinaryErrorDiffusionProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { TPixel upperColor = this.definition.UpperColor.ToPixel(); TPixel lowerColor = this.definition.LowerColor.ToPixel(); @@ -34,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization byte threshold = (byte)MathF.Round(this.definition.Threshold * 255F); bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; @@ -69,9 +76,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization } TPixel transformedPixel = luminance >= threshold ? upperColor : lowerColor; - diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); + diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, endX, endY); } } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs index 6cf1a95266..0a426c893a 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.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 System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { @@ -51,10 +52,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization public Color LowerColor { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new BinaryOrderedDitherProcessor(this); - } + => new BinaryOrderedDitherProcessor(this, source, sourceRectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs index b3d174dfbe..e0aae0279f 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs @@ -19,13 +19,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization { private readonly BinaryOrderedDitherProcessor definition; - public BinaryOrderedDitherProcessor(BinaryOrderedDitherProcessor definition) + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public BinaryOrderedDitherProcessor(BinaryOrderedDitherProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { IOrderedDither dither = this.definition.Dither; TPixel upperColor = this.definition.UpperColor.ToPixel(); @@ -33,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; @@ -72,4 +79,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs index 7f00d0219d..a33c464694 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization { @@ -49,10 +50,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization public Color LowerColor { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new BinaryThresholdProcessor(this); - } + => new BinaryThresholdProcessor(this, source, sourceRectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs index 0d90d3647d..7208fc3cd5 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs @@ -4,7 +4,7 @@ using System; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -19,21 +19,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization { private readonly BinaryThresholdProcessor definition; - public BinaryThresholdProcessor(BinaryThresholdProcessor definition) + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public BinaryThresholdProcessor(BinaryThresholdProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { byte threshold = (byte)MathF.Round(this.definition.Threshold * 255F); TPixel upper = this.definition.UpperColor.ToPixel(); TPixel lower = this.definition.LowerColor.ToPixel(); + Rectangle sourceRectangle = this.SourceRectangle; + Configuration configuration = this.Configuration; + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; @@ -67,4 +74,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization }); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs index 8150d59218..5e9ca2e542 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs @@ -1,141 +1,22 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { /// - /// Allows the application of processing algorithms to a clone of the original image. + /// The base class for all cloning image processors. /// - /// The pixel format. - internal abstract class CloningImageProcessor : ICloningImageProcessor - where TPixel : struct, IPixel + public abstract class CloningImageProcessor : ICloningImageProcessor { /// - public Image CloneAndApply(Image source, Rectangle sourceRectangle) - { - try - { - Image clone = this.CreateDestination(source, sourceRectangle); - - if (clone.Frames.Count != source.Frames.Count) - { - throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames."); - } - - Configuration configuration = source.GetConfiguration(); - this.BeforeImageApply(source, clone, sourceRectangle); - - for (int i = 0; i < source.Frames.Count; i++) - { - ImageFrame sourceFrame = source.Frames[i]; - ImageFrame clonedFrame = clone.Frames[i]; - - this.BeforeFrameApply(sourceFrame, clonedFrame, sourceRectangle, configuration); - this.OnFrameApply(sourceFrame, clonedFrame, sourceRectangle, configuration); - this.AfterFrameApply(sourceFrame, clonedFrame, sourceRectangle, configuration); - } - - this.AfterImageApply(source, clone, sourceRectangle); - - return clone; - } -#if DEBUG - catch (Exception) - { - throw; -#else - catch (Exception ex) - { - throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); -#endif - } - } + public abstract ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) + where TPixel : struct, IPixel; /// - public void Apply(Image source, Rectangle sourceRectangle) - { - using (Image cloned = this.CloneAndApply(source, sourceRectangle)) - { - // we now need to move the pixel data/size data from one image base to another - if (cloned.Frames.Count != source.Frames.Count) - { - throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames."); - } - - source.SwapOrCopyPixelsBuffersFrom(cloned); - } - } - - /// - /// Generates a deep clone of the source image that operations should be applied to. - /// - /// The source image. Cannot be null. - /// The source rectangle. - /// The cloned image. - protected virtual Image CreateDestination(Image source, Rectangle sourceRectangle) - { - return source.Clone(); - } - - /// - /// This method is called before the process is applied to prepare the processor. - /// - /// The source image. Cannot be null. - /// The cloned/destination image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected virtual void BeforeImageApply(Image source, Image destination, Rectangle sourceRectangle) - { - } - - /// - /// This method is called before the process is applied to prepare the processor. - /// - /// The source image. Cannot be null. - /// The cloned/destination image. Cannot be null. - /// The structure that specifies the portion of the image object to draw. - /// The configuration. - protected virtual void BeforeFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) - { - } - - /// - /// Applies the process to the specified portion of the specified at the specified location - /// and with the specified size. - /// - /// The source image. Cannot be null. - /// The cloned/destination image. Cannot be null. - /// The structure that specifies the portion of the image object to draw. - /// The configuration. - protected abstract void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration); - - /// - /// This method is called after the process is applied to prepare the processor. - /// - /// The source image. Cannot be null. - /// The cloned/destination image. Cannot be null. - /// The structure that specifies the portion of the image object to draw. - /// The configuration. - protected virtual void AfterFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) - { - } - - /// - /// This method is called after the process is applied to prepare the processor. - /// - /// The source image. Cannot be null. - /// The cloned/destination image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected virtual void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) - { - } + IImageProcessor IImageProcessor.CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + => this.CreatePixelSpecificCloningProcessor(source, sourceRectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs new file mode 100644 index 0000000000..42d2f0e1df --- /dev/null +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs @@ -0,0 +1,196 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// The base class for all pixel specific cloning image processors. + /// Allows the application of processing algorithms to the image. + /// The image is cloned before operating upon and the buffers swapped upon completion. + /// + /// The pixel format. + public abstract class CloningImageProcessor : ICloningImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + protected CloningImageProcessor(Image source, Rectangle sourceRectangle) + { + this.Source = source; + this.SourceRectangle = sourceRectangle; + this.Configuration = this.Source.GetConfiguration(); + } + + /// + /// Gets The source for the current processor instance. + /// + protected Image Source { get; } + + /// + /// Gets The source area to process for the current processor instance. + /// + protected Rectangle SourceRectangle { get; } + + /// + /// Gets the instance to use when performing operations. + /// + protected Configuration Configuration { get; } + + /// + Image ICloningImageProcessor.CloneAndExecute() + { + try + { + Image clone = this.CreateTarget(); + this.CheckFrameCount(this.Source, clone); + + Configuration configuration = this.Source.GetConfiguration(); + this.BeforeImageApply(clone); + + for (int i = 0; i < this.Source.Frames.Count; i++) + { + ImageFrame sourceFrame = this.Source.Frames[i]; + ImageFrame clonedFrame = clone.Frames[i]; + + this.BeforeFrameApply(sourceFrame, clonedFrame); + this.OnFrameApply(sourceFrame, clonedFrame); + this.AfterFrameApply(sourceFrame, clonedFrame); + } + + this.AfterImageApply(clone); + + return clone; + } +#if DEBUG + catch (Exception) + { + throw; +#else + catch (Exception ex) + { + throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); +#endif + } + } + + /// + void IImageProcessor.Execute() + { + // Create an interim clone of the source image to operate on. + // Doing this allows for the application of transforms that will alter + // the dimensions of the image. + Image clone = default; + try + { + clone = ((ICloningImageProcessor)this).CloneAndExecute(); + + // We now need to move the pixel data/size data from the clone to the source. + this.CheckFrameCount(this.Source, clone); + this.Source.SwapOrCopyPixelsBuffersFrom(clone); + } + finally + { + // Dispose of the clone now that we have swapped the pixel/size data. + clone?.Dispose(); + } + } + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Gets the size of the target image. + /// + /// The . + protected abstract Size GetTargetSize(); + + /// + /// This method is called before the process is applied to prepare the processor. + /// + /// The cloned/destination image. Cannot be null. + protected virtual void BeforeImageApply(Image destination) + { + } + + /// + /// This method is called before the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + protected virtual void BeforeFrameApply(ImageFrame source, ImageFrame destination) + { + } + + /// + /// Applies the process to the specified portion of the specified at the specified location + /// and with the specified size. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + protected abstract void OnFrameApply(ImageFrame source, ImageFrame destination); + + /// + /// This method is called after the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + protected virtual void AfterFrameApply(ImageFrame source, ImageFrame destination) + { + } + + /// + /// This method is called after the process is applied to prepare the processor. + /// + /// The cloned/destination image. Cannot be null. + protected virtual void AfterImageApply(Image destination) + { + } + + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + /// Whether to dispose managed and unmanaged objects. + protected virtual void Dispose(bool disposing) + { + } + + private Image CreateTarget() + { + Image source = this.Source; + Size targetSize = this.GetTargetSize(); + + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = source.Frames.Select, ImageFrame>( + x => new ImageFrame( + source.GetConfiguration(), + targetSize.Width, + targetSize.Height, + x.Metadata.DeepClone())); + + // Use the overload to prevent an extra frame being added + return new Image(this.Configuration, source.Metadata.DeepClone(), frames); + } + + private void CheckFrameCount(Image a, Image b) + { + if (a.Frames.Count != b.Frames.Count) + { + throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames."); + } + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index ede68e3191..b7e102deb5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -112,10 +113,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public BokehBlurExecutionMode ExecutionMode { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new BokehBlurProcessor(this); + return new BokehBlurProcessor(this, source, sourceRectangle); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index a083026c37..f8fb3f796d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -8,8 +8,8 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters; @@ -75,7 +75,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// /// The defining the processor parameters. - public BokehBlurProcessor(BokehBlurProcessor definition) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public BokehBlurProcessor(BokehBlurProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.radius = definition.Radius; this.kernelSize = (this.radius * 2) + 1; @@ -271,36 +274,36 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { // Preliminary gamma highlight pass - this.ApplyGammaExposure(source.PixelBuffer, sourceRectangle, configuration); + this.ApplyGammaExposure(source.PixelBuffer, this.SourceRectangle, this.Configuration); // Create a 0-filled buffer to use to store the result of the component convolutions - using (Buffer2D processing = configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean)) + using (Buffer2D processing = this.Configuration.MemoryAllocator.Allocate2D(source.Size(), AllocationOptions.Clean)) { if (this.executionMode == BokehBlurExecutionMode.PreferLowMemoryUsage) { // Memory usage priority: allocate a shared buffer and execute the second convolution in sequential mode - using (Buffer2D buffer = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height + this.radius)) + using (Buffer2D buffer = this.Configuration.MemoryAllocator.Allocate2D(source.Width, source.Height + this.radius)) using (Buffer2D firstPassBuffer = buffer.Slice(this.radius, source.Height)) using (Buffer2D secondPassBuffer = buffer.Slice(0, source.Height)) { - this.OnFrameApplyCore(source, sourceRectangle, configuration, processing, firstPassBuffer, secondPassBuffer); + this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processing, firstPassBuffer, secondPassBuffer); } } else { // Performance priority: allocate two independent buffers and execute both convolutions in parallel mode - using (Buffer2D firstPassValues = configuration.MemoryAllocator.Allocate2D(source.Size())) - using (Buffer2D secondPassBuffer = configuration.MemoryAllocator.Allocate2D(source.Size())) + using (Buffer2D firstPassValues = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) + using (Buffer2D secondPassBuffer = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) { - this.OnFrameApplyCore(source, sourceRectangle, configuration, processing, firstPassValues, secondPassBuffer); + this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processing, firstPassValues, secondPassBuffer); } } // Apply the inverse gamma exposure pass, and write the final pixel data - this.ApplyInverseGammaExposure(source.PixelBuffer, processing, sourceRectangle, configuration); + this.ApplyInverseGammaExposure(source.PixelBuffer, processing, this.SourceRectangle, this.Configuration); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index 726947ac91..a5368c4639 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -40,10 +41,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public int Radius { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new BoxBlurProcessor(this); + return new BoxBlurProcessor(this, source, sourceRectangle); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs index f4252e840d..77110e642d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs @@ -20,7 +20,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// /// The defining the processor parameters. - public BoxBlurProcessor(BoxBlurProcessor definition) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public BoxBlurProcessor(BoxBlurProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; int kernelSize = (definition.Radius * 2) + 1; @@ -39,14 +42,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public DenseMatrix KernelY { get; } /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) => - new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply( - source, - sourceRectangle, - configuration); + protected override void OnFrameApply(ImageFrame source) + { + using (var processor = new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle)) + { + processor.Apply(source); + } + } /// /// Create a 1 dimensional Box kernel. @@ -60,4 +62,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution return kernel; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs index 299b1d41c1..4419f064e5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs @@ -5,8 +5,8 @@ using System; using System.Numerics; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; @@ -26,7 +26,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// The horizontal gradient operator. /// The vertical gradient operator. /// Whether the convolution filter is applied to alpha as well as the color channels. - public Convolution2DProcessor(in DenseMatrix kernelX, in DenseMatrix kernelY, bool preserveAlpha) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public Convolution2DProcessor( + in DenseMatrix kernelX, + in DenseMatrix kernelY, + bool preserveAlpha, + Image source, + Rectangle sourceRectangle) + : base(source, sourceRectangle) { Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same."); this.KernelX = kernelX; @@ -50,16 +58,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public bool PreserveAlpha { get; } /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { DenseMatrix matrixY = this.KernelY; DenseMatrix matrixX = this.KernelX; bool preserveAlpha = this.PreserveAlpha; - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; @@ -67,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution int maxY = endY - 1; int maxX = endX - 1; - using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height)) + using (Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Width, source.Height)) { source.CopyTo(targetPixels); @@ -76,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution ParallelHelper.IterateRowsWithTempBuffer( workingRectangle, - configuration, + this.Configuration, (rows, vectorBuffer) => { Span vectorSpan = vectorBuffer.Span; @@ -86,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); - PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan); + PixelOperations.Instance.ToVector4(this.Configuration, targetRowSpan.Slice(0, length), vectorSpan); if (preserveAlpha) { @@ -123,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(this.Configuration, vectorSpan, targetRowSpan); } }); @@ -131,4 +136,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs index 03268c9dda..9fad8b5b73 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs @@ -1,11 +1,12 @@ -// 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 System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; @@ -25,10 +26,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// The horizontal gradient operator. /// The vertical gradient operator. /// Whether the convolution filter is applied to alpha as well as the color channels. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. public Convolution2PassProcessor( in DenseMatrix kernelX, in DenseMatrix kernelY, - bool preserveAlpha) + bool preserveAlpha, + Image source, + Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.KernelX = kernelX; this.KernelY = kernelY; @@ -51,13 +57,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public bool PreserveAlpha { get; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { - using (Buffer2D firstPassPixels = configuration.MemoryAllocator.Allocate2D(source.Size())) + using (Buffer2D firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) { - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - this.ApplyConvolution(firstPassPixels, source.PixelBuffer, interest, this.KernelX, configuration); - this.ApplyConvolution(source.PixelBuffer, firstPassPixels, interest, this.KernelY, configuration); + var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); + this.ApplyConvolution(firstPassPixels, source.PixelBuffer, interest, this.KernelX, this.Configuration); + this.ApplyConvolution(source.PixelBuffer, firstPassPixels, interest, this.KernelY, this.Configuration); } } @@ -144,4 +150,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution }); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs index 6c3b9a46f5..f657e131dd 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -1,11 +1,12 @@ -// 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 System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; @@ -24,7 +25,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// The 2d gradient operator. /// Whether the convolution filter is applied to alpha as well as the color channels. - public ConvolutionProcessor(in DenseMatrix kernelXY, bool preserveAlpha) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public ConvolutionProcessor( + in DenseMatrix kernelXY, + bool preserveAlpha, + Image source, + Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.KernelXY = kernelXY; this.PreserveAlpha = preserveAlpha; @@ -41,12 +49,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public bool PreserveAlpha { get; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { DenseMatrix matrix = this.KernelXY; bool preserveAlpha = this.PreserveAlpha; - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; @@ -54,7 +62,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution int maxY = endY - 1; int maxX = endX - 1; - using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Size())) + using (Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) { source.CopyTo(targetPixels); @@ -63,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution ParallelHelper.IterateRowsWithTempBuffer( workingRectangle, - configuration, + this.Configuration, (rows, vectorBuffer) => { Span vectorSpan = vectorBuffer.Span; @@ -73,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); - PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan); + PixelOperations.Instance.ToVector4(this.Configuration, targetRowSpan.Slice(0, length), vectorSpan); if (preserveAlpha) { @@ -108,7 +116,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(this.Configuration, vectorSpan, targetRowSpan); } }); @@ -116,4 +124,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs index f6771288fe..8358abe7df 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.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; @@ -21,7 +21,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// The horizontal gradient operator. /// The vertical gradient operator. /// Whether to convert the image to grayscale before performing edge detection. - internal EdgeDetector2DProcessor(in DenseMatrix kernelX, in DenseMatrix kernelY, bool grayscale) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + internal EdgeDetector2DProcessor( + in DenseMatrix kernelX, + in DenseMatrix kernelY, + bool grayscale, + Image source, + Rectangle sourceRectangle) + : base(source, sourceRectangle) { Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same."); this.KernelX = kernelX; @@ -41,17 +49,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public bool Grayscale { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new Convolution2DProcessor(this.KernelX, this.KernelY, true).Apply(source, sourceRectangle, configuration); - /// - protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void BeforeImageApply() { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); + new GrayscaleBt709Processor(1F).Execute(this.Source, this.SourceRectangle); + } + + base.BeforeImageApply(); + } + + /// + protected override void OnFrameApply(ImageFrame source) + { + using (var processor = new Convolution2DProcessor(this.KernelX, this.KernelY, true, this.Source, this.SourceRectangle)) + { + processor.Apply(source); } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index 5995ac844f..4fa87bc98e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.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; @@ -6,8 +6,8 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -27,7 +27,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the kernels to use. /// Whether to convert the image to grayscale before performing edge detection. - internal EdgeDetectorCompassProcessor(CompassKernels kernels, bool grayscale) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + internal EdgeDetectorCompassProcessor(CompassKernels kernels, bool grayscale, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.Grayscale = grayscale; this.Kernels = kernels; @@ -38,23 +41,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution private bool Grayscale { get; } /// - protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void BeforeImageApply() { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); + new GrayscaleBt709Processor(1F).Execute(this.Source, this.SourceRectangle); } + + base.BeforeImageApply(); } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { DenseMatrix[] kernels = this.Kernels.Flatten(); - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; + int startY = this.SourceRectangle.Y; + int endY = this.SourceRectangle.Bottom; + int startX = this.SourceRectangle.X; + int endX = this.SourceRectangle.Right; // Align start/end positions. int minX = Math.Max(0, startX); @@ -65,7 +70,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution // we need a clean copy for each pass to start from using (ImageFrame cleanCopy = source.Clone()) { - new ConvolutionProcessor(kernels[0], true).Apply(source, sourceRectangle, configuration); + using (var processor = new ConvolutionProcessor(kernels[0], true, this.Source, this.SourceRectangle)) + { + processor.Apply(source); + } if (kernels.Length == 1) { @@ -94,14 +102,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { using (ImageFrame pass = cleanCopy.Clone()) { - new ConvolutionProcessor(kernels[i], true).Apply(pass, sourceRectangle, configuration); + using (var processor = new ConvolutionProcessor(kernels[i], true, this.Source, this.SourceRectangle)) + { + processor.Apply(pass); + } Buffer2D passPixels = pass.PixelBuffer; Buffer2D targetPixels = source.PixelBuffer; ParallelHelper.IterateRows( workingRect, - configuration, + this.Configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) @@ -132,4 +143,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs index 75be15bcc7..24b95da696 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -25,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public bool Grayscale { get; } /// - public abstract IImageProcessor CreatePixelSpecificProcessor() + public abstract IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs index 041c548032..5246dc3b72 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs @@ -20,7 +20,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// The 2d gradient operator. /// Whether to convert the image to grayscale before performing edge detection. - public EdgeDetectorProcessor(in DenseMatrix kernelXY, bool grayscale) + /// The source for the current processor instance. + /// The target area to process for the current processor instance. + public EdgeDetectorProcessor(in DenseMatrix kernelXY, bool grayscale, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.KernelXY = kernelXY; this.Grayscale = grayscale; @@ -34,16 +37,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public DenseMatrix KernelXY { get; } /// - protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void BeforeImageApply() { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); + new GrayscaleBt709Processor(1F).Execute(this.Source, this.SourceRectangle); } + + base.BeforeImageApply(); } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new ConvolutionProcessor(this.KernelXY, true).Apply(source, sourceRectangle, configuration); + protected override void OnFrameApply(ImageFrame source) + { + using (var processor = new ConvolutionProcessor(this.KernelXY, true, this.Source, this.SourceRectangle)) + { + processor.Apply(source); + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index 8504db1617..aabc8041d9 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -70,10 +71,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public int Radius { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new GaussianBlurProcessor(this); + return new GaussianBlurProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs index a129ff5473..bbf36ea5e8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs @@ -18,7 +18,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// /// The defining the processor parameters. - public GaussianBlurProcessor(GaussianBlurProcessor definition) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public GaussianBlurProcessor(GaussianBlurProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { int kernelSize = (definition.Radius * 2) + 1; this.KernelX = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma); @@ -36,13 +39,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public DenseMatrix KernelY { get; } /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) => - new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply( - source, - sourceRectangle, - configuration); + protected override void OnFrameApply(ImageFrame source) + { + using (var processor = new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle)) + { + processor.Apply(source); + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 75acc90e0c..0262ec8e44 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { @@ -70,10 +71,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public int Radius { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new GaussianSharpenProcessor(this); + return new GaussianSharpenProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs index edde9f9e7f..dab55b2328 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs @@ -18,7 +18,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// /// The defining the processor parameters. - public GaussianSharpenProcessor(GaussianSharpenProcessor definition) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public GaussianSharpenProcessor(GaussianSharpenProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { int kernelSize = (definition.Radius * 2) + 1; this.KernelX = ConvolutionProcessorHelpers.CreateGaussianSharpenKernel(kernelSize, definition.Sigma); @@ -36,7 +39,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public DenseMatrix KernelY { get; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); + protected override void OnFrameApply(ImageFrame source) + { + using (var processor = new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle)) + { + processor.Apply(source); + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs index 2d0f056b61..2026512617 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -19,12 +21,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { return new EdgeDetector2DProcessor( KayyaliKernels.KayyaliX, KayyaliKernels.KayyaliY, - this.Grayscale); + this.Grayscale, + source, + sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs index 9e95344222..bbbfc64d92 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { - return new EdgeDetectorCompassProcessor(new KirschKernels(), this.Grayscale); + return new EdgeDetectorCompassProcessor(new KirschKernels(), this.Grayscale, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs index ab6658f3b2..64f99ebe61 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { - return new EdgeDetectorProcessor(LaplacianKernels.Laplacian3x3, this.Grayscale); + return new EdgeDetectorProcessor(LaplacianKernels.Laplacian3x3, this.Grayscale, source, sourceRectangle); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs index fa0c8c5aa3..d1c909a941 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { - return new EdgeDetectorProcessor(LaplacianKernels.Laplacian5x5, this.Grayscale); + return new EdgeDetectorProcessor(LaplacianKernels.Laplacian5x5, this.Grayscale, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs index 2caff8201c..0eecaefe16 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { - return new EdgeDetectorProcessor(LaplacianKernels.LaplacianOfGaussianXY, this.Grayscale); + return new EdgeDetectorProcessor(LaplacianKernels.LaplacianOfGaussianXY, this.Grayscale, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs index 29f6fc279c..242e3f7b97 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { - return new EdgeDetector2DProcessor(PrewittKernels.PrewittX, PrewittKernels.PrewittY, this.Grayscale); + return new EdgeDetector2DProcessor(PrewittKernels.PrewittX, PrewittKernels.PrewittY, this.Grayscale, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs index ca7d8895a8..481a990ff9 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -19,9 +21,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { - return new EdgeDetector2DProcessor(RobertsCrossKernels.RobertsCrossX, RobertsCrossKernels.RobertsCrossY, this.Grayscale); + return new EdgeDetector2DProcessor( + RobertsCrossKernels.RobertsCrossX, + RobertsCrossKernels.RobertsCrossY, + this.Grayscale, + source, + sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs index 6f5373fae5..324ed31545 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { - return new EdgeDetectorCompassProcessor(new RobinsonKernels(), this.Grayscale); + return new EdgeDetectorCompassProcessor(new RobinsonKernels(), this.Grayscale, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs index da76aa971c..6a4bf6afd5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { - return new EdgeDetector2DProcessor(ScharrKernels.ScharrX, ScharrKernels.ScharrY, this.Grayscale); + return new EdgeDetector2DProcessor(ScharrKernels.ScharrX, ScharrKernels.ScharrY, this.Grayscale, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs index 5fb32f4e62..96ed3bcea5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// @@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { - return new EdgeDetector2DProcessor(SobelKernels.SobelX, SobelKernels.SobelY, this.Grayscale); + return new EdgeDetector2DProcessor(SobelKernels.SobelX, SobelKernels.SobelY, this.Grayscale, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs index 0461d179ff..f167ac5cb9 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/AtkinsonDiffuser.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; @@ -11,22 +11,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class AtkinsonDiffuser : ErrorDiffuser { + private const float Divisor = 8F; + /// /// The diffusion matrix /// private static readonly DenseMatrix AtkinsonMatrix = new float[,] { - { 0, 0, 1, 1 }, - { 1, 1, 1, 0 }, - { 0, 1, 0, 0 } + { 0, 0, 1 / Divisor, 1 / Divisor }, + { 1 / Divisor, 1 / Divisor, 1 / Divisor, 0 }, + { 0, 1 / Divisor, 0, 0 } }; /// /// Initializes a new instance of the class. /// public AtkinsonDiffuser() - : base(AtkinsonMatrix, 8) + : base(AtkinsonMatrix) { } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs index 23d4321e96..3c1ff75f4c 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/BurksDiffuser.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; @@ -11,22 +11,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class BurksDiffuser : ErrorDiffuser { + private const float Divisor = 32F; + /// /// The diffusion matrix /// private static readonly DenseMatrix BurksMatrix = new float[,] { - { 0, 0, 0, 8, 4 }, - { 2, 4, 8, 4, 2 } + { 0, 0, 0, 8 / Divisor, 4 / Divisor }, + { 2 / Divisor, 4 / Divisor, 8 / Divisor, 4 / Divisor, 2 / Divisor } }; /// /// Initializes a new instance of the class. /// public BurksDiffuser() - : base(BurksMatrix, 32) + : base(BurksMatrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs index 1c8156bf51..7911c6ca96 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.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; @@ -15,66 +15,39 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public abstract class ErrorDiffuser : IErrorDiffuser { - /// - /// The vector to perform division. - /// - private readonly Vector4 divisorVector; - - /// - /// The matrix width. - /// - private readonly int matrixHeight; - - /// - /// The matrix height. - /// - private readonly int matrixWidth; - - /// - /// The offset at which to start the dithering operation. - /// - private readonly int startingOffset; - - /// - /// The diffusion matrix. - /// + private readonly int offset; private readonly DenseMatrix matrix; /// /// Initializes a new instance of the class. /// /// The dithering matrix. - /// The divisor. - internal ErrorDiffuser(in DenseMatrix matrix, byte divisor) + internal ErrorDiffuser(in DenseMatrix matrix) { - Guard.MustBeGreaterThan(divisor, 0, nameof(divisor)); - - this.matrix = matrix; - this.matrixWidth = this.matrix.Columns; - this.matrixHeight = this.matrix.Rows; - this.divisorVector = new Vector4(divisor); + // Calculate the offset position of the pixel relative to + // the diffusion matrix. + this.offset = 0; - this.startingOffset = 0; - for (int i = 0; i < this.matrixWidth; i++) + for (int col = 0; col < matrix.Columns; col++) { - // Good to disable here as we are not comparing mathematical output. - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (matrix[0, i] != 0) + if (matrix[0, col] != 0) { - this.startingOffset = (byte)(i - 1); + this.offset = col - 1; break; } } + + this.matrix = matrix; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) + [MethodImpl(InliningOptions.ShortMethod)] + public void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int maxX, int maxY) where TPixel : struct, IPixel { image[x, y] = transformed; - // Equal? Break out as there's nothing to pass. + // Equal? Break out as there's no error to pass. if (source.Equals(transformed)) { return; @@ -82,45 +55,40 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering // Calculate the error Vector4 error = source.ToVector4() - transformed.ToVector4(); - this.DoDither(image, x, y, minX, minY, maxX, maxY, error); + this.DoDither(image, x, y, minX, maxX, maxY, error); } - [MethodImpl(MethodImplOptions.NoInlining)] - private void DoDither(ImageFrame image, int x, int y, int minX, int minY, int maxX, int maxY, Vector4 error) + [MethodImpl(InliningOptions.ShortMethod)] + private void DoDither(ImageFrame image, int x, int y, int minX, int maxX, int maxY, Vector4 error) where TPixel : struct, IPixel { + int offset = this.offset; + DenseMatrix matrix = this.matrix; + // Loop through and distribute the error amongst neighboring pixels. - for (int row = 0; row < this.matrixHeight; row++) + for (int row = 0, targetY = y; row < matrix.Rows && targetY < maxY; row++, targetY++) { - int matrixY = y + row; - if (matrixY > minY && matrixY < maxY) - { - Span rowSpan = image.GetPixelRowSpan(matrixY); + Span rowSpan = image.GetPixelRowSpan(targetY); - for (int col = 0; col < this.matrixWidth; col++) + for (int col = 0; col < matrix.Columns; col++) + { + int targetX = x + (col - offset); + if (targetX >= minX && targetX < maxX) { - int matrixX = x + (col - this.startingOffset); - - if (matrixX > minX && matrixX < maxX) + float coefficient = matrix[row, col]; + if (coefficient == 0) { - float coefficient = this.matrix[row, col]; - - // Good to disable here as we are not comparing mathematical output. - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (coefficient == 0) - { - continue; - } + continue; + } - ref TPixel pixel = ref rowSpan[matrixX]; - var offsetColor = pixel.ToVector4(); + ref TPixel pixel = ref rowSpan[targetX]; + var result = pixel.ToVector4(); - Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor; - pixel.FromVector4(result); - } + result += error * coefficient; + pixel.FromVector4(result); } } } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs index e612b4bf03..4e45130cc3 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs @@ -1,7 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { @@ -57,9 +58,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering public float Threshold { get; } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { - return new ErrorDiffusionPaletteProcessor(this); + return new ErrorDiffusionPaletteProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs index 7edf287e85..37dcd7d5cb 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs @@ -13,23 +13,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// An that dithers an image using error diffusion. /// /// The pixel format. - internal class ErrorDiffusionPaletteProcessor : PaletteDitherProcessor + internal sealed class ErrorDiffusionPaletteProcessor : PaletteDitherProcessor where TPixel : struct, IPixel { - public ErrorDiffusionPaletteProcessor(ErrorDiffusionPaletteProcessor definition) - : base(definition) + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public ErrorDiffusionPaletteProcessor(ErrorDiffusionPaletteProcessor definition, Image source, Rectangle sourceRectangle) + : base(definition, source, sourceRectangle) { } private new ErrorDiffusionPaletteProcessor Definition => (ErrorDiffusionPaletteProcessor)base.Definition; /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { byte threshold = (byte)MathF.Round(this.Definition.Threshold * 255F); - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; @@ -43,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + byte luminance = ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { @@ -66,16 +70,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } sourcePixel.ToRgba32(ref rgba); - luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + luminance = ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; } TPixel transformedPixel = luminance >= threshold ? pair.Second : pair.First; - this.Definition.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); + this.Definition.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, endX, endY); } } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs index 78a28a693a..ca0e3c647e 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/FloydSteinbergDiffuser.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; @@ -11,22 +11,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class FloydSteinbergDiffuser : ErrorDiffuser { + private const float Divisor = 16F; + /// /// The diffusion matrix /// private static readonly DenseMatrix FloydSteinbergMatrix = new float[,] { - { 0, 0, 7 }, - { 3, 5, 1 } + { 0, 0, 7 / Divisor }, + { 3 / Divisor, 5 / Divisor, 1 / Divisor } }; /// /// Initializes a new instance of the class. /// public FloydSteinbergDiffuser() - : base(FloydSteinbergMatrix, 16) + : base(FloydSteinbergMatrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/IErrorDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/IErrorDiffuser.cs index 5b30c0dc4d..8f4381d308 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/IErrorDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/IErrorDiffuser.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,11 +19,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// The column index. /// The row index. /// The minimum column value. - /// The minimum row value. /// The maximum column value. /// The maximum row value. /// The pixel format. - void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) + void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int maxX, int maxY) where TPixel : struct, IPixel; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs index 64c8610833..682db83523 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/JarvisJudiceNinkeDiffuser.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; @@ -11,23 +11,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class JarvisJudiceNinkeDiffuser : ErrorDiffuser { + private const float Divisor = 48F; + /// /// The diffusion matrix /// private static readonly DenseMatrix JarvisJudiceNinkeMatrix = new float[,] { - { 0, 0, 0, 7, 5 }, - { 3, 5, 7, 5, 3 }, - { 1, 3, 5, 3, 1 } + { 0, 0, 0, 7 / Divisor, 5 / Divisor }, + { 3 / Divisor, 5 / Divisor, 7 / Divisor, 5 / Divisor, 3 / Divisor }, + { 1 / Divisor, 3 / Divisor, 5 / Divisor, 3 / Divisor, 1 / Divisor } }; /// /// Initializes a new instance of the class. /// public JarvisJudiceNinkeDiffuser() - : base(JarvisJudiceNinkeMatrix, 48) + : base(JarvisJudiceNinkeMatrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs index ac6554d4c1..87bb3e5171 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs @@ -1,7 +1,8 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { @@ -34,9 +35,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering public IOrderedDither Dither { get; } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { - return new OrderedDitherPaletteProcessor(this); + return new OrderedDitherPaletteProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs index eefa6e5229..8cde8943e3 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs @@ -16,19 +16,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering internal class OrderedDitherPaletteProcessor : PaletteDitherProcessor where TPixel : struct, IPixel { - public OrderedDitherPaletteProcessor(OrderedDitherPaletteProcessor definition) - : base(definition) + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public OrderedDitherPaletteProcessor(OrderedDitherPaletteProcessor definition, Image source, Rectangle sourceRectangle) + : base(definition, source, sourceRectangle) { } private new OrderedDitherPaletteProcessor Definition => (OrderedDitherPaletteProcessor)base.Definition; /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; @@ -42,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required - byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + byte luminance = ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { @@ -65,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } sourcePixel.ToRgba32(ref rgba); - luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + luminance = ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; @@ -76,4 +80,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs index de798b64bc..0de964b526 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.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 System; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering public ReadOnlyMemory Palette { get; } /// - public abstract IImageProcessor CreatePixelSpecificProcessor() + public abstract IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs index 205b589b1e..10e9639423 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs @@ -2,11 +2,13 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; - +using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering @@ -19,49 +21,67 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering where TPixel : struct, IPixel { private readonly Dictionary> cache = new Dictionary>(); - - private TPixel[] palette; - - /// - /// The vector representation of the image palette. - /// - private Vector4[] paletteVector; + private IMemoryOwner palette; + private IMemoryOwner paletteVector; + private bool palleteVectorMapped; + private bool isDisposed; /// /// Initializes a new instance of the class. /// - protected PaletteDitherProcessor(PaletteDitherProcessor definition) + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + protected PaletteDitherProcessor(PaletteDitherProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.Definition = definition; + this.palette = this.Configuration.MemoryAllocator.Allocate(definition.Palette.Length); + this.paletteVector = this.Configuration.MemoryAllocator.Allocate(definition.Palette.Length); } protected PaletteDitherProcessor Definition { get; } - protected override void BeforeFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) + /// + protected override void BeforeFrameApply(ImageFrame source) { - base.BeforeFrameApply(source, sourceRectangle, configuration); - - // Lazy init palette: - if (this.palette is null) + // Lazy init palettes: + if (!this.palleteVectorMapped) { ReadOnlySpan sourcePalette = this.Definition.Palette.Span; - this.palette = new TPixel[sourcePalette.Length]; - Color.ToPixel(configuration, sourcePalette, this.palette); - } + Color.ToPixel(this.Configuration, sourcePalette, this.palette.Memory.Span); - // Lazy init paletteVector: - if (this.paletteVector is null) - { - this.paletteVector = new Vector4[this.palette.Length]; PixelOperations.Instance.ToVector4( - configuration, - (ReadOnlySpan)this.palette, - (Span)this.paletteVector, + this.Configuration, + this.palette.Memory.Span, + this.paletteVector.Memory.Span, PixelConversionModifiers.Scale); } + + this.palleteVectorMapped = true; + + base.BeforeFrameApply(source); + } + + /// + protected override void Dispose(bool disposing) + { + if (this.isDisposed) + { + return; + } + + if (disposing) + { + this.palette?.Dispose(); + this.paletteVector?.Dispose(); + } + + this.palette = null; + this.paletteVector = null; + + this.isDisposed = true; + base.Dispose(disposing); } /// @@ -91,21 +111,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering TPixel closest = default; TPixel secondClosest = default; - for (int index = 0; index < this.paletteVector.Length; index++) + Span paletteSpan = this.palette.Memory.Span; + ref TPixel paletteSpanBase = ref MemoryMarshal.GetReference(paletteSpan); + Span paletteVectorSpan = this.paletteVector.Memory.Span; + ref Vector4 paletteVectorSpanBase = ref MemoryMarshal.GetReference(paletteVectorSpan); + + for (int index = 0; index < paletteVectorSpan.Length; index++) { - ref Vector4 candidate = ref this.paletteVector[index]; + ref Vector4 candidate = ref Unsafe.Add(ref paletteVectorSpanBase, index); float distance = Vector4.DistanceSquared(vector, candidate); if (distance < leastDistance) { leastDistance = distance; secondClosest = closest; - closest = this.palette[index]; + closest = Unsafe.Add(ref paletteSpanBase, index); } else if (distance < secondLeastDistance) { secondLeastDistance = distance; - secondClosest = this.palette[index]; + secondClosest = Unsafe.Add(ref paletteSpanBase, index); } } @@ -116,4 +141,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering return pair; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs index b489f8f28d..03791bff25 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/Sierra2Diffuser.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; @@ -11,22 +11,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class Sierra2Diffuser : ErrorDiffuser { + private const float Divisor = 16F; + /// /// The diffusion matrix /// private static readonly DenseMatrix Sierra2Matrix = new float[,] { - { 0, 0, 0, 4, 3 }, - { 1, 2, 3, 2, 1 } + { 0, 0, 0, 4 / Divisor, 3 / Divisor }, + { 1 / Divisor, 2 / Divisor, 3 / Divisor, 2 / Divisor, 1 / Divisor } }; /// /// Initializes a new instance of the class. /// public Sierra2Diffuser() - : base(Sierra2Matrix, 16) + : base(Sierra2Matrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs index 04abc782a6..c7d7acc82f 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/Sierra3Diffuser.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; @@ -11,23 +11,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class Sierra3Diffuser : ErrorDiffuser { + private const float Divisor = 32F; + /// /// The diffusion matrix /// private static readonly DenseMatrix Sierra3Matrix = new float[,] { - { 0, 0, 0, 5, 3 }, - { 2, 4, 5, 4, 2 }, - { 0, 2, 3, 2, 0 } + { 0, 0, 0, 5 / Divisor, 3 / Divisor }, + { 2 / Divisor, 4 / Divisor, 5 / Divisor, 4 / Divisor, 2 / Divisor }, + { 0, 2 / Divisor, 3 / Divisor, 2 / Divisor, 0 } }; /// /// Initializes a new instance of the class. /// public Sierra3Diffuser() - : base(Sierra3Matrix, 32) + : base(Sierra3Matrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs index 2ac69cf456..e969f1b70b 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/SierraLiteDiffuser.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; @@ -11,22 +11,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class SierraLiteDiffuser : ErrorDiffuser { + private const float Divisor = 4F; + /// /// The diffusion matrix /// private static readonly DenseMatrix SierraLiteMatrix = new float[,] { - { 0, 0, 2 }, - { 1, 1, 0 } + { 0, 0, 2 / Divisor }, + { 1 / Divisor, 1 / Divisor, 0 } }; /// /// Initializes a new instance of the class. /// public SierraLiteDiffuser() - : base(SierraLiteMatrix, 4) + : base(SierraLiteMatrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs index b929a28d30..61727325ab 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/StevensonArceDiffuser.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; @@ -10,24 +10,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class StevensonArceDiffuser : ErrorDiffuser { + private const float Divisor = 200F; + /// /// The diffusion matrix /// private static readonly DenseMatrix StevensonArceMatrix = new float[,] { - { 0, 0, 0, 0, 0, 32, 0 }, - { 12, 0, 26, 0, 30, 0, 16 }, - { 0, 12, 0, 26, 0, 12, 0 }, - { 5, 0, 12, 0, 12, 0, 5 } + { 0, 0, 0, 0, 0, 32 / Divisor, 0 }, + { 12 / Divisor, 0, 26 / Divisor, 0, 30 / Divisor, 0, 16 / Divisor }, + { 0, 12 / Divisor, 0, 26 / Divisor, 0, 12 / Divisor, 0 }, + { 5 / Divisor, 0, 12 / Divisor, 0, 12 / Divisor, 0, 5 / Divisor } }; /// /// Initializes a new instance of the class. /// public StevensonArceDiffuser() - : base(StevensonArceMatrix, 200) + : base(StevensonArceMatrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs index bb3aebc3f5..76203201cc 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/StuckiDiffuser.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; @@ -11,23 +11,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public sealed class StuckiDiffuser : ErrorDiffuser { + private const float Divisor = 42F; + /// /// The diffusion matrix /// private static readonly DenseMatrix StuckiMatrix = new float[,] { - { 0, 0, 0, 8, 4 }, - { 2, 4, 8, 4, 2 }, - { 1, 2, 4, 2, 1 } + { 0, 0, 0, 8 / Divisor, 4 / Divisor }, + { 2 / Divisor, 4 / Divisor, 8 / Divisor, 4 / Divisor, 2 / Divisor }, + { 1 / Divisor, 2 / Divisor, 4 / Divisor, 2 / Divisor, 1 / Divisor } }; /// /// Initializes a new instance of the class. /// public StuckiDiffuser() - : base(StuckiMatrix, 42) + : base(StuckiMatrix) { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Dithering/error_diffusion.txt b/src/ImageSharp/Processing/Processors/Dithering/error_diffusion.txt index ea412f6351..27dea8af17 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/error_diffusion.txt +++ b/src/ImageSharp/Processing/Processors/Dithering/error_diffusion.txt @@ -1,3 +1,6 @@ +Reference: +http://bisqwit.iki.fi/jutut/kuvat/ordered_dither/error_diffusion.txt + List of error diffusion schemes. Quantization error of *current* pixel is added to the pixels diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index 741ba9eced..6f9e1869a3 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects { @@ -39,10 +40,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects public int BrushSize { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new OilPaintingProcessor(this); + return new OilPaintingProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index d31858ba15..4cac6b0f66 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -5,8 +5,8 @@ using System; using System.Numerics; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -22,16 +22,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects { private readonly OilPaintingProcessor definition; - public OilPaintingProcessor(OilPaintingProcessor definition) + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public OilPaintingProcessor(OilPaintingProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { int brushSize = this.definition.BrushSize; if (brushSize <= 0 || brushSize > source.Height || brushSize > source.Width) @@ -39,24 +43,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects throw new ArgumentOutOfRangeException(nameof(brushSize)); } - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; + int startY = this.SourceRectangle.Y; + int endY = this.SourceRectangle.Bottom; + int startX = this.SourceRectangle.X; + int endX = this.SourceRectangle.Right; int maxY = endY - 1; int maxX = endX - 1; int radius = brushSize >> 1; int levels = this.definition.Levels; - using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Size())) + using (Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) { source.CopyTo(targetPixels); var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); ParallelHelper.IterateRows( workingRect, - configuration, + this.Configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs index 1599c9dab8..2d7cef8fff 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects { @@ -29,10 +30,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects public int Size { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new PixelateProcessor(this); + return new PixelateProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs index 21f3bb774d..53acc351cf 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs @@ -25,7 +25,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// Initializes a new instance of the class. /// /// The . - public PixelateProcessor(PixelateProcessor definition) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public PixelateProcessor(PixelateProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } @@ -33,17 +36,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects private int Size => this.definition.Size; /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { if (this.Size <= 0 || this.Size > source.Height || this.Size > source.Width) { throw new ArgumentOutOfRangeException(nameof(this.Size)); } - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; + int startY = this.SourceRectangle.Y; + int endY = this.SourceRectangle.Bottom; + int startX = this.SourceRectangle.X; + int endX = this.SourceRectangle.Right; int size = this.Size; int offset = this.Size / 2; @@ -69,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects Parallel.ForEach( range, - configuration.GetParallelOptions(), + this.Configuration.GetParallelOptions(), y => { int offsetY = y - startY; @@ -108,4 +111,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects }); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index 36c43d8446..9cd4a9e460 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -1,8 +1,9 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { @@ -23,10 +24,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters public ColorMatrix Matrix { get; } /// - public virtual IImageProcessor CreatePixelSpecificProcessor() + public virtual IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new FilterProcessor(this); + return new FilterProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs index ab60a42793..731cd2a052 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -1,10 +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.Numerics; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Primitives; @@ -20,22 +20,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters { private readonly FilterProcessor definition; - public FilterProcessor(FilterProcessor definition) + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public FilterProcessor(FilterProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startX = interest.X; ColorMatrix matrix = this.definition.Matrix; ParallelHelper.IterateRowsWithTempBuffer( interest, - configuration, + this.Configuration, (rows, vectorBuffer) => { for (int y = rows.Min; y < rows.Max; y++) @@ -43,13 +50,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters Span vectorSpan = vectorBuffer.Span; int length = vectorSpan.Length; Span rowSpan = source.GetPixelRowSpan(y).Slice(startX, length); - PixelOperations.Instance.ToVector4(configuration, rowSpan, vectorSpan); + PixelOperations.Instance.ToVector4(this.Configuration, rowSpan, vectorSpan); Vector4Utils.Transform(vectorSpan, ref matrix); - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan); + PixelOperations.Instance.FromVector4Destructive(this.Configuration, vectorSpan, rowSpan); } }); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs new file mode 100644 index 0000000000..49be3b6a67 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Processing.Processors.Filters +{ + /// + /// Applies a lightness filter matrix using the given amount. + /// + public sealed class LightnessProcessor : FilterProcessor + { + /// + /// Initializes a new instance of the class. + /// + /// + /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing lighter results. + /// + /// The proportion of the conversion. Must be greater than or equal to 0. + public LightnessProcessor(float amount) + : base(KnownFilterMatrices.CreateLightnessFilter(amount)) + { + this.Amount = amount; + } + + /// + /// Gets the proportion of the conversion + /// + public float Amount { get; } + } +} diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs index f5a1befffb..fdfaa9cb07 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// @@ -17,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - public override IImageProcessor CreatePixelSpecificProcessor() => - new LomographProcessor(this); + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) => + new LomographProcessor(this, source, sourceRectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs index e0f85945aa..7d3a5bbc0a 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs @@ -19,15 +19,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// Initializes a new instance of the class. /// /// The defining the parameters. - public LomographProcessor(LomographProcessor definition) - : base(definition) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public LomographProcessor(LomographProcessor definition, Image source, Rectangle sourceRectangle) + : base(definition, source, sourceRectangle) { } /// - protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void AfterImageApply() { - new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration); + new VignetteProcessor(VeryDarkGreen).Execute(this.Source, this.SourceRectangle); + base.AfterImageApply(); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs index 676bbc06bb..c8527a29cf 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// @@ -17,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - public override IImageProcessor CreatePixelSpecificProcessor() => - new PolaroidProcessor(this); + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) => + new PolaroidProcessor(this, source, sourceRectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs index 0f511ee296..f7ab1a1ec2 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs @@ -21,19 +21,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// Initializes a new instance of the class. /// /// The defining the parameters. - public PolaroidProcessor(PolaroidProcessor definition) - : base(definition) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public PolaroidProcessor(PolaroidProcessor definition, Image source, Rectangle sourceRectangle) + : base(definition, source, sourceRectangle) { } /// - protected override void AfterFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) + protected override void AfterImageApply() { - new VignetteProcessor(VeryDarkOrange).Apply(source, sourceRectangle, configuration); - new GlowProcessor(LightOrange, source.Width / 4F).Apply(source, sourceRectangle, configuration); + new VignetteProcessor(VeryDarkOrange).Execute(this.Source, this.SourceRectangle); + new GlowProcessor(LightOrange, this.Source.Width / 4F).Execute(this.Source, this.SourceRectangle); + base.AfterImageApply(); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs index 024ccbced1..554a4b8860 100644 --- a/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ICloningImageProcessor.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; @@ -7,26 +7,21 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { /// - /// Encapsulates methods to alter the pixels of a new image, cloned from the original image. + /// Defines an algorithm to alter the pixels of a cloned image. /// - /// The pixel format. - internal interface ICloningImageProcessor : IImageProcessor - where TPixel : struct, IPixel + public interface ICloningImageProcessor : IImageProcessor { /// - /// Applies the process to the specified portion of the specified . + /// Creates a pixel specific that is capable of executing + /// the processing algorithm on an . /// + /// The pixel type. /// The source image. Cannot be null. /// /// The structure that specifies the portion of the image object to draw. /// - /// - /// is null. - /// - /// - /// doesn't fit the dimension of the image. - /// - /// Returns the cloned image after there processor has been applied to it. - Image CloneAndApply(Image source, Rectangle sourceRectangle); + /// The + ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) + where TPixel : struct, IPixel; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs new file mode 100644 index 0000000000..84b1262299 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Implements an algorithm to alter the pixels of a cloned image. + /// + /// The pixel format. + public interface ICloningImageProcessor : IImageProcessor + where TPixel : struct, IPixel + { + /// + /// Clones the specified and executes the process against the clone. + /// + /// The . + Image CloneAndExecute(); + } +} diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index 9b030a6fea..fb7a6a4d9d 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { @@ -14,12 +15,16 @@ namespace SixLabors.ImageSharp.Processing.Processors public interface IImageProcessor { /// - /// Creates a pixel specific that is capable for executing + /// Creates a pixel specific that is capable of executing /// the processing algorithm on an . /// /// The pixel type. + /// The source image. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// /// The - IImageProcessor CreatePixelSpecificProcessor() + IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs index 90dfaf8db5..1b874e4b94 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { @@ -10,22 +10,12 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Implements an algorithm to alter the pixels of an image. /// /// The pixel format. - public interface IImageProcessor + public interface IImageProcessor : IDisposable where TPixel : struct, IPixel { /// - /// Applies the process to the specified portion of the specified . + /// Executes the process against the specified . /// - /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - /// - /// is null. - /// - /// - /// doesn't fit the dimension of the image. - /// - void Apply(Image source, Rectangle sourceRectangle); + void Execute(); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor.cs b/src/ImageSharp/Processing/Processors/ImageProcessor.cs deleted file mode 100644 index 0d27a9e1e8..0000000000 --- a/src/ImageSharp/Processing/Processors/ImageProcessor.cs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors -{ - /// - /// Allows the application of processors to images. - /// - /// The pixel format. - internal abstract class ImageProcessor : IImageProcessor - where TPixel : struct, IPixel - { - /// - public void Apply(Image source, Rectangle sourceRectangle) - { - try - { - Configuration config = source.GetConfiguration(); - this.BeforeImageApply(source, sourceRectangle); - - foreach (ImageFrame sourceFrame in source.Frames) - { - this.Apply(sourceFrame, sourceRectangle, config); - } - - this.AfterImageApply(source, sourceRectangle); - } -#if DEBUG - catch (Exception) - { - throw; -#else - catch (Exception ex) - { - throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); -#endif - } - } - - /// - /// Applies the processor to just a single ImageBase - /// - /// the source image - /// the target - /// The configuration. - public void Apply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - try - { - this.BeforeFrameApply(source, sourceRectangle, configuration); - this.OnFrameApply(source, sourceRectangle, configuration); - this.AfterFrameApply(source, sourceRectangle, configuration); - } -#if DEBUG - catch (Exception) - { - throw; -#else - catch (Exception ex) - { - throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); -#endif - } - } - - /// - /// This method is called before the process is applied to prepare the processor. - /// - /// The source image. Cannot be null. - /// The structure that specifies the portion of the image object to draw. - protected virtual void BeforeImageApply(Image source, Rectangle sourceRectangle) - { - } - - /// - /// This method is called before the process is applied to prepare the processor. - /// - /// The source image. Cannot be null. - /// The structure that specifies the portion of the image object to draw. - /// The configuration. - protected virtual void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - } - - /// - /// Applies the process to the specified portion of the specified at the specified location - /// and with the specified size. - /// - /// The source image. Cannot be null. - /// The structure that specifies the portion of the image object to draw. - /// The configuration. - protected abstract void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration); - - /// - /// This method is called after the process is applied to prepare the processor. - /// - /// The source image. Cannot be null. - /// The structure that specifies the portion of the image object to draw. - /// The configuration. - protected virtual void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - } - - /// - /// This method is called after the process is applied to prepare the processor. - /// - /// The source image. Cannot be null. - /// The structure that specifies the portion of the image object to draw. - protected virtual void AfterImageApply(Image source, Rectangle sourceRectangle) - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs index 91b2b30d02..ce8ed813b5 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -8,34 +9,21 @@ namespace SixLabors.ImageSharp.Processing.Processors { internal static class ImageProcessorExtensions { - public static void Apply(this IImageProcessor processor, Image source, Rectangle sourceRectangle) - { - var visitor = new ApplyVisitor(processor, sourceRectangle); - source.AcceptVisitor(visitor); - } - /// - /// Apply an to a frame. - /// Only works from processors implemented by an subclass. + /// Executes the processor against the given source image and rectangle bounds. /// - internal static void Apply( - this IImageProcessor processor, - ImageFrame frame, - Rectangle sourceRectangle, - Configuration configuration) - where TPixel : struct, IPixel - { - var processorImpl = (ImageProcessor)processor.CreatePixelSpecificProcessor(); - processorImpl.Apply(frame, sourceRectangle, configuration); - } + /// The processor. + /// The source image. + /// The source bounds. + public static void Execute(this IImageProcessor processor, Image source, Rectangle sourceRectangle) + => source.AcceptVisitor(new ExecuteVisitor(processor, sourceRectangle)); - private class ApplyVisitor : IImageVisitor + private class ExecuteVisitor : IImageVisitor { private readonly IImageProcessor processor; - private readonly Rectangle sourceRectangle; - public ApplyVisitor(IImageProcessor processor, Rectangle sourceRectangle) + public ExecuteVisitor(IImageProcessor processor, Rectangle sourceRectangle) { this.processor = processor; this.sourceRectangle = sourceRectangle; @@ -44,9 +32,11 @@ namespace SixLabors.ImageSharp.Processing.Processors public void Visit(Image image) where TPixel : struct, IPixel { - var processorImpl = this.processor.CreatePixelSpecificProcessor(); - processorImpl.Apply(image, this.sourceRectangle); + using (IImageProcessor processorImpl = this.processor.CreatePixelSpecificProcessor(image, this.sourceRectangle)) + { + processorImpl.Execute(); + } } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs new file mode 100644 index 0000000000..b8bbe1e031 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs @@ -0,0 +1,148 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// The base class for all pixel specific image processors. + /// Allows the application of processing algorithms to the image. + /// + /// The pixel format. + public abstract class ImageProcessor : IImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + protected ImageProcessor(Image source, Rectangle sourceRectangle) + { + this.Source = source; + this.SourceRectangle = sourceRectangle; + this.Configuration = this.Source.GetConfiguration(); + } + + /// + /// Gets The source for the current processor instance. + /// + protected Image Source { get; } + + /// + /// Gets The source area to process for the current processor instance. + /// + protected Rectangle SourceRectangle { get; } + + /// + /// Gets the instance to use when performing operations. + /// + protected Configuration Configuration { get; } + + /// + void IImageProcessor.Execute() + { + try + { + this.BeforeImageApply(); + + foreach (ImageFrame sourceFrame in this.Source.Frames) + { + this.Apply(sourceFrame); + } + + this.AfterImageApply(); + } +#if DEBUG + catch (Exception) + { + throw; +#else + catch (Exception ex) + { + throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); +#endif + } + } + + /// + /// Applies the processor to a single image frame. + /// + /// the source image. + public void Apply(ImageFrame source) + { + try + { + this.BeforeFrameApply(source); + this.OnFrameApply(source); + this.AfterFrameApply(source); + } +#if DEBUG + catch (Exception) + { + throw; +#else + catch (Exception ex) + { + throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); +#endif + } + } + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// This method is called before the process is applied to prepare the processor. + /// + protected virtual void BeforeImageApply() + { + } + + /// + /// This method is called before the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + protected virtual void BeforeFrameApply(ImageFrame source) + { + } + + /// + /// Applies the process to the specified portion of the specified at the specified location + /// and with the specified size. + /// + /// The source image. Cannot be null. + protected abstract void OnFrameApply(ImageFrame source); + + /// + /// This method is called after the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + protected virtual void AfterFrameApply(ImageFrame source) + { + } + + /// + /// This method is called after the process is applied to prepare the processor. + /// + protected virtual void AfterImageApply() + { + } + + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + /// Whether to dispose managed and unmanaged objects. + protected virtual void Dispose(bool disposing) + { + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs index ad27ae020c..68c1474bea 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// @@ -15,14 +17,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. public AdaptiveHistogramEqualizationProcessor( int luminanceLevels, bool clipHistogram, - float clipLimitPercentage, + int clipLimit, int numberOfTiles) - : base(luminanceLevels, clipHistogram, clipLimitPercentage) + : base(luminanceLevels, clipHistogram, clipLimit) { this.NumberOfTiles = numberOfTiles; } @@ -33,13 +35,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public int NumberOfTiles { get; } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { return new AdaptiveHistogramEqualizationProcessor( this.LuminanceLevels, this.ClipHistogram, - this.ClipLimitPercentage, - this.NumberOfTiles); + this.ClipLimit, + this.NumberOfTiles, + source, + sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index 537a3089ec..e3960035e0 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -30,10 +30,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) - : base(luminanceLevels, clipHistogram, clipLimitPercentage) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public AdaptiveHistogramEqualizationProcessor( + int luminanceLevels, + bool clipHistogram, + int clipLimit, + int tiles, + Image source, + Rectangle sourceRectangle) + : base(luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) { Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); @@ -47,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization private int Tiles { get; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { int sourceWidth = source.Width; int sourceHeight = source.Height; @@ -59,22 +67,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization int luminanceLevels = this.LuminanceLevels; // The image is split up into tiles. For each tile the cumulative distribution function will be calculated. - using (var cdfData = new CdfTileData(configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels)) + using (var cdfData = new CdfTileData(this.Configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels)) { cdfData.CalculateLookupTables(source, this); var tileYStartPositions = new List<(int y, int cdfY)>(); int cdfY = 0; - for (int y = halfTileHeight; y < sourceHeight - halfTileHeight; y += tileHeight) + int yStart = halfTileHeight; + for (int tile = 0; tile < tileCount - 1; tile++) { - tileYStartPositions.Add((y, cdfY)); + tileYStartPositions.Add((yStart, cdfY)); cdfY++; + yStart += tileHeight; } Parallel.For( 0, tileYStartPositions.Count, - new ParallelOptions { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }, + new ParallelOptions { MaxDegreeOfParallelism = this.Configuration.MaxDegreeOfParallelism }, index => { int y = tileYStartPositions[index].y; @@ -84,7 +94,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization ref TPixel sourceBase = ref source.GetPixelReference(0, 0); int cdfX = 0; - for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) + int x = halfTileWidth; + for (int tile = 0; tile < tileCount - 1; tile++) { int tileY = 0; int yEnd = Math.Min(y + tileHeight, sourceHeight); @@ -117,24 +128,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization } cdfX++; + x += tileWidth; } }); ref TPixel pixelsBase = ref source.GetPixelReference(0, 0); // Fix left column - ProcessBorderColumn(ref pixelsBase, cdfData, 0, sourceWidth, sourceHeight, tileWidth, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels); + ProcessBorderColumn(ref pixelsBase, cdfData, 0, sourceWidth, sourceHeight, this.Tiles, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels); // Fix right column int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth; - ProcessBorderColumn(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, sourceHeight, tileWidth, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); + ProcessBorderColumn(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, sourceHeight, this.Tiles, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); // Fix top row - ProcessBorderRow(ref pixelsBase, cdfData, 0, sourceWidth, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); + ProcessBorderRow(ref pixelsBase, cdfData, 0, sourceWidth, this.Tiles, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); // Fix bottom row int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight; - ProcessBorderRow(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); + ProcessBorderRow(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, this.Tiles, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); // Left top corner ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); @@ -198,7 +210,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// The X index of the lookup table to use. /// The source image width. /// The source image height. - /// The width of a tile. + /// The number of vertical tiles. /// The height of a tile. /// X start position in the image. /// X end position of the image. @@ -212,7 +224,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization int cdfX, int sourceWidth, int sourceHeight, - int tileWidth, + int tileCount, int tileHeight, int xStart, int xEnd, @@ -221,7 +233,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization int halfTileHeight = tileHeight / 2; int cdfY = 0; - for (int y = halfTileHeight; y < sourceHeight - halfTileHeight; y += tileHeight) + int y = halfTileHeight; + for (int tile = 0; tile < tileCount - 1; tile++) { int yLimit = Math.Min(y + tileHeight, sourceHeight - 1); int tileY = 0; @@ -239,6 +252,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization } cdfY++; + y += tileHeight; } } @@ -249,6 +263,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// The pre-computed lookup tables to remap the grey values for each tiles. /// The Y index of the lookup table to use. /// The source image width. + /// The number of horizontal tiles. /// The width of a tile. /// Y start position in the image. /// Y end position of the image. @@ -261,6 +276,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization CdfTileData cdfData, int cdfY, int sourceWidth, + int tileCount, int tileWidth, int yStart, int yEnd, @@ -269,7 +285,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization int halfTileWidth = tileWidth / 2; int cdfX = 0; - for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) + int x = halfTileWidth; + for (int tile = 0; tile < tileCount - 1; tile++) { for (int dy = yStart; dy < yEnd; dy++) { @@ -286,6 +303,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization } cdfX++; + x += tileWidth; } } @@ -494,7 +512,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization if (processor.ClipHistogramEnabled) { - processor.ClipHistogram(histogram, processor.ClipLimitPercentage, this.pixelsInTile); + processor.ClipHistogram(histogram, processor.ClipLimit); } Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs index 36f798975a..632cfcd599 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// @@ -14,14 +16,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. public AdaptiveHistogramEqualizationSlidingWindowProcessor( int luminanceLevels, bool clipHistogram, - float clipLimitPercentage, + int clipLimit, int numberOfTiles) - : base(luminanceLevels, clipHistogram, clipLimitPercentage) + : base(luminanceLevels, clipHistogram, clipLimit) { this.NumberOfTiles = numberOfTiles; } @@ -32,13 +34,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public int NumberOfTiles { get; } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { return new AdaptiveHistogramEqualizationSlidingWindowProcessor( this.LuminanceLevels, this.ClipHistogram, - this.ClipLimitPercentage, - this.NumberOfTiles); + this.ClipLimit, + this.NumberOfTiles, + source, + sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs index 377af51354..f2f11cbfe5 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs @@ -29,10 +29,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistogramEqualizationSlidingWindowProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) - : base(luminanceLevels, clipHistogram, clipLimitPercentage) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public AdaptiveHistogramEqualizationSlidingWindowProcessor( + int luminanceLevels, + bool clipHistogram, + int clipLimit, + int tiles, + Image source, + Rectangle sourceRectangle) + : base(luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) { Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); @@ -46,11 +54,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization private int Tiles { get; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { - MemoryAllocator memoryAllocator = configuration.MemoryAllocator; + MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator; - var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }; + var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = this.Configuration.MaxDegreeOfParallelism }; int tileWidth = source.Width / this.Tiles; int tileHeight = tileWidth; int pixelInTile = tileWidth * tileHeight; @@ -58,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization int halfTileWidth = halfTileHeight; var slidingWindowInfos = new SlidingWindowInfos(tileWidth, tileHeight, halfTileWidth, halfTileHeight, pixelInTile); - using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height)) + using (Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Width, source.Height)) { // Process the inner tiles, which do not require to check the borders. Parallel.For( @@ -73,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization yStart: halfTileHeight, yEnd: source.Height - halfTileHeight, useFastPath: true, - configuration)); + this.Configuration)); // Process the left border of the image. Parallel.For( @@ -88,7 +96,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization yStart: 0, yEnd: source.Height, useFastPath: false, - configuration)); + this.Configuration)); // Process the right border of the image. Parallel.For( @@ -103,7 +111,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization yStart: 0, yEnd: source.Height, useFastPath: false, - configuration)); + this.Configuration)); // Process the top border of the image. Parallel.For( @@ -118,7 +126,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization yStart: 0, yEnd: halfTileHeight, useFastPath: false, - configuration)); + this.Configuration)); // Process the bottom border of the image. Parallel.For( @@ -133,7 +141,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization yStart: source.Height - halfTileHeight, yEnd: source.Height, useFastPath: false, - configuration)); + this.Configuration)); Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } @@ -202,7 +210,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization { // Clipping the histogram, but doing it on a copy to keep the original un-clipped values for the next iteration. histogram.CopyTo(histogramCopy); - this.ClipHistogram(histogramCopy, this.ClipLimitPercentage, swInfos.PixelInTile); + this.ClipHistogram(histogramCopy, this.ClipLimit); } // Calculate the cumulative distribution function, which will map each input pixel in the current tile to a new value. diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs index 9af2c8352b..0666b21bf9 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.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.Primitives; + namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// @@ -13,19 +15,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// The number of luminance levels. /// A value indicating whether to clip the histogram bins at a specific value. - /// The histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) - : base(luminanceLevels, clipHistogram, clipLimitPercentage) + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. + public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, int clipLimit) + : base(luminanceLevels, clipHistogram, clipLimit) { } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { return new GlobalHistogramEqualizationProcessor( this.LuminanceLevels, this.ClipHistogram, - this.ClipLimitPercentage); + this.ClipLimit, + source, + sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index a790263fa1..f8cd5620a2 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -8,8 +8,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -31,16 +31,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// or 65536 for 16-bit grayscale images. /// /// Indicating whether to clip the histogram bins at a specific value. - /// Histogram clip limit in percent of the total pixels. Histogram bins which exceed this limit, will be capped at this value. - public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) - : base(luminanceLevels, clipHistogram, clipLimitPercentage) + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public GlobalHistogramEqualizationProcessor( + int luminanceLevels, + bool clipHistogram, + int clipLimit, + Image source, + Rectangle sourceRectangle) + : base(luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) { } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { - MemoryAllocator memoryAllocator = configuration.MemoryAllocator; + MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator; int numberOfPixels = source.Width * source.Height; var workingRect = new Rectangle(0, 0, source.Width, source.Height); @@ -50,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization // Build the histogram of the grayscale levels. ParallelHelper.IterateRows( workingRect, - configuration, + this.Configuration, rows => { ref int histogramBase = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan()); @@ -69,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization Span histogram = histogramBuffer.GetSpan(); if (this.ClipHistogramEnabled) { - this.ClipHistogram(histogram, this.ClipLimitPercentage, numberOfPixels); + this.ClipHistogram(histogram, this.ClipLimit); } // Calculate the cumulative distribution function, which will map each input pixel to a new value. @@ -83,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization // Apply the cdf to each pixel of the image ParallelHelper.IterateRows( workingRect, - configuration, + this.Configuration, rows => { ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs index 1d9d5c986a..b55b725a6b 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.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.Processing.Processors.Normalization @@ -20,24 +20,32 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Gets or sets the number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. Defaults to 256. + /// or 65536 for 16-bit grayscale images. + /// Defaults to 256. /// public int LuminanceLevels { get; set; } = 256; /// - /// Gets or sets a value indicating whether to clip the histogram bins at a specific value. Defaults to false. + /// Gets or sets a value indicating whether to clip the histogram bins at a specific value. + /// It is recommended to use clipping when the AdaptiveTileInterpolation method is used, to suppress artifacts which can occur on the borders of the tiles. + /// Defaults to false. /// public bool ClipHistogram { get; set; } = false; /// - /// Gets or sets the histogram clip limit in percent of the total pixels in a tile. Histogram bins which exceed this limit, will be capped at this value. - /// Defaults to 0.035f. + /// Gets or sets the histogram clip limit. Adaptive histogram equalization may cause noise to be amplified in near constant + /// regions. To reduce this problem, histogram bins which exceed a given limit will be capped at this value. The exceeding values + /// will be redistributed equally to all other bins. The clipLimit depends on the size of the tiles the image is split into + /// and therefore the image size itself. + /// Defaults to 350. /// - public float ClipLimitPercentage { get; set; } = 0.035f; + /// For more information, see also: https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE + public int ClipLimit { get; set; } = 350; /// - /// Gets or sets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. Defaults to 10. + /// Gets or sets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. + /// Defaults to 8. /// - public int NumberOfTiles { get; set; } = 10; + public int NumberOfTiles { get; set; } = 8; } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs index b1d12f8478..4273e93753 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Normalization { @@ -16,12 +17,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicates, if histogram bins should be clipped. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. + protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, int clipLimit) { this.LuminanceLevels = luminanceLevels; this.ClipHistogram = clipHistogram; - this.ClipLimitPercentage = clipLimitPercentage; + this.ClipLimit = clipLimit; } /// @@ -35,12 +36,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public bool ClipHistogram { get; } /// - /// Gets the histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// Gets the histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// - public float ClipLimitPercentage { get; } + public int ClipLimit { get; } /// - public abstract IImageProcessor CreatePixelSpecificProcessor() + public abstract IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; /// @@ -59,14 +60,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization processor = new GlobalHistogramEqualizationProcessor( options.LuminanceLevels, options.ClipHistogram, - options.ClipLimitPercentage); + options.ClipLimit); break; case HistogramEqualizationMethod.AdaptiveTileInterpolation: processor = new AdaptiveHistogramEqualizationProcessor( options.LuminanceLevels, options.ClipHistogram, - options.ClipLimitPercentage, + options.ClipLimit, options.NumberOfTiles); break; @@ -74,7 +75,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization processor = new AdaptiveHistogramEqualizationSlidingWindowProcessor( options.LuminanceLevels, options.ClipHistogram, - options.ClipLimitPercentage, + options.ClipLimit, options.NumberOfTiles); break; @@ -82,11 +83,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization processor = new GlobalHistogramEqualizationProcessor( options.LuminanceLevels, options.ClipHistogram, - options.ClipLimitPercentage); + options.ClipLimit); break; } return processor; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs index 8dbc903f29..6e4c16de76 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs @@ -7,6 +7,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Normalization { @@ -25,16 +26,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicates, if histogram bins should be clipped. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) + /// The histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + protected HistogramEqualizationProcessor( + int luminanceLevels, + bool clipHistogram, + int clipLimit, + Image source, + Rectangle sourceRectangle) + : base(source, sourceRectangle) { Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels)); - Guard.MustBeGreaterThan(clipLimitPercentage, 0F, nameof(clipLimitPercentage)); + Guard.MustBeGreaterThan(clipLimit, 1, nameof(clipLimit)); this.LuminanceLevels = luminanceLevels; this.luminanceLevelsFloat = luminanceLevels; this.ClipHistogramEnabled = clipHistogram; - this.ClipLimitPercentage = clipLimitPercentage; + this.ClipLimit = clipLimit; } /// @@ -48,9 +57,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public bool ClipHistogramEnabled { get; } /// - /// Gets the histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// Gets the histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. /// - public float ClipLimitPercentage { get; } + public int ClipLimit { get; } /// /// Calculates the cumulative distribution function. @@ -87,11 +96,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// the values over the clip limit to all other bins equally. /// /// The histogram to apply the clipping. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - /// The numbers of pixels inside the tile. - public void ClipHistogram(Span histogram, float clipLimitPercentage, int pixelCount) + /// Histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. + public void ClipHistogram(Span histogram, int clipLimit) { - int clipLimit = (int)MathF.Round(pixelCount * clipLimitPercentage); int sumOverClip = 0; ref int histogramBase = ref MemoryMarshal.GetReference(histogram); @@ -105,6 +112,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization } } + // Redistribute the clipped pixels over all bins of the histogram. int addToEachBin = sumOverClip > 0 ? (int)MathF.Floor(sumOverClip / this.luminanceLevelsFloat) : 0; if (addToEachBin > 0) { @@ -113,6 +121,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization Unsafe.Add(ref histogramBase, i) += addToEachBin; } } + + int residual = sumOverClip - (addToEachBin * this.LuminanceLevels); + if (residual != 0) + { + int residualStep = Math.Max(this.LuminanceLevels / residual, 1); + for (int i = 0; i < this.LuminanceLevels && residual > 0; i += residualStep, residual--) + { + ref int histogramLevel = ref Unsafe.Add(ref histogramBase, i); + histogramLevel++; + } + } } /// @@ -136,4 +155,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public static int GetLuminance(ref Vector4 vector, int luminanceLevels) => (int)MathF.Round(((.2126F * vector.X) + (.7152F * vector.Y) + (.0722F * vector.Y)) * (luminanceLevels - 1)); } -} \ 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 62848172a1..4b4c537277 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { @@ -32,10 +33,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays public Color Color { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new BackgroundColorProcessor(this); + return new BackgroundColorProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs index c4af59fecb..7e3d2fdc5b 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs @@ -5,8 +5,8 @@ using System; using System.Buffers; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -24,24 +24,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// /// Initializes a new instance of the class. /// - public BackgroundColorProcessor(BackgroundColorProcessor definition) + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public BackgroundColorProcessor(BackgroundColorProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { TPixel color = this.definition.Color.ToPixel(); GraphicsOptions graphicsOptions = this.definition.GraphicsOptions; - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; + int startY = this.SourceRectangle.Y; + int endY = this.SourceRectangle.Bottom; + int startX = this.SourceRectangle.X; + int endX = this.SourceRectangle.Right; // Align start/end positions. int minX = Math.Max(0, startX); @@ -78,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays ParallelHelper.IterateRows( workingRect, - configuration, + this.Configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) @@ -98,4 +99,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 255be5ed59..0958e3aa9e 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -1,8 +1,9 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { @@ -69,10 +70,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays internal ValueSize Radius { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new GlowProcessor(this); + return new GlowProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs index 3201fcbfee..4082d9a65b 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs @@ -6,8 +6,8 @@ using System.Buffers; using System.Numerics; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -24,31 +24,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays private readonly GlowProcessor definition; - public GlowProcessor(GlowProcessor definition) + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public GlowProcessor(GlowProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; this.blender = PixelOperations.Instance.GetPixelBlender(definition.GraphicsOptions); } /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { // TODO: can we simplify the rectangle calculation? - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; + int startY = this.SourceRectangle.Y; + int endY = this.SourceRectangle.Bottom; + int startX = this.SourceRectangle.X; + int endX = this.SourceRectangle.Right; TPixel glowColor = this.definition.GlowColor.ToPixel(); - Vector2 center = Rectangle.Center(sourceRectangle); + Vector2 center = Rectangle.Center(this.SourceRectangle); float finalRadius = this.definition.Radius.Calculate(source.Size()); float maxDistance = finalRadius > 0 - ? MathF.Min(finalRadius, sourceRectangle.Width * .5F) - : sourceRectangle.Width * .5F; + ? MathF.Min(finalRadius, this.SourceRectangle.Width * .5F) + : this.SourceRectangle.Width * .5F; // Align start/end positions. int minX = Math.Max(0, startX); @@ -80,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays ParallelHelper.IterateRowsWithTempBuffer( workingRect, - configuration, + this.Configuration, (rows, amounts) => { Span amountsSpan = amounts.Span; @@ -109,4 +113,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 66bf9f1af7..2365318f3d 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -1,8 +1,9 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Overlays { @@ -67,10 +68,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays internal ValueSize RadiusY { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new VignetteProcessor(this); + return new VignetteProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs index 78c3cec5e7..c27a634f3d 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs @@ -6,8 +6,8 @@ using System.Buffers; using System.Numerics; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -24,34 +24,38 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays private readonly VignetteProcessor definition; - public VignetteProcessor(VignetteProcessor definition) + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public VignetteProcessor(VignetteProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; this.blender = PixelOperations.Instance.GetPixelBlender(definition.GraphicsOptions); } /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; + int startY = this.SourceRectangle.Y; + int endY = this.SourceRectangle.Bottom; + int startX = this.SourceRectangle.X; + int endX = this.SourceRectangle.Right; TPixel vignetteColor = this.definition.VignetteColor.ToPixel(); - Vector2 centre = Rectangle.Center(sourceRectangle); + Vector2 centre = Rectangle.Center(this.SourceRectangle); Size sourceSize = source.Size(); float finalRadiusX = this.definition.RadiusX.Calculate(sourceSize); float finalRadiusY = this.definition.RadiusY.Calculate(sourceSize); float rX = finalRadiusX > 0 - ? MathF.Min(finalRadiusX, sourceRectangle.Width * .5F) - : sourceRectangle.Width * .5F; + ? MathF.Min(finalRadiusX, this.SourceRectangle.Width * .5F) + : this.SourceRectangle.Width * .5F; float rY = finalRadiusY > 0 - ? MathF.Min(finalRadiusY, sourceRectangle.Height * .5F) - : sourceRectangle.Height * .5F; + ? MathF.Min(finalRadiusY, this.SourceRectangle.Height * .5F) + : this.SourceRectangle.Height * .5F; float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY)); // Align start/end positions. @@ -83,7 +87,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays ParallelHelper.IterateRowsWithTempBuffer( workingRect, - configuration, + this.Configuration, (rows, amounts) => { Span amountsSpan = amounts.Span; @@ -111,4 +115,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs index e6ffecc84d..71013548b8 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs @@ -1,11 +1,12 @@ -// 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.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; - +using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; @@ -31,7 +32,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// The vector representation of the image palette. /// - private Vector4[] paletteVector; + private IMemoryOwner paletteVector; + + private bool isDisposed; /// /// Initializes a new instance of the class. @@ -80,8 +83,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization public bool Dither { get; } /// - public virtual void Dispose() + public void Dispose() { + this.Dispose(true); + GC.SuppressFinalize(this); } /// @@ -103,11 +108,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization // Collect the palette. Required before the second pass runs. ReadOnlyMemory palette = this.GetPalette(); - this.paletteVector = new Vector4[palette.Length]; + this.paletteVector = image.Configuration.MemoryAllocator.Allocate(palette.Length); PixelOperations.Instance.ToVector4( image.Configuration, palette.Span, - (Span)this.paletteVector, + this.paletteVector.Memory.Span, PixelConversionModifiers.Scale); var quantizedFrame = new QuantizedFrame(image.MemoryAllocator, width, height, palette); @@ -129,6 +134,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization return quantizedFrame; } + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + /// Whether to dispose managed and unmanaged objects. + protected virtual void Dispose(bool disposing) + { + if (this.isDisposed) + { + return; + } + + if (disposing) + { + this.paletteVector?.Dispose(); + } + + this.paletteVector = null; + + this.isDisposed = true; + } + /// /// Execute the first pass through the pixels in the image to create the palette. /// @@ -161,7 +187,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Retrieve the palette for the quantized image. /// /// - /// + /// /// protected abstract ReadOnlyMemory GetPalette(); @@ -173,12 +199,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization protected byte GetTransparentIndex() { // Transparent pixels are much more likely to be found at the end of a palette. - int paletteVectorLengthMinus1 = this.paletteVector.Length - 1; + Span paletteVectorSpan = this.paletteVector.Memory.Span; + ref Vector4 paletteVectorSpanBase = ref MemoryMarshal.GetReference(paletteVectorSpan); + + int paletteVectorLengthMinus1 = paletteVectorSpan.Length - 1; int index = paletteVectorLengthMinus1; for (int i = paletteVectorLengthMinus1; i >= 0; i--) { - ref Vector4 candidate = ref this.paletteVector[i]; + ref Vector4 candidate = ref Unsafe.Add(ref paletteVectorSpanBase, i); if (candidate.Equals(default)) { index = i; @@ -211,10 +240,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization float leastDistance = float.MaxValue; Vector4 vector = pixel.ToScaledVector4(); float epsilon = Constants.EpsilonSquared; + Span paletteVectorSpan = this.paletteVector.Memory.Span; + ref Vector4 paletteVectorSpanBase = ref MemoryMarshal.GetReference(paletteVectorSpan); - for (int index = 0; index < this.paletteVector.Length; index++) + for (int index = 0; index < paletteVectorSpan.Length; index++) { - ref Vector4 candidate = ref this.paletteVector[index]; + ref Vector4 candidate = ref Unsafe.Add(ref paletteVectorSpanBase, index); float distance = Vector4.DistanceSquared(vector, candidate); // Greater... Move on. @@ -239,4 +270,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization return result; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs index 85a4d20295..393cb5f602 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.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; @@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height); + this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, width, height); } output[(y * source.Width) + x] = pixelValue; @@ -571,4 +571,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs index 265c343e68..f774f80be2 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.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; @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height); + this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, width, height); } output[(y * source.Width) + x] = pixelValue; @@ -98,4 +98,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization [MethodImpl(MethodImplOptions.AggressiveInlining)] private byte QuantizePixel(ref TPixel pixel) => this.GetClosestPixel(ref pixel); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs index 0aee0b4831..8cc14da675 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Quantization { @@ -25,10 +26,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization public IQuantizer Quantizer { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new QuantizeProcessor(this.Quantizer); + return new QuantizeProcessor(this.Quantizer, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs index 5cb45e8d70..9309467229 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs @@ -22,15 +22,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Initializes a new instance of the class. /// /// The quantizer used to reduce the color palette. - public QuantizeProcessor(IQuantizer quantizer) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public QuantizeProcessor(IQuantizer quantizer, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { Guard.NotNull(quantizer, nameof(quantizer)); this.quantizer = quantizer; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { + Configuration configuration = this.Configuration; using (IFrameQuantizer frameQuantizer = this.quantizer.CreateFrameQuantizer(configuration)) using (IQuantizedFrame quantized = frameQuantizer.QuantizeFrame(source)) { @@ -57,4 +61,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs index 87d696dc91..ee2751eaf2 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs @@ -117,6 +117,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// private Box[] colorCube; + private bool isDisposed; + /// /// Initializes a new instance of the class. /// @@ -158,15 +160,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } /// - public override void Dispose() + protected override void Dispose(bool disposing) { - this.vwt?.Dispose(); - this.vmr?.Dispose(); - this.vmg?.Dispose(); - this.vmb?.Dispose(); - this.vma?.Dispose(); - this.m2?.Dispose(); - this.tag?.Dispose(); + if (this.isDisposed) + { + return; + } + + if (disposing) + { + this.vwt?.Dispose(); + this.vmr?.Dispose(); + this.vmg?.Dispose(); + this.vmb?.Dispose(); + this.vma?.Dispose(); + this.m2?.Dispose(); + this.tag?.Dispose(); + } this.vwt = null; this.vmr = null; @@ -175,6 +185,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.vma = null; this.m2 = null; this.tag = null; + + this.isDisposed = true; + base.Dispose(true); } internal ReadOnlyMemory AotGetPalette() => this.GetPalette(); @@ -260,7 +273,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height); + this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, 0, width, height); } output[(y * source.Width) + x] = pixelValue; diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index 713f042653..6ca844fae6 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -1,9 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; - -using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -11,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Defines an affine transformation applicable on an . /// - public class AffineTransformProcessor : IImageProcessor + public class AffineTransformProcessor : CloningImageProcessor { /// /// Initializes a new instance of the class. @@ -42,11 +40,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public Size TargetDimensions { get; } - /// - public virtual IImageProcessor CreatePixelSpecificProcessor() - where TPixel : struct, IPixel - { - return new AffineTransformProcessor(this); - } + /// + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) + => new AffineTransformProcessor(this, source, sourceRectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index a33352c73b..e693de8f6e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -2,12 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Linq; using System.Numerics; - using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -20,52 +17,46 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class AffineTransformProcessor : TransformProcessor where TPixel : struct, IPixel { - public AffineTransformProcessor(AffineTransformProcessor definition) + private Size targetSize; + private Matrix3x2 transformMatrix; + private readonly IResampler resampler; + + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public AffineTransformProcessor(AffineTransformProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { - this.Definition = definition; + this.targetSize = definition.TargetDimensions; + this.transformMatrix = definition.TransformMatrix; + this.resampler = definition.Sampler; } - protected AffineTransformProcessor Definition { get; } - - private Size TargetDimensions => this.Definition.TargetDimensions; - - private Matrix3x2 TransformMatrix => this.Definition.TransformMatrix; - - /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) - { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select, ImageFrame>( - x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); - } + protected override Size GetTargetSize() => this.targetSize; /// - protected override void OnFrameApply( - ImageFrame source, - ImageFrame destination, - Rectangle sourceRectangle, - Configuration configuration) + protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { // Handle transforms that result in output identical to the original. - if (this.TransformMatrix.Equals(default) || this.TransformMatrix.Equals(Matrix3x2.Identity)) + if (this.transformMatrix.Equals(default) || this.transformMatrix.Equals(Matrix3x2.Identity)) { // The clone will be blank here copy all the pixel data over source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); return; } - int width = this.TargetDimensions.Width; - var targetBounds = new Rectangle(Point.Empty, this.TargetDimensions); + int width = this.targetSize.Width; + Rectangle sourceBounds = this.SourceRectangle; + var targetBounds = new Rectangle(Point.Empty, this.targetSize); + Configuration configuration = this.Configuration; // Convert from screen to world space. - Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 matrix); + Matrix3x2.Invert(this.transformMatrix, out Matrix3x2 matrix); - var sampler = this.Definition.Sampler; - - if (sampler is NearestNeighborResampler) + if (this.resampler is NearestNeighborResampler) { ParallelHelper.IterateRows( targetBounds, @@ -79,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int x = 0; x < width; x++) { var point = Point.Transform(new Point(x, y), matrix); - if (sourceRectangle.Contains(point.X, point.Y)) + if (sourceBounds.Contains(point.X, point.Y)) { destRow[x] = source[point.X, point.Y]; } @@ -90,7 +81,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), sampler); + var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler); + try { ParallelHelper.IterateRowsWithTempBuffer( diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs index 3eb0d998a2..eef7643da3 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -11,10 +12,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public sealed class AutoOrientProcessor : IImageProcessor { /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new AutoOrientProcessor(); + return new AutoOrientProcessor(source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs index 8b3ec8690e..b9952ac8fe 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs @@ -16,41 +16,51 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class AutoOrientProcessor : ImageProcessor where TPixel : struct, IPixel { + /// + /// Initializes a new instance of the class. + /// + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public AutoOrientProcessor(Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) + { + } + /// - protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + protected override void BeforeImageApply() { - OrientationMode orientation = GetExifOrientation(source); - Size size = sourceRectangle.Size; + OrientationMode orientation = GetExifOrientation(this.Source); + Size size = this.SourceRectangle.Size; switch (orientation) { case OrientationMode.TopRight: - new FlipProcessor(FlipMode.Horizontal).Apply(source, sourceRectangle); + new FlipProcessor(FlipMode.Horizontal).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.BottomRight: - new RotateProcessor((int)RotateMode.Rotate180, size).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateMode.Rotate180, size).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.BottomLeft: - new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle); + new FlipProcessor(FlipMode.Vertical).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.LeftTop: - new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle); - new FlipProcessor(FlipMode.Horizontal).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Source, this.SourceRectangle); + new FlipProcessor(FlipMode.Horizontal).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.RightTop: - new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.RightBottom: - new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle); - new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle); + new FlipProcessor(FlipMode.Vertical).Execute(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.LeftBottom: - new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.Unknown: @@ -58,13 +68,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms default: break; } + + base.BeforeImageApply(); } /// - protected override void OnFrameApply( - ImageFrame sourceBase, - Rectangle sourceRectangle, - Configuration config) + protected override void OnFrameApply(ImageFrame sourceBase) { // All processing happens at the image level within BeforeImageApply(); } @@ -103,4 +112,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return orientation; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 76f223e038..245a542084 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -1,7 +1,6 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -9,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Defines a crop operation on an image. /// - public sealed class CropProcessor : IImageProcessor + public sealed class CropProcessor : CloningImageProcessor { /// /// Initializes a new instance of the class. @@ -23,6 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms new Rectangle(Point.Empty, sourceSize).Contains(cropRectangle), nameof(cropRectangle), "Crop rectangle should be smaller than the source bounds."); + this.CropRectangle = cropRectangle; } @@ -32,10 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Rectangle CropRectangle { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() - where TPixel : struct, IPixel - { - return new CropProcessor(this); - } + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) + => new CropProcessor(this, source, sourceRectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index 25f8d78491..2cc4a38675 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -2,11 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Linq; - using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -19,62 +16,52 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class CropProcessor : TransformProcessor where TPixel : struct, IPixel { - private readonly CropProcessor definition; + private Rectangle cropRectangle; /// /// Initializes a new instance of the class. /// /// The . - public CropProcessor(CropProcessor definition) - { - this.definition = definition; - } - - private Rectangle CropRectangle => this.definition.CropRectangle; + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public CropProcessor(CropProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) + => this.cropRectangle = definition.CropRectangle; /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) - { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select, ImageFrame>( - x => new ImageFrame( - source.GetConfiguration(), - this.CropRectangle.Width, - this.CropRectangle.Height, - x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); - } + protected override Size GetTargetSize() => new Size(this.cropRectangle.Width, this.cropRectangle.Height); /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { - // Handle resize dimensions identical to the original - if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.CropRectangle) + // Handle crop dimensions identical to the original + if (source.Width == destination.Width + && source.Height == destination.Height + && this.SourceRectangle == this.cropRectangle) { // the cloned will be blank here copy all the pixel data over source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); return; } - Rectangle rect = this.CropRectangle; + Rectangle bounds = this.cropRectangle; // Copying is cheap, we should process more pixels per task: - ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); + ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(this.Configuration) + .MultiplyMinimumPixelsPerTask(4); ParallelHelper.IterateRows( - rect, + bounds, parallelSettings, rows => { for (int y = rows.Min; y < rows.Max; y++) { - Span sourceRow = source.GetPixelRowSpan(y).Slice(rect.Left); - Span targetRow = destination.GetPixelRowSpan(y - rect.Top); - sourceRow.Slice(0, rect.Width).CopyTo(targetRow); + Span sourceRow = source.GetPixelRowSpan(y).Slice(bounds.Left); + Span targetRow = destination.GetPixelRowSpan(y - bounds.Top); + sourceRow.Slice(0, bounds.Width).CopyTo(targetRow); } }); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs index dee5e7fb37..22eecb598e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -37,10 +38,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public float Threshold { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new EntropyCropProcessor(this); + return new EntropyCropProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs index 4bfeb25198..2b900ee360 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs @@ -22,38 +22,44 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Initializes a new instance of the class. /// /// The . - public EntropyCropProcessor(EntropyCropProcessor definition) + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public EntropyCropProcessor(EntropyCropProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } /// - protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + protected override void BeforeImageApply() { Rectangle rectangle; + // TODO: This is clunky. We should add behavior enum to ExtractFrame. // All frames have be the same size so we only need to calculate the correct dimensions for the first frame - using (ImageFrame temp = source.Frames.RootFrame.Clone()) + using (var temp = new Image(this.Configuration, this.Source.Metadata.DeepClone(), new[] { this.Source.Frames.RootFrame.Clone() })) { - Configuration configuration = source.GetConfiguration(); + Configuration configuration = this.Source.GetConfiguration(); // Detect the edges. - new SobelProcessor(false).Apply(temp, sourceRectangle, configuration); + new SobelProcessor(false).Execute(temp, this.SourceRectangle); // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.definition.Threshold).Apply(temp, sourceRectangle, configuration); + new BinaryThresholdProcessor(this.definition.Threshold).Execute(temp, this.SourceRectangle); // Search for the first white pixels - rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); + rectangle = ImageMaths.GetFilteredBoundingRectangle(temp.Frames.RootFrame, 0); } - new CropProcessor(rectangle, source.Size()).Apply(source, sourceRectangle); + new CropProcessor(rectangle, this.Source.Size()).Execute(this.Source, this.SourceRectangle); + + base.BeforeImageApply(); } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { // All processing happens at the image level within BeforeImageApply(); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index f604d8399f..e2364e180f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -1,7 +1,8 @@ -// 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.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { @@ -25,10 +26,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public FlipMode FlipMode { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new FlipProcessor(this); + return new FlipProcessor(this, source, sourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs index 0247862096..9ee8d09229 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs @@ -5,7 +5,7 @@ using System; using System.Buffers; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -20,22 +20,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { private readonly FlipProcessor definition; - public FlipProcessor(FlipProcessor definition) + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public FlipProcessor(FlipProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source) { switch (this.definition.FlipMode) { // No default needed as we have already set the pixels. case FlipMode.Vertical: - this.FlipX(source, configuration); + this.FlipX(source, this.Configuration); break; case FlipMode.Horizontal: - this.FlipY(source, configuration); + this.FlipY(source, this.Configuration); break; } } @@ -84,4 +91,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms }); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index 3a86b3fe4f..d91db9a72b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -1,9 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; - -using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -11,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Defines a projective transformation applicable to an . /// - public sealed class ProjectiveTransformProcessor : IImageProcessor + public sealed class ProjectiveTransformProcessor : CloningImageProcessor { /// /// Initializes a new instance of the class. @@ -43,10 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Size TargetDimensions { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() - where TPixel : struct, IPixel - { - return new ProjectiveTransformProcessor(this); - } + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) + => new ProjectiveTransformProcessor(this, source, sourceRectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index f95040facd..c6212a7d3b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -2,12 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Linq; using System.Numerics; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -20,56 +18,46 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class ProjectiveTransformProcessor : TransformProcessor where TPixel : struct, IPixel { - private readonly ProjectiveTransformProcessor definition; - - public ProjectiveTransformProcessor(ProjectiveTransformProcessor definition) + private Size targetSize; + private readonly IResampler resampler; + private Matrix4x4 transformMatrix; + + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public ProjectiveTransformProcessor(ProjectiveTransformProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { - this.definition = definition; + this.targetSize = definition.TargetDimensions; + this.transformMatrix = definition.TransformMatrix; + this.resampler = definition.Sampler; } - private Size TargetDimensions => this.definition.TargetDimensions; - - /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) - { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select, ImageFrame>( - x => new ImageFrame( - source.GetConfiguration(), - this.TargetDimensions.Width, - this.TargetDimensions.Height, - x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); - } + protected override Size GetTargetSize() => this.targetSize; /// - protected override void OnFrameApply( - ImageFrame source, - ImageFrame destination, - Rectangle sourceRectangle, - Configuration configuration) + protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { - Matrix4x4 transformMatrix = this.definition.TransformMatrix; - // Handle transforms that result in output identical to the original. - if (transformMatrix.Equals(default) || transformMatrix.Equals(Matrix4x4.Identity)) + if (this.transformMatrix.Equals(default) || this.transformMatrix.Equals(Matrix4x4.Identity)) { // The clone will be blank here copy all the pixel data over source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); return; } - int width = this.TargetDimensions.Width; - var targetBounds = new Rectangle(Point.Empty, this.TargetDimensions); + int width = this.targetSize.Width; + Rectangle sourceBounds = this.SourceRectangle; + var targetBounds = new Rectangle(Point.Empty, this.targetSize); + Configuration configuration = this.Configuration; // Convert from screen to world space. - Matrix4x4.Invert(transformMatrix, out Matrix4x4 matrix); + Matrix4x4.Invert(this.transformMatrix, out Matrix4x4 matrix); - IResampler sampler = this.definition.Sampler; - - if (sampler is NearestNeighborResampler) + if (this.resampler is NearestNeighborResampler) { ParallelHelper.IterateRows( targetBounds, @@ -86,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int px = (int)MathF.Round(point.X); int py = (int)MathF.Round(point.Y); - if (sourceRectangle.Contains(px, py)) + if (sourceBounds.Contains(px, py)) { destRow[x] = source[px, py]; } @@ -97,7 +85,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), sampler); + var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler); + try { ParallelHelper.IterateRowsWithTempBuffer( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs index ae7b112fc1..eacd3834f1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs @@ -2,9 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Linq; using System.Numerics; - using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -30,17 +28,34 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// The source image size. /// The resize options. - /// The target width - /// The target height /// /// The tuple representing the location and the bounds /// - public static (Size, Rectangle) CalculateTargetLocationAndBounds( - Size sourceSize, - ResizeOptions options, - int width, - int height) + public static (Size, Rectangle) CalculateTargetLocationAndBounds(Size sourceSize, ResizeOptions options) { + int width = options.Size.Width; + int height = options.Size.Height; + + if (width <= 0 && height <= 0) + { + ThrowInvalid($"Target width {width} and height {height} must be greater than zero."); + } + + // Ensure target size is populated across both dimensions. + // These dimensions are used to calculate the final dimensions determined by the mode algorithm. + // If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio. + // If it is not possible to keep aspect ratio, make sure at least the minimum is is kept. + const int Min = 1; + if (width == 0 && height > 0) + { + width = (int)MathF.Max(Min, MathF.Round(sourceSize.Width * height / (float)sourceSize.Height)); + } + + if (height == 0 && width > 0) + { + height = (int)MathF.Max(Min, MathF.Round(sourceSize.Height * width / (float)sourceSize.Width)); + } + switch (options.Mode) { case ResizeMode.Crop: @@ -50,11 +65,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms case ResizeMode.BoxPad: return CalculateBoxPadRectangle(sourceSize, options, width, height); case ResizeMode.Max: - return CalculateMaxRectangle(sourceSize, options, width, height); + return CalculateMaxRectangle(sourceSize, width, height); case ResizeMode.Min: - return CalculateMinRectangle(sourceSize, options, width, height); + return CalculateMinRectangle(sourceSize, width, height); + case ResizeMode.Manual: + return CalculateManualRectangle(options, width, height); - // Last case ResizeMode.Stretch: + // case ResizeMode.Stretch: default: return (new Size(width, height), new Rectangle(0, 0, width, height)); } @@ -66,11 +83,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int width, int height) { - if (width <= 0 || height <= 0) - { - return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height)); - } - int sourceWidth = source.Width; int sourceHeight = source.Height; @@ -84,55 +96,55 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Only calculate if upscaling. if (sourceWidth < boxPadWidth && sourceHeight < boxPadHeight) { - int destinationX; - int destinationY; - int destinationWidth = sourceWidth; - int destinationHeight = sourceHeight; + int targetX; + int targetY; + int targetWidth = sourceWidth; + int targetHeight = sourceHeight; width = boxPadWidth; height = boxPadHeight; switch (options.Position) { case AnchorPositionMode.Left: - destinationY = (height - sourceHeight) / 2; - destinationX = 0; + targetY = (height - sourceHeight) / 2; + targetX = 0; break; case AnchorPositionMode.Right: - destinationY = (height - sourceHeight) / 2; - destinationX = width - sourceWidth; + targetY = (height - sourceHeight) / 2; + targetX = width - sourceWidth; break; case AnchorPositionMode.TopRight: - destinationY = 0; - destinationX = width - sourceWidth; + targetY = 0; + targetX = width - sourceWidth; break; case AnchorPositionMode.Top: - destinationY = 0; - destinationX = (width - sourceWidth) / 2; + targetY = 0; + targetX = (width - sourceWidth) / 2; break; case AnchorPositionMode.TopLeft: - destinationY = 0; - destinationX = 0; + targetY = 0; + targetX = 0; break; case AnchorPositionMode.BottomRight: - destinationY = height - sourceHeight; - destinationX = width - sourceWidth; + targetY = height - sourceHeight; + targetX = width - sourceWidth; break; case AnchorPositionMode.Bottom: - destinationY = height - sourceHeight; - destinationX = (width - sourceWidth) / 2; + targetY = height - sourceHeight; + targetX = (width - sourceWidth) / 2; break; case AnchorPositionMode.BottomLeft: - destinationY = height - sourceHeight; - destinationX = 0; + targetY = height - sourceHeight; + targetX = 0; break; default: - destinationY = (height - sourceHeight) / 2; - destinationX = (width - sourceWidth) / 2; + targetY = (height - sourceHeight) / 2; + targetX = (width - sourceWidth) / 2; break; } - return (new Size(width, height), - new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); + // Target image width and height can be different to the rectangle width and height. + return (new Size(width, height), new Rectangle(targetX, targetY, targetWidth, targetHeight)); } // Switch to pad mode to downscale and calculate from there. @@ -145,19 +157,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int width, int height) { - if (width <= 0 || height <= 0) - { - return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height)); - } - float ratio; int sourceWidth = source.Width; int sourceHeight = source.Height; - int destinationX = 0; - int destinationY = 0; - int destinationWidth = width; - int destinationHeight = height; + int targetX = 0; + int targetY = 0; + int targetWidth = width; + int targetHeight = height; // Fractional variants for preserving aspect ratio. float percentHeight = MathF.Abs(height / (float)sourceHeight); @@ -167,19 +174,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { ratio = percentWidth; - if (options.CenterCoordinates.Any()) + if (options.CenterCoordinates.HasValue) { - float center = -(ratio * sourceHeight) * options.CenterCoordinates.ToArray()[1]; - destinationY = (int)MathF.Round(center + (height / 2F)); + float center = -(ratio * sourceHeight) * options.CenterCoordinates.Value.Y; + targetY = (int)MathF.Round(center + (height / 2F)); - if (destinationY > 0) + if (targetY > 0) { - destinationY = 0; + targetY = 0; } - if (destinationY < (int)MathF.Round(height - (sourceHeight * ratio))) + if (targetY < (int)MathF.Round(height - (sourceHeight * ratio))) { - destinationY = (int)MathF.Round(height - (sourceHeight * ratio)); + targetY = (int)MathF.Round(height - (sourceHeight * ratio)); } } else @@ -189,38 +196,38 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms case AnchorPositionMode.Top: case AnchorPositionMode.TopLeft: case AnchorPositionMode.TopRight: - destinationY = 0; + targetY = 0; break; case AnchorPositionMode.Bottom: case AnchorPositionMode.BottomLeft: case AnchorPositionMode.BottomRight: - destinationY = (int)MathF.Round(height - (sourceHeight * ratio)); + targetY = (int)MathF.Round(height - (sourceHeight * ratio)); break; default: - destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F); + targetY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F); break; } } - destinationHeight = (int)MathF.Ceiling(sourceHeight * percentWidth); + targetHeight = (int)MathF.Ceiling(sourceHeight * percentWidth); } else { ratio = percentHeight; - if (options.CenterCoordinates.Any()) + if (options.CenterCoordinates.HasValue) { - float center = -(ratio * sourceWidth) * options.CenterCoordinates.First(); - destinationX = (int)MathF.Round(center + (width / 2F)); + float center = -(ratio * sourceWidth) * options.CenterCoordinates.Value.X; + targetX = (int)MathF.Round(center + (width / 2F)); - if (destinationX > 0) + if (targetX > 0) { - destinationX = 0; + targetX = 0; } - if (destinationX < (int)MathF.Round(width - (sourceWidth * ratio))) + if (targetX < (int)MathF.Round(width - (sourceWidth * ratio))) { - destinationX = (int)MathF.Round(width - (sourceWidth * ratio)); + targetX = (int)MathF.Round(width - (sourceWidth * ratio)); } } else @@ -230,68 +237,64 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms case AnchorPositionMode.Left: case AnchorPositionMode.TopLeft: case AnchorPositionMode.BottomLeft: - destinationX = 0; + targetX = 0; break; case AnchorPositionMode.Right: case AnchorPositionMode.TopRight: case AnchorPositionMode.BottomRight: - destinationX = (int)MathF.Round(width - (sourceWidth * ratio)); + targetX = (int)MathF.Round(width - (sourceWidth * ratio)); break; default: - destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F); + targetX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F); break; } } - destinationWidth = (int)MathF.Ceiling(sourceWidth * percentHeight); + targetWidth = (int)MathF.Ceiling(sourceWidth * percentHeight); } - return (new Size(width, height), - new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); + // Target image width and height can be different to the rectangle width and height. + return (new Size(width, height), new Rectangle(targetX, targetY, targetWidth, targetHeight)); } private static (Size, Rectangle) CalculateMaxRectangle( Size source, - ResizeOptions options, int width, int height) { - int destinationWidth = width; - int destinationHeight = height; + int targetWidth = width; + int targetHeight = height; // Fractional variants for preserving aspect ratio. float percentHeight = MathF.Abs(height / (float)source.Height); float percentWidth = MathF.Abs(width / (float)source.Width); // Integers must be cast to floats to get needed precision - float ratio = options.Size.Height / (float)options.Size.Width; + float ratio = height / (float)width; float sourceRatio = source.Height / (float)source.Width; if (sourceRatio < ratio) { - destinationHeight = (int)MathF.Round(source.Height * percentWidth); - height = destinationHeight; + targetHeight = (int)MathF.Round(source.Height * percentWidth); } else { - destinationWidth = (int)MathF.Round(source.Width * percentHeight); - width = destinationWidth; + targetWidth = (int)MathF.Round(source.Width * percentHeight); } // Replace the size to match the rectangle. - return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight)); + return (new Size(targetWidth, targetHeight), new Rectangle(0, 0, targetWidth, targetHeight)); } private static (Size, Rectangle) CalculateMinRectangle( Size source, - ResizeOptions options, int width, int height) { int sourceWidth = source.Width; int sourceHeight = source.Height; - int destinationWidth; - int destinationHeight; + int targetWidth = width; + int targetHeight = height; // Don't upscale if (width > sourceWidth || height > sourceHeight) @@ -306,58 +309,45 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms if (widthDiff < heightDiff) { float sourceRatio = (float)sourceHeight / sourceWidth; - destinationHeight = (int)MathF.Round(width * sourceRatio); - height = destinationHeight; - destinationWidth = width; + targetHeight = (int)MathF.Round(width * sourceRatio); } else if (widthDiff > heightDiff) { float sourceRatioInverse = (float)sourceWidth / sourceHeight; - destinationWidth = (int)MathF.Round(height * sourceRatioInverse); - destinationHeight = height; - width = destinationWidth; + targetWidth = (int)MathF.Round(height * sourceRatioInverse); } else { if (height > width) { - destinationWidth = width; float percentWidth = MathF.Abs(width / (float)sourceWidth); - destinationHeight = (int)MathF.Round(sourceHeight * percentWidth); - height = destinationHeight; + targetHeight = (int)MathF.Round(sourceHeight * percentWidth); } else { - destinationHeight = height; float percentHeight = MathF.Abs(height / (float)sourceHeight); - destinationWidth = (int)MathF.Round(sourceWidth * percentHeight); - width = destinationWidth; + targetWidth = (int)MathF.Round(sourceWidth * percentHeight); } } // Replace the size to match the rectangle. - return (new Size(width, height), new Rectangle(0, 0, destinationWidth, destinationHeight)); + return (new Size(targetWidth, targetHeight), new Rectangle(0, 0, targetWidth, targetHeight)); } private static (Size, Rectangle) CalculatePadRectangle( - Size source, + Size sourceSize, ResizeOptions options, int width, int height) { - if (width <= 0 || height <= 0) - { - return (new Size(source.Width, source.Height), new Rectangle(0, 0, source.Width, source.Height)); - } - float ratio; - int sourceWidth = source.Width; - int sourceHeight = source.Height; + int sourceWidth = sourceSize.Width; + int sourceHeight = sourceSize.Height; - int destinationX = 0; - int destinationY = 0; - int destinationWidth = width; - int destinationHeight = height; + int targetX = 0; + int targetY = 0; + int targetWidth = width; + int targetHeight = height; // Fractional variants for preserving aspect ratio. float percentHeight = MathF.Abs(height / (float)sourceHeight); @@ -366,50 +356,73 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms if (percentHeight < percentWidth) { ratio = percentHeight; - destinationWidth = (int)MathF.Round(sourceWidth * percentHeight); + targetWidth = (int)MathF.Round(sourceWidth * percentHeight); switch (options.Position) { case AnchorPositionMode.Left: case AnchorPositionMode.TopLeft: case AnchorPositionMode.BottomLeft: - destinationX = 0; + targetX = 0; break; case AnchorPositionMode.Right: case AnchorPositionMode.TopRight: case AnchorPositionMode.BottomRight: - destinationX = (int)MathF.Round(width - (sourceWidth * ratio)); + targetX = (int)MathF.Round(width - (sourceWidth * ratio)); break; default: - destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F); + targetX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F); break; } } else { ratio = percentWidth; - destinationHeight = (int)MathF.Round(sourceHeight * percentWidth); + targetHeight = (int)MathF.Round(sourceHeight * percentWidth); switch (options.Position) { case AnchorPositionMode.Top: case AnchorPositionMode.TopLeft: case AnchorPositionMode.TopRight: - destinationY = 0; + targetY = 0; break; case AnchorPositionMode.Bottom: case AnchorPositionMode.BottomLeft: case AnchorPositionMode.BottomRight: - destinationY = (int)MathF.Round(height - (sourceHeight * ratio)); + targetY = (int)MathF.Round(height - (sourceHeight * ratio)); break; default: - destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F); + targetY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F); break; } } - return (new Size(width, height), - new Rectangle(destinationX, destinationY, destinationWidth, destinationHeight)); + // Target image width and height can be different to the rectangle width and height. + return (new Size(width, height), new Rectangle(targetX, targetY, targetWidth, targetHeight)); } + + private static (Size, Rectangle) CalculateManualRectangle( + ResizeOptions options, + int width, + int height) + { + if (!options.TargetRectangle.HasValue) + { + ThrowInvalid("Manual resizing requires a target location and size."); + } + + Rectangle targetRectangle = options.TargetRectangle.Value; + + int targetX = targetRectangle.X; + int targetY = targetRectangle.Y; + int targetWidth = targetRectangle.Width > 0 ? targetRectangle.Width : width; + int targetHeight = targetRectangle.Height > 0 ? targetRectangle.Height : height; + + // Target image width and height can be different to the rectangle width and height. + return (new Size(width, height), new Rectangle(targetX, targetY, targetWidth, targetHeight)); + } + + private static void ThrowInvalid(string message) => throw new InvalidOperationException(message); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs index 9abbb66e3a..64c74a8b43 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.DestinationLength = destinationLength; this.MaxDiameter = (radius * 2) + 1; this.data = memoryAllocator.Allocate2D(this.MaxDiameter, bufferHeight, AllocationOptions.Clean); - this.pinHandle = this.data.Memory.Pin(); + this.pinHandle = this.data.GetMemory().Pin(); this.kernels = new ResizeKernel[destinationLength]; this.tempValues = new double[this.MaxDiameter]; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 8762d6b263..ccaa1ef9e5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -1,9 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; - -using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -11,47 +8,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Defines an image resizing operation with the given and dimensional parameters. /// - public class ResizeProcessor : IImageProcessor + public class ResizeProcessor : CloningImageProcessor { - /// - /// Initializes a new instance of the class. - /// - /// The . - /// The width. - /// The height. - /// The size of the source image. - /// The target rectangle to resize into. - /// A value indicating whether to apply RGBA companding. - public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize, Rectangle targetRectangle, bool compand) - { - Guard.NotNull(sampler, nameof(sampler)); - - // Ensure size is populated across both dimensions. - // If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio. - // If it is not possible to keep aspect ratio, make sure at least the minimum is is kept. - const int min = 1; - if (width == 0 && height > 0) - { - width = (int)MathF.Max(min, MathF.Round(sourceSize.Width * height / (float)sourceSize.Height)); - targetRectangle.Width = width; - } - - if (height == 0 && width > 0) - { - height = (int)MathF.Max(min, MathF.Round(sourceSize.Height * width / (float)sourceSize.Width)); - targetRectangle.Height = height; - } - - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - - this.Sampler = sampler; - this.Width = width; - this.Height = height; - this.TargetRectangle = targetRectangle; - this.Compand = compand; - } - /// /// Initializes a new instance of the class. /// @@ -62,48 +20,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Guard.NotNull(options, nameof(options)); Guard.NotNull(options.Sampler, nameof(options.Sampler)); - int targetWidth = options.Size.Width; - int targetHeight = options.Size.Height; - - // Ensure size is populated across both dimensions. - // These dimensions are used to calculate the final dimensions determined by the mode algorithm. - // If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio. - // If it is not possible to keep aspect ratio, make sure at least the minimum is is kept. - const int min = 1; - if (targetWidth == 0 && targetHeight > 0) - { - targetWidth = (int)MathF.Max(min, MathF.Round(sourceSize.Width * targetHeight / (float)sourceSize.Height)); - } - - if (targetHeight == 0 && targetWidth > 0) - { - targetHeight = (int)MathF.Max(min, MathF.Round(sourceSize.Height * targetWidth / (float)sourceSize.Width)); - } - - Guard.MustBeGreaterThan(targetWidth, 0, nameof(targetWidth)); - Guard.MustBeGreaterThan(targetHeight, 0, nameof(targetHeight)); - - (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, targetWidth, targetHeight); + (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options); this.Sampler = options.Sampler; - this.Width = size.Width; - this.Height = size.Height; + this.TargetWidth = size.Width; + this.TargetHeight = size.Height; this.TargetRectangle = rectangle; this.Compand = options.Compand; } - /// - /// Initializes a new instance of the class. - /// - /// The sampler to perform the resize operation. - /// The target width. - /// The target height. - /// The source image size - public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize) - : this(sampler, width, height, sourceSize, new Rectangle(0, 0, width, height), false) - { - } - /// /// Gets the sampler to perform the resize operation. /// @@ -112,12 +37,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Gets the target width. /// - public int Width { get; } + public int TargetWidth { get; } /// /// Gets the target height. /// - public int Height { get; } + public int TargetHeight { get; } /// /// Gets the resize rectangle. @@ -130,10 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public bool Compand { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() - where TPixel : struct, IPixel - { - return new ResizeProcessor(this); - } + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) + => new ResizeProcessor(this, source, sourceRectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index b68a7f93f7..3b508032a0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -2,12 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Linq; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -24,114 +22,86 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class ResizeProcessor : TransformProcessor where TPixel : struct, IPixel { + private bool isDisposed; + private readonly int targetWidth; + private readonly int targetHeight; + private readonly IResampler resampler; + private Rectangle targetRectangle; + private readonly bool compand; + // The following fields are not immutable but are optionally created on demand. private ResizeKernelMap horizontalKernelMap; private ResizeKernelMap verticalKernelMap; - private readonly ResizeProcessor parameterSource; - - public ResizeProcessor(ResizeProcessor parameterSource) + public ResizeProcessor(ResizeProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { - this.parameterSource = parameterSource; + this.targetWidth = definition.TargetWidth; + this.targetHeight = definition.TargetHeight; + this.targetRectangle = definition.TargetRectangle; + this.resampler = definition.Sampler; + this.compand = definition.Compand; } - /// - /// Gets the sampler to perform the resize operation. - /// - public IResampler Sampler => this.parameterSource.Sampler; - - /// - /// Gets the target width. - /// - public int Width => this.parameterSource.Width; - - /// - /// Gets the target height. - /// - public int Height => this.parameterSource.Height; - - /// - /// Gets the resize rectangle. - /// - public Rectangle TargetRectangle => this.parameterSource.TargetRectangle; - - /// - /// Gets a value indicating whether to compress or expand individual pixel color values on processing. - /// - public bool Compand => this.parameterSource.Compand; - - /// - /// This is a shim for tagging the CreateDestination virtual generic method for the AoT iOS compiler. - /// This method should never be referenced outside of the AotCompiler code. - /// - /// Passed through as source to the CreateDestination method. - /// Passed through as sourceRectangle to the CreateDestination method. - /// The result returned from CreateDestination. - internal Image AotCreateDestination(Image source, Rectangle sourceRectangle) => this.CreateDestination(source, sourceRectangle); - /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) - { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select, ImageFrame>( - x => new ImageFrame( - source.GetConfiguration(), - this.Width, - this.Height, - x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); - } + protected override Size GetTargetSize() => new Size(this.targetWidth, this.targetHeight); /// - protected override void BeforeImageApply(Image source, Image destination, Rectangle sourceRectangle) + protected override void BeforeImageApply(Image destination) { - if (!(this.Sampler is NearestNeighborResampler)) + if (!(this.resampler is NearestNeighborResampler)) { + Image source = this.Source; + Rectangle sourceRectangle = this.SourceRectangle; + // Since all image frame dimensions have to be the same we can calculate this for all frames. MemoryAllocator memoryAllocator = source.GetMemoryAllocator(); this.horizontalKernelMap = ResizeKernelMap.Calculate( - this.Sampler, - this.TargetRectangle.Width, + this.resampler, + this.targetRectangle.Width, sourceRectangle.Width, memoryAllocator); this.verticalKernelMap = ResizeKernelMap.Calculate( - this.Sampler, - this.TargetRectangle.Height, + this.resampler, + this.targetRectangle.Height, sourceRectangle.Height, memoryAllocator); } + + base.BeforeImageApply(destination); } /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { + Rectangle sourceRectangle = this.SourceRectangle; + Configuration configuration = this.Configuration; + // Handle resize dimensions identical to the original - if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.TargetRectangle) + if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.targetRectangle) { // The cloned will be blank here copy all the pixel data over source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); return; } - int width = this.Width; - int height = this.Height; + int width = this.targetWidth; + int height = this.targetHeight; int sourceX = sourceRectangle.X; int sourceY = sourceRectangle.Y; - int startY = this.TargetRectangle.Y; - int startX = this.TargetRectangle.X; + int startY = this.targetRectangle.Y; + int startX = this.targetRectangle.X; var targetWorkingRect = Rectangle.Intersect( - this.TargetRectangle, + this.targetRectangle, new Rectangle(0, 0, width, height)); - if (this.Sampler is NearestNeighborResampler) + if (this.resampler is NearestNeighborResampler) { // Scaling factors - float widthFactor = sourceRectangle.Width / (float)this.TargetRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)this.TargetRectangle.Height; + float widthFactor = sourceRectangle.Width / (float)this.targetRectangle.Width; + float heightFactor = sourceRectangle.Height / (float)this.targetRectangle.Height; ParallelHelper.IterateRows( targetWorkingRect, @@ -141,8 +111,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int y = rows.Min; y < rows.Max; y++) { // Y coordinates of source points - Span sourceRow = - source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); + Span sourceRow = source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); Span targetRow = destination.GetPixelRowSpan(y); for (int x = targetWorkingRect.Left; x < targetWorkingRect.Right; x++) @@ -157,7 +126,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } PixelConversionModifiers conversionModifiers = - PixelConversionModifiers.Premultiply.ApplyCompanding(this.Compand); + PixelConversionModifiers.Premultiply.ApplyCompanding(this.compand); BufferArea sourceArea = source.PixelBuffer.GetArea(sourceRectangle); @@ -171,7 +140,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.verticalKernelMap, width, targetWorkingRect, - this.TargetRectangle.Location)) + this.targetRectangle.Location)) { worker.Initialize(); @@ -180,15 +149,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } } - protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + /// + protected override void Dispose(bool disposing) { - base.AfterImageApply(source, destination, sourceRectangle); + if (this.isDisposed) + { + return; + } + + if (disposing) + { + this.horizontalKernelMap?.Dispose(); + this.horizontalKernelMap = null; + this.verticalKernelMap?.Dispose(); + this.verticalKernelMap = null; + } - // TODO: An exception in the processing chain can leave these buffers undisposed. We should consider making image processors IDisposable! - this.horizontalKernelMap?.Dispose(); - this.horizontalKernelMap = null; - this.verticalKernelMap?.Dispose(); - this.verticalKernelMap = null; + this.isDisposed = true; + base.Dispose(disposing); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 00a8cfbf3d..8461272757 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -115,6 +115,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public void FillDestinationPixels(RowInterval rowInterval, Buffer2D destination) { Span tempColSpan = this.tempColumnBuffer.GetSpan(); + Span transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.GetSpan(); for (int y = rowInterval.Min; y < rowInterval.Max; y++) { @@ -129,8 +130,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); int top = kernel.StartIndex - this.currentWindow.Min; - - ref Vector4 fpBase = ref this.transposedFirstPassBuffer.Span[top]; + ref Vector4 fpBase = ref transposedFirstPassBufferSpan[top]; for (int x = 0; x < this.destWidth; x++) { @@ -167,6 +167,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private void CalculateFirstPassValues(RowInterval calculationInterval) { Span tempRowSpan = this.tempRowBuffer.GetSpan(); + Span transposedFirstPassBufferSpan = this.transposedFirstPassBuffer.GetSpan(); + for (int y = calculationInterval.Min; y < calculationInterval.Max; y++) { Span sourceRow = this.source.GetRowSpan(y); @@ -177,17 +179,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms tempRowSpan, this.conversionModifiers); - // Span firstPassSpan = this.transposedFirstPassBuffer.Span.Slice(y - this.currentWindow.Min); - ref Vector4 firstPassBaseRef = ref this.transposedFirstPassBuffer.Span[y - this.currentWindow.Min]; + // optimization for: + // Span firstPassSpan = transposedFirstPassBufferSpan.Slice(y - this.currentWindow.Min); + ref Vector4 firstPassBaseRef = ref transposedFirstPassBufferSpan[y - this.currentWindow.Min]; for (int x = this.targetWorkingRect.Left; x < this.targetWorkingRect.Right; x++) { ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.targetOrigin.X); + // optimization for: // firstPassSpan[x * this.workerHeight] = kernel.Convolve(tempRowSpan); Unsafe.Add(ref firstPassBaseRef, x * this.workerHeight) = kernel.Convolve(tempRowSpan); } } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index ef0671d20d..7d6ec0e08e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -1,8 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; - using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -47,9 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public float Degrees { get; } /// - public override IImageProcessor CreatePixelSpecificProcessor() - { - return new RotateProcessor(this); - } + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) + => new RotateProcessor(this, source, sourceRectangle); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs index 252cb77aba..92776b7db5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs @@ -4,8 +4,8 @@ using System; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -18,8 +18,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class RotateProcessor : AffineTransformProcessor where TPixel : struct, IPixel { - public RotateProcessor(RotateProcessor definition) - : base(definition) + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + public RotateProcessor(RotateProcessor definition, Image source, Rectangle sourceRectangle) + : base(definition, source, sourceRectangle) { this.Degrees = definition.Degrees; } @@ -27,10 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private float Degrees { get; } /// - protected override void AfterImageApply( - Image source, - Image destination, - Rectangle sourceRectangle) + protected override void AfterImageApply(Image destination) { ExifProfile profile = destination.Metadata.ExifProfile; if (profile is null) @@ -46,22 +49,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms profile.RemoveValue(ExifTag.Orientation); - base.AfterImageApply(source, destination, sourceRectangle); + base.AfterImageApply(destination); } /// - protected override void OnFrameApply( - ImageFrame source, - ImageFrame destination, - Rectangle sourceRectangle, - Configuration configuration) + protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { - if (this.OptimizedApply(source, destination, configuration)) + if (this.OptimizedApply(source, destination, this.Configuration)) { return; } - base.OnFrameApply(source, destination, sourceRectangle, configuration); + base.OnFrameApply(source, destination); } /// @@ -222,4 +221,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms }); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 4b87d6d2c1..fb2114e03c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -1,8 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; - using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -56,4 +55,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public float DegreesY { get; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs index f6d3299fcd..1f0c6ebc86 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.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; @@ -13,8 +13,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal abstract class TransformProcessor : CloningImageProcessor where TPixel : struct, IPixel { + /// + /// Initializes a new instance of the class. + /// + /// The source for the current processor instance. + /// The source area to process for the current processor instance. + protected TransformProcessor(Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) + { + } + /// - protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) - => TransformProcessorHelpers.UpdateDimensionalMetadata(destination); + protected override void AfterImageApply(Image destination) + { + TransformProcessorHelpers.UpdateDimensionalMetadata(destination); + base.AfterImageApply(destination); + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/ResizeMode.cs b/src/ImageSharp/Processing/ResizeMode.cs index 6adeac66da..142a926b30 100644 --- a/src/ImageSharp/Processing/ResizeMode.cs +++ b/src/ImageSharp/Processing/ResizeMode.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.Processing @@ -42,6 +42,11 @@ namespace SixLabors.ImageSharp.Processing /// /// Stretches the resized image to fit the bounds of its container. /// - Stretch + Stretch, + + /// + /// The target location and size of the resized image has been manually set. + /// + Manual } } diff --git a/src/ImageSharp/Processing/ResizeOptions.cs b/src/ImageSharp/Processing/ResizeOptions.cs index ee0dde6b27..ef88dc35b3 100644 --- a/src/ImageSharp/Processing/ResizeOptions.cs +++ b/src/ImageSharp/Processing/ResizeOptions.cs @@ -1,8 +1,6 @@ -// 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; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Primitives; @@ -26,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Gets or sets the center coordinates. /// - public IEnumerable CenterCoordinates { get; set; } = Array.Empty(); + public PointF? CenterCoordinates { get; set; } /// /// Gets or sets the target size. @@ -43,5 +41,10 @@ namespace SixLabors.ImageSharp.Processing /// or expand individual pixel colors the value on processing. /// public bool Compand { get; set; } = false; + + /// + /// Gets or sets the target rectangle to resize into. + /// + public Rectangle? TargetRectangle { get; set; } } -} \ No newline at end of file +} diff --git a/standards b/standards deleted file mode 160000 index 8b085c0ec4..0000000000 --- a/standards +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8b085c0ec4fb64797b9965741f7138f8f66a6b44 diff --git a/stylecop.json b/stylecop.json deleted file mode 100644 index 485ab604a5..0000000000 --- a/stylecop.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", - "settings": { - "orderingRules": { - "usingDirectivesPlacement": "outsideNamespace", - "elementOrder": [ - "kind" - ] - }, - "documentationRules": { - "xmlHeader": false, - "documentInternalElements": false, - "copyrightText": "Copyright (c) Six Labors and contributors.\nLicensed under the Apache License, Version 2.0." - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs b/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs new file mode 100644 index 0000000000..6e67d11ef0 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs @@ -0,0 +1,49 @@ +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Dithering; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Benchmarks.Samplers +{ + [Config(typeof(Config.ShortClr))] + public class Diffuse + { + [Benchmark] + public Size DoDiffuse() + { + using (var image = new Image(Configuration.Default, 800, 800, Rgba32.BlanchedAlmond)) + { + image.Mutate(x => x.Diffuse()); + + return image.Size(); + } + } + } +} + +// #### 25th October 2019 #### +// +// BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362 +// Intel Core i7-8650U CPU 1.90GHz(Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +// .NET Core SDK = 3.0.100 +// +// [Host] : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT +// Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.8.4018.0 +// Core : .NET Core 2.1.13 (CoreCLR 4.6.28008.01, CoreFX 4.6.28008.01), 64bit RyuJIT +// +// IterationCount=3 LaunchCount=1 WarmupCount=3 +// +// #### Before #### +// +// | Method | Job | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |---------- |----- |-------- |----------:|---------:|---------:|------:|------:|------:|----------:| +// | DoDiffuse | Clr | Clr | 129.58 ms | 24.60 ms | 1.349 ms | - | - | - | 6 KB | +// | DoDiffuse | Core | Core | 92.63 ms | 89.78 ms | 4.921 ms | - | - | - | 4.58 KB | +// +// #### After #### +// +// | Method | Job | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | +// |---------- |----- |-------- |----------:|----------:|----------:|------:|------:|------:|----------:| +// | DoDiffuse | Clr | Clr | 124.93 ms | 33.297 ms | 1.8251 ms | - | - | - | 2 KB | +// | DoDiffuse | Core | Core | 89.63 ms | 9.895 ms | 0.5424 ms | - | - | - | 1.91 KB | diff --git a/tests/ImageSharp.Benchmarks/Samplers/GaussianBlur.cs b/tests/ImageSharp.Benchmarks/Samplers/GaussianBlur.cs index 47bd42a3c2..3a47d016a4 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/GaussianBlur.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/GaussianBlur.cs @@ -1,4 +1,4 @@ -using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs deleted file mode 100644 index 28cb047356..0000000000 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; -using System.Numerics; - -using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Benchmarks -{ - using CoreSize = SixLabors.Primitives.Size; - - public class Glow : BenchmarkBase - { - private GlowProcessor bulk; - - private GlowProcessorParallel parallel; - - [GlobalSetup] - public void Setup() - { - this.bulk = new GlowProcessor(Color.Beige, 800 * .5f, GraphicsOptions.Default); - this.parallel = new GlowProcessorParallel(Color.Beige) { Radius = 800 * .5f, }; - } - - [Benchmark(Description = "ImageSharp Glow - Bulk")] - public CoreSize GlowBulk() - { - using (var image = new Image(800, 800)) - { - this.bulk.Apply(image, image.Bounds()); - return new CoreSize(image.Width, image.Height); - } - } - - [Benchmark(Description = "ImageSharp Glow - Parallel")] - public CoreSize GLowSimple() - { - using (var image = new Image(800, 800)) - { - this.parallel.Apply(image, image.Bounds()); - return new CoreSize(image.Width, image.Height); - } - } - - internal class GlowProcessorParallel : ImageProcessor - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The color or the glow. - public GlowProcessorParallel(TPixel color) - { - this.GlowColor = color; - } - - /// - /// Gets or sets the glow color to apply. - /// - public TPixel GlowColor { get; set; } - - /// - /// Gets or sets the the radius. - /// - public float Radius { get; set; } - - /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) - { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - TPixel glowColor = this.GlowColor; - Vector2 centre = Rectangle.Center(sourceRectangle); - float maxDistance = this.Radius > 0 - ? Math.Min(this.Radius, sourceRectangle.Width * .5F) - : sourceRectangle.Width * .5F; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - int width = maxX - minX; - using (IMemoryOwner rowColors = Configuration.Default.MemoryAllocator.Allocate(width)) - { - Buffer2D sourcePixels = source.PixelBuffer; - rowColors.GetSpan().Fill(glowColor); - - var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); - ParallelHelper.IterateRows( - workingRect, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - int offsetY = y - startY; - - for (int x = minX; x < maxX; x++) - { - int offsetX = x - startX; - float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); - Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); - TPixel packed = default; - packed.FromVector4( - PremultipliedLerp( - sourceColor, - glowColor.ToVector4(), - 1 - (.95F * (distance / maxDistance)))); - sourcePixels[offsetX, offsetY] = packed; - } - } - }); - } - } - - public static Vector4 PremultipliedLerp(Vector4 backdrop, Vector4 source, float amount) - { - amount = amount.Clamp(0, 1); - - // Sanitize on zero alpha - if (Math.Abs(backdrop.W) < Constants.Epsilon) - { - source.W *= amount; - return source; - } - - if (Math.Abs(source.W) < Constants.Epsilon) - { - return backdrop; - } - - // Premultiply the source vector. - // Oddly premultiplying the background vector creates dark outlines when pixels - // Have low alpha values. - source = new Vector4(source.X, source.Y, source.Z, 1) * (source.W * amount); - - // This should be implementing the following formula - // https://en.wikipedia.org/wiki/Alpha_compositing - // Vout = Vs + Vb (1 - Vsa) - // Aout = Vsa + Vsb (1 - Vsa) - var inverseW = new Vector3(1 - source.W); - var xyzB = new Vector3(backdrop.X, backdrop.Y, backdrop.Z); - var xyzS = new Vector3(source.X, source.Y, source.Z); - - return new Vector4(xyzS + (xyzB * inverseW), source.W + (backdrop.W * (1 - source.W))); - } - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs index 502df84133..8b1fed84c2 100644 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.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; @@ -65,6 +65,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion [InlineData(1, 0, 0, 0, 1, .5F)] [InlineData(0, 1, 0, 120, 1, .5F)] [InlineData(0, 0, 1, 240, 1, .5F)] + [InlineData(0.7, 0.8, 0.6, 90, 0.3333, 0.7F)] public void Convert_Rgb_To_Hsl(float r, float g, float b, float h, float s, float l) { // Arrange diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 6f68d04288..a0e552aebf 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -61,12 +61,28 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [InlineData(0)] - [InlineData(-42)] - public void Set_MaxDegreeOfParallelism_ToNonPositiveValue_Throws(int value) + [InlineData(-3, true)] + [InlineData(-2, true)] + [InlineData(-1, false)] + [InlineData(0, true)] + [InlineData(1, false)] + [InlineData(5, false)] + public void MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(int maxDegreeOfParallelism, bool throws) { var cfg = new Configuration(); - Assert.Throws(() => cfg.MaxDegreeOfParallelism = value); + if (throws) + { + Assert.Throws( + () => + { + cfg.MaxDegreeOfParallelism = maxDegreeOfParallelism; + }); + } + else + { + cfg.MaxDegreeOfParallelism = maxDegreeOfParallelism; + Assert.Equal(maxDegreeOfParallelism, cfg.MaxDegreeOfParallelism); + } } @@ -117,4 +133,4 @@ namespace SixLabors.ImageSharp.Tests Assert.True(config.WorkingBufferSizeHintInBytes > 1024); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs new file mode 100644 index 0000000000..1ab747bafc --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs @@ -0,0 +1,157 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing +{ + [GroupOutput("Drawing/GradientBrushes")] + public class FillPathGradientBrushTests + { + public static ImageComparer TolerantComparer = ImageComparer.TolerantPercentage(0.01f); + + [Theory] + [WithBlankImages(10, 10, PixelTypes.Rgba32)] + public void FillRectangleWithDifferentColors(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.VerifyOperation( + TolerantComparer, + image => + { + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; + Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; + + var brush = new PathGradientBrush(points, colors); + + image.Mutate(x => x.Fill(brush)); + image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); + }); + } + + [Theory] + [WithBlankImages(10, 10, PixelTypes.Rgba32)] + public void FillTriangleWithDifferentColors(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.VerifyOperation( + TolerantComparer, + image => + { + PointF[] points = { new PointF(5, 0), new PointF(10, 10), new PointF(0, 10) }; + Color[] colors = { Color.Red, Color.Green, Color.Blue }; + + var brush = new PathGradientBrush(points, colors); + + image.Mutate(x => x.Fill(brush)); + image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); + }); + } + + [Theory] + [WithBlankImages(10, 10, PixelTypes.Rgba32)] + public void FillRectangleWithSingleColor(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; + Color[] colors = { Color.Red }; + + var brush = new PathGradientBrush(points, colors); + + image.Mutate(x => x.Fill(brush)); + + image.ComparePixelBufferTo(Color.Red); + } + } + + [Theory] + [WithBlankImages(10, 10, PixelTypes.Rgba32)] + public void ShouldRotateTheColorsWhenThereAreMorePoints(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.VerifyOperation( + TolerantComparer, + image => + { + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; + Color[] colors = { Color.Red, Color.Yellow }; + + var brush = new PathGradientBrush(points, colors); + + image.Mutate(x => x.Fill(brush)); + image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); + }); + } + + [Theory] + [WithBlankImages(10, 10, PixelTypes.Rgba32)] + public void FillWithCustomCenterColor(TestImageProvider provider) + where TPixel : struct, IPixel + { + provider.VerifyOperation( + TolerantComparer, + image => + { + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; + Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; + + var brush = new PathGradientBrush(points, colors, Color.White); + + image.Mutate(x => x.Fill(brush)); + image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); + }); + } + + [Fact] + public void ShouldThrowArgumentNullExceptionWhenLinesAreNull() + { + Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; + + PathGradientBrush Create() => new PathGradientBrush(null, colors, Color.White); + + Assert.Throws(Create); + } + + [Fact] + public void ShouldThrowArgumentOutOfRangeExceptionWhenLessThan3PointsAreGiven() + { + PointF[] points = { new PointF(0, 0), new PointF(10, 0) }; + Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; + + PathGradientBrush Create() => new PathGradientBrush(points, colors, Color.White); + + Assert.Throws(Create); + } + + [Fact] + public void ShouldThrowArgumentNullExceptionWhenColorsAreNull() + { + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; + + PathGradientBrush Create() => new PathGradientBrush(points, null, Color.White); + + Assert.Throws(Create); + } + + [Fact] + public void ShouldThrowArgumentOutOfRangeExceptionWhenEmptyColorArrayIsGiven() + { + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; + + var colors = new Color[0]; + + PathGradientBrush Create() => new PathGradientBrush(points, colors, Color.White); + + Assert.Throws(Create); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs index 972f5cf4a1..c0388ea2d4 100644 --- a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing }; var processor = new FillRegionProcessor(brush.Object, region, options); var img = new Image(1, 1); - processor.Apply(img, bounds); + processor.Execute(img, bounds); Assert.Equal(4, region.ScanInvocationCounter); } @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing var options = new GraphicsOptions(true); var processor = new FillRegionProcessor(brush.Object, new MockRegion1(), options); var img = new Image(10, 10); - processor.Apply(img, bounds); + processor.Execute(img, bounds); } [Fact] diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs index ee95bc743b..3691b54ce3 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs @@ -6,6 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Drawing; +using SixLabors.ImageSharp.Tests.Processing; using SixLabors.Shapes; using Xunit; diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs index d12441ac2f..160ff22a3e 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs @@ -6,6 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Drawing; +using SixLabors.ImageSharp.Tests.Processing; using SixLabors.Shapes; using Xunit; diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs index 17bb7e6ee5..b76ee8ffcd 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs @@ -6,6 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Drawing; +using SixLabors.ImageSharp.Tests.Processing; using SixLabors.Shapes; using Xunit; diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs index 22b741ec1e..c62a871481 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs @@ -6,6 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Drawing; +using SixLabors.ImageSharp.Tests.Processing; using SixLabors.Shapes; using Xunit; diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs index 6cb052aa8f..17a2b87c0e 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs @@ -5,6 +5,8 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Drawing; +using SixLabors.ImageSharp.Tests.Processing; + using Xunit; namespace SixLabors.ImageSharp.Tests.Drawing.Paths diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs index 8b45bd5240..e6866c6579 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs @@ -5,6 +5,7 @@ using System.Numerics; using SixLabors.Fonts; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Text; +using SixLabors.ImageSharp.Tests.Processing; using SixLabors.Primitives; using Xunit; diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs index 7bebdabd3a..a767a686ed 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs @@ -65,6 +65,24 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text } } + [Theory] + [WithSolidFilledImages(1500, 500, "White", PixelTypes.Rgba32)] + public void DoesntThrowExceptionWhenOverlappingRightEdge_Issue688_2(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + Font font = CreateFont("OpenSans-Regular.ttf", 39); + string text = new string('a', 10000); // exception + // string text = "Hello"; // no exception + Rgba32 color = Rgba32.Black; + var point = new PointF(100, 100); + + img.Mutate(ctx => ctx.DrawText(text, font, color, point)); + } + } + + [Theory] [WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)] [WithSolidFilledImages(900, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)] diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 2e9fd7481e..2a76310fcd 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -40,7 +40,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png TestImages.Png.GrayAlpha8Bit, TestImages.Png.Gray1BitTrans, TestImages.Png.Bad.ZlibOverflow, - TestImages.Png.Bad.ZlibOverflow2 + TestImages.Png.Bad.ZlibOverflow2, + TestImages.Png.Bad.ZlibZtxtBadHeader, }; public static readonly string[] TestImages48Bpp = @@ -74,6 +75,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png TestImages.Png.GrayAlpha8BitInterlaced }; + public static readonly string[] TestImagesIssue1014 = + { + TestImages.Png.Issue1014_1, TestImages.Png.Issue1014_2, + TestImages.Png.Issue1014_3, TestImages.Png.Issue1014_4, + TestImages.Png.Issue1014_5, TestImages.Png.Issue1014_6 + }; + [Theory] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] public void Decode(TestImageProvider provider) @@ -198,5 +206,22 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); } } + + [Theory] + [WithFileCollection(nameof(TestImagesIssue1014), PixelTypes.Rgba32)] + public void Issue1014(TestImageProvider provider) + where TPixel : struct, IPixel + { + System.Exception ex = Record.Exception( + () => + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + // TODO: compare to expected output + } + }); + Assert.Null(ex); + } } } diff --git a/tests/ImageSharp.Tests/Helpers/GuardTests.cs b/tests/ImageSharp.Tests/Helpers/GuardTests.cs deleted file mode 100644 index 6bccea2c3e..0000000000 --- a/tests/ImageSharp.Tests/Helpers/GuardTests.cs +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Diagnostics.CodeAnalysis; - -using Xunit; -// ReSharper disable InconsistentNaming - -namespace SixLabors.ImageSharp.Tests.Helpers -{ - /// - /// Tests the helper. - /// - public class GuardTests - { - class Test - { - } - - [Theory] - [InlineData(0, 0)] - [InlineData(0, 1)] - [InlineData(0, 42)] - [InlineData(1, 1)] - [InlineData(10, 42)] - [InlineData(42, 42)] - public void DestinationShouldNotBeTooShort_WhenOk(int sourceLength, int destLength) - { - ReadOnlySpan source = new int[sourceLength]; - Span dest = new float[destLength]; - - Guard.DestinationShouldNotBeTooShort(source, dest, nameof(dest)); - } - - [Theory] - [InlineData(1, 0)] - [InlineData(42, 41)] - public void DestinationShouldNotBeTooShort_WhenThrows(int sourceLength, int destLength) - { - Assert.ThrowsAny( - () => - { - ReadOnlySpan source = new int[sourceLength]; - Span dest = new float[destLength]; - Guard.DestinationShouldNotBeTooShort(source, dest, nameof(dest)); - }); - } - - /// - /// Tests that the method throws when the argument is null. - /// - [Fact] - public void NotNullThrowsWhenArgIsNull() - { - Assert.Throws(() => Guard.NotNull((Test)null, "foo")); - } - - /// - /// Tests that the method throws when the argument name is empty. - /// - [Fact] - public void NotNullThrowsWhenArgNameEmpty() - { - Assert.Throws(() => Guard.NotNull((Test)null, string.Empty)); - } - - /// - /// Tests that the method throws when the argument is empty. - /// - [Fact] - [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1122:UseStringEmptyForEmptyStrings", Justification = "Reviewed. Suppression is OK here.")] - public void NotEmptyOrWhiteSpaceThrowsWhenEmpty() - { - Assert.Throws(() => Guard.NotNullOrWhiteSpace("", string.Empty)); - } - - /// - /// Tests that the method throws when the argument is whitespace. - /// - [Fact] - public void NotEmptyOrWhiteSpaceThrowsOnWhitespace() - { - Assert.Throws(() => Guard.NotNullOrWhiteSpace(" ", string.Empty)); - } - - /// - /// Tests that the method throws when the argument name is null. - /// - [Fact] - public void NotEmptyOrWhiteSpaceThrowsWhenParameterNameNull() - { - Assert.Throws(() => Guard.NotNullOrWhiteSpace(null, null)); - } - - /// - /// Tests that the method throws when the argument is greater. - /// - [Fact] - public void LessThanThrowsWhenArgIsGreater() - { - Assert.Throws(() => Guard.MustBeLessThan(1, 0, "foo")); - } - - /// - /// Tests that the method throws when the argument is equal. - /// - [Fact] - public void LessThanThrowsWhenArgIsEqual() - { - Assert.Throws(() => Guard.MustBeLessThan(1, 1, "foo")); - } - - /// - /// Tests that the method throws when the argument is greater. - /// - [Fact] - public void LessThanOrEqualToThrowsWhenArgIsGreater() - { - Assert.Throws(() => Guard.MustBeLessThanOrEqualTo(1, 0, "foo")); - } - - /// - /// Tests that the method does not throw when the argument - /// is less. - /// - [Fact] - public void LessThanOrEqualToDoesNotThrowWhenArgIsLess() - { - Exception ex = Record.Exception(() => Guard.MustBeLessThanOrEqualTo(0, 1, "foo")); - Assert.Null(ex); - } - - /// - /// Tests that the method does not throw when the argument - /// is equal. - /// - [Fact] - public void LessThanOrEqualToDoesNotThrowWhenArgIsEqual() - { - Exception ex = Record.Exception(() => Guard.MustBeLessThanOrEqualTo(1, 1, "foo")); - Assert.Equal(1, 1); - Assert.Null(ex); - } - - /// - /// Tests that the method throws when the argument is greater. - /// - [Fact] - public void GreaterThanThrowsWhenArgIsLess() - { - Assert.Throws(() => Guard.MustBeGreaterThan(0, 1, "foo")); - } - - /// - /// Tests that the method throws when the argument is greater. - /// - [Fact] - public void GreaterThanThrowsWhenArgIsEqual() - { - Assert.Throws(() => Guard.MustBeGreaterThan(1, 1, "foo")); - } - - /// - /// Tests that the method throws when the argument name is greater. - /// - [Fact] - public void GreaterThanOrEqualToThrowsWhenArgIsLess() - { - Assert.Throws(() => Guard.MustBeGreaterThanOrEqualTo(0, 1, "foo")); - } - - /// - /// Tests that the method does not throw when the argument - /// is less. - /// - [Fact] - public void GreaterThanOrEqualToDoesNotThrowWhenArgIsGreater() - { - Exception ex = Record.Exception(() => Guard.MustBeGreaterThanOrEqualTo(1, 0, "foo")); - Assert.Null(ex); - } - - /// - /// Tests that the method does not throw when the argument - /// is equal. - /// - [Fact] - public void GreaterThanOrEqualToDoesNotThrowWhenArgIsEqual() - { - Exception ex = Record.Exception(() => Guard.MustBeGreaterThanOrEqualTo(1, 1, "foo")); - Assert.Equal(1, 1); - Assert.Null(ex); - } - - /// - /// Tests that the method throws when the argument is less. - /// - [Fact] - public void BetweenOrEqualToThrowsWhenArgIsLess() - { - Assert.Throws(() => Guard.MustBeBetweenOrEqualTo(-2, -1, 1, "foo")); - } - - /// - /// Tests that the method throws when the argument is greater. - /// - [Fact] - public void BetweenOrEqualToThrowsWhenArgIsGreater() - { - Assert.Throws(() => Guard.MustBeBetweenOrEqualTo(2, -1, 1, "foo")); - } - - /// - /// Tests that the method does not throw when the argument - /// is equal. - /// - [Fact] - public void BetweenOrEqualToDoesNotThrowWhenArgIsEqual() - { - Exception ex = Record.Exception(() => Guard.MustBeBetweenOrEqualTo(1, 1, 1, "foo")); - Assert.Null(ex); - } - - /// - /// Tests that the method does not throw when the argument - /// is equal. - /// - [Fact] - public void BetweenOrEqualToDoesNotThrowWhenArgIsBetween() - { - Exception ex = Record.Exception(() => Guard.MustBeBetweenOrEqualTo(0, -1, 1, "foo")); - Assert.Null(ex); - } - - /// - /// Tests that the method throws when the argument is false. - /// - [Fact] - public void IsTrueThrowsWhenArgIsFalse() - { - Assert.Throws(() => Guard.IsTrue(false, "foo", "message")); - } - - /// - /// Tests that the method does not throw when the argument is true. - /// - [Fact] - public void IsTrueDoesThrowsWhenArgIsTrue() - { - Exception ex = Record.Exception(() => Guard.IsTrue(true, "foo", "message")); - Assert.Null(ex); - } - - /// - /// Tests that the method throws when the argument is true. - /// - [Fact] - public void IsFalseThrowsWhenArgIsFalse() - { - Assert.Throws(() => Guard.IsFalse(true, "foo", "message")); - } - - /// - /// Tests that the method does not throw when the argument is false. - /// - [Fact] - public void IsFalseDoesThrowsWhenArgIsTrue() - { - Exception ex = Record.Exception(() => Guard.IsFalse(false, "foo", "message")); - Assert.Null(ex); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs new file mode 100644 index 0000000000..3cfce6b8e2 --- /dev/null +++ b/tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs @@ -0,0 +1,40 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced.ParallelUtils; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Helpers +{ + public class ParallelExecutionSettingsTests + { + [Theory] + [InlineData(-3, true)] + [InlineData(-2, true)] + [InlineData(-1, false)] + [InlineData(0, true)] + [InlineData(1, false)] + [InlineData(5, false)] + public void Constructor_MaxDegreeOfParallelism_CompatibleWith_ParallelOptions(int maxDegreeOfParallelism, bool throws) + { + if (throws) + { + Assert.Throws( + () => + { + _ = new ParallelExecutionSettings( + maxDegreeOfParallelism, + Configuration.Default.MemoryAllocator); + }); + } + else + { + var parallelSettings = new ParallelExecutionSettings( + maxDegreeOfParallelism, + Configuration.Default.MemoryAllocator); + Assert.Equal(maxDegreeOfParallelism, parallelSettings.MaxDegreeOfParallelism); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs index aeadfcebb5..4b5c87c7f5 100644 --- a/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs +++ b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs @@ -7,8 +7,8 @@ using System.Linq; using System.Numerics; using System.Threading; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -332,7 +332,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers }); // Assert: - TestImageExtensions.CompareBuffers(expected.Span, actual.Span); + TestImageExtensions.CompareBuffers(expected.GetSpan(), actual.GetSpan()); } } diff --git a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs index 45067f82fe..0bb3f49d64 100644 --- a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs +++ b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers Span span = buffer.GetMultiRowSpan(rows); - ref int expected0 = ref buffer.Span[min * width]; + ref int expected0 = ref buffer.GetSpan()[min * width]; int expectedLength = (max - min) * width; ref int actual0 = ref span[0]; diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index ee32be3ca7..a0e4f54ac8 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Tests.Memory { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); - Assert.Equal(width * height, buffer.Memory.Length); + Assert.Equal(width * height, buffer.GetMemory().Length); } } @@ -75,22 +75,6 @@ namespace SixLabors.ImageSharp.Tests.Memory } } - [Theory] - [InlineData(7, 42, 0, 0)] - [InlineData(7, 42, 3, 10)] - [InlineData(17, 42, 0, 41)] - public void GetRowSpanXY(int width, int height, int x, int y) - { - using (Buffer2D buffer = this.MemoryAllocator.Allocate2D(width, height)) - { - Span span = buffer.GetRowSpan(x, y); - - // Assert.Equal(width * y + x, span.Start); - Assert.Equal(width - x, span.Length); - Assert.SpanPointsTo(span, buffer.MemorySource.MemoryOwner, width * y + x); - } - } - [Theory] [InlineData(42, 8, 0, 0)] [InlineData(400, 1000, 20, 10)] @@ -140,7 +124,7 @@ namespace SixLabors.ImageSharp.Tests.Memory var rnd = new Random(123); using (Buffer2D b = this.MemoryAllocator.Allocate2D(width, height)) { - rnd.RandomFill(b.Span, 0, 1); + rnd.RandomFill(b.GetSpan(), 0, 1); b.CopyColumns(startIndex, destIndex, columnCount); @@ -162,7 +146,7 @@ namespace SixLabors.ImageSharp.Tests.Memory var rnd = new Random(123); using (Buffer2D b = this.MemoryAllocator.Allocate2D(100, 100)) { - rnd.RandomFill(b.Span, 0, 1); + rnd.RandomFill(b.GetSpan(), 0, 1); b.CopyColumns(0, 50, 22); b.CopyColumns(0, 50, 22); diff --git a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs similarity index 97% rename from tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs rename to tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs index d298f61976..140af9563a 100644 --- a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs +++ b/tests/ImageSharp.Tests/Processing/BaseImageOperationsExtensionTest.cs @@ -4,9 +4,10 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; + using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Processing { public abstract class BaseImageOperationsExtensionTest { diff --git a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs similarity index 96% rename from tests/ImageSharp.Tests/FakeImageOperationsProvider.cs rename to tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs index fb4afc28d1..38d53c7b6c 100644 --- a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs +++ b/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs @@ -3,14 +3,15 @@ using System.Collections.Generic; using System.Linq; + using SixLabors.ImageSharp.Advanced; -using SixLabors.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Memory; using SixLabors.Primitives; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Processing { internal class FakeImageOperationsProvider : IImageProcessingContextFactory { @@ -19,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests public bool HasCreated(Image source) where TPixel : struct, IPixel { - return Created(source).Any(); + return this.Created(source).Any(); } public IEnumerable> Created(Image source) where TPixel : struct, IPixel { @@ -29,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests public IEnumerable.AppliedOperation> AppliedOperations(Image source) where TPixel : struct, IPixel { - return Created(source) + return this.Created(source) .SelectMany(x => x.Applied); } diff --git a/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs new file mode 100644 index 0000000000..29d1d63e62 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class LightnessTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Lightness_amount_LightnessProcessorDefaultsSet() + { + this.operations.Lightness(.5F); + LightnessProcessor processor = this.Verify(); + + Assert.Equal(.5F, processor.Amount); + } + + [Fact] + public void Lightness_amount_rect_LightnessProcessorDefaultsSet() + { + this.operations.Lightness(.5F, this.rect); + LightnessProcessor processor = this.Verify(this.rect); + + Assert.Equal(.5F, processor.Amount); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs index a9aecaa9ca..65e04fbcc7 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Tests.Processing; + using Xunit; namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/ImageOperationTests.cs b/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs similarity index 99% rename from tests/ImageSharp.Tests/ImageOperationTests.cs rename to tests/ImageSharp.Tests/Processing/ImageOperationTests.cs index d2dc95cfe8..1b5c16538a 100644 --- a/tests/ImageSharp.Tests/ImageOperationTests.cs +++ b/tests/ImageSharp.Tests/Processing/ImageOperationTests.cs @@ -12,7 +12,7 @@ using SixLabors.ImageSharp.Processing.Processors; using Xunit; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Processing { /// /// Tests the configuration class. diff --git a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs new file mode 100644 index 0000000000..9d16583cd8 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs @@ -0,0 +1,200 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using Moq; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing +{ + /// + /// Contains test cases for default implementation. + /// + public class ImageProcessingContextTests + { + private readonly Image image = new Image(10, 10); + + private readonly Mock processorDefinition; + + private readonly Mock cloningProcessorDefinition; + + private readonly Mock> regularProcessorImpl; + + private readonly Mock> cloningProcessorImpl; + + private static readonly Rectangle Bounds = new Rectangle(3, 3, 5, 5); + + public ImageProcessingContextTests() + { + this.processorDefinition = new Mock(); + this.cloningProcessorDefinition = new Mock(); + this.regularProcessorImpl = new Mock>(); + this.cloningProcessorImpl = new Mock>(); + } + + // bool throwException, bool useBounds + public static readonly TheoryData ProcessorTestData = new TheoryData() + { + { false, false }, + { false, true }, + { true, false }, + { true, true } + }; + + [Theory] + [MemberData(nameof(ProcessorTestData))] + public void Mutate_RegularProcessor(bool throwException, bool useBounds) + { + this.SetupRegularProcessor(throwException); + + if (throwException) + { + Assert.Throws(() => this.MutateRegularApply(useBounds)); + } + else + { + this.MutateRegularApply(useBounds); + } + + this.regularProcessorImpl.Verify(p => p.Execute(), Times.Once()); + this.regularProcessorImpl.Verify(p => p.Dispose(), Times.Once()); + } + + [Theory] + [MemberData(nameof(ProcessorTestData))] + public void Clone_RegularProcessor(bool throwException, bool useBounds) + { + this.SetupRegularProcessor(throwException); + + if (throwException) + { + Assert.Throws(() => this.CloneRegularApply(useBounds)); + } + else + { + this.CloneRegularApply(useBounds); + } + + this.regularProcessorImpl.Verify(p => p.Execute(), Times.Once); + this.regularProcessorImpl.Verify(p => p.Dispose(), Times.Once); + } + + [Theory] + [MemberData(nameof(ProcessorTestData))] + public void Mutate_CloningProcessor(bool throwException, bool useBounds) + { + this.SetupCloningProcessor(throwException); + + if (throwException) + { + Assert.Throws(() => this.MutateCloneApply(useBounds)); + } + else + { + this.MutateCloneApply(useBounds); + } + + this.cloningProcessorImpl.Verify(p => p.Execute(), Times.Once()); + this.cloningProcessorImpl.Verify(p => p.Dispose(), Times.Once()); + } + + [Theory] + [MemberData(nameof(ProcessorTestData))] + public void Clone_CloningProcessor(bool throwException, bool useBounds) + { + this.SetupCloningProcessor(throwException); + + if (throwException) + { + Assert.Throws(() => this.CloneCloneApply(useBounds)); + } + else + { + this.CloneCloneApply(useBounds); + } + + this.cloningProcessorImpl.Verify(p => p.CloneAndExecute(), Times.Once()); + this.cloningProcessorImpl.Verify(p => p.Dispose(), Times.Once()); + } + + private void MutateRegularApply(bool useBounds) + { + if (useBounds) + { + this.image.Mutate(c => c.ApplyProcessor(this.processorDefinition.Object, Bounds)); + } + else + { + this.image.Mutate(c => c.ApplyProcessor(this.processorDefinition.Object)); + } + } + + private void MutateCloneApply(bool useBounds) + { + if (useBounds) + { + this.image.Mutate(c => c.ApplyProcessor(this.cloningProcessorDefinition.Object, Bounds)); + } + else + { + this.image.Mutate(c => c.ApplyProcessor(this.cloningProcessorDefinition.Object)); + } + } + + private void CloneRegularApply(bool useBounds) + { + if (useBounds) + { + this.image.Clone(c => c.ApplyProcessor(this.processorDefinition.Object, Bounds)).Dispose(); + } + else + { + this.image.Clone(c => c.ApplyProcessor(this.processorDefinition.Object)).Dispose(); + } + } + + private void CloneCloneApply(bool useBounds) + { + if (useBounds) + { + this.image.Clone(c => c.ApplyProcessor(this.cloningProcessorDefinition.Object, Bounds)).Dispose(); + } + else + { + this.image.Clone(c => c.ApplyProcessor(this.cloningProcessorDefinition.Object)).Dispose(); + } + } + + private void SetupRegularProcessor(bool throwsException) + { + if (throwsException) + { + this.regularProcessorImpl.Setup(p => p.Execute()).Throws(new ImageProcessingException("Test")); + } + + this.processorDefinition + .Setup(p => p.CreatePixelSpecificProcessor(It.IsAny>(), It.IsAny())) + .Returns(this.regularProcessorImpl.Object); + } + + private void SetupCloningProcessor(bool throwsException) + { + if (throwsException) + { + this.cloningProcessorImpl.Setup(p => p.Execute()).Throws(new ImageProcessingException("Test")); + this.cloningProcessorImpl.Setup(p => p.CloneAndExecute()).Throws(new ImageProcessingException("Test")); + } + + this.cloningProcessorDefinition + .Setup(p => p.CreatePixelSpecificCloningProcessor(It.IsAny>(), It.IsAny())) + .Returns(this.cloningProcessorImpl.Object); + + this.cloningProcessorDefinition + .Setup(p => p.CreatePixelSpecificProcessor(It.IsAny>(), It.IsAny())) + .Returns(this.cloningProcessorImpl.Object); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index 5d8a155f05..32da38621a 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -9,6 +9,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Normalization { + // ReSharper disable InconsistentNaming public class HistogramEqualizationTests { private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0456F); @@ -113,5 +114,32 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization image.CompareToReferenceOutput(ValidatorComparer, provider); } } + + /// + /// This is regression test for a bug with the calculation of the y-start positions, + /// where it could happen that one too much start position was calculated in some cases. + /// See: https://github.com/SixLabors/ImageSharp/pull/984 + /// + [Theory] + [WithTestPatternImages(110, 110, PixelTypes.Rgb24)] + [WithTestPatternImages(170, 170, PixelTypes.Rgb24)] + public void Issue984(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new HistogramEqualizationOptions() + { + Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, + LuminanceLevels = 256, + ClipHistogram = true, + ClipLimit = 5, + NumberOfTiles = 10 + }; + image.Mutate(x => x.HistogramEqualization(options)); + image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider); + } + } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index 3796c51a3b..296b8d1d7f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -56,17 +56,22 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } // Make sure the kernel components are the same - var definition = new BokehBlurProcessor(10, BokehBlurProcessor.DefaultComponents, BokehBlurProcessor.DefaultGamma); - var processor = new BokehBlurProcessor(definition); - Assert.Equal(components.Count, processor.Kernels.Count); - foreach ((Complex64[] a, Complex64[] b) in components.Zip(processor.Kernels, (a, b) => (a, b))) + using (var image = new Image(1, 1)) { - Span spanA = a.AsSpan(), spanB = b.AsSpan(); - Assert.Equal(spanA.Length, spanB.Length); - for (int i = 0; i < spanA.Length; i++) + var definition = new BokehBlurProcessor(10, BokehBlurProcessor.DefaultComponents, BokehBlurProcessor.DefaultGamma); + using (var processor = (BokehBlurProcessor)definition.CreatePixelSpecificProcessor(image, image.Bounds())) { - Assert.True(Math.Abs(Math.Abs(spanA[i].Real) - Math.Abs(spanB[i].Real)) < 0.0001f); - Assert.True(Math.Abs(Math.Abs(spanA[i].Imaginary) - Math.Abs(spanB[i].Imaginary)) < 0.0001f); + Assert.Equal(components.Count, processor.Kernels.Count); + foreach ((Complex64[] a, Complex64[] b) in components.Zip(processor.Kernels, (a, b) => (a, b))) + { + Span spanA = a.AsSpan(), spanB = b.AsSpan(); + Assert.Equal(spanA.Length, spanB.Length); + for (int i = 0; i < spanA.Length; i++) + { + Assert.True(Math.Abs(Math.Abs(spanA[i].Real) - Math.Abs(spanB[i].Real)) < 0.0001f); + Assert.True(Math.Abs(Math.Abs(spanA[i].Imaginary) - Math.Abs(spanB[i].Imaginary)) < 0.0001f); + } + } } } } @@ -122,7 +127,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { provider.RunValidatingProcessorTest( x => x.BokehBlur(value.Radius, value.Components, value.Gamma), - testOutputDetails: value.ToString(), + testOutputDetails: value.ToString(), appendPixelTypeToFileName: false); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs new file mode 100644 index 0000000000..c330ed6d9a --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + using SixLabors.ImageSharp.Processing; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + + [GroupOutput("Filters")] + public class LightnessTest + { + private readonly ImageComparer imageComparer = ImageComparer.Tolerant(0.007F); + + public static readonly TheoryData LightnessValues + = new TheoryData + { + .5F, + 1.5F + }; + + [Theory] + [WithTestPatternImages(nameof(LightnessValues), 48, 48, PixelTypes.Rgba32)] + public void ApplyLightnessFilter(TestImageProvider provider, float value) + where TPixel : struct, IPixel => provider.RunValidatingProcessorTest(ctx => ctx.Lightness(value), value, this.imageComparer); + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs index b5ed64f7ef..b351ec235f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeHelperTests.cs @@ -11,19 +11,18 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { public class ResizeHelperTests { - [Theory] [InlineData(20, 100, 1, 2)] - [InlineData(20, 100, 20*100*16, 2)] - [InlineData(20, 100, 40*100*16, 2)] - [InlineData(20, 100, 59*100*16, 2)] - [InlineData(20, 100, 60*100*16, 3)] - [InlineData(17, 63, 5*17*63*16, 5)] - [InlineData(17, 63, 5*17*63*16+1, 5)] - [InlineData(17, 63, 6*17*63*16-1, 5)] - [InlineData(33, 400, 1*1024*1024, 4)] - [InlineData(33, 400, 8*1024*1024, 39)] - [InlineData(50, 300, 1*1024*1024, 4)] + [InlineData(20, 100, 20 * 100 * 16, 2)] + [InlineData(20, 100, 40 * 100 * 16, 2)] + [InlineData(20, 100, 59 * 100 * 16, 2)] + [InlineData(20, 100, 60 * 100 * 16, 3)] + [InlineData(17, 63, 5 * 17 * 63 * 16, 5)] + [InlineData(17, 63, (5 * 17 * 63 * 16) + 1, 5)] + [InlineData(17, 63, (6 * 17 * 63 * 16) - 1, 5)] + [InlineData(33, 400, 1 * 1024 * 1024, 4)] + [InlineData(33, 400, 8 * 1024 * 1024, 39)] + [InlineData(50, 300, 1 * 1024 * 1024, 4)] public void CalculateResizeWorkerHeightInWindowBands( int windowDiameter, int width, @@ -40,17 +39,121 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms var sourceSize = new Size(200, 100); var target = new Size(400, 200); - var actual = ResizeHelper.CalculateTargetLocationAndBounds( + (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds( sourceSize, - new ResizeOptions{ + new ResizeOptions + { Mode = ResizeMode.Min, Size = target - }, - target.Width, - target.Height); - - Assert.Equal(sourceSize, actual.Item1); - Assert.Equal(new Rectangle(0, 0, sourceSize.Width, sourceSize.Height), actual.Item2); + }); + + Assert.Equal(sourceSize, size); + Assert.Equal(new Rectangle(0, 0, sourceSize.Width, sourceSize.Height), rectangle); + } + + [Fact] + public void MaxSizeAndRectangleAreCorrect() + { + var sourceSize = new Size(5072, 6761); + var target = new Size(0, 450); + + var expectedSize = new Size(338, 450); + var expectedRectangle = new Rectangle(Point.Empty, expectedSize); + + (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds( + sourceSize, + new ResizeOptions + { + Mode = ResizeMode.Max, + Size = target + }); + + Assert.Equal(expectedSize, size); + Assert.Equal(expectedRectangle, rectangle); + } + + [Fact] + public void CropSizeAndRectangleAreCorrect() + { + var sourceSize = new Size(100, 100); + var target = new Size(25, 50); + + var expectedSize = new Size(25, 50); + var expectedRectangle = new Rectangle(-12, 0, 50, 50); + + (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds( + sourceSize, + new ResizeOptions + { + Mode = ResizeMode.Crop, + Size = target + }); + + Assert.Equal(expectedSize, size); + Assert.Equal(expectedRectangle, rectangle); + } + + [Fact] + public void BoxPadSizeAndRectangleAreCorrect() + { + var sourceSize = new Size(100, 100); + var target = new Size(120, 110); + + var expectedSize = new Size(120, 110); + var expectedRectangle = new Rectangle(10, 5, 100, 100); + + (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds( + sourceSize, + new ResizeOptions + { + Mode = ResizeMode.BoxPad, + Size = target + }); + + Assert.Equal(expectedSize, size); + Assert.Equal(expectedRectangle, rectangle); + } + + [Fact] + public void PadSizeAndRectangleAreCorrect() + { + var sourceSize = new Size(100, 100); + var target = new Size(120, 110); + + var expectedSize = new Size(120, 110); + var expectedRectangle = new Rectangle(5, 0, 110, 110); + + (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds( + sourceSize, + new ResizeOptions + { + Mode = ResizeMode.Pad, + Size = target + }); + + Assert.Equal(expectedSize, size); + Assert.Equal(expectedRectangle, rectangle); + } + + [Fact] + public void StretchSizeAndRectangleAreCorrect() + { + var sourceSize = new Size(100, 100); + var target = new Size(57, 32); + + var expectedSize = new Size(57, 32); + var expectedRectangle = new Rectangle(Point.Empty, expectedSize); + + (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds( + sourceSize, + new ResizeOptions + { + Mode = ResizeMode.Stretch, + Size = target + }); + + Assert.Equal(expectedSize, size); + Assert.Equal(expectedRectangle, rectangle); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index b870ddd08a..33da33c717 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -20,8 +20,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms this.operations.Pad(width, height); ResizeProcessor resizeProcessor = this.Verify(); - Assert.Equal(width, resizeProcessor.Width); - Assert.Equal(height, resizeProcessor.Height); + Assert.Equal(width, resizeProcessor.TargetWidth); + Assert.Equal(height, resizeProcessor.TargetHeight); Assert.Equal(sampler, resizeProcessor.Sampler); } } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index c7ebe65e8c..f268eda86c 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.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; @@ -18,8 +18,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms this.operations.Resize(width, height); ResizeProcessor resizeProcessor = this.Verify(); - Assert.Equal(width, resizeProcessor.Width); - Assert.Equal(height, resizeProcessor.Height); + Assert.Equal(width, resizeProcessor.TargetWidth); + Assert.Equal(height, resizeProcessor.TargetHeight); } [Fact] @@ -31,8 +31,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms this.operations.Resize(width, height, sampler); ResizeProcessor resizeProcessor = this.Verify(); - Assert.Equal(width, resizeProcessor.Width); - Assert.Equal(height, resizeProcessor.Height); + Assert.Equal(width, resizeProcessor.TargetWidth); + Assert.Equal(height, resizeProcessor.TargetHeight); Assert.Equal(sampler, resizeProcessor.Sampler); } @@ -48,8 +48,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms this.operations.Resize(width, height, sampler, compand); ResizeProcessor resizeProcessor = this.Verify(); - Assert.Equal(width, resizeProcessor.Width); - Assert.Equal(height, resizeProcessor.Height); + Assert.Equal(width, resizeProcessor.TargetWidth); + Assert.Equal(height, resizeProcessor.TargetHeight); Assert.Equal(sampler, resizeProcessor.Sampler); Assert.Equal(compand, resizeProcessor.Compand); } @@ -74,8 +74,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms this.operations.Resize(resizeOptions); ResizeProcessor resizeProcessor = this.Verify(); - Assert.Equal(width, resizeProcessor.Width); - Assert.Equal(height, resizeProcessor.Height); + Assert.Equal(width, resizeProcessor.TargetWidth); + Assert.Equal(height, resizeProcessor.TargetHeight); Assert.Equal(sampler, resizeProcessor.Sampler); Assert.Equal(compand, resizeProcessor.Compand); @@ -87,4 +87,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms Assert.Equal(mode, resizeOptions.Mode); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index e95ce09073..146f2efcdb 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -82,6 +82,14 @@ namespace SixLabors.ImageSharp.Tests public const string Ducky = "Png/ducky.png"; public const string Rainbow = "Png/rainbow.png"; + // Issue 1014: https://github.com/SixLabors/ImageSharp/issues/1014 + public const string Issue1014_1 = "Png/issues/Issue_1014_1.png"; + public const string Issue1014_2 = "Png/issues/Issue_1014_2.png"; + public const string Issue1014_3 = "Png/issues/Issue_1014_3.png"; + public const string Issue1014_4 = "Png/issues/Issue_1014_4.png"; + public const string Issue1014_5 = "Png/issues/Issue_1014_5.png"; + public const string Issue1014_6 = "Png/issues/Issue_1014_6.png"; + public static class Bad { // Odd chunk lengths @@ -90,6 +98,7 @@ namespace SixLabors.ImageSharp.Tests public const string CorruptedChunk = "Png/big-corrupted-chunk.png"; public const string ZlibOverflow = "Png/zlib-overflow.png"; public const string ZlibOverflow2 = "Png/zlib-overflow2.png"; + public const string ZlibZtxtBadHeader = "Png/zlib-ztxt-bad-header.png"; } public static readonly string[] All = diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 91943d9a6a..9da39fbe40 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -7,9 +7,9 @@ using System.IO; using System.Numerics; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors; @@ -448,10 +448,10 @@ namespace SixLabors.ImageSharp.Tests { imageFrame.ComparePixelBufferTo(expectedPixel); } - + return image; } - + /// /// All pixels in all frames should be exactly equal to 'expectedPixelColor.ToPixel()'. /// @@ -462,7 +462,7 @@ namespace SixLabors.ImageSharp.Tests { imageFrame.ComparePixelBufferTo(expectedPixelColor.ToPixel()); } - + return image; } @@ -481,7 +481,7 @@ namespace SixLabors.ImageSharp.Tests return imageFrame; } - + public static ImageFrame ComparePixelBufferTo( this ImageFrame image, Span expectedPixels) @@ -524,7 +524,7 @@ namespace SixLabors.ImageSharp.Tests var testFile = TestFile.Create(path); referenceDecoder = referenceDecoder ?? TestEnvironment.GetReferenceDecoder(path); - + using (var original = Image.Load(testFile.Bytes, referenceDecoder)) { comparer.VerifySimilarity(original, image); @@ -558,7 +558,7 @@ namespace SixLabors.ImageSharp.Tests appendPixelTypeToFileName: appendPixelTypeToFileName, appendSourceFileOrDescription: appendSourceFileOrDescription); - image.CompareToReferenceOutput(comparer, + image.CompareToReferenceOutput(comparer, provider, testOutputDetails, appendPixelTypeToFileName: appendPixelTypeToFileName, @@ -678,25 +678,31 @@ namespace SixLabors.ImageSharp.Tests private class MakeOpaqueProcessor : IImageProcessor { - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new MakeOpaqueProcessor(); - } + => new MakeOpaqueProcessor(source, sourceRectangle); } private class MakeOpaqueProcessor : ImageProcessor where TPixel : struct, IPixel { - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + public MakeOpaqueProcessor(Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) + { + + } + + protected override void OnFrameApply(ImageFrame source) { + Rectangle sourceRectangle = this.SourceRectangle; + Configuration configuration = this.Configuration; ParallelHelper.IterateRowsWithTempBuffer(sourceRectangle, configuration, (rows, temp) => { Span tempSpan = temp.Span; for (int y = rows.Min; y < rows.Max; y++) { - var rowSpan = source.GetPixelRowSpan(y).Slice(sourceRectangle.Left, sourceRectangle.Width); + Span rowSpan = source.GetPixelRowSpan(y).Slice(sourceRectangle.Left, sourceRectangle.Width); PixelOperations.Instance.ToVector4(configuration, rowSpan, tempSpan, PixelConversionModifiers.Scale); for (int i = 0; i < tempSpan.Length; i++) { diff --git a/tests/ImageSharp.Tests/xunit.runner.json b/tests/ImageSharp.Tests/xunit.runner.json index 5204242f03..749ece4387 100644 --- a/tests/ImageSharp.Tests/xunit.runner.json +++ b/tests/ImageSharp.Tests/xunit.runner.json @@ -1,5 +1,5 @@ { - "shadowCopy": false, - "methodDisplay": "method", - "diagnosticMessages": true -} \ No newline at end of file + "shadowCopy": false, + "methodDisplay": "method", + "diagnosticMessages": true +} diff --git a/tests/Images/External b/tests/Images/External index 36f39bc624..563ec6f777 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 36f39bc624f8a49caf512077bf70cab30c2e5fb4 +Subproject commit 563ec6f7774734ba39924174c8961705a1ea6fa2 diff --git a/tests/Images/Input/Png/issues/Issue_1014_1.png b/tests/Images/Input/Png/issues/Issue_1014_1.png new file mode 100644 index 0000000000..2cbd401e61 Binary files /dev/null and b/tests/Images/Input/Png/issues/Issue_1014_1.png differ diff --git a/tests/Images/Input/Png/issues/Issue_1014_2.png b/tests/Images/Input/Png/issues/Issue_1014_2.png new file mode 100644 index 0000000000..ca1c8ea5b9 Binary files /dev/null and b/tests/Images/Input/Png/issues/Issue_1014_2.png differ diff --git a/tests/Images/Input/Png/issues/Issue_1014_3.png b/tests/Images/Input/Png/issues/Issue_1014_3.png new file mode 100644 index 0000000000..3a7a8ed2c0 Binary files /dev/null and b/tests/Images/Input/Png/issues/Issue_1014_3.png differ diff --git a/tests/Images/Input/Png/issues/Issue_1014_4.png b/tests/Images/Input/Png/issues/Issue_1014_4.png new file mode 100644 index 0000000000..54d099427d Binary files /dev/null and b/tests/Images/Input/Png/issues/Issue_1014_4.png differ diff --git a/tests/Images/Input/Png/issues/Issue_1014_5.png b/tests/Images/Input/Png/issues/Issue_1014_5.png new file mode 100644 index 0000000000..9e719ca302 Binary files /dev/null and b/tests/Images/Input/Png/issues/Issue_1014_5.png differ diff --git a/tests/Images/Input/Png/issues/Issue_1014_6.png b/tests/Images/Input/Png/issues/Issue_1014_6.png new file mode 100644 index 0000000000..7b90f49da6 Binary files /dev/null and b/tests/Images/Input/Png/issues/Issue_1014_6.png differ diff --git a/tests/Images/Input/Png/zlib-ztxt-bad-header.png b/tests/Images/Input/Png/zlib-ztxt-bad-header.png new file mode 100644 index 0000000000..c13f98fd16 Binary files /dev/null and b/tests/Images/Input/Png/zlib-ztxt-bad-header.png differ