From 726b6ab7c2f91f042d50a3677188a6de2b9ab976 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 12 Aug 2019 20:10:32 +0200 Subject: [PATCH 01/87] Remove unnecessary tileX and tileY declarations --- ...AdaptiveHistogramEqualizationProcessor{TPixel}.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index 7d8ba62083..5d3e865ed0 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -126,11 +126,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization 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, 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, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); // Fix top row ProcessBorderRow(ref pixelsBase, cdfData, 0, sourceWidth, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); @@ -201,7 +201,6 @@ 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 height of a tile. /// X start position in the image. /// X end position of the image. @@ -215,13 +214,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization int cdfX, int sourceWidth, int sourceHeight, - int tileWidth, int tileHeight, int xStart, int xEnd, int luminanceLevels) { - int halfTileWidth = tileWidth / 2; int halfTileHeight = tileHeight / 2; int cdfY = 0; @@ -232,13 +229,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization for (int dy = y; dy < yLimit; dy++) { int dyOffSet = dy * sourceWidth; - int tileX = halfTileWidth; for (int dx = xStart; dx < xEnd; dx++) { ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX, cdfY + 1, tileY, tileHeight, luminanceLevels); pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); - tileX++; } tileY++; @@ -277,7 +272,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization int cdfX = 0; for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) { - int tileY = 0; for (int dy = yStart; dy < yEnd; dy++) { int dyOffSet = dy * sourceWidth; @@ -290,8 +284,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); tileX++; } - - tileY++; } cdfX++; From 26042ec82c69f7b93394aa564fbd50843f4a61f8 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 13 Aug 2019 19:47:09 +0200 Subject: [PATCH 02/87] Add comment about when to use clipping --- .../Normalization/HistogramEqualizationOptions.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs index 1d9d5c986a..8ddb4834d9 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 @@ -25,7 +25,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization 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; From 91a4ed96a11cc120020e1f0a175488b44ddad911 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 15 Aug 2019 19:02:14 +1000 Subject: [PATCH 03/87] remove patternVector in patternBrush --- src/ImageSharp.Drawing/Processing/PatternBrush.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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; } /// From 3f4378381e20530bac67ea9a0f048203fe7477d5 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 15 Aug 2019 19:06:04 +1000 Subject: [PATCH 04/87] ExifTagDescriptionAttribute doesnt need fields since the reflection is done on the constructor arguments --- .../MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs | 5 ----- 1 file changed, 5 deletions(-) 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; } /// From 5a9b87414bf3c6decbd62ef565c8fda61760e821 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 15 Aug 2019 19:39:25 +1000 Subject: [PATCH 05/87] use SUPPORTS_EXTENDED_INTRINSICS to filter out some BasicIntrinsics256 methods --- src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs | 2 ++ 1 file changed, 2 insertions(+) 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 . From 3013857bf9728e51f2f4d2604fc669b4e4b9f6b3 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 15 Aug 2019 19:10:05 +0200 Subject: [PATCH 06/87] Changed processing of border rows and columns to use the tile count and process only a maximum of tileCount - 1 tiles --- ...eHistogramEqualizationProcessor{TPixel}.cs | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index 5d3e865ed0..1c7130dfad 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -65,10 +65,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization 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( @@ -87,7 +89,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization ref TPixel sourceBase = ref source.GetPixelReference(0, 0); cdfX = 0; - for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) + int x = halfTileWidth; + for (int tile = 0; tile < tileCount - 1; tile++) { tileY = 0; int yEnd = Math.Min(y + tileHeight, sourceHeight); @@ -120,24 +123,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, 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, 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); @@ -201,6 +205,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 number of vertical tiles. /// The height of a tile. /// X start position in the image. /// X end position of the image. @@ -214,6 +219,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization int cdfX, int sourceWidth, int sourceHeight, + int tileCount, int tileHeight, int xStart, int xEnd, @@ -222,7 +228,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; @@ -240,6 +247,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization } cdfY++; + y += tileHeight; } } @@ -250,6 +258,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. @@ -262,6 +271,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization CdfTileData cdfData, int cdfY, int sourceWidth, + int tileCount, int tileWidth, int yStart, int yEnd, @@ -270,7 +280,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++) { @@ -287,6 +298,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization } cdfX++; + x += tileWidth; } } From 79f5ff5bd5ce550a0e3d6fc594a4215c06595ea5 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 22 Aug 2019 20:00:59 +1000 Subject: [PATCH 07/87] remove some redundant constructor overloads from exceptions (#979) * remove some redundant constructor overloads from exceptions * re add ImageProcessingException ctor only used in release --- .../Exceptions/UnknownImageFormatException.cs | 14 -------------- 1 file changed, 14 deletions(-) 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) - { - } } } From da9e84917965069b820abfd72301468ac0d71898 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 22 Aug 2019 14:54:05 +0200 Subject: [PATCH 08/87] Add regression test for Issue984 --- .../HistogramEqualizationTests.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index 5d8a155f05..3610f9683d 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,30 @@ 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.Rgba32)] + [WithTestPatternImages(170, 170, PixelTypes.Rgba32)] + public void Issue984_DoesNotThrowException(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new HistogramEqualizationOptions() + { + Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, + LuminanceLevels = 256, + ClipHistogram = true, + NumberOfTiles = 10 + }; + System.Exception ex = Record.Exception(() => image.Mutate(x => x.HistogramEqualization(options))); + Assert.Null(ex); + } + } } } From 13989ac1263671dc7bcda2d47a95523500504522 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 25 Aug 2019 04:35:50 +0200 Subject: [PATCH 09/87] Processors refactoring (#983) * Added new properties to ImageProcessor * Fixed constructors for convolution processors * Fixed constructors for binarization processors * Fixed constructor for dithering processor * Fixed constructors for effects processors * Fixed constructor for filter processor * Fixed constructor for normalization processor * Fixed constructors for overlay processors * Fixed constructor for quantization processor * Fixed constructors for transforms processors * Updated CreatePixelSpecificProcessor definition * Fixed convolution processors creation * Fixed leftover dithering processor constructor * Fixed another leftover dithering processor constructor * Fixed dithering processors creation * Fixed effects processors creation * Fixed filters processor * Fixed normalization processors creation * Fixed overlays processors creation * Fixed quantizer processor creation * Fixed constructors for some remaining processors * Fixed transform processors creation * ImageProcessor class refactored * Renamed some parameters * Convolution processors refactored * Refactored filters and dithering processors * Refactored normalization processors * Overlays processors refactored * Renamed some parameters * CloningImageProcessor class refactored * Transforms processors refactored * Updated DefaultImageProcessingContext class * Src builds, tests still require updating. * Fix tests * Removed unnecessary local variable --- .../Processors/Drawing/DrawImageProcessor.cs | 16 +- .../DrawImageProcessor{TPixelBg,TPixelFg}.cs | 22 ++- .../Processors/Drawing/FillProcessor.cs | 9 +- .../Drawing/FillProcessor{TPixel}.cs | 9 +- .../Processors/Drawing/FillRegionProcessor.cs | 9 +- .../Drawing/FillRegionProcessor{TPixel}.cs | 6 +- .../Processors/Text/DrawTextProcessor.cs | 8 +- .../Text/DrawTextProcessor{TPixel}.cs | 49 ++--- src/ImageSharp/Advanced/AotCompilerTools.cs | 5 +- .../DefaultImageProcessorContext.cs | 26 +-- .../Extensions/GrayscaleExtensions.cs | 6 +- .../BinaryErrorDiffusionProcessor.cs | 11 +- .../BinaryErrorDiffusionProcessor{TPixel}.cs | 15 +- .../BinaryOrderedDitherProcessor.cs | 11 +- .../BinaryOrderedDitherProcessor{TPixel}.cs | 15 +- .../Binarization/BinaryThresholdProcessor.cs | 11 +- .../BinaryThresholdProcessor{TPixel}.cs | 19 +- .../Processors/CloningImageProcessor.cs | 91 ++++----- .../Convolution/BokehBlurProcessor.cs | 5 +- .../Convolution/BokehBlurProcessor{TPixel}.cs | 23 ++- .../Convolution/BoxBlurProcessor.cs | 5 +- .../Convolution/BoxBlurProcessor{TPixel}.cs | 17 +- .../Convolution2DProcessor{TPixel}.cs | 27 +-- .../Convolution2PassProcessor{TPixel}.cs | 21 ++- .../ConvolutionProcessor{TPixel}.cs | 25 ++- .../EdgeDetector2DProcessor{TPixel}.cs | 26 ++- .../EdgeDetectorCompassProcessor{TPixel}.cs | 29 +-- .../Convolution/EdgeDetectorProcessor.cs | 7 +- .../EdgeDetectorProcessor{TPixel}.cs | 15 +- .../Convolution/GaussianBlurProcessor.cs | 9 +- .../GaussianBlurProcessor{TPixel}.cs | 17 +- .../Convolution/GaussianSharpenProcessor.cs | 9 +- .../GaussianSharpenProcessor{TPixel}.cs | 11 +- .../Convolution/KayyaliProcessor.cs | 12 +- .../Processors/Convolution/KirschProcessor.cs | 10 +- .../Convolution/Laplacian3x3Processor.cs | 8 +- .../Convolution/Laplacian5x5Processor.cs | 10 +- .../LaplacianOfGaussianProcessor.cs | 10 +- .../Convolution/PrewittProcessor.cs | 10 +- .../Convolution/RobertsCrossProcessor.cs | 15 +- .../Convolution/RobinsonProcessor.cs | 10 +- .../Processors/Convolution/ScharrProcessor.cs | 10 +- .../Processors/Convolution/SobelProcessor.cs | 10 +- .../ErrorDiffusionPaletteProcessor.cs | 9 +- .../ErrorDiffusionPaletteProcessor{TPixel}.cs | 16 +- .../OrderedDitherPaletteProcessor.cs | 9 +- .../OrderedDitherPaletteProcessor{TPixel}.cs | 16 +- .../Dithering/PaletteDitherProcessor.cs | 7 +- .../PaletteDitherProcessor{TPixel}.cs | 20 +- .../Effects/OilPaintingProcessor.cs | 9 +- .../Effects/OilPaintingProcessor{TPixel}.cs | 26 +-- .../Processors/Effects/PixelateProcessor.cs | 9 +- .../Effects/PixelateProcessor{TPixel}.cs | 19 +- .../Processors/Filters/FilterProcessor.cs | 9 +- .../Filters/FilterProcessor{TPixel}.cs | 23 ++- .../Processors/Filters/LomographProcessor.cs | 10 +- .../Filters/LomographProcessor{TPixel}.cs | 12 +- .../Processors/Filters/PolaroidProcessor.cs | 10 +- .../Filters/PolaroidProcessor{TPixel}.cs | 17 +- .../Processors/ICloningImageProcessor.cs | 14 +- .../Processing/Processors/IImageProcessor.cs | 11 +- .../Processors/IImageProcessor{TPixel}.cs | 12 +- .../Processing/Processors/ImageProcessor.cs | 76 +++++--- .../Processors/ImageProcessorExtensions.cs | 21 +-- .../AdaptiveHistogramEqualizationProcessor.cs | 12 +- ...eHistogramEqualizationProcessor{TPixel}.cs | 18 +- ...ogramEqualizationSlidingWindowProcessor.cs | 12 +- ...alizationSlidingWindowProcessor{TPixel}.cs | 30 +-- .../GlobalHistogramEqualizationProcessor.cs | 12 +- ...lHistogramEqualizationProcessor{TPixel}.cs | 19 +- .../HistogramEqualizationProcessor.cs | 7 +- .../HistogramEqualizationProcessor{TPixel}.cs | 13 +- .../Overlays/BackgroundColorProcessor.cs | 9 +- .../BackgroundColorProcessor{TPixel}.cs | 23 +-- .../Processors/Overlays/GlowProcessor.cs | 9 +- .../Overlays/GlowProcessor{TPixel}.cs | 32 ++-- .../Processors/Overlays/VignetteProcessor.cs | 9 +- .../Overlays/VignetteProcessor{TPixel}.cs | 36 ++-- .../Quantization/QuantizeProcessor.cs | 9 +- .../Quantization/QuantizeProcessor{TPixel}.cs | 10 +- .../Transforms/AffineTransformProcessor.cs | 8 +- .../AffineTransformProcessor{TPixel}.cs | 37 ++-- .../Transforms/AutoOrientProcessor.cs | 9 +- .../Transforms/AutoOrientProcessor{TPixel}.cs | 41 ++-- .../Processors/Transforms/CropProcessor.cs | 8 +- .../Transforms/CropProcessor{TPixel}.cs | 21 ++- .../Transforms/EntropyCropProcessor.cs | 7 +- .../EntropyCropProcessor{TPixel}.cs | 23 ++- .../Processors/Transforms/FlipProcessor.cs | 9 +- .../Transforms/FlipProcessor{TPixel}.cs | 17 +- .../ProjectiveTransformProcessor.cs | 8 +- .../ProjectiveTransformProcessor{TPixel}.cs | 35 ++-- .../Transforms/Resize/ResizeProcessor.cs | 8 +- .../Resize/ResizeProcessor{TPixel}.cs | 33 ++-- .../Processors/Transforms/RotateProcessor.cs | 8 +- .../Transforms/RotateProcessor{TPixel}.cs | 29 ++- .../Transforms/TransformProcessor.cs | 16 +- .../Samplers/GaussianBlur.cs | 2 +- tests/ImageSharp.Benchmarks/Samplers/Glow.cs | 176 ------------------ .../Processors/Convolution/BokehBlurTest.cs | 23 ++- .../TestUtilities/TestImageExtensions.cs | 30 +-- 101 files changed, 961 insertions(+), 891 deletions(-) delete mode 100644 tests/ImageSharp.Benchmarks/Samplers/Glow.cs diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs index 7ec359220e..dc55112c9c 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.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; @@ -59,10 +59,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 +71,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 +88,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 +97,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..6cfa23cce6 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -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; @@ -101,11 +106,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing 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); + 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..a7c22f6d7b 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs @@ -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; @@ -115,4 +118,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..3809200d5f 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,7 +83,7 @@ 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) { @@ -279,19 +280,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/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 439ea6de0c..8d3a074b5f 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -179,9 +179,8 @@ namespace SixLabors.ImageSharp.Advanced 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); + var genericResizeProcessor = (ResizeProcessor)new ResizeProcessor(new ResizeOptions(), default).CreatePixelSpecificProcessor(new Image(0, 0), default); + genericResizeProcessor.AotCreateDestination(); } } } diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext.cs index d0c4c076f1..7b6fbebd12 100644 --- a/src/ImageSharp/Processing/DefaultImageProcessorContext.cs +++ b/src/ImageSharp/Processing/DefaultImageProcessorContext.cs @@ -53,43 +53,33 @@ 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) + if (processor.CreatePixelSpecificProcessor(this.source, rectangle) is ICloningImageProcessor cloningImageProcessor) { - this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle); + this.destination = cloningImageProcessor.CloneAndApply(); return this; } this.destination = this.source.Clone(); } - processor.Apply(this.destination, rectangle); + processor.CreatePixelSpecificProcessor(this.destination, rectangle).Apply(); return this; } - private IImageProcessingContext ApplyProcessor(IImageProcessor processor) - { - return this.ApplyProcessor(processor, this.GetCurrentBounds()); - } - private Rectangle GetCurrentBounds() => this.destination?.Bounds() ?? this.source.Bounds(); } -} \ 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/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..7e3458ae3e 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs @@ -19,13 +19,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization { 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; @@ -74,4 +81,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization } } } -} \ 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..7234955edb 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs @@ -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..518fdf0cb1 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor.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,32 +15,58 @@ namespace SixLabors.ImageSharp.Processing.Processors internal 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; } + /// - public Image CloneAndApply(Image source, Rectangle sourceRectangle) + public Image CloneAndApply() { try { - Image clone = this.CreateDestination(source, sourceRectangle); + Image clone = this.CreateDestination(); - if (clone.Frames.Count != source.Frames.Count) + if (clone.Frames.Count != this.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); + this.BeforeImageApply(clone); - for (int i = 0; i < source.Frames.Count; i++) + for (int i = 0; i < this.Source.Frames.Count; i++) { - ImageFrame sourceFrame = source.Frames[i]; + ImageFrame sourceFrame = this.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.BeforeFrameApply(sourceFrame, clonedFrame); + this.OnFrameApply(sourceFrame, clonedFrame); + this.AfterFrameApply(sourceFrame, clonedFrame); } - this.AfterImageApply(source, clone, sourceRectangle); + this.AfterImageApply(clone); return clone; } @@ -57,40 +83,31 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - public void Apply(Image source, Rectangle sourceRectangle) + public void Apply() { - using (Image cloned = this.CloneAndApply(source, sourceRectangle)) + using (Image cloned = this.CloneAndApply()) { // we now need to move the pixel data/size data from one image base to another - if (cloned.Frames.Count != source.Frames.Count) + if (cloned.Frames.Count != this.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); + this.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(); - } + protected virtual Image CreateDestination() => this.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) + protected virtual void BeforeImageApply(Image destination) { } @@ -99,9 +116,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// 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) + protected virtual void BeforeFrameApply(ImageFrame source, ImageFrame destination) { } @@ -111,31 +126,23 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// 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); + 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. - /// 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) + protected virtual void AfterFrameApply(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. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected virtual void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + protected virtual void AfterImageApply(Image destination) { } } -} \ No newline at end of file +} 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..9339c8fe43 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -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..b9ca2df0b5 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,8 @@ 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) => + new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle).Apply(source); /// /// Create a 1 dimensional Box kernel. @@ -60,4 +57,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..b38d87cd4f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs @@ -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..a523f7c227 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{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; @@ -25,10 +25,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 +56,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 +149,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..5bdec738d7 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{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; @@ -24,7 +24,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 +48,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 +61,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 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution ParallelHelper.IterateRowsWithTempBuffer( workingRectangle, - configuration, + this.Configuration, (rows, vectorBuffer) => { Span vectorSpan = vectorBuffer.Span; @@ -73,7 +80,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 +115,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(this.Configuration, vectorSpan, targetRowSpan); } }); @@ -116,4 +123,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..26a789cfc7 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,17 @@ 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).Apply(this.Source, this.SourceRectangle); } } + + /// + protected override void OnFrameApply(ImageFrame source) + => new Convolution2DProcessor(this.KernelX, this.KernelY, true, this.Source, this.SourceRectangle).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..543014f6f1 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; @@ -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,23 @@ 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).Apply(this.Source, this.SourceRectangle); } } /// - 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 +68,7 @@ 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); + new ConvolutionProcessor(kernels[0], true, this.Source, this.SourceRectangle).Apply(source); if (kernels.Length == 1) { @@ -94,14 +97,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { using (ImageFrame pass = cleanCopy.Clone()) { - new ConvolutionProcessor(kernels[i], true).Apply(pass, sourceRectangle, configuration); + new ConvolutionProcessor(kernels[i], true, this.Source, this.SourceRectangle).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 +135,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..7ee9d68293 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,16 @@ 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).Apply(this.Source, this.SourceRectangle); } } /// - 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) + => new ConvolutionProcessor(this.KernelXY, true, this.Source, this.SourceRectangle).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..da7924d031 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,7 @@ 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) => + new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle).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..0d99ceb9c0 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,7 @@ 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) + => new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle).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/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..557a31c336 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs @@ -16,20 +16,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering internal 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; @@ -78,4 +84,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } } } -} \ 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..08eaec503d 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs @@ -16,19 +16,25 @@ 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; @@ -76,4 +82,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..291e6ac0ec 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs @@ -30,26 +30,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// /// 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; } 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); + base.BeforeFrameApply(source); // Lazy init palette: if (this.palette is null) { 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); } // Lazy init paletteVector: @@ -57,7 +59,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering { this.paletteVector = new Vector4[this.palette.Length]; PixelOperations.Instance.ToVector4( - configuration, + this.Configuration, (ReadOnlySpan)this.palette, (Span)this.paletteVector, PixelConversionModifiers.Scale); @@ -116,4 +118,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering return pair; } } -} \ No newline at end of file +} 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..ea7ba7409d 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -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..5fa233d180 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{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; @@ -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/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..52f0b37678 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs @@ -19,15 +19,17 @@ 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 AfterFrameApply(ImageFrame source) { - new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration); + new VignetteProcessor(VeryDarkGreen).CreatePixelSpecificProcessor(this.Source, this.SourceRectangle).Apply(); } } -} \ 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..5aad1005bb 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs @@ -21,19 +21,18 @@ 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).Apply(this.Source, this.SourceRectangle); + new GlowProcessor(LightOrange, this.Source.Width / 4F).Apply(this.Source, this.SourceRectangle); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs index 024ccbced1..1a21be1f93 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; @@ -16,17 +16,13 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Applies the process to the specified portion of the specified . /// - /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// /// - /// is null. + /// The target is null. /// /// - /// doesn't fit the dimension of the image. + /// The target 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); + Image CloneAndApply(); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index 9b030a6fea..4fff5273ac 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 { @@ -18,8 +19,12 @@ namespace SixLabors.ImageSharp.Processing.Processors /// 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..e2bc1b0ffb 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs @@ -16,16 +16,12 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// Applies the process to the specified portion of the specified . /// - /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// /// - /// is null. + /// The target is null. /// /// - /// doesn't fit the dimension of the image. + /// The target doesn't fit the dimension of the image. /// - void Apply(Image source, Rectangle sourceRectangle); + void Apply(); } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor.cs b/src/ImageSharp/Processing/Processors/ImageProcessor.cs index 0d27a9e1e8..39f3dd6360 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessor.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,20 +15,46 @@ namespace SixLabors.ImageSharp.Processing.Processors internal 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; } + /// - public void Apply(Image source, Rectangle sourceRectangle) + public void Apply() { try { - Configuration config = source.GetConfiguration(); - this.BeforeImageApply(source, sourceRectangle); + this.BeforeImageApply(); - foreach (ImageFrame sourceFrame in source.Frames) + foreach (ImageFrame sourceFrame in this.Source.Frames) { - this.Apply(sourceFrame, sourceRectangle, config); + this.Apply(sourceFrame); } - this.AfterImageApply(source, sourceRectangle); + this.AfterImageApply(); } #if DEBUG catch (Exception) @@ -43,18 +69,16 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - /// Applies the processor to just a single ImageBase + /// Applies the processor to just a single ImageBase. /// - /// the source image - /// the target - /// The configuration. - public void Apply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// the source image. + public void Apply(ImageFrame source) { try { - this.BeforeFrameApply(source, sourceRectangle, configuration); - this.OnFrameApply(source, sourceRectangle, configuration); - this.AfterFrameApply(source, sourceRectangle, configuration); + this.BeforeFrameApply(source); + this.OnFrameApply(source); + this.AfterFrameApply(source); } #if DEBUG catch (Exception) @@ -71,9 +95,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// 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) + protected virtual void BeforeImageApply() { } @@ -81,9 +103,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// 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) + protected virtual void BeforeFrameApply(ImageFrame source) { } @@ -92,27 +112,21 @@ namespace SixLabors.ImageSharp.Processing.Processors /// 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); + 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. - /// The structure that specifies the portion of the image object to draw. - /// The configuration. - protected virtual void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected virtual void AfterFrameApply(ImageFrame source) { } /// /// 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) + protected virtual void AfterImageApply() { } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs index 91b2b30d02..9eb10b6e43 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -14,21 +14,6 @@ namespace SixLabors.ImageSharp.Processing.Processors source.AcceptVisitor(visitor); } - /// - /// Apply an to a frame. - /// Only works from processors implemented by an subclass. - /// - 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); - } - private class ApplyVisitor : IImageVisitor { private readonly IImageProcessor processor; @@ -44,9 +29,9 @@ namespace SixLabors.ImageSharp.Processing.Processors public void Visit(Image image) where TPixel : struct, IPixel { - var processorImpl = this.processor.CreatePixelSpecificProcessor(); - processorImpl.Apply(image, this.sourceRectangle); + IImageProcessor processorImpl = this.processor.CreatePixelSpecificProcessor(image, this.sourceRectangle); + processorImpl.Apply(); } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs index ad27ae020c..af3a336a4d 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 { /// @@ -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.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..08222d4caf 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -32,8 +32,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// 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 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, + float clipLimitPercentage, + int tiles, + Image source, + Rectangle sourceRectangle) + : base(luminanceLevels, clipHistogram, clipLimitPercentage, 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,7 +67,7 @@ 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); @@ -74,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization Parallel.For( 0, tileYStartPositions.Count, - new ParallelOptions { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }, + new ParallelOptions { MaxDegreeOfParallelism = this.Configuration.MaxDegreeOfParallelism }, index => { int y = tileYStartPositions[index].y; diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs index 36f798975a..3ff001c522 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 { /// @@ -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.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..24ac5ccef2 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs @@ -31,8 +31,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// 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 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, + float clipLimitPercentage, + int tiles, + Image source, + Rectangle sourceRectangle) + : base(luminanceLevels, clipHistogram, clipLimitPercentage, 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); } diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs index 9af2c8352b..dab101fcc2 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 { /// @@ -20,12 +22,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { return new GlobalHistogramEqualizationProcessor( this.LuminanceLevels, this.ClipHistogram, - this.ClipLimitPercentage); + this.ClipLimitPercentage, + 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..6ae6882479 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -32,15 +32,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// 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 source for the current processor instance. + /// The source area to process for the current processor instance. + public GlobalHistogramEqualizationProcessor( + int luminanceLevels, + bool clipHistogram, + float clipLimitPercentage, + Image source, + Rectangle sourceRectangle) + : base(luminanceLevels, clipHistogram, clipLimitPercentage, 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()); @@ -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/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs index b1d12f8478..01a687ac5c 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 { @@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public float ClipLimitPercentage { get; } /// - public abstract IImageProcessor CreatePixelSpecificProcessor() + public abstract IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; /// @@ -89,4 +90,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization 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..f8515ece6f 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 { @@ -26,7 +27,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// 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 source for the current processor instance. + /// The source area to process for the current processor instance. + protected HistogramEqualizationProcessor( + int luminanceLevels, + bool clipHistogram, + float clipLimitPercentage, + Image source, + Rectangle sourceRectangle) + : base(source, sourceRectangle) { Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels)); Guard.MustBeGreaterThan(clipLimitPercentage, 0F, nameof(clipLimitPercentage)); @@ -136,4 +145,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..2459b47069 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs @@ -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..756e8647ba 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs @@ -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..8569410d22 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs @@ -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/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/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index 713f042653..6e669e7779 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; @@ -43,10 +43,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Size TargetDimensions { get; } /// - public virtual IImageProcessor CreatePixelSpecificProcessor() + public virtual IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new AffineTransformProcessor(this); + return 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..7c50c04f3a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -20,7 +20,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class AffineTransformProcessor : TransformProcessor where TPixel : struct, IPixel { - public AffineTransformProcessor(AffineTransformProcessor 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 AffineTransformProcessor(AffineTransformProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.Definition = definition; } @@ -32,22 +39,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private Matrix3x2 TransformMatrix => this.Definition.TransformMatrix; /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + protected override Image CreateDestination() { // 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())); + IEnumerable> frames = this.Source.Frames.Select, ImageFrame>( + x => new ImageFrame(this.Configuration, this.TargetDimensions, x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); + return new Image(this.Configuration, this.Source.Metadata.DeepClone(), frames); } /// - 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)) @@ -63,13 +66,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Convert from screen to world space. Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 matrix); - var sampler = this.Definition.Sampler; + IResampler sampler = this.Definition.Sampler; if (sampler is NearestNeighborResampler) { ParallelHelper.IterateRows( targetBounds, - configuration, + this.Configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) @@ -79,7 +82,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 (this.SourceRectangle.Contains(point.X, point.Y)) { destRow[x] = source[point.X, point.Y]; } @@ -90,19 +93,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), sampler); + var kernel = new TransformKernelMap(this.Configuration, source.Size(), destination.Size(), sampler); try { ParallelHelper.IterateRowsWithTempBuffer( targetBounds, - configuration, + this.Configuration, (rows, vectorBuffer) => { Span vectorSpan = vectorBuffer.Span; for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); + PixelOperations.Instance.ToVector4(this.Configuration, targetRowSpan, vectorSpan); ref float ySpanRef = ref kernel.GetYStartReference(y); ref float xSpanRef = ref kernel.GetXStartReference(y); @@ -121,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } PixelOperations.Instance.FromVector4Destructive( - configuration, + this.Configuration, vectorSpan, targetRowSpan); } 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..e68a24b727 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).Apply(this.Source, this.SourceRectangle); break; case OrientationMode.BottomRight: - new RotateProcessor((int)RotateMode.Rotate180, size).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateMode.Rotate180, size).Apply(this.Source, this.SourceRectangle); break; case OrientationMode.BottomLeft: - new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle); + new FlipProcessor(FlipMode.Vertical).Apply(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).Apply(this.Source, this.SourceRectangle); + new FlipProcessor(FlipMode.Horizontal).Apply(this.Source, this.SourceRectangle); break; case OrientationMode.RightTop: - new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateMode.Rotate90, size).Apply(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).Apply(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate270, size).Apply(this.Source, this.SourceRectangle); break; case OrientationMode.LeftBottom: - new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateMode.Rotate270, size).Apply(this.Source, this.SourceRectangle); break; case OrientationMode.Unknown: @@ -61,10 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - 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 +110,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..6105330df3 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.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; @@ -32,10 +32,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Rectangle CropRectangle { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new CropProcessor(this); + return 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..539a11f028 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -25,7 +25,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Initializes a new instance of the class. /// /// The . - public CropProcessor(CropProcessor definition) + /// 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.definition = definition; } @@ -33,25 +36,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private Rectangle CropRectangle => this.definition.CropRectangle; /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + protected override Image CreateDestination() { // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select, ImageFrame>( + IEnumerable> frames = this.Source.Frames.Select, ImageFrame>( x => new ImageFrame( - source.GetConfiguration(), + this.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); + return new Image(this.Source.GetConfiguration(), this.Source.Metadata.DeepClone(), frames); } /// - 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) + 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()); @@ -61,7 +64,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Rectangle rect = this.CropRectangle; // Copying is cheap, we should process more pixels per task: - ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); + ParallelExecutionSettings parallelSettings = this.Configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); ParallelHelper.IterateRows( rect, @@ -77,4 +80,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms }); } } -} \ 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..6129e69ef4 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs @@ -22,38 +22,41 @@ 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; // 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).Apply(temp, this.SourceRectangle); // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.definition.Threshold).Apply(temp, sourceRectangle, configuration); + new BinaryThresholdProcessor(this.definition.Threshold).Apply(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()).Apply(this.Source, this.SourceRectangle); } /// - 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..9374af476b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs @@ -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..15a6e2d095 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; @@ -43,10 +43,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Size TargetDimensions { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel { - return new ProjectiveTransformProcessor(this); + return 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..29dc8a070d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -22,7 +22,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { private readonly ProjectiveTransformProcessor definition; - public ProjectiveTransformProcessor(ProjectiveTransformProcessor 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 ProjectiveTransformProcessor(ProjectiveTransformProcessor definition, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.definition = definition; } @@ -30,26 +37,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private Size TargetDimensions => this.definition.TargetDimensions; /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + protected override Image CreateDestination() { // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select, ImageFrame>( + IEnumerable> frames = this.Source.Frames.Select, ImageFrame>( x => new ImageFrame( - source.GetConfiguration(), + this.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); + return new Image(this.Source.GetConfiguration(), this.Source.Metadata.DeepClone(), frames); } /// - 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; @@ -73,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { ParallelHelper.IterateRows( targetBounds, - configuration, + this.Configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) @@ -86,7 +89,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 (this.SourceRectangle.Contains(px, py)) { destRow[x] = source[px, py]; } @@ -97,19 +100,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), sampler); + var kernel = new TransformKernelMap(this.Configuration, source.Size(), destination.Size(), sampler); try { ParallelHelper.IterateRowsWithTempBuffer( targetBounds, - configuration, + this.Configuration, (rows, vectorBuffer) => { Span vectorSpan = vectorBuffer.Span; for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); + PixelOperations.Instance.ToVector4(this.Configuration, targetRowSpan, vectorSpan); ref float ySpanRef = ref kernel.GetYStartReference(y); ref float xSpanRef = ref kernel.GetXStartReference(y); @@ -128,7 +131,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } PixelOperations.Instance.FromVector4Destructive( - configuration, + this.Configuration, vectorSpan, targetRowSpan); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 8762d6b263..cf27de5eb1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -130,10 +130,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public bool Compand { get; } /// - public IImageProcessor CreatePixelSpecificProcessor() + public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel - { - return new ResizeProcessor(this); - } + => 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..2033fd38c0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -30,7 +30,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ResizeProcessor parameterSource; - public ResizeProcessor(ResizeProcessor parameterSource) + public ResizeProcessor(ResizeProcessor parameterSource, Image source, Rectangle sourceRectangle) + : base(source, sourceRectangle) { this.parameterSource = parameterSource; } @@ -64,31 +65,36 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// 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); + /// The result returned from . + internal Image AotCreateDestination() + => this.CreateDestination(); /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + protected override Image CreateDestination() { + Image source = this.Source; + Configuration configuration = this.Configuration; + // 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(), + configuration, 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); + return new Image(configuration, source.Metadata.DeepClone(), frames); } /// - protected override void BeforeImageApply(Image source, Image destination, Rectangle sourceRectangle) + protected override void BeforeImageApply(Image destination) { if (!(this.Sampler 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( @@ -106,8 +112,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - 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) { @@ -180,9 +189,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } } - protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + protected override void AfterImageApply(Image destination) { - base.AfterImageApply(source, destination, sourceRectangle); + base.AfterImageApply(destination); // TODO: An exception in the processing chain can leave these buffers undisposed. We should consider making image processors IDisposable! this.horizontalKernelMap?.Dispose(); diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index ef0671d20d..10d6cdc943 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; @@ -47,9 +47,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public float Degrees { get; } /// - public override IImageProcessor CreatePixelSpecificProcessor() + public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) { - return new RotateProcessor(this); + return 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..1ed4c362c5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs @@ -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/TransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs index f6d3299fcd..ee501adf31 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,18 @@ 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) + protected override void AfterImageApply(Image destination) => TransformProcessorHelpers.UpdateDimensionalMetadata(destination); } -} \ No newline at end of file +} 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/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index 3796c51a3b..a23de2b681 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -56,17 +56,20 @@ 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); + var processor = (BokehBlurProcessor)definition.CreatePixelSpecificProcessor(image, image.Bounds()); + Assert.Equal(components.Count, processor.Kernels.Count); + foreach ((Complex64[] a, Complex64[] b) in components.Zip(processor.Kernels, (a, b) => (a, b))) { - 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); + 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 +125,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/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 91943d9a6a..95f7f81fda 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -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++) { From 02d1f1b2504bd40213788e7e9dffae0603654115 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 27 Aug 2019 09:58:16 +1000 Subject: [PATCH 10/87] Implement IDisposable in IImageProcessor instances. (#990) * Implement IDisposable and ensure inheritance calls base * add ImageProcessingContextTests and move some other test classes * loosen up tests and leave TODO notes --- ...> DefaultImageProcessorContext{TPixel}.cs} | 19 +- ...InternalImageProcessingContext{TPixel}.cs} | 0 ...or.cs => CloningImageProcessor{TPixel}.cs} | 21 +++ .../Convolution/BoxBlurProcessor{TPixel}.cs | 9 +- .../EdgeDetector2DProcessor{TPixel}.cs | 9 +- .../EdgeDetectorCompassProcessor{TPixel}.cs | 12 +- .../EdgeDetectorProcessor{TPixel}.cs | 9 +- .../GaussianBlurProcessor{TPixel}.cs | 9 +- .../GaussianSharpenProcessor{TPixel}.cs | 7 +- .../PaletteDitherProcessor{TPixel}.cs | 4 +- .../Filters/LomographProcessor{TPixel}.cs | 5 +- .../Filters/PolaroidProcessor{TPixel}.cs | 1 + ...r.cs => ICloningImageProcessor{TPixel}.cs} | 0 .../Processors/IImageProcessor{TPixel}.cs | 9 +- .../Processors/ImageProcessorExtensions.cs | 9 +- ...Processor.cs => ImageProcessor{TPixel}.cs} | 19 ++ .../Transforms/AutoOrientProcessor{TPixel}.cs | 2 + .../EntropyCropProcessor{TPixel}.cs | 3 + .../Resize/ResizeProcessor{TPixel}.cs | 30 ++- .../Transforms/TransformProcessor.cs | 5 +- .../Drawing/Paths/DrawPathCollection.cs | 1 + .../Drawing/Paths/FillPath.cs | 1 + .../Drawing/Paths/FillPathCollection.cs | 1 + .../Drawing/Paths/FillPolygon.cs | 1 + .../Drawing/Paths/FillRectangle.cs | 2 + .../ImageSharp.Tests/Drawing/Text/DrawText.cs | 1 + .../BaseImageOperationsExtensionTest.cs | 3 +- .../FakeImageOperationsProvider.cs | 9 +- .../Processing/Filters/LomographTest.cs | 2 + .../{ => Processing}/ImageOperationTests.cs | 2 +- .../Processing/ImageProcessingContextTests.cs | 171 ++++++++++++++++++ .../Processors/Convolution/BokehBlurTest.cs | 18 +- 32 files changed, 344 insertions(+), 50 deletions(-) rename src/ImageSharp/Processing/{DefaultImageProcessorContext.cs => DefaultImageProcessorContext{TPixel}.cs} (76%) rename src/ImageSharp/Processing/{IInternalImageProcessingContext.cs => IInternalImageProcessingContext{TPixel}.cs} (100%) rename src/ImageSharp/Processing/Processors/{CloningImageProcessor.cs => CloningImageProcessor{TPixel}.cs} (90%) rename src/ImageSharp/Processing/Processors/{ICloningImageProcessor.cs => ICloningImageProcessor{TPixel}.cs} (100%) rename src/ImageSharp/Processing/Processors/{ImageProcessor.cs => ImageProcessor{TPixel}.cs} (89%) rename tests/ImageSharp.Tests/{ => Processing}/BaseImageOperationsExtensionTest.cs (97%) rename tests/ImageSharp.Tests/{ => Processing}/FakeImageOperationsProvider.cs (96%) rename tests/ImageSharp.Tests/{ => Processing}/ImageOperationTests.cs (99%) create mode 100644 tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs similarity index 76% rename from src/ImageSharp/Processing/DefaultImageProcessorContext.cs rename to src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs index 7b6fbebd12..a6e3dc03a0 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; @@ -67,16 +67,25 @@ namespace SixLabors.ImageSharp.Processing // 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.CreatePixelSpecificProcessor(this.source, rectangle) is ICloningImageProcessor cloningImageProcessor) + using (IImageProcessor specificProcessor = processor.CreatePixelSpecificProcessor(this.source, rectangle)) { - this.destination = cloningImageProcessor.CloneAndApply(); - return this; + // TODO: if 'specificProcessor' is not an ICloningImageProcessor we are unnecessarily disposing and recreating it. + // This should be solved in a future refactor. + if (specificProcessor is ICloningImageProcessor cloningImageProcessor) + { + this.destination = cloningImageProcessor.CloneAndApply(); + return this; + } } this.destination = this.source.Clone(); } - processor.CreatePixelSpecificProcessor(this.destination, rectangle).Apply(); + using (IImageProcessor specificProcessor = processor.CreatePixelSpecificProcessor(this.destination, rectangle)) + { + specificProcessor.Apply(); + } + return this; } 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/Processors/CloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs similarity index 90% rename from src/ImageSharp/Processing/Processors/CloningImageProcessor.cs rename to src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs index 518fdf0cb1..8b3e1eb965 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs @@ -15,6 +15,8 @@ namespace SixLabors.ImageSharp.Processing.Processors internal abstract class CloningImageProcessor : ICloningImageProcessor where TPixel : struct, IPixel { + private bool isDisposed; + /// /// Initializes a new instance of the class. /// @@ -54,6 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Processors throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames."); } + Configuration configuration = this.Source.GetConfiguration(); this.BeforeImageApply(clone); for (int i = 0; i < this.Source.Frames.Count; i++) @@ -97,6 +100,12 @@ namespace SixLabors.ImageSharp.Processing.Processors } } + /// + public void Dispose() + { + this.Dispose(true); + } + /// /// Generates a deep clone of the source image that operations should be applied to. /// @@ -144,5 +153,17 @@ namespace SixLabors.ImageSharp.Processing.Processors 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) + { + if (!this.isDisposed) + { + this.isDisposed = true; + } + } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs index b9ca2df0b5..77110e642d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs @@ -42,8 +42,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public DenseMatrix KernelY { get; } /// - protected override void OnFrameApply(ImageFrame source) => - new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle).Apply(source); + 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. diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs index 26a789cfc7..7b070f99a3 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs @@ -56,10 +56,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { new GrayscaleBt709Processor(1F).Apply(this.Source, this.SourceRectangle); } + + base.BeforeImageApply(); } /// protected override void OnFrameApply(ImageFrame source) - => new Convolution2DProcessor(this.KernelX, this.KernelY, true, this.Source, this.SourceRectangle).Apply(source); + { + using (var processor = new Convolution2DProcessor(this.KernelX, this.KernelY, true, this.Source, this.SourceRectangle)) + { + processor.Apply(source); + } + } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index 543014f6f1..b7119ef44b 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -47,6 +47,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { new GrayscaleBt709Processor(1F).Apply(this.Source, this.SourceRectangle); } + + base.BeforeImageApply(); } /// @@ -68,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, this.Source, this.SourceRectangle).Apply(source); + using (var processor = new ConvolutionProcessor(kernels[0], true, this.Source, this.SourceRectangle)) + { + processor.Apply(source); + } if (kernels.Length == 1) { @@ -97,7 +102,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { using (ImageFrame pass = cleanCopy.Clone()) { - new ConvolutionProcessor(kernels[i], true, this.Source, this.SourceRectangle).Apply(pass); + using (var processor = new ConvolutionProcessor(kernels[i], true, this.Source, this.SourceRectangle)) + { + processor.Apply(pass); + } Buffer2D passPixels = pass.PixelBuffer; Buffer2D targetPixels = source.PixelBuffer; diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs index 7ee9d68293..fc762cf1bb 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs @@ -43,10 +43,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { new GrayscaleBt709Processor(1F).Apply(this.Source, this.SourceRectangle); } + + base.BeforeImageApply(); } /// protected override void OnFrameApply(ImageFrame source) - => new ConvolutionProcessor(this.KernelXY, true, this.Source, this.SourceRectangle).Apply(source); + { + using (var processor = new ConvolutionProcessor(this.KernelXY, true, this.Source, this.SourceRectangle)) + { + processor.Apply(source); + } + } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs index da7924d031..bbf36ea5e8 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs @@ -39,7 +39,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public DenseMatrix KernelY { get; } /// - protected override void OnFrameApply(ImageFrame source) => - new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle).Apply(source); + protected override void OnFrameApply(ImageFrame source) + { + using (var processor = new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle)) + { + processor.Apply(source); + } + } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs index 0d99ceb9c0..dab55b2328 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs @@ -40,6 +40,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source) - => new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle).Apply(source); + { + using (var processor = new Convolution2PassProcessor(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle)) + { + processor.Apply(source); + } + } } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs index 291e6ac0ec..9f817267fe 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs @@ -44,8 +44,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// protected override void BeforeFrameApply(ImageFrame source) { - base.BeforeFrameApply(source); - // Lazy init palette: if (this.palette is null) { @@ -64,6 +62,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering (Span)this.paletteVector, PixelConversionModifiers.Scale); } + + base.BeforeFrameApply(source); } /// diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs index 52f0b37678..ab832a2759 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs @@ -27,9 +27,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - protected override void AfterFrameApply(ImageFrame source) + protected override void AfterImageApply() { - new VignetteProcessor(VeryDarkGreen).CreatePixelSpecificProcessor(this.Source, this.SourceRectangle).Apply(); + new VignetteProcessor(VeryDarkGreen).Apply(this.Source, this.SourceRectangle); + base.AfterImageApply(); } } } diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs index 5aad1005bb..0be5bbb0de 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs @@ -33,6 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters { new VignetteProcessor(VeryDarkOrange).Apply(this.Source, this.SourceRectangle); new GlowProcessor(LightOrange, this.Source.Width / 4F).Apply(this.Source, this.SourceRectangle); + base.AfterImageApply(); } } } diff --git a/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs rename to src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs index e2bc1b0ffb..3d6e0d765e 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -10,16 +11,16 @@ 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 . + /// Applies the process to the specified portion of the specified . /// - /// + /// /// The target is null. /// - /// + /// /// The target doesn't fit the dimension of the image. /// void Apply(); diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs index 9eb10b6e43..53eedfd207 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -10,8 +10,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { public static void Apply(this IImageProcessor processor, Image source, Rectangle sourceRectangle) { - var visitor = new ApplyVisitor(processor, sourceRectangle); - source.AcceptVisitor(visitor); + source.AcceptVisitor(new ApplyVisitor(processor, sourceRectangle)); } private class ApplyVisitor : IImageVisitor @@ -29,8 +28,10 @@ namespace SixLabors.ImageSharp.Processing.Processors public void Visit(Image image) where TPixel : struct, IPixel { - IImageProcessor processorImpl = this.processor.CreatePixelSpecificProcessor(image, this.sourceRectangle); - processorImpl.Apply(); + using (IImageProcessor processorImpl = this.processor.CreatePixelSpecificProcessor(image, this.sourceRectangle)) + { + processorImpl.Apply(); + } } } } diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor.cs b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs similarity index 89% rename from src/ImageSharp/Processing/Processors/ImageProcessor.cs rename to src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs index 39f3dd6360..8ac8cd67bc 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs @@ -15,6 +15,8 @@ namespace SixLabors.ImageSharp.Processing.Processors internal abstract class ImageProcessor : IImageProcessor where TPixel : struct, IPixel { + private bool isDisposed; + /// /// Initializes a new instance of the class. /// @@ -92,6 +94,11 @@ namespace SixLabors.ImageSharp.Processing.Processors } } + /// + public virtual void Dispose() + { + } + /// /// This method is called before the process is applied to prepare the processor. /// @@ -128,5 +135,17 @@ namespace SixLabors.ImageSharp.Processing.Processors 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) + { + if (!this.isDisposed) + { + this.isDisposed = true; + } + } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs index e68a24b727..a5170c96a1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs @@ -68,6 +68,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms default: break; } + + base.BeforeImageApply(); } /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs index 6129e69ef4..b74fbb0ab7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs @@ -35,6 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { 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 (var temp = new Image(this.Configuration, this.Source.Metadata.DeepClone(), new[] { this.Source.Frames.RootFrame.Clone() })) { @@ -51,6 +52,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } new CropProcessor(rectangle, this.Source.Size()).Apply(this.Source, this.SourceRectangle); + + base.BeforeImageApply(); } /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 2033fd38c0..e16d4801ec 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -24,12 +24,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class ResizeProcessor : TransformProcessor where TPixel : struct, IPixel { + private readonly ResizeProcessor parameterSource; + private bool isDisposed; + // 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, Image source, Rectangle sourceRectangle) : base(source, sourceRectangle) { @@ -109,6 +110,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms sourceRectangle.Height, memoryAllocator); } + + base.BeforeImageApply(destination); } /// @@ -189,15 +192,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } } - protected override void AfterImageApply(Image destination) + /// + protected override void Dispose(bool disposing) { - base.AfterImageApply(destination); + 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/TransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs index ee501adf31..1f0c6ebc86 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs @@ -25,6 +25,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// protected override void AfterImageApply(Image destination) - => TransformProcessorHelpers.UpdateDimensionalMetadata(destination); + { + TransformProcessorHelpers.UpdateDimensionalMetadata(destination); + base.AfterImageApply(destination); + } } } 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/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/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..589d595275 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs @@ -0,0 +1,171 @@ +// 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> regularProcessorImpl; + + private readonly Mock> cloningProcessorImpl; + + private static readonly Rectangle Bounds = new Rectangle(3, 3, 5, 5); + + public ImageProcessingContextTests() + { + this.processorDefinition = 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.MutateApply(useBounds)); + } + else + { + this.MutateApply(useBounds); + } + + this.regularProcessorImpl.Verify(p => p.Apply(), 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.CloneApply(useBounds)); + } + else + { + this.CloneApply(useBounds); + } + + // TODO: This should be Times.Once(). See comments in DefaultImageProcessingContext.ApplyProcessor() + this.regularProcessorImpl.Verify(p => p.Apply(), Times.AtLeast(1)); + this.regularProcessorImpl.Verify(p => p.Dispose(), Times.AtLeast(1)); + } + + [Theory] + [MemberData(nameof(ProcessorTestData))] + public void Mutate_CloningProcessor(bool throwException, bool useBounds) + { + this.SetupCloningProcessor(throwException); + + if (throwException) + { + Assert.Throws(() => this.MutateApply(useBounds)); + } + else + { + this.MutateApply(useBounds); + } + + this.cloningProcessorImpl.Verify(p => p.Apply(), 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.CloneApply(useBounds)); + } + else + { + this.CloneApply(useBounds); + } + + this.cloningProcessorImpl.Verify(p => p.CloneAndApply(), Times.Once()); + this.cloningProcessorImpl.Verify(p => p.Dispose(), Times.Once()); + } + + private void MutateApply(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 CloneApply(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 SetupRegularProcessor(bool throwsException) + { + if (throwsException) + { + this.regularProcessorImpl.Setup(p => p.Apply()).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.Apply()).Throws(new ImageProcessingException("Test")); + this.cloningProcessorImpl.Setup(p => p.CloneAndApply()).Throws(new ImageProcessingException("Test")); + } + + this.processorDefinition + .Setup(p => p.CreatePixelSpecificProcessor(It.IsAny>(), It.IsAny())) + .Returns(this.cloningProcessorImpl.Object); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index a23de2b681..296b8d1d7f 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -59,16 +59,18 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution using (var image = new Image(1, 1)) { var definition = new BokehBlurProcessor(10, BokehBlurProcessor.DefaultComponents, BokehBlurProcessor.DefaultGamma); - var processor = (BokehBlurProcessor)definition.CreatePixelSpecificProcessor(image, image.Bounds()); - Assert.Equal(components.Count, processor.Kernels.Count); - foreach ((Complex64[] a, Complex64[] b) in components.Zip(processor.Kernels, (a, b) => (a, b))) + using (var processor = (BokehBlurProcessor)definition.CreatePixelSpecificProcessor(image, image.Bounds())) { - Span spanA = a.AsSpan(), spanB = b.AsSpan(); - Assert.Equal(spanA.Length, spanB.Length); - for (int i = 0; i < spanA.Length; i++) + Assert.Equal(components.Count, processor.Kernels.Count); + foreach ((Complex64[] a, Complex64[] b) in components.Zip(processor.Kernels, (a, b) => (a, b))) { - 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); + 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); + } } } } From 9f93b09a111401982b0f6bffb08b670b4cddace3 Mon Sep 17 00:00:00 2001 From: Anton Firsov Date: Tue, 27 Aug 2019 23:38:07 +0200 Subject: [PATCH 11/87] Patch MemoryOwnerExtensions to fix #987 --- src/ImageSharp/Memory/MemoryOwnerExtensions.cs | 1 - 1 file changed, 1 deletion(-) 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; From a79c203b48feb33174b1df559f8d3c074cac259e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 30 Aug 2019 01:28:18 +0200 Subject: [PATCH 12/87] Fixed XML docs for RgbaVector pixel type (#992) --- src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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. From 085e390f3980f50ffc63b136d08c9c2d6c580d72 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 1 Sep 2019 20:43:49 +1000 Subject: [PATCH 13/87] Removed submodule --- .gitmodules | 3 --- standards | 1 - 2 files changed, 4 deletions(-) delete mode 160000 standards diff --git a/.gitmodules b/.gitmodules index 37ef701cdf..e7972649f4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,6 +2,3 @@ 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 diff --git a/standards b/standards deleted file mode 160000 index 8b085c0ec4..0000000000 --- a/standards +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8b085c0ec4fb64797b9965741f7138f8f66a6b44 From 2ba319415145e9c718e779182fb6cfad5ab28483 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 1 Sep 2019 23:21:54 +1000 Subject: [PATCH 14/87] Update dependencies and submodule --- .editorconfig | 13 +- .gitmodules | 3 + Directory.Build.props | 2 +- Directory.Build.targets | 8 +- ImageSharp.sln | 1 - shared-infrastructure | 1 + src/Directory.Build.props | 4 +- .../ImageSharp.Drawing.csproj | 11 +- src/ImageSharp/Common/Helpers/DebugGuard.cs | 178 +---------- src/ImageSharp/Common/Helpers/Guard.cs | 294 ------------------ src/ImageSharp/ImageSharp.csproj | 13 + src/ImageSharp/Memory/BufferArea{T}.cs | 14 +- stylecop.json | 16 - 13 files changed, 52 insertions(+), 506 deletions(-) create mode 160000 shared-infrastructure delete mode 100644 src/ImageSharp/Common/Helpers/Guard.cs delete mode 100644 stylecop.json 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/.gitmodules b/.gitmodules index e7972649f4..55389121f2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = tests/Images/External url = https://github.com/SixLabors/Imagesharp.Tests.Images.git branch = master +[submodule "shared-infrastructure"] + path = shared-infrastructure + url = https://github.com/SixLabors/SharedInfrastructure 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..1551a29d8c 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -28,10 +28,10 @@ - - - - + + + + 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/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/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/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/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/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 From 43d8241641d8c8e5063728ec4dd5edbfd70e882b Mon Sep 17 00:00:00 2001 From: Xavier Cho Date: Mon, 2 Sep 2019 05:41:17 +0900 Subject: [PATCH 15/87] Implement gradient brush similar to PathGradientBrush (#969) (#989) * Implement gradient brush similar to PathGradientBrush (#969) A gradient brush implementation that works similar to System.Drawing.Drawing2D.PathGradientBrush. This fixes #969, but only convex paths are supported for now. * Update submodule to add test fixtures for #989 * Performance optimization Use LengthSquared() instead of Length() when it's possible. Avoid unnecessary ordering of elements. * Avoid using LINQ in a hotspot * Validate arguments for the public constructor --- .../Processing/PathGradientBrush.cs | 286 ++++++++++++++++++ .../Drawing/FillPathGradientBrushTests.cs | 209 +++++++++++++ tests/Images/External | 2 +- 3 files changed, 496 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp.Drawing/Processing/PathGradientBrush.cs create mode 100644 tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs diff --git a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs new file mode 100644 index 0000000000..fc84e4a1fa --- /dev/null +++ b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs @@ -0,0 +1,286 @@ +// 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 Polygon path; + + private readonly IList edges; + + private readonly Color centerColor; + + /// + /// Initializes a new instance of the class. + /// + /// Line segments of 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(ILineSegment[] lines, Color[] colors, Color centerColor) + { + if (lines == null) + { + throw new ArgumentNullException(nameof(lines)); + } + + if (lines.Length < 3) + { + throw new ArgumentOutOfRangeException( + nameof(lines), + "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."); + } + + this.path = new Polygon(lines); + this.centerColor = centerColor; + + Color ColorAt(int index) => colors[index % colors.Length]; + + this.edges = this.path.LineSegments.Select(s => new Path(s)) + .Select((path, i) => new Edge(path, ColorAt(i), ColorAt(i + 1))).ToList(); + } + + /// + /// Initializes a new instance of the class. + /// + /// Line segments of a polygon that represents the gradient area. + /// Array of colors that correspond to each point in the polygon. + public PathGradientBrush(ILineSegment[] lines, Color[] colors) + : this(lines, colors, CalculateCenterColor(colors)) + { + } + + /// + public BrushApplicator CreateApplicator( + ImageFrame source, + RectangleF region, + GraphicsOptions options) + where TPixel : struct, IPixel + { + return new PathGradientBrushApplicator(source, this.path, 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 Path path; + + 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. + /// A polygon that represents the gradient area. + /// Edges of the polygon. + /// Color at the center of the gradient area to which the other colors converge. + /// The options. + public PathGradientBrushApplicator( + ImageFrame source, + Path path, + IList edges, + Color centerColor, + GraphicsOptions options) + : base(source, options) + { + this.path = path; + this.edges = edges; + + PointF[] points = path.LineSegments.Select(s => s.EndPoint).ToArray(); + + this.center = points.Aggregate((p1, p2) => p1 + p2) / points.Length; + 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(); + } + + if (!this.path.Contains(point)) + { + return Color.Transparent.ToPixel(); + } + + Vector2 direction = Vector2.Normalize(point - this.center); + + PointF end = point + (PointF)(direction * this.maxDistance); + + (Edge edge, Intersection? info) = this.FindIntersection(point, end); + + 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/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs new file mode 100644 index 0000000000..d76893108b --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs @@ -0,0 +1,209 @@ +// 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 SixLabors.Shapes; + +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 => + { + ILineSegment[] path = + { + new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), + new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), + new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), + new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) + }; + + Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; + + var brush = new PathGradientBrush(path, 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 => + { + ILineSegment[] path = + { + new LinearLineSegment(new PointF(5, 0), new PointF(10, 10)), + new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), + new LinearLineSegment(new PointF(0, 10), new PointF(5, 0)) + }; + + Color[] colors = { Color.Red, Color.Green, Color.Blue }; + + var brush = new PathGradientBrush(path, 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()) + { + ILineSegment[] path = + { + new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), + new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), + new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), + new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) + }; + + Color[] colors = { Color.Red }; + + var brush = new PathGradientBrush(path, 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 => + { + ILineSegment[] path = + { + new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), + new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), + new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), + new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) + }; + + Color[] colors = { Color.Red, Color.Yellow }; + + var brush = new PathGradientBrush(path, 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 => + { + ILineSegment[] path = + { + new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), + new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), + new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), + new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) + }; + + Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; + + var brush = new PathGradientBrush(path, 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 ShouldThrowArgumentOutOfRangeExceptionWhenLessThan3LinesAreGiven() + { + ILineSegment[] path = + { + new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), + new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)) + }; + + Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; + + PathGradientBrush Create() => new PathGradientBrush(path, colors, Color.White); + + Assert.Throws(Create); + } + + [Fact] + public void ShouldThrowArgumentNullExceptionWhenColorsAreNull() + { + ILineSegment[] path = + { + new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), + new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), + new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), + new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) + }; + + PathGradientBrush Create() => new PathGradientBrush(path, null, Color.White); + + Assert.Throws(Create); + } + + [Fact] + public void ShouldThrowArgumentOutOfRangeExceptionWhenEmptyColorArrayIsGiven() + { + ILineSegment[] path = + { + new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), + new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), + new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), + new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) + }; + + var colors = new Color[0]; + + PathGradientBrush Create() => new PathGradientBrush(path, colors, Color.White); + + Assert.Throws(Create); + } + } +} diff --git a/tests/Images/External b/tests/Images/External index 36f39bc624..99a2bc523c 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 36f39bc624f8a49caf512077bf70cab30c2e5fb4 +Subproject commit 99a2bc523cd4eb00e37af20d1b2088fa11564c57 From 9ff4af6b0cf6f449b046be803c7c2f04cfd14d79 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 1 Sep 2019 22:54:13 +0200 Subject: [PATCH 16/87] debug save the output for Issue984 --- .../Processing/Normalization/HistogramEqualizationTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index 3610f9683d..c712325249 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -123,7 +123,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization [Theory] [WithTestPatternImages(110, 110, PixelTypes.Rgba32)] [WithTestPatternImages(170, 170, PixelTypes.Rgba32)] - public void Issue984_DoesNotThrowException(TestImageProvider provider) + public void Issue984(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -135,8 +135,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization ClipHistogram = true, NumberOfTiles = 10 }; - System.Exception ex = Record.Exception(() => image.Mutate(x => x.HistogramEqualization(options))); - Assert.Null(ex); + image.Mutate(x => x.HistogramEqualization(options)); + image.DebugSave(provider); } } } From ba3bd56c2cb191c5d9ea3add3a522e2443355942 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 1 Sep 2019 23:11:12 +0200 Subject: [PATCH 17/87] delete GuardTests --- tests/ImageSharp.Tests/Helpers/GuardTests.cs | 274 ------------------- 1 file changed, 274 deletions(-) delete mode 100644 tests/ImageSharp.Tests/Helpers/GuardTests.cs 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 From 5056744ebbb948568811c7b664ac9ff1f8b568af Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 7 Sep 2019 12:04:51 +1000 Subject: [PATCH 18/87] Fix #997 --- .../Implementation/Converters/HslAndRgbConverter.cs | 4 ++-- .../Colorspaces/Conversion/RgbAndHslConversionTest.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) 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/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 From 0cc178d662ce81d172153f7613715f373e8a4014 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 8 Sep 2019 13:49:47 +1000 Subject: [PATCH 19/87] Fix #999 and add tests --- .../Transforms/Resize/ResizeHelper.cs | 228 ++++++++---------- .../Transforms/Resize/ResizeProcessor.cs | 43 +--- .../Resize/ResizeProcessor{TPixel}.cs | 14 +- src/ImageSharp/Processing/ResizeOptions.cs | 8 +- .../Transforms/ResizeHelperTests.cs | 143 +++++++++-- .../Processing/Transforms/PadTest.cs | 4 +- .../Processing/Transforms/ResizeTests.cs | 20 +- tests/ImageSharp.Tests/xunit.runner.json | 9 +- 8 files changed, 268 insertions(+), 201 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs index ae7b112fc1..c9df1b2542 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,32 @@ 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; + + // 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)); + } + + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + switch (options.Mode) { case ResizeMode.Crop: @@ -50,9 +63,9 @@ 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); // Last case ResizeMode.Stretch: default: @@ -66,11 +79,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 +92,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 +153,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 +170,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 +192,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 +233,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 +305,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 +352,50 @@ 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)); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index cf27de5eb1..6f5f09e717 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -26,19 +26,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { Guard.NotNull(sampler, nameof(sampler)); - // Ensure size is populated across both dimensions. + // Ensure target 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; + const int Min = 1; if (width == 0 && height > 0) { - width = (int)MathF.Max(min, MathF.Round(sourceSize.Width * height / (float)sourceSize.Height)); + 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)); + height = (int)MathF.Max(Min, MathF.Round(sourceSize.Height * width / (float)sourceSize.Width)); targetRectangle.Height = height; } @@ -46,8 +46,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Guard.MustBeGreaterThan(height, 0, nameof(height)); this.Sampler = sampler; - this.Width = width; - this.Height = height; + this.TargetWidth = width; + this.TargetHeight = height; this.TargetRectangle = targetRectangle; this.Compand = compand; } @@ -62,32 +62,11 @@ 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; } @@ -112,12 +91,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. diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index e16d4801ec..b85983a481 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -45,15 +45,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Gets the target width. /// - public int Width => this.parameterSource.Width; + public int TargetWidth => this.parameterSource.TargetWidth; /// /// Gets the target height. /// - public int Height => this.parameterSource.Height; + public int TargetHeight => this.parameterSource.TargetHeight; /// - /// Gets the resize rectangle. + /// Gets the target resize rectangle. /// public Rectangle TargetRectangle => this.parameterSource.TargetRectangle; @@ -80,8 +80,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms IEnumerable> frames = source.Frames.Select, ImageFrame>( x => new ImageFrame( configuration, - this.Width, - this.Height, + this.TargetWidth, + this.TargetHeight, x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added @@ -128,8 +128,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms 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; diff --git a/src/ImageSharp/Processing/ResizeOptions.cs b/src/ImageSharp/Processing/ResizeOptions.cs index ee0dde6b27..96de1eee11 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. @@ -44,4 +42,4 @@ namespace SixLabors.ImageSharp.Processing /// public bool Compand { get; set; } = false; } -} \ No newline at end of file +} 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/xunit.runner.json b/tests/ImageSharp.Tests/xunit.runner.json index 5204242f03..d7b466d09d 100644 --- a/tests/ImageSharp.Tests/xunit.runner.json +++ b/tests/ImageSharp.Tests/xunit.runner.json @@ -1,5 +1,6 @@ { - "shadowCopy": false, - "methodDisplay": "method", - "diagnosticMessages": true -} \ No newline at end of file + "shadowCopy": false, + "methodDisplay": "method", + "diagnosticMessages": true, + "preEnumerateTheories": false +} From 4edf86ff30fbb525bdafd773a2d9ca91f83238e8 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 8 Sep 2019 17:20:45 +1000 Subject: [PATCH 20/87] revert preenumeration rule --- tests/ImageSharp.Tests/xunit.runner.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/xunit.runner.json b/tests/ImageSharp.Tests/xunit.runner.json index d7b466d09d..749ece4387 100644 --- a/tests/ImageSharp.Tests/xunit.runner.json +++ b/tests/ImageSharp.Tests/xunit.runner.json @@ -1,6 +1,5 @@ { "shadowCopy": false, "methodDisplay": "method", - "diagnosticMessages": true, - "preEnumerateTheories": false + "diagnosticMessages": true } From dbc5c76eb1e43a694b34eb616c547121be8952ea Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 8 Sep 2019 22:49:42 +1000 Subject: [PATCH 21/87] Fix #1004 --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 38 ++++++++++++++---- .../Formats/Png/Zlib/ZlibInflateStream.cs | 36 +++++++++++++---- .../Formats/Png/PngDecoderTests.cs | 3 +- tests/ImageSharp.Tests/TestImages.cs | 1 + .../Images/Input/Png/zlib-ztxt-bad-header.png | Bin 0 -> 22781 bytes 5 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 tests/Images/Input/Png/zlib-ztxt-bad-header.png diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 9bc5a5079d..a9e588f6ee 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -175,11 +175,18 @@ namespace SixLabors.ImageSharp.Formats.Png this.InitializeImage(metadata, out image); } - using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk)) + var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk); + try { - deframeStream.AllocateNewBytes(chunk.Length); + deframeStream.AllocateNewBytes(chunk.Length, true); this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetadata); } + finally + { + // If an invalid Zlib stream is discovered the decoder will throw an exception + // due to the critical nature of the data chunk. + deframeStream.Dispose(); + } break; case PngChunkType.Palette: @@ -924,7 +931,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 +998,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 +1016,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)) { - 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 +1039,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..df0e723322 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs @@ -87,13 +87,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// Adds new bytes from a frame found in the original stream /// /// blabla - public void AllocateNewBytes(int bytes) + /// 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) { - this.InitializeInflateStream(); + return this.InitializeInflateStream(isCriticalChunk); } + + return true; } /// @@ -197,7 +201,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 +219,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib this.currentDataRemaining -= 2; if (cmf == -1 || flag == -1) { - return; + return false; } if ((cmf & 0x0F) == 8) @@ -225,14 +229,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. @@ -247,6 +265,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib // Initialize the deflate Stream. this.compressedStream = new DeflateStream(this, CompressionMode.Decompress, true); + + return true; } } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 2e9fd7481e..91b1ef2c17 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 = diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index e95ce09073..163d09bdde 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -90,6 +90,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/Images/Input/Png/zlib-ztxt-bad-header.png b/tests/Images/Input/Png/zlib-ztxt-bad-header.png new file mode 100644 index 0000000000000000000000000000000000000000..c13f98fd16ed8318e88c2e550f31cca566aabdf9 GIT binary patch literal 22781 zcmeFZWmH^E(>9vm1b2524#71LAcI4K1)bm?Ai-UOd$8anKyVMi0)rC*!QCymyL>(O z`@ZKq&vX8rKi^v4IcsfLv!?gn-CbQ(-Bs6B6RxhRfc1p@$)iV)uplpAXg+#`$P7OJ zMneJr2YQL6A3Z|I@O-WN-rilo8T!sj#nH^%?$M)poj;BllDPM_!w1F50VD5Y?R^ak zt&_iPKzH&Hxpetc$wnD(cDzWkWodcp-ZUw{DhR&WZ94H;oMwJ5&OIpGekPsg9QhbQd~AzKDig+1{^lG1$KOQXjm(eH zsa?FAldanuY!EsI$Z0RIdhbGY9t0Mwt(#*N($jT#%Bm>nk8c$syA0X%!V?2Yn}XSv z**3mv&}xV!*6%1>qLXog>ya+Xm9igl$46J}MvlRwYo!T*GXn&;wlB`5g3k$l*;M7r?nIx|Tp zGUp9Z(>T9k0n7Jmy1hQM9cl{G%<6gTa4|bRBBosz|Ydtb78-{ zc>CKnHC@q1f3$zQq*KtNhSxWDj_7F-{_VnTh92j;|J%Lb(Vw&gAMNnTn1hrJV+kJP z;r`1-8Y5i|JzkA<{%n==ACJBN^b?KYnKvV!vS{R+y2yVYLIGYJG_Q*8-+LeafWW8x zV*%Ob-|zkCk4X^yFMY!M4=5*ygN~eWsUyCSM*7#wEY%A0KL?WOkC|BR7C`;NZ#xc_#P)_zrXWQmAqOzyj+b9Lnblcv1{6^ z*YL91VJrNMzmes&UwO0Nq?4C<(0b!A<#o^k+sconjuAiZwW8Fjy4^8;ctT?A?KJf^ zk`q6pVbS*{WUpaS@^;r!iXv$v`~3)Hxu3lPQ;Dr;Y;*;X((W2*i}PF&670p6xy!0OQ4}-79YD2y0_g|qlI);`tK;C z3W>CJ>2A*|{05shKj}N54{*16Z+s{-GP#?LZndl1`(cdqBBudv8GHBRv*FU~=-%hk zmeBjFO`M9hd(ScLD4csSqgEipN+2V-wpZ#~lW%Ox`rC}(ATD~J4Dt*<8^ydh5!w7q zAUJDRQ{FbJDz-;R7Rg$!W9;)`jjHjm0|VAg;Yj&O_igyQNkccRZ_u|xF%-0<3^l*_ zl+3Xbg%;cyl*RUHVP`Bxg-j_1d{NIvUM{bv7{@dRyDj>-t)>@d`-=V&m^7fGLWJDS z{z3ib4EG_u5MZOK>OYjh1YD3Dnvc4zK4kH&M)AvMArcB1bA4Y5w5t2uQOEkLb8E6L zCkLBCu=J02l7sexhGx^95hQ|r`Q^jyrc{1?G;cR)P{dsXd-=y^B_~+$=je*o>!&sY z!r)~^_WhnyI8wK`q1aS2JfhlftiSvuWiDhi`VCqu&nEPRx*tx4q!$11(!v-^E>4@! zK1w&B0dW$0Na%o(J*TCfAiSXTK5R$*^YmDO#)ohorpT}8-5mG&cDH6o{^}_q;nuW8 z24|Gm{`c@n-fbF zkyx(FgyX~78*kQhV|M_+^1pQRo^B-Y}Id2U^1#>W2F00bC&0J@kf%7`hv4Y zN5;EB(j_Y9bgMj`b|ive4p^{9`}YmKIfeUI+WR^VJU`Uo8&Wzh@WRVrgHuOGkgN?) zb|QRF$ktEowTlDwcOrRrK`?_YOZBTF) zvuIX0S6UxB8VHPYSx%hWswOOTZ3(>ZX3?}neFu0Jv54;@qURmS&PET463li+BW zbJkc~@O!v}{ubG^yY&V}tZ;zO{qFl(f>HA8eas_9SAys)w3_?f%o@ z?m_=o++;1b-iXjBVyVJ4RT zo&i39L2h0#0s8iXhY=|g>yE`UW(icejEjz!%hyz{Q@P7jE1pS5mRTMa!VLaY^{%t_ z4f4)j2{Nj_kaQ^i0n3d4;|P4mkn^0{j2>2Q_U^b4!!J%Ip0L(q>&WyXkUkqFQQVr)Bea#-pG#;{RTB*Wa{=i?qku|O7u{?{VgB7Vp2I3 z^U}zA=!vEE25(ggtWx-F>=kV6l^T^rw$FK|`Zr;;?A=(rP|a}KZk{QymiYsTQIw9U zIkYQPbT2-}F=#jHEf?mzJ83l_(5W91Kl!yK8bwX`%lLMM1j|7(xHJvAqBA`$GgWUZ z|F{R2@pS|)t+<#SyE3mV%&V)HY?-{Hnw6Ag_obZF_54ru;~}t(%CnM~BJR73CAhA4 zT(*lr(bsz@U6gBQDMDG6)k)>uX<6-(Z591K6UnoeP6d(%bsuLWG-Dn~ejRl0+zP`I zn&a^_O0&-)Lp`lJW6!`&MGf~8;*MJU!3bo3>t3+f6`-Taw^PyGV|93~6gd4@ZTDSeba!2KDt1k>Fe)J>CGi<3oZ zX0>#9hk~?zy;I@H_-8i6Zc@>m;0n{%YfKx9fY9mpHR}(#GPTnSI>`lovjzHx>II%7 zluw312*1;K2s>czIu&+3E%X+T(pDnzoTi{*au=Jt%HLz+XpZUCJ*R(6Z{Fq5*4z+= z2JfxOwFZ^dC&K&_b}%e`a(a7ObglaiAfe$ydoMjLoi(SfqJ}xMl@Ii~BydT^K0kNf zZ`_m*MS$u+JRs*>3#JJh@dN2qk_Skwqz*@hb3CmaQIS0C8v)r;q)flaXufVnQ}HWb z-hnOMG+wz`_PjE3W=QvQV%7?~5u@LmwA1N<^lf0bRmio4Y&z?DX&h_NuJ$b~xR`ke z8|AVfab=wj@(+IR<(nzxdS!7mma0R$cS}ZSiHY2?tv>8Q9ZIj*;-Tp7roE0n&{6qg zw|W3&O_GpTnS&(MbCl5Pn0-ReGV^1T$MQ%P)W=4I)#JDFQ7Ae!`R@%j&yTqmKMIbz zV7(JF!BKh-nQjXn0hMx5WY^-YhEB~x=gty8$n= z58{;(iF40tuyH!iL2SI0Y*Z% z1|i$7@0uElP(|2y=D5*NhxSlSlKr;oH=)>`wX%i<8`c&l99d$VsrV#S;XmLDQQAIn zYA&r#o~mg4k>hwP3KY6!Wk@Lc>~)8v!{s{E`d}|QLOjXh#q)k!t zJ&CSKfYrJLCsK%)T|-5IdigWC+WF9MAxbNPhfy#Q%dVjrF))P?|~zmrOu^lkPUW|JQd^-LFUTLZk~I0k|f)#WDC}K zQyF|eJs6UC+9oc37+&|M4oYlNZr}Vz1G3?p!<|@d9G{LXaecc)=n0)A54XPT{aU|i zcb)3!ASCY5-B3xAW%J?NO4Io-{7 zOBBAj_`{bC@tBVl!zjq~Lc6CMnkjFFhY*aaX zZqZ{iQ=XoWPS?Wj%CM2zb1RosV}ZnPL2eG?=Lg7>!bQ4YVVi5b%0;8D^MO$DM#)xA zzV1g)%ItF~aQLr5{E}Z5mRLU}kPnWnDYfh=2ThEK!wmzDKC=!mB)NrVkIEGj*>34M z;r4ta$8Lr$Ow4=Lhgl0Wh?x16i=Q1oimRt-7^mV0G!xR~j3>DC&%`IYEV=FHVY#+`JfgMENOo5BP&i^*3mv8sP){rg9~QXht8vTbfk{AU=Qy>53~k_%iQ_33mdLE(VHJ<` zB^7;dWoC6GF8+iS#@Z$B1wyEpX6~d*?O(p^#)2|SJc{B^AfXU(vluLt!<=EGC>ojU zQp2dnN~aDD&<-zm$y=dr#w(i7axz|r6S(P?g*81`;tva1+#d`-jB7#n>Lwiyt7S!2 zQSeYUL8wF!0(EkY7P7jV+9)Lz7b9%`cq>Gwznj{vYCQJ5kG$=Ut?YCH#F~5bAQ@uF zs1*xjrjM>0<(svqcZ5>OSwckUl=Rfqw;Eq<>MSeV0_$RS>l4=cTcrsQDe-$^T0-r> zMysNC^^Qyb>>OB<&`rT@b75MK5H`>G{d^*!TNLZ8H*J{;>%F^zJf%~o75fqhXSgqf ztCBr175!$uM~BxYE{ceCMZKa9ZSUuDBo)xMB55T1^e}=B3%1N1GsQF~T0qYqY-2GZ zs22dUU?+9ooidlF^lJmbcBt3l<&6v0Sw?`vhpYRpoK-zvQv`miz3YM#o9YFn|MElgXDk8)TT2z+;lHJUt&Bd2&`Ujp>t|#?9@{)YY=+9b{b4VX zf!x*cJwYZbVgGI^2q&-L9U8U8ZPnY`e)3vLmVy+A)5qJ1m$oq(B^5HQLxT zS|GG!pnk5dl3tR~!l28osYAG(XKluv&O7puMyw>n_GsUoVPwDk<`n<(5!-8ni-dwPuXE(s#t3CXn=HH(~(GC3A zV}grz*^HyTaA)H;vl_Y2D1cUxtMK1>K{!GJ(mA|;&3_4?! zMrh;LkN=aY0?8_#DG>Nz3OY%s{agO?g~=m$_k{57|KBx@Bh!u1JjOzoH54u ze`Ku#eCc@*xGC<2{03t0PR~7c;D-BuUjd4^q$a7a^R#SU^q{3K|Be0=BcV+6Z;5(o zJOoY6$l-4nb$rU(^L{ohXs^@!*OZdI6zmBCJPG2wi`^cGY+5iAWxm_^R581jHK|3D z1SrBA-d_|s(W~1cByiX5x4w-|foewSWL8Xcmb)Aj@6oHFTY!K zD);!5pCFVRt*1{3?28K+B0W_F4J%Xy8{yK?=r3ExzMn)08iqnN-k!A>5j3^>6n=`7 z9tRDtTf)X9@rt+~_oZB)8!mqu;9-j@trxO-J6XRo_71N|95nR1#gnrl)G^UsY0j3j zFT5jT?n)a!!&ce-asGLR6JeYMG~7vQV#wu`BQY9p&$uwnaKZ|ZYSS0;!VO1bUe(Cu zpnwLOVTes0+E@~hfr?nUa*a7iJ!lVkJ*(Q8cC@KO8w&spRGtuf!T?WvAOp*`WSct7 zM9nE1k3?oy&L)*;;(#}}pg~TkIG$cz2`-%7dgtg*P|X;9s)Lcva?^_9L3(u@&~UcJ zQm2Wu{SwF^rbWAYkWX1Luw>aD2xs@1fF6>K3}|4c`eG3wjRhA@AsT4-QaGlHS40CB&Y9j{Vj!I8a#tanx0B@qV{FOM1*uEd^p7oW7>E{B%oK%GaaR} zWatQf>znz~aKn;)O;foXZMcxf4Q)!%#v*_$%YKy9b@%gFM;mAE1J!xuSX@T0&JCB& zHg}yi()J4=1l5-DxBBL(wP=;}4Y3q5OW8shwJbxO)c7)&w`~uv*T%;%!ReF>=;U8# z)U6}>VHoJc^SjGHb2?@s-`F>gp?SJsAsIB(0#epNOajb=I~lamVBAmT8#~2Xz=s+# z(q4_oU3%7{oeG0tQy(nRSFpee(8_F#BcD4>`enGQ1_mUUrKUcwzVbWJXAbuFSI2|f znVR&m+$!+%Ij=y6qH^`2XZ<|I&_V%gKKU-qeK*G(cNn8X+`9pZ<4>m(Cd0EIr8B@v z){+%{10b6fP?J98`e8dZV(C(Bb|8;0^*{jIH~=EzM}};Z~z?q}}FB z#yozIU(NZeW}DU#qW9y68q#$JX!E;hTQEWrg@83pe9vywfSDNKrj9YFG`rlP!{!LV z@c&SPcFGIhC%Nz4@&oOCDtIRBu}-5W8MD@Y=EUrT-T3o`go&$fD0o|z@G;Kac)HrM_p0^qiu`UC-Rn^{9` zi-vC)2Iw_LhbThY7(Te2{mFqyX$7Dd?+G2xo&q=8BKh}qr0$0to<6OA&Lx@P6*G`m zAHx07+`$}to>4bLI~-Kt2+$d``gj)#Ftu3rIwK@npbJjmOaOEt-a8&T!q)sv2kD2b zbcC$i_nl(;VI26da#PM?=!dBg`a=6+rEH)zVt2WP5z-zA3{k_wBu~qPU$!P4n9j2y z%md9D;XL*%M>~}a`0h=%#2dddd=n(?IOT;@Abe_+y&e4`=P7vOw-ExHFtk%@a4Kn; zEc9f+L(`;uh6Y9svXRGMUYTiCfOz;HfOqRTf+nSB<*PGAdUZT76QaLp0(nuCh|(rK$*b(}q1S`Mt$>jDN~^Wb%S%K!B` z+OFqtr1!rKQTr^yAJ`Q@X#=PkX}fY!#k49I5s2VkEtTA?7v3!Iv6O0oT$v;Kd*xo{MYNiB;j!F|C>pMqu&bt4;SEn z$AkmE;a*O*-4nq<5_y+yN|#?e-K3_kzj)Xvpo3*$%K1wSL}GyS)I8PTT|4sfe$xRq z9PhjYfjKPu{(X-64$G)iaE^b(UN{9&S?Fr4l;kgoi#h1Uyj#Vb?qLO2S8fnuZr3*&kG1%B0Kg2v zQ=oJb_85O#7v8{cak1$407tXJ64fL{ObFHC@Y=f`Dz{ojikg|5zlGWICn1|X1B&cr zgzyAE&-<<6rEVMTH;+E~Y3k}K!4ah({`;Tyi;03j;RJ9o62!*{V#lSX-3X{OfY~N41eDMkO&5V0R2uE+wRXQU=Wr} zLO2YywK$Lj&{px%ru`q#x^Zm`zx$JH2J<|{u9=IP>^qL;%azDRA|K4QFDXW%aDnaEA3;$3ThpwsO;Cx1E7xYN@@PprGvmkS5tihGXk5oi}z>UG!ZQS|Np{ zl)vmuVla%{mqU7cO*O0%RRq!VqxckKnznr)ZM`Jdt{~HE|9%1tp3))}8ru0=7GmHs zD1aFGg^;k@=X|=bp30}6tuPo03l*60I~f#Gz^L8hxD$4pX&>5oQ|2oRFol|TeM~7Y zB1fYZ`^h5{9l>->jF&7AV@D-pes{(Sx0ka2P5iB|ehbOgenp(ttX2g}b+-EbUM zAu3{QJ-7OenGA&mL|YsMFN@bW%kTbbIoPi_hCDdqn$zV2a%vX+w__*MU)CgJsou+jewoUq8)U z>8$)W*m-QE^kw6;cJ`Gpow~k8f#@GeV@oME>`aU4{p%8w)KsBH0+*p?l6PX)TloxR zRB-2D+%W`n`|ytfO$hKg?8oQz)tWl;*!3mGO;@|FEwX>$0>SlFHVb9@3&}!^av~Is zlrD-0)TuSzyZ+uoP=U8>peAwlTIuB3LYD7mG4q?w#Yt6ILFR|L)%l|Q<4|;V>-vNP zV^Ov!_HuQAiCsKCkWTMe2MJ|#?v_){wGR5wr1AU9r)@&Y@BUP;go%-?d#=SR#2o9v z!Q`=fX_G4Fmy$hj17wX2@~YO>{5Z5Bxe#Dmm~Qi}^IaGp0mXBrRAiY`0IgzywXz<& z*G9F9zgYx4iqQ7P9%`>(SsaOHCND>fG5f(ZCF0p`|5g;(9(FfhZDhprDoU`5R;oro z3m;fN1rM}z+TY(SKEzyGrCWa`<8k&>=>gPx5JgR*akzUVnMAqUeL5ds)&BYvh zrTrG*xb)Mq67H2XYsrp1ygifz9z~5LX+LWc`=>3lsp{vP2et~I$#HRqi95%MDB?@H zAf{z9Q+Vsg$M+&960urIhGD0v_VTiCH?v%MuALP0RXmMqbFIx?T*G*B1=uVUxu&^E zAwt~^$uMj&j$NR$fBaUW#m}cq33&Mx$X@;8B;`XHm4OXY4I3tO<6oR$MLxu2wO?c& zkhdK*11gPJe>*3>g^Cg9qFMJTw%9K2IneYipXOFvzxKSnK{2-?lKW@e#Ik#8Pa3 z<^H2*cl3Gq>GrD8KE!iU{D(I6G=v6hf&|dz5Rw?9#JDjz62oUsbW&ir{GjdjmTkwmPM5GLsOqQ4z)g zN=c3O76r3d=ptvs9KP=3)p?&~NkK_zwwkxOL5$D%d|695cr7^Wx+b3o@^YnYA^}bkdILs`)*hB?Z2>wSvcd0gzd&`j`H>#1#;DmX(Ae7Ae)0d zG}g?lndc#X$g$;Y!e9MuIVZ)FjIMgcwu^jkktKgba=MQS)E;KfT-olM6c$aVGVV=$ zx*H-rdZ)rkl4LqyUx8_LtLcfnvToDA6s`8Z>iJ%Hd0;*vEW^vJmf}b*Ppm=2`gcpm zdihLlHZp+%2d0)T792C(QS2!6xi3=JBF$WJ(6B#4d_O;Pis-z^4tD4|yPl=HZ8;EJn&_B$L?NvuR&9(K{{D$lmyQiYMw=FHy znGLO_PFiahisa1hoa!CnN?(^YVh;$-!}VAvg5Osqs2oWrt`t?ZrWrtJC=oZK=_+14 zLyUyh0?LkD%Au1*^5a$56U5Ur%dBwb1|sgcONuAm#{=9_27G?EY0cbcQ~RRkm;~FW zPBlgW%R-VrN*@t%%3%@&UjXtCwM2*T&tX>yh;zz~7CXP7k8XM$2_k9`jHJR}AS3Rc zx|?pOHa4p3?M%(xjWZ2`S^cy{q_#?#NhSRhO4Din3eQy3fF$=I4;%kOBkJ^~X?x-f zkRRh#1xOrj=|pwBq+86%z6^ytZffgiz}`YoLORBPsI8KOy`KZ%3IE7SZT&n3Pd6_2Lem7jg8#; zuFEagW4aa_KSG@WP$IAKm{5CIqgmtA(I@qU)2h6^JKzDcMBH=_&BOTFx7368ZVqg{ zg9@5mCIy*ZUWcCSlS*h0BMy@czDn0&u@l_CWq$It&?5R3x8LadGWu$Q)nD|MD?{~F zx&@M>keUUCp@ZaS%Id9fx`~yuNnOusPSw&SX$=L5;+?5f9e)=Vi9yxOuoIvAE{f3U>BU(=pl$8N8y#p zvR7?s${;d#IV1QQI?~aCCa@s1-#W-IqTeVV3SmDL^>R#yYFgauPZ4s~Cr=g^W{Ex# zk~_yO@9z;W=inEd#)>p7AkE)+-c@9o+jA_Tj&MjxTk+D@++1h?af<{uigCy?B#lx~ zhAJ-1`YdTVjuADjLXt43>q!!aWbdum$>)wa#$S=P=mh2t`Oh?UgH4uE(gK^`eo1sp z@JMbvK&)*TNlf;@;7!^vQbhLzx3t3-l-i^Fg$Ua3h4+zvh5s=dyhv~GcR+~kZN?gu9)PTCo9S11+y zeIsYi;v>yE2?(Xsbr_AkHP+lN%~n2p3GzUjwjODZ`CT+E$|?GYe;#mqf`ZgvN`?Ru zRVx*Ge>NC0YHd+~pTdXgV$w^Y0nX~m<6P_w(J#@;q`?u3>;5>@2t6d*%M1sJMNZl2 zO=B_{%}sc+2n|d$l8fgY0da!r3H2vY6pC`FX$K_Q28k>D8gFi&#^cnS$nciG?2>Kc z=z+POnfx*-#l83&c@|58)LMRNgLaXSYD;G@s2$E3AZ#rVL2Bm8oX1$kLu9UhdhM%4 z8R5L$eFk92w5wR<%3ysnOw-?+fwV(B!piZgv#Ou9cz+Xh_)Bi(1m(TcFm84eJy4Z( z!coSzB*vSf!cDXLf{D5y0>SnS8TL}%(_6@RYm^8$iO=53SIr46x(@KKI@afCYNNQB z?$+(3-90tFlwTefItVy=k!$wnPZ+~-#-vDd%+gJ}Owm=wUv53qr?F7y1&>WR3>>+L zp}S5~s3q=|M_Pecvc2PokMHdIvvN`b;%@WB^E{Uno_85Lka#Q(2Ylopx9J^*!S^zb z>bYJf!7~O&xm(tukc8G{lI&j$DS&pCoY`95L*v(6HRv^c9TC+~Ys+q(%7b>9TK&ed z@v*Ox9)_$;@tUbpU`pdy1{rfJV#4oj6B(W~jn8=WjE~J#gfa9F5mU^VwI7Z|QwxK! zY17b2n2(m+rEZBkedSgF`OJoq;VLG75CnFrwH;x}17enS+{LSUXUh1x&4>h9L@L5> zC0tq`IVD+Bpj}?CI{XnweAeof!nEUD(HMW_R9o2LIO15Q`>6vYypq+PGog8f7zh<&H%Zo zhi^qG6woy)UeL&#(5E8&m5sBzhCzs&mG^SGYis;}xx^=5EE%4k?{oO$ z=5^|J^S?5)EEkp{Hore_Xp@7Ils>8cz7|HUvg>1%_4L`lFbM1+Z{H+bi1vx zOuXoj#}4w@fV8n=>zx#iw#||$*4r2Q86{9Y<$vfiuL)8fJx=A|;2q#umQ@q4<5Xun zqGRbwcsqJomD+h*RTesW{8HDS)?mVwt=eNS5bYqTp`h4K=?4hVLnBN~=;n;bsYSdg-PE(zVfF{&ys2afvehCH3W!4I&lyTecu3fbphla=gMW$t+W6WHK zAVqj7+p&=Uywv?6%!<*rd@vyb7h^|!ZOoY}G_RVx4;mCq9-kWF#p9daOoPPw$RLak zirf_tTlRspX>JXYaB{FAD|q{sHK6*e&(Lj+oSs%C;mm1jJX>@4U4_h-Cj53i0QyV-hqFsdu25 zVDd^mxIq0U2aNIH)-ohpg;>|jgpT$v?Fluy%r`Rc;xaa}fa>tO65!EBv1tc^)9`zyq?$~wxP-gJ+Vj{NQ? z@#8x;PFw7PI-$Rk+_DMuSrRhBK@cMmTLR6>{x-O z>nK2eXzYH22kUIIU5#jRvmf0gQ094VX409AJR`4)8j{I9)it1ILlZXR0PF1NF;V2g zx339Y30yXBx_>tCst>uoQ|_)@I7xb5a-6-50^hmRa$G+n!bH*@8l{$C5~V zoL5u_$XQa|nB6Ul(t%x=&Z_F}tk(AY?!HVSZ1D@S=NK3erxgzo#=hq?{o{6Lsg-IQ zd>Ax#d4X?(0+-xwj_l9HP-g-a@yO=tpHHkuTpPBo2`D%JZWjumT3qR3QJ$5_5wb!f zIleNQbG)KHa(t@?W6avx#lbz_JChH(c$Ne<kdxst*^+7CR-b86l?uqC+ zmgOf#^#r+oF>2xs;z<^wQ1(zr`sZW5@zaq~^>X@Zel=E|`0E+E^rw!ghY1iUfXRN_ zrd}4*F|toL@o~2QD1vq?hVwnu)zhLcgJk%*HA43oAG*zKqdY3&4FaSj*o6 z`Z3`lJ|V7zcNA3l5xP>LqB$4tST}01M|SjGo0~C_P0gtfE>ccGI?U$Lb7rzvpHBY_l z!WS(0Ns#gScBL%E*nBqQncK?HOVQnFVqB|yPCMXY)|zG>e}rT#T?R2jm~tGadcVF3 zML$TWJ&Kna>RNM5DA(Gc2*ge!7S*Y5b?q&CL(1&?^JwI#h7S1Axt;lBZ%n;$S=!3$ zkR|CqrwDmP&_K8L^LERX>I8@{5)^5-pzf%fLdYQ3{k1z6>V-Qj$kkaJJ&PzwpBZiT ze0rbIm#G>EIwXh8+iu!=^E>WwFmy$UpX9ywqfY|L2~kffFsEHs6!GM#(a%rGHn}U) z{ZWzg0|rsNOGT-Pw58D6tiqu9Lz71=HJVZ=ED_it;IfHcDfGXvT1==v1-6q8kKVxcS7N?N5Ou`;H=Z_LdLD=-kFFY2jO@0N2t zK8(2bwtyV>d3?0ri&d8L1m#_#Es&^Ilnm=DU}8rccf8XX<5^xE(Ee%7jt$AQ2w!1E z``r20?O`sEDOuY+IlmVBwtBLl4v*al7Cvg8S#xOTb;ukI2AM@k9Q_m*3DEYKYYz!z$L ztaxW7k_|Ex9%h^j>?EMi*p`{!-BtT-{o<3P9ebwuN_xEx~PCzxRcAtW$k>C&lyKT5e@$?VBms zVg!@@m%TZYwj047wfAR3CjNa_w(wBRN2!Q(cfg9AY6B7;w*# ztp8~`!a`=LyH$ExWApQhsWQJ{Z*+fl^CowC17VI}F`cJR`q1lJAsL#ot9E$8HGWqt zk|d!}19itM;j!KIL6%jsIvk&WW~MWQhAC{Z6Q zcH__aiLc`j-PKmVY~61964GdEwBWTbLaw{TUZq@Lz^kQpiDBJ!!B8tU*x5XM4@pH#xb=>#;La%8OuVCIiqtr559Pv zb{vTvKc*D7rEbCY_{hO=2-VRpPgkiUl4;I#bN5HfFEiP&aXX_Lo`E@w=dr_1ZDn%`F2?vrn%wI&p?a)Fsa#dC#< z%glut$6=J~KZUhXtx@^tW1B6B;=M`_gw=V=eqwt>e}-mk8F3|WuY0C={KSs=rO^t3 zLty@+E2{$hg=G4K@BLkO9p0 z4zTPuMYin3O?h-Ppl?O`ws_s2S|KiCbibUb-x<6vFWP0~3x6lvZ7~w6Ah%;{Zg5ry z9eE-=KdXnZkgvJMk^H;aR>rh;hYDZ`LdKfTKFUHD@Brv-h@J!emsxnK?t^wH&CSW z_$;32u9;(HP7t>eH~3Nqa7U8hysDUtef$97$6_5abUP1f@}c1ykS}O)97-#k57`%; zSBfpta^wtMx%5=I79XW?0C()a_y^K-B2KiA9uXtMe-}UwkQRzxDio2?532&E#J_Mp z$Z-G_A`?!*{)bQTJ|2)0xH=~D<#Kueapcq;b?59wP9(gv9j}#|6Mkn`?te2c)*|5* z=+cF7qR~GD2S9h~%8fj95l=^KfkQw)A4i=Mg}tfjlbvXf6!|v6L_h2VgifU}H-gA$ z{HCKYqe(fZ5R(9V{btY6k0P@*!&Q@c5?&|zr#MTAqw0l@+UCCVBF@F7*M1|*XMlD7 zS(bz+;Ng^D{)`@p-E@JAIhQbix=4ufo}JG+;(!}1bsZYlJK9lcDDPCv4*|V{)O*7t ztG65b|0;v#UIa*I2e_Rk)1j0eK?_dp8_bILF1BbZ(&|DxO@bZ+q%jx{`cKjMc&NiS zDkvyggb0`&k7=BzgOV~10|1W$5`ixopek6v%eDKp$B%tdjD00c~bBzTRND>7bPY&RH-x{`)_b%L7BNkuw>7(qmTR zH?~Z?vHh_BoTyaq_={&I{S~0^SbvdA;KpmgBY!wCPL(=-aHrYUJPd_16P5uch6O6E z9CdAqzo0z31ND+vI))##E%xox2^k9Y|a$K-Y{H^ z1cmm2`>5+@Y)U$h0;)a$205zgT)}&#a9@DMz%coL;npqx5BWf>+of2vpOpD~G%rec zERAUtZ{DUSth1T*3wc4t8@O6BdE2*;#cmDf%2LSpz2U{aRlNmUE zY~{f#*{ReoK$$nWk>A~!vFSHoB2$?WCtL= z(c+CsUBvnvbAWtO4B6*M$DQy1Ma(ZIrGSM&0WL#P{#b4ef4OYY=faRNh4i2|p<%(3 zUGYbcm?)@_f40qB3y0^Ud{^I%f<^W9*XaDbN|X2sp22bliS)d-_~xNo)q#W6=fCu2 z?Blfzgh2Uq_ezu>#8*N7rm?5l684A`Pf`F*0NP7!b|#pHNihp~_-W@z0W#4`3p&SD zJ`9m20852{N^ibnQez;==o@_s&a?0O$6rM67_W{WeCVz;Go7Xtl{}yJbW%|0g`yAt zqE~rhOpoV1sv<=7^OJ2Kh8{rK$lJ_4Bv;ztwV}~*HJ6CZo58VnaEO`dQx69E2IF=9 zcm1l{7=Tdn+0_gRV&$`_xoeZbIyVz_r#_9#a@>VdIFR}@udV~@ikZ40l6TS1a-Fe; z0!q>o3cJeZ?=x@`-2p(KC(1jr5@P11&20&waszSooPAIQHa0BKJpw9#N!T<#V2;PF z3`gkN9kei879}Hp1jW(ye-S@&T8Q)G?6*DK^8hM|U^zE^%J;qvB9|a6&K1bTS~BnE zZ!q-Ao$@B^Nv<`NTytWF?afY5Urp!$kjtM2=OL7B16)lUe8S$#3sSesIDbM(k2+Q` zsTqkeN_A7&yL*79k*`0dr|@a-h0%WG9wAK5;K=!>5IbhRe%67akD6wCfIq$gmFm5E z?W3Sln0r);gU7^v)Wy){0_Or~ zv)YemXF6evAO4ib$zfdbaR=E#3cr)ww*W4D<+(a<2Uri8jBS#D+*QmFka`9x(!N#& zeIi3Xq7XGtojUPmm3>38Vm||Z2?7S3z^ty}S;x3BDEj5S1V1EUyULOzsGZ=GeVBE~ za{%F|20!572ufb5@~$0PuQpiOrb)!j*Vr`RL?!vD#UnI}b)gmO%+qWDAc5 zWti+{veYzLvagRVD#Tc_4mD%nWl7m03~KBo5?xFo1|^JrOWg0=bM8I&{(H~){r8(Q z=QrQ)XFl_t_vih7O@I-yb~ZMGH~ixq=ZSW?IVZQedlI2AH)LR=rB`>}6Nc-=9EIM_ zN7qs7Cck+`p!@hBmVh12J^~N$WM1iRHV?&|mI2fZ-@Q5UV@ML{1F;&ExmB>RAKTNS zJ{XBck7?qY*wMXV1QTBYyrwZ&riwU+;Ygt>fC&zzJ*W=QVUIs-2 zh*REeA?jPhdPQRo$ox1?5y2`Wi{NBnSCT#0eIuDvn}~KQjFUOd!zCc@;iM&lh+AD( z%^#f$sU5@UOrgU3yzL_V0OZ?ljd1Z(xny>z1TdCxBj zY&=f<%i}SR-z^e0T$X5X8V*@En?d9g-|?s+NzcJ5>|Iykws~DVQ;uE7`Nn2{1v$Q= zc88R;Ag$IYN8rvwB>uq&cS|jgGVS8z7vxQGx}d2AOi77hq`gApD^wXzvU_93E>_%yL0=7n^Q=Elww0aQX~pyY8nAZyijLT}a-6XqMMb13w7K@EEzy`}}TE zBjJU*-@{GAq0ZT=GdI@dGjH=Q>X|@@RnpF!Stbw}gb0t%4qN5(Qk8F)}I_FeOka4i(c40vUJA6^dI}NjT4I@d(!r4c?%Qz&kEkwz2P7FC?)z{5a)4pu*jd; zpS@d~FsyvEI{)_-sLgN`CN- zlK0=sSGBd>iBy9RtMm47AA4%yd`ieu??h(~04)bYz-!C=mS+W@)B|+xO%dw`?S0M?*8~AEsf3h${@+^(7Dsk#n(>EjMZsY~)dNZjpX zt|NC%M^H--D+97NtNI63GMqHz)~6Fa|C)bx6Jy-Qk#yL5Kc4!8YWx{eK1Tx|{RJrI zkJot&#)Sf7W*;vPu~&18OI#6b2mtxTYq;yA>PTEd$wZ;;4tD*stxj8CAL7s3@xGuz?Y>t6d6egOE0>i37gIN~Nn6_`x zCY!pS&gRjC)R4r-Q9-;(OUC$bqZMDI>p7dmF@GZFq&uXScteoQa&FR~%$wB@HFv&) zGSAF$V7wu&lTvT=k7b{_J=SutJ5$VlA_5?iY+4%{rq5Z5CGTH^1l~3uIOwhGa?4Gt zPUA>()>>|ICe0+)Kn_uUjGC$r{wN@^%a(Cr7SJkvukK!z_-W7Ausm zZck2IX$Xbot}h?ikkBmT}z`vq&m5<9?rxRp{U5ae*kUc zX7DA$B-w>faJ-Ep=^JqJ=WW8rMGIsbwB0f{tYd_MPgk?m5vQ)u@1NNc&E>1@kh1UX zP#bH%QDBeQs21p;X8V6Yd^x41_;bypR=``CCY5Ujm$jKeY!J^9=BB1EbQC9iL2kPA z!BVjnXIw8;L-9&@C2O%fr^(NaxHyb!HH`H*!qNQ$2r@o8@yS$Yzc|&ysYSy#7OUL# zu15zz8Nv+VlW}Vhlysh-+&JD?QFvv`651i{S)#09^9m3y_M!7(dys9Q`EyHQMUem( z^N|bgL^5%bx$}ILZoseY`U%yFy+xIi_Lyrmq+~qVe;4K~O8M{zGEn+PDAhQDAq%~t ziX0Jqc-T@~z)Ewac%`Z908=r;i3hji=L^!;iS_5zoQPC$3SMd7Cg8xRk8n)+jM*sp zB;~obM8Iw=*CqNR$E|Tx_w>m6@dY}(Y4X%3l=1{#$RT=79CbL0DA1Y63LW9#Z^tj8 znSM89O}WnnrQe@_iZ0}d3xvo-+23)QpV+zN-ZE3X`4=5v6sy)H+o7tAJUrz$f*6l# z@c_1A^PF6V+hQKRZWK-t2d>vz%heq$-ZBmQ2=G6Zqdt0Rd?fQRtBqm4kC`|H)04>0 z#o{PdEy3C)sIwO;+@yad=MoaX(LgeH!fE5$LrT*EAzNZD92d#0Z`u;DBNbmRxD$Cc z`fSb{s9ko!8V3AE^$}hMT`gk3D;+nkv{Q`(x*aPOez2fu^YCb1)6*(CI=@UG9avH|4CN26!R2v(tZa#I1!t>KJp2?q?-@9B37``y5__`M zz>375mxcLzu^VyNG4olFr(f(<;WjtcT700}mX&EQM0SmRzq4#_-MjxdyHcGg>eKjy z2NP3timaf-$?L_PdU=PWetAOKbD+Mj=q+INCB!qHKr9f8^80mGZ&)(A4xO&I(&0RZ zt3mBgrZTGDg0Iu4;X5S<;-13Wf!;w-Xwk!G2k58A@5oAkiz>2ZdZkyD8uo){96x5% zCJIOnVdpf$&j;EK3ClR2b(|;erAX@3E^l?R;>HjS-<;%aDNj~Q@3oV-BjFH5e#pCrrxvqBniZ?T5|20+eExdK;wOQ5b77pTo4dgt~QM; z!!m8cde0}I13By0%w8(V6TlHtB{b%Z+B|?C#V!yCdBK@BZ)?cfy*gZG#p!^G@=Bn| zW)w!JcNOUgR(+@LzDU(BYv1kCkjX8h4jliyhgW*6`H#t?>Re@{$91T&i}$sJPh9|% zwf3FQyEmWNM_uBZ);gIrJ<5t}dp$a|Ect6&7&fx{yy15i3Z**@Ge_&e$Nvt^;7F6JBk6k?OGPe5;36y2nrH-#lEk7NqqrDY2gG<~FMT z^xWJktftLSGdkzz_*YhRRdmOAD?|*oc!59knXrk6LN=j?PJjakLDf4KsNc-H+#QKl zHE8(RWj=24?jQ@SFB8pWWYyYrj@)AA7-Dj8#cQe4`c&u?q{z**Gv!_|EEo&vd+EiF(rF+QBOo_Ax|I_W=b>WixNg4|- zr;U+i0=K!yr}l8PYt|Fr2knqKjTv^fLaO$ZfT|vxEvB8nx);t!EdZCbD`*iLA>7U zQ0|GyjUm$8nLF=mTg2j4ZAudC`zw@(Xj8D7m6-L&VXz4A@JwB38GDrD;wt3An;7jv)afz!)wJUC9Puje5txs3So5p%l&EmERBTG_5SVGDvvBP&DAP|QKyblQ|H;61{p z`ECMdDsEu9d=o)iH%XrzZtQ4wP)eFj3Gz=(x(pXUM}j~Rw7KBLz>|;(;4Ue7V5J_Snk~RhNQ7ZqumliuA-cB|7_I4YYJ6* Date: Mon, 9 Sep 2019 23:41:51 +1000 Subject: [PATCH 22/87] Refactor helper to reduce code duplication --- .../Processing/Extensions/ResizeExtensions.cs | 50 +++++++++++++----- .../Transforms/Resize/ResizeHelper.cs | 35 +++++++++++-- .../Transforms/Resize/ResizeProcessor.cs | 51 ------------------- src/ImageSharp/Processing/ResizeMode.cs | 9 +++- src/ImageSharp/Processing/ResizeOptions.cs | 5 ++ 5 files changed, 79 insertions(+), 71 deletions(-) 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/Processors/Transforms/Resize/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs index c9df1b2542..eacd3834f1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs @@ -36,6 +36,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms 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. @@ -51,9 +56,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms height = (int)MathF.Max(Min, MathF.Round(sourceSize.Height * width / (float)sourceSize.Width)); } - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - switch (options.Mode) { case ResizeMode.Crop: @@ -66,8 +68,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return CalculateMaxRectangle(sourceSize, width, height); case ResizeMode.Min: 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)); } @@ -397,5 +401,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // 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); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 6f5f09e717..35e22757c1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -13,45 +13,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public class ResizeProcessor : IImageProcessor { - /// - /// 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 target 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.TargetWidth = width; - this.TargetHeight = height; - this.TargetRectangle = targetRectangle; - this.Compand = compand; - } - /// /// Initializes a new instance of the class. /// @@ -71,18 +32,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms 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. /// 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 96de1eee11..ef88dc35b3 100644 --- a/src/ImageSharp/Processing/ResizeOptions.cs +++ b/src/ImageSharp/Processing/ResizeOptions.cs @@ -41,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; } } } From 221000ed036ea81a093cfe7733e532ae648d7e90 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 10 Sep 2019 17:34:14 +1000 Subject: [PATCH 23/87] Clean up scanline decoding code. --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 38 ++++++++---------- .../Formats/Png/Zlib/ZlibInflateStream.cs | 40 +++++++++++-------- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index a9e588f6ee..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,18 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Png this.InitializeImage(metadata, out image); } - var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk); - try - { - deframeStream.AllocateNewBytes(chunk.Length, true); - this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetadata); - } - finally - { - // If an invalid Zlib stream is discovered the decoder will throw an exception - // due to the critical nature of the data chunk. - deframeStream.Dispose(); - } + this.ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata); break; case PngChunkType.Palette: @@ -472,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); + } } } @@ -1021,7 +1017,7 @@ namespace SixLabors.ImageSharp.Formats.Png 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)) { if (!inflateStream.AllocateNewBytes(compressedData.Length, false)) { diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs index df0e723322..e4645c44ac 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs @@ -20,14 +20,14 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib 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. @@ -55,8 +55,17 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// /// 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,12 +85,12 @@ 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 /// - public DeflateStream CompressedStream => this.compressedStream; + public DeflateStream CompressedStream { get; private set; } /// /// Adds new bytes from a frame found in the original stream @@ -92,7 +101,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib public bool AllocateNewBytes(int bytes, bool isCriticalChunk) { this.currentDataRemaining = bytes; - if (this.compressedStream is null) + if (this.CompressedStream is null) { return this.InitializeInflateStream(isCriticalChunk); } @@ -101,10 +110,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib } /// - public override void Flush() - { - throw new NotSupportedException(); - } + public override void Flush() => throw new NotSupportedException(); /// public override int ReadByte() @@ -186,10 +192,10 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib if (disposing) { // dispose managed resources - if (this.compressedStream != null) + if (this.CompressedStream != null) { - this.compressedStream.Dispose(); - this.compressedStream = null; + this.CompressedStream.Dispose(); + this.CompressedStream = null; } } @@ -264,7 +270,7 @@ 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; } From 4697564541757e0bea0cf17b3c840455fa20c555 Mon Sep 17 00:00:00 2001 From: ip75 Date: Fri, 6 Sep 2019 11:07:56 +0300 Subject: [PATCH 24/87] image lightness filter --- .../Extensions/LightnessExtension.cs | 35 ++++++++++++++++ .../Processing/KnownFilterMatrices.cs | 42 +++++++++++++++++++ .../Processors/Filters/LightnessProcessor.cs | 27 ++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 src/ImageSharp/Processing/Extensions/LightnessExtension.cs create mode 100644 src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs diff --git a/src/ImageSharp/Processing/Extensions/LightnessExtension.cs b/src/ImageSharp/Processing/Extensions/LightnessExtension.cs new file mode 100644 index 0000000000..b56eee1b86 --- /dev/null +++ b/src/ImageSharp/Processing/Extensions/LightnessExtension.cs @@ -0,0 +1,35 @@ +// 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 hue component of an + /// using Mutate/Clone. + /// + public static class LightnessExtension + { + /// + /// Alters the hue component of the image. + /// + /// The image this method extends. + /// Lightness parameter of image in HSL color scheme. + /// The to allow chaining of operations. + public static IImageProcessingContext Lightness(this IImageProcessingContext source, float lightness) + => source.ApplyProcessor(new LightnessProcessor(lightness)); + + /// + /// Alters the hue component of the image. + /// + /// The image this method extends. + /// Lightness parameter of image in HSL color scheme. + /// + /// 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 lightness, Rectangle rectangle) + => source.ApplyProcessor(new LightnessProcessor(lightness), rectangle); + } +} diff --git a/src/ImageSharp/Processing/KnownFilterMatrices.cs b/src/ImageSharp/Processing/KnownFilterMatrices.cs index 1f36e2593a..c6852d6320 100644 --- a/src/ImageSharp/Processing/KnownFilterMatrices.cs +++ b/src/ImageSharp/Processing/KnownFilterMatrices.cs @@ -432,6 +432,48 @@ 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 makes the image completely white. + /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. + /// + /// The proportion of the conversion. Must be greater than or equal to 0. + /// The + public static ColorMatrix CreateLightnessFilter(float amount) + { + Guard.MustBeBetweenOrEqualTo(amount, 0, 1F, nameof(amount)); + + + /// James Jackson-South @JimBobSquarePants 03:54 + /// Our colormatrix is a column-major version of the Android colormatrix + /// ``` + /// // | 0| 1| 2| 3| 4| |0|5|10|15| |M11|M12|M13|M14| + /// // | 5| 6| 7| 8| 9| |1|6|11|16| |M21|M22|M23|M24| + /// // |10|11|12|13|14| = |2|7|12|17| = |M31|M32|M33|M34| + /// // |15|16|17|18|19| |3|8|13|18| |M41|M42|M43|M44| + /// // |4|9|14|19| |M51|M52|M53|M54| + /// ``` + /// + /// James Jackson-South @JimBobSquarePants 03:54 + /// So given the information you have supplied and the stackoverflow answer (which has one too many rows in the code) you would use + /// the identity matrix and change 4, 9, 14 in the android matrix. In our matrix you would use identity again but change M51, M52, M53. + /// We use column major layout as that matches the system drawing matrix. + /// + // See https://en.wikipedia.org/wiki/HSL_and_HSV#Lightness and https://stackoverflow.com/questions/9175088/adjusting-lightness-using-colormatrix#answer-27179516 + 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/Filters/LightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs new file mode 100644 index 0000000000..68257e718c --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs @@ -0,0 +1,27 @@ +// 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 + /// + public sealed class LightnessProcessor : FilterProcessor + { + + /// + /// Initializes a new instance of the class. + /// + /// Lightness of image in HSL color scheme + public LightnessProcessor(float lightness) + : base(KnownFilterMatrices.CreateLightnessFilter(lightness)) + { + this.Lightness = lightness; + } + + /// + /// Gets Lightness of image in HSL color scheme. + /// The "brightness relative to the brightness of a similarly illuminated white" https://en.wikipedia.org/wiki/HSL_and_HSV#Lightness + /// + public float Lightness { get; } + } +} From fcf5d02f6b1a9b8d0ccd1766c8f9c9d7259846dd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 1 Sep 2019 20:43:49 +1000 Subject: [PATCH 25/87] Removed submodule --- .gitmodules | 3 --- standards | 1 - 2 files changed, 4 deletions(-) delete mode 160000 standards diff --git a/.gitmodules b/.gitmodules index 37ef701cdf..e7972649f4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,6 +2,3 @@ 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 diff --git a/standards b/standards deleted file mode 160000 index 8b085c0ec4..0000000000 --- a/standards +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8b085c0ec4fb64797b9965741f7138f8f66a6b44 From 19c7f178d18763b277946a5d67b6174cd19444f3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 1 Sep 2019 23:21:54 +1000 Subject: [PATCH 26/87] Update dependencies and submodule --- .editorconfig | 13 +- .gitmodules | 3 + Directory.Build.props | 2 +- Directory.Build.targets | 8 +- ImageSharp.sln | 1 - shared-infrastructure | 1 + src/Directory.Build.props | 4 +- .../ImageSharp.Drawing.csproj | 11 +- src/ImageSharp/Common/Helpers/DebugGuard.cs | 178 +---------- src/ImageSharp/Common/Helpers/Guard.cs | 294 ------------------ src/ImageSharp/ImageSharp.csproj | 13 + src/ImageSharp/Memory/BufferArea{T}.cs | 14 +- stylecop.json | 16 - 13 files changed, 52 insertions(+), 506 deletions(-) create mode 160000 shared-infrastructure delete mode 100644 src/ImageSharp/Common/Helpers/Guard.cs delete mode 100644 stylecop.json 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/.gitmodules b/.gitmodules index e7972649f4..55389121f2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = tests/Images/External url = https://github.com/SixLabors/Imagesharp.Tests.Images.git branch = master +[submodule "shared-infrastructure"] + path = shared-infrastructure + url = https://github.com/SixLabors/SharedInfrastructure 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..1551a29d8c 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -28,10 +28,10 @@ - - - - + + + + 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/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/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/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/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/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 From 33f319a669cb888da1fd380310edf88c32c19924 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 1 Sep 2019 23:11:12 +0200 Subject: [PATCH 27/87] delete GuardTests --- tests/ImageSharp.Tests/Helpers/GuardTests.cs | 274 ------------------- 1 file changed, 274 deletions(-) delete mode 100644 tests/ImageSharp.Tests/Helpers/GuardTests.cs 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 From ac9b1d0f9735011a57b6816d357c1333e9f52229 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 7 Sep 2019 12:04:51 +1000 Subject: [PATCH 28/87] Fix #997 --- .../Implementation/Converters/HslAndRgbConverter.cs | 4 ++-- .../Colorspaces/Conversion/RgbAndHslConversionTest.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) 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/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 From b6fcda1dce5131d0338e32fe07e0927942c28016 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 15 Aug 2019 19:02:14 +1000 Subject: [PATCH 29/87] remove patternVector in patternBrush --- src/ImageSharp.Drawing/Processing/PatternBrush.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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; } /// From a3f39ee6d328142e65c41ad0dc45c720001a5b16 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 8 Sep 2019 13:49:47 +1000 Subject: [PATCH 30/87] Fix #999 and add tests --- .../Transforms/Resize/ResizeHelper.cs | 228 ++++++++---------- .../Transforms/Resize/ResizeProcessor.cs | 43 +--- .../Resize/ResizeProcessor{TPixel}.cs | 14 +- src/ImageSharp/Processing/ResizeOptions.cs | 8 +- .../Transforms/ResizeHelperTests.cs | 143 +++++++++-- .../Processing/Transforms/PadTest.cs | 4 +- .../Processing/Transforms/ResizeTests.cs | 20 +- tests/ImageSharp.Tests/xunit.runner.json | 9 +- 8 files changed, 268 insertions(+), 201 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs index ae7b112fc1..c9df1b2542 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,32 @@ 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; + + // 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)); + } + + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + switch (options.Mode) { case ResizeMode.Crop: @@ -50,9 +63,9 @@ 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); // Last case ResizeMode.Stretch: default: @@ -66,11 +79,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 +92,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 +153,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 +170,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 +192,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 +233,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 +305,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 +352,50 @@ 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)); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index cf27de5eb1..6f5f09e717 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -26,19 +26,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { Guard.NotNull(sampler, nameof(sampler)); - // Ensure size is populated across both dimensions. + // Ensure target 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; + const int Min = 1; if (width == 0 && height > 0) { - width = (int)MathF.Max(min, MathF.Round(sourceSize.Width * height / (float)sourceSize.Height)); + 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)); + height = (int)MathF.Max(Min, MathF.Round(sourceSize.Height * width / (float)sourceSize.Width)); targetRectangle.Height = height; } @@ -46,8 +46,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Guard.MustBeGreaterThan(height, 0, nameof(height)); this.Sampler = sampler; - this.Width = width; - this.Height = height; + this.TargetWidth = width; + this.TargetHeight = height; this.TargetRectangle = targetRectangle; this.Compand = compand; } @@ -62,32 +62,11 @@ 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; } @@ -112,12 +91,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. diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index e16d4801ec..b85983a481 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -45,15 +45,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Gets the target width. /// - public int Width => this.parameterSource.Width; + public int TargetWidth => this.parameterSource.TargetWidth; /// /// Gets the target height. /// - public int Height => this.parameterSource.Height; + public int TargetHeight => this.parameterSource.TargetHeight; /// - /// Gets the resize rectangle. + /// Gets the target resize rectangle. /// public Rectangle TargetRectangle => this.parameterSource.TargetRectangle; @@ -80,8 +80,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms IEnumerable> frames = source.Frames.Select, ImageFrame>( x => new ImageFrame( configuration, - this.Width, - this.Height, + this.TargetWidth, + this.TargetHeight, x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added @@ -128,8 +128,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms 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; diff --git a/src/ImageSharp/Processing/ResizeOptions.cs b/src/ImageSharp/Processing/ResizeOptions.cs index ee0dde6b27..96de1eee11 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. @@ -44,4 +42,4 @@ namespace SixLabors.ImageSharp.Processing /// public bool Compand { get; set; } = false; } -} \ No newline at end of file +} 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/xunit.runner.json b/tests/ImageSharp.Tests/xunit.runner.json index 5204242f03..d7b466d09d 100644 --- a/tests/ImageSharp.Tests/xunit.runner.json +++ b/tests/ImageSharp.Tests/xunit.runner.json @@ -1,5 +1,6 @@ { - "shadowCopy": false, - "methodDisplay": "method", - "diagnosticMessages": true -} \ No newline at end of file + "shadowCopy": false, + "methodDisplay": "method", + "diagnosticMessages": true, + "preEnumerateTheories": false +} From f23030e14a3b112f0590db3ce896aa7353aec54a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 8 Sep 2019 17:20:45 +1000 Subject: [PATCH 31/87] revert preenumeration rule --- tests/ImageSharp.Tests/xunit.runner.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/xunit.runner.json b/tests/ImageSharp.Tests/xunit.runner.json index d7b466d09d..749ece4387 100644 --- a/tests/ImageSharp.Tests/xunit.runner.json +++ b/tests/ImageSharp.Tests/xunit.runner.json @@ -1,6 +1,5 @@ { "shadowCopy": false, "methodDisplay": "method", - "diagnosticMessages": true, - "preEnumerateTheories": false + "diagnosticMessages": true } From 2ea69b4a751131975a7165b0a284bc2716597b8d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 9 Sep 2019 23:41:51 +1000 Subject: [PATCH 32/87] Refactor helper to reduce code duplication --- .../Processing/Extensions/ResizeExtensions.cs | 50 +++++++++++++----- .../Transforms/Resize/ResizeHelper.cs | 35 +++++++++++-- .../Transforms/Resize/ResizeProcessor.cs | 51 ------------------- src/ImageSharp/Processing/ResizeMode.cs | 9 +++- src/ImageSharp/Processing/ResizeOptions.cs | 5 ++ 5 files changed, 79 insertions(+), 71 deletions(-) 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/Processors/Transforms/Resize/ResizeHelper.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs index c9df1b2542..eacd3834f1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeHelper.cs @@ -36,6 +36,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms 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. @@ -51,9 +56,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms height = (int)MathF.Max(Min, MathF.Round(sourceSize.Height * width / (float)sourceSize.Width)); } - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - switch (options.Mode) { case ResizeMode.Crop: @@ -66,8 +68,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return CalculateMaxRectangle(sourceSize, width, height); case ResizeMode.Min: 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)); } @@ -397,5 +401,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // 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); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 6f5f09e717..35e22757c1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -13,45 +13,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public class ResizeProcessor : IImageProcessor { - /// - /// 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 target 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.TargetWidth = width; - this.TargetHeight = height; - this.TargetRectangle = targetRectangle; - this.Compand = compand; - } - /// /// Initializes a new instance of the class. /// @@ -71,18 +32,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms 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. /// 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 96de1eee11..ef88dc35b3 100644 --- a/src/ImageSharp/Processing/ResizeOptions.cs +++ b/src/ImageSharp/Processing/ResizeOptions.cs @@ -41,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; } } } From 99a74ebdc6b1446eb863695acc3d57704f8b775d Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 15 Aug 2019 19:39:25 +1000 Subject: [PATCH 33/87] use SUPPORTS_EXTENDED_INTRINSICS to filter out some BasicIntrinsics256 methods --- src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs | 2 ++ 1 file changed, 2 insertions(+) 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 . From dbf88680985e1c10e9be2502089efdcd50da6b64 Mon Sep 17 00:00:00 2001 From: ip75 Date: Tue, 10 Sep 2019 15:01:45 +0300 Subject: [PATCH 34/87] fix comments cosmetic warning-errors --- .../Processing/KnownFilterMatrices.cs | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Processing/KnownFilterMatrices.cs b/src/ImageSharp/Processing/KnownFilterMatrices.cs index c6852d6320..ab24f78a34 100644 --- a/src/ImageSharp/Processing/KnownFilterMatrices.cs +++ b/src/ImageSharp/Processing/KnownFilterMatrices.cs @@ -445,22 +445,19 @@ namespace SixLabors.ImageSharp.Processing { Guard.MustBeBetweenOrEqualTo(amount, 0, 1F, nameof(amount)); - - /// James Jackson-South @JimBobSquarePants 03:54 - /// Our colormatrix is a column-major version of the Android colormatrix - /// ``` - /// // | 0| 1| 2| 3| 4| |0|5|10|15| |M11|M12|M13|M14| - /// // | 5| 6| 7| 8| 9| |1|6|11|16| |M21|M22|M23|M24| - /// // |10|11|12|13|14| = |2|7|12|17| = |M31|M32|M33|M34| - /// // |15|16|17|18|19| |3|8|13|18| |M41|M42|M43|M44| - /// // |4|9|14|19| |M51|M52|M53|M54| - /// ``` - /// - /// James Jackson-South @JimBobSquarePants 03:54 - /// So given the information you have supplied and the stackoverflow answer (which has one too many rows in the code) you would use - /// the identity matrix and change 4, 9, 14 in the android matrix. In our matrix you would use identity again but change M51, M52, M53. - /// We use column major layout as that matches the system drawing matrix. - /// + // James Jackson-South @JimBobSquarePants 03:54 + // Our colormatrix is a column-major version of the Android colormatrix + // + // | 0| 1| 2| 3| 4| |0|5|10|15| |M11|M12|M13|M14| + // | 5| 6| 7| 8| 9| |1|6|11|16| |M21|M22|M23|M24| + // |10|11|12|13|14| = |2|7|12|17| = |M31|M32|M33|M34| + // |15|16|17|18|19| |3|8|13|18| |M41|M42|M43|M44| + // |4|9|14|19| |M51|M52|M53|M54| + // James Jackson-South @JimBobSquarePants 03:54 + // So given the information you have supplied and the stackoverflow answer (which has one too many rows in the code) you would use + // the identity matrix and change 4, 9, 14 in the android matrix. In our matrix you would use identity again but change M51, M52, M53. + // We use column major layout as that matches the system drawing matrix. + // // See https://en.wikipedia.org/wiki/HSL_and_HSV#Lightness and https://stackoverflow.com/questions/9175088/adjusting-lightness-using-colormatrix#answer-27179516 return new ColorMatrix { From 695171725bc257e3d79f7dc7734c6ec5395f2ffd Mon Sep 17 00:00:00 2001 From: ip75 Date: Tue, 10 Sep 2019 15:18:09 +0300 Subject: [PATCH 35/87] warnings-errors --- src/ImageSharp/Processing/KnownFilterMatrices.cs | 15 --------------- .../Processors/Filters/LightnessProcessor.cs | 2 +- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/ImageSharp/Processing/KnownFilterMatrices.cs b/src/ImageSharp/Processing/KnownFilterMatrices.cs index ab24f78a34..999ecdc155 100644 --- a/src/ImageSharp/Processing/KnownFilterMatrices.cs +++ b/src/ImageSharp/Processing/KnownFilterMatrices.cs @@ -444,21 +444,6 @@ namespace SixLabors.ImageSharp.Processing public static ColorMatrix CreateLightnessFilter(float amount) { Guard.MustBeBetweenOrEqualTo(amount, 0, 1F, nameof(amount)); - - // James Jackson-South @JimBobSquarePants 03:54 - // Our colormatrix is a column-major version of the Android colormatrix - // - // | 0| 1| 2| 3| 4| |0|5|10|15| |M11|M12|M13|M14| - // | 5| 6| 7| 8| 9| |1|6|11|16| |M21|M22|M23|M24| - // |10|11|12|13|14| = |2|7|12|17| = |M31|M32|M33|M34| - // |15|16|17|18|19| |3|8|13|18| |M41|M42|M43|M44| - // |4|9|14|19| |M51|M52|M53|M54| - // James Jackson-South @JimBobSquarePants 03:54 - // So given the information you have supplied and the stackoverflow answer (which has one too many rows in the code) you would use - // the identity matrix and change 4, 9, 14 in the android matrix. In our matrix you would use identity again but change M51, M52, M53. - // We use column major layout as that matches the system drawing matrix. - // - // See https://en.wikipedia.org/wiki/HSL_and_HSV#Lightness and https://stackoverflow.com/questions/9175088/adjusting-lightness-using-colormatrix#answer-27179516 return new ColorMatrix { M11 = 1F, diff --git a/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs index 68257e718c..35b76f5dea 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs @@ -1,5 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. + namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// @@ -7,7 +8,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// public sealed class LightnessProcessor : FilterProcessor { - /// /// Initializes a new instance of the class. /// From 1314d001f22dae708c195e90c12e36fb3ecc5832 Mon Sep 17 00:00:00 2001 From: Xavier Cho Date: Thu, 12 Sep 2019 12:55:05 +0900 Subject: [PATCH 36/87] Remove a redundant check for the paint boundary --- src/ImageSharp.Drawing/Processing/PathGradientBrush.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs index fc84e4a1fa..553ed181c0 100644 --- a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs @@ -231,11 +231,6 @@ namespace SixLabors.ImageSharp.Processing return new Color(this.centerColor).ToPixel(); } - if (!this.path.Contains(point)) - { - return Color.Transparent.ToPixel(); - } - Vector2 direction = Vector2.Normalize(point - this.center); PointF end = point + (PointF)(direction * this.maxDistance); From 68424083165e62e341e4e464c02664b8b66b75d2 Mon Sep 17 00:00:00 2001 From: Xavier Cho Date: Thu, 12 Sep 2019 12:55:28 +0900 Subject: [PATCH 37/87] Ignore invalid gradient positions rather than throwing an error There can be such cases where it fails to find intersection points for a given position, especially when the polygon is small. Such an input would result in an error previously, but now it renders correctly. --- src/ImageSharp.Drawing/Processing/PathGradientBrush.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs index 553ed181c0..636551b642 100644 --- a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs @@ -237,6 +237,11 @@ namespace SixLabors.ImageSharp.Processing (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); From bec033603ef1e1fb1ee88b83e3479f12f2c9a652 Mon Sep 17 00:00:00 2001 From: Turnerj Date: Thu, 12 Sep 2019 16:27:56 +0930 Subject: [PATCH 38/87] Fix horizontally out-of-bounds error when drawing text --- .../Text/DrawTextProcessor{TPixel}.cs | 17 +++++++++++------ .../Drawing/Text/DrawTextOnImageTests.cs | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs index 3809200d5f..ea042635dd 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs @@ -90,26 +90,31 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text 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); } } diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs index 7bebdabd3a..dc1ccc8134 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 = SystemFonts.CreateFont("Arial", 39, FontStyle.Regular); + 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)] From ea7cc57823ff247a55c2825722f4327076490622 Mon Sep 17 00:00:00 2001 From: Turnerj Date: Thu, 12 Sep 2019 16:41:11 +0930 Subject: [PATCH 39/87] Updated font used for test --- tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs index dc1ccc8134..90740a4bb2 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text { using (Image img = provider.GetImage()) { - Font font = SystemFonts.CreateFont("Arial", 39, FontStyle.Regular); + Font font = SystemFonts.CreateFont("OpenSans-Regular.ttf", 39, FontStyle.Regular); string text = new string('a', 10000); // exception // string text = "Hello"; // no exception Rgba32 color = Rgba32.Black; From 026ae4579938b461ce0ce33b5fd1bfc3d0caee33 Mon Sep 17 00:00:00 2001 From: Turnerj Date: Thu, 12 Sep 2019 16:56:30 +0930 Subject: [PATCH 40/87] Use local CreateFont method --- tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs index 90740a4bb2..a767a686ed 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text { using (Image img = provider.GetImage()) { - Font font = SystemFonts.CreateFont("OpenSans-Regular.ttf", 39, FontStyle.Regular); + Font font = CreateFont("OpenSans-Regular.ttf", 39); string text = new string('a', 10000); // exception // string text = "Hello"; // no exception Rgba32 color = Rgba32.Black; From 1eea497e74d456ff63c4690e924e6e257a42c0b7 Mon Sep 17 00:00:00 2001 From: Xavier Cho Date: Thu, 12 Sep 2019 19:37:07 +0900 Subject: [PATCH 41/87] Accept points instead of line segments as constructor argument Note that this is a breaking change. --- .../Processing/PathGradientBrush.cs | 43 +++++----- .../Drawing/FillPathGradientBrushTests.cs | 86 ++++--------------- 2 files changed, 39 insertions(+), 90 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs index 636551b642..7315dc5a3e 100644 --- a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs @@ -18,8 +18,6 @@ namespace SixLabors.ImageSharp.Processing /// public sealed class PathGradientBrush : IBrush { - private readonly Polygon path; - private readonly IList edges; private readonly Color centerColor; @@ -27,20 +25,20 @@ namespace SixLabors.ImageSharp.Processing /// /// Initializes a new instance of the class. /// - /// Line segments of a polygon that represents the gradient area. + /// 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(ILineSegment[] lines, Color[] colors, Color centerColor) + public PathGradientBrush(PointF[] points, Color[] colors, Color centerColor) { - if (lines == null) + if (points == null) { - throw new ArgumentNullException(nameof(lines)); + throw new ArgumentNullException(nameof(points)); } - if (lines.Length < 3) + if (points.Length < 3) { throw new ArgumentOutOfRangeException( - nameof(lines), + nameof(points), "There must be at least 3 lines to construct a path gradient brush."); } @@ -56,22 +54,30 @@ namespace SixLabors.ImageSharp.Processing "One or more color is needed to construct a path gradient brush."); } - this.path = new Polygon(lines); + 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 = this.path.LineSegments.Select(s => new Path(s)) + 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. /// - /// Line segments of a polygon that represents the gradient area. + /// Points that constitute a polygon that represents the gradient area. /// Array of colors that correspond to each point in the polygon. - public PathGradientBrush(ILineSegment[] lines, Color[] colors) - : this(lines, colors, CalculateCenterColor(colors)) + public PathGradientBrush(PointF[] points, Color[] colors) + : this(points, colors, CalculateCenterColor(colors)) { } @@ -82,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing GraphicsOptions options) where TPixel : struct, IPixel { - return new PathGradientBrushApplicator(source, this.path, this.edges, this.centerColor, options); + return new PathGradientBrushApplicator(source, this.edges, this.centerColor, options); } private static Color CalculateCenterColor(Color[] colors) @@ -182,8 +188,6 @@ namespace SixLabors.ImageSharp.Processing private class PathGradientBrushApplicator : BrushApplicator where TPixel : struct, IPixel { - private readonly Path path; - private readonly PointF center; private readonly Vector4 centerColor; @@ -196,24 +200,21 @@ namespace SixLabors.ImageSharp.Processing /// Initializes a new instance of the class. /// /// The source image. - /// A polygon that represents the gradient area. /// Edges of the polygon. /// Color at the center of the gradient area to which the other colors converge. /// The options. public PathGradientBrushApplicator( ImageFrame source, - Path path, IList edges, Color centerColor, GraphicsOptions options) : base(source, options) { - this.path = path; this.edges = edges; - PointF[] points = path.LineSegments.Select(s => s.EndPoint).ToArray(); + PointF[] points = edges.Select(s => s.Start).ToArray(); - this.center = points.Aggregate((p1, p2) => p1 + p2) / points.Length; + 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(); diff --git a/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs index d76893108b..1ab747bafc 100644 --- a/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs @@ -7,7 +7,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; -using SixLabors.Shapes; using Xunit; @@ -27,17 +26,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) - }; - + 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(path, colors); + var brush = new PathGradientBrush(points, colors); image.Mutate(x => x.Fill(brush)); image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); @@ -53,16 +45,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(5, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(5, 0)) - }; - + 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(path, colors); + var brush = new PathGradientBrush(points, colors); image.Mutate(x => x.Fill(brush)); image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); @@ -76,17 +62,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing { using (Image image = provider.GetImage()) { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) - }; - + 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(path, colors); + var brush = new PathGradientBrush(points, colors); image.Mutate(x => x.Fill(brush)); @@ -103,17 +82,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) - }; - + 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(path, colors); + var brush = new PathGradientBrush(points, colors); image.Mutate(x => x.Fill(brush)); image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); @@ -129,17 +101,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) - }; - + 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(path, colors, Color.White); + var brush = new PathGradientBrush(points, colors, Color.White); image.Mutate(x => x.Fill(brush)); image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); @@ -157,17 +122,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing } [Fact] - public void ShouldThrowArgumentOutOfRangeExceptionWhenLessThan3LinesAreGiven() + public void ShouldThrowArgumentOutOfRangeExceptionWhenLessThan3PointsAreGiven() { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)) - }; - + PointF[] points = { new PointF(0, 0), new PointF(10, 0) }; Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; - PathGradientBrush Create() => new PathGradientBrush(path, colors, Color.White); + PathGradientBrush Create() => new PathGradientBrush(points, colors, Color.White); Assert.Throws(Create); } @@ -175,15 +135,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing [Fact] public void ShouldThrowArgumentNullExceptionWhenColorsAreNull() { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) - }; + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; - PathGradientBrush Create() => new PathGradientBrush(path, null, Color.White); + PathGradientBrush Create() => new PathGradientBrush(points, null, Color.White); Assert.Throws(Create); } @@ -191,17 +145,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing [Fact] public void ShouldThrowArgumentOutOfRangeExceptionWhenEmptyColorArrayIsGiven() { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) - }; + 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(path, colors, Color.White); + PathGradientBrush Create() => new PathGradientBrush(points, colors, Color.White); Assert.Throws(Create); } From 1bb93523a5291bc5995b03692e6c726c772786dc Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 16 Sep 2019 15:10:35 +0200 Subject: [PATCH 42/87] Change clipLimit to be an absolute value. Fixes issue #994 --- .../AdaptiveHistogramEqualizationProcessor.cs | 8 ++--- ...eHistogramEqualizationProcessor{TPixel}.cs | 8 ++--- ...ogramEqualizationSlidingWindowProcessor.cs | 8 ++--- ...alizationSlidingWindowProcessor{TPixel}.cs | 8 ++--- .../GlobalHistogramEqualizationProcessor.cs | 8 ++--- ...lHistogramEqualizationProcessor{TPixel}.cs | 8 ++--- .../HistogramEqualizationOptions.cs | 9 +++--- .../HistogramEqualizationProcessor.cs | 18 +++++------ .../HistogramEqualizationProcessor{TPixel}.cs | 30 ++++++++++++------- 9 files changed, 57 insertions(+), 48 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs index af3a336a4d..68c1474bea 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs @@ -17,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; } @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization return new AdaptiveHistogramEqualizationProcessor( this.LuminanceLevels, this.ClipHistogram, - this.ClipLimitPercentage, + this.ClipLimit, this.NumberOfTiles, source, sourceRectangle); diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index 4cda4030fd..e3960035e0 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -30,18 +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. /// The source for the current processor instance. /// The source area to process for the current processor instance. public AdaptiveHistogramEqualizationProcessor( int luminanceLevels, bool clipHistogram, - float clipLimitPercentage, + int clipLimit, int tiles, Image source, Rectangle sourceRectangle) - : base(luminanceLevels, clipHistogram, clipLimitPercentage, source, sourceRectangle) + : base(luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) { Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); @@ -512,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 3ff001c522..632cfcd599 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs @@ -16,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; } @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization return new AdaptiveHistogramEqualizationSlidingWindowProcessor( this.LuminanceLevels, this.ClipHistogram, - this.ClipLimitPercentage, + this.ClipLimit, this.NumberOfTiles, source, sourceRectangle); diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs index 24ac5ccef2..f2f11cbfe5 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs @@ -29,18 +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. /// The source for the current processor instance. /// The source area to process for the current processor instance. public AdaptiveHistogramEqualizationSlidingWindowProcessor( int luminanceLevels, bool clipHistogram, - float clipLimitPercentage, + int clipLimit, int tiles, Image source, Rectangle sourceRectangle) - : base(luminanceLevels, clipHistogram, clipLimitPercentage, source, sourceRectangle) + : base(luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) { Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); @@ -210,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 dab101fcc2..0666b21bf9 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs @@ -15,9 +15,9 @@ 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) { } @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization return new GlobalHistogramEqualizationProcessor( this.LuminanceLevels, this.ClipHistogram, - this.ClipLimitPercentage, + this.ClipLimit, source, sourceRectangle); } diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index 6ae6882479..8aaa5403d4 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -31,16 +31,16 @@ 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. + /// 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, - float clipLimitPercentage, + int clipLimit, Image source, Rectangle sourceRectangle) - : base(luminanceLevels, clipHistogram, clipLimitPercentage, source, sourceRectangle) + : base(luminanceLevels, clipHistogram, clipLimit, source, sourceRectangle) { } @@ -76,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. diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs index 8ddb4834d9..bbb4e3bd25 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs @@ -32,14 +32,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization 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. Histogram bins which exceed this limit, will be capped at this value. Defaults to 40. /// - public float ClipLimitPercentage { get; set; } = 0.035f; + public int ClipLimit { get; set; } = 40; /// - /// 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 01a687ac5c..4273e93753 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs @@ -17,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; } /// @@ -36,9 +36,9 @@ 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(Image source, Rectangle sourceRectangle) @@ -60,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; @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization processor = new AdaptiveHistogramEqualizationSlidingWindowProcessor( options.LuminanceLevels, options.ClipHistogram, - options.ClipLimitPercentage, + options.ClipLimit, options.NumberOfTiles); break; @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization processor = new GlobalHistogramEqualizationProcessor( options.LuminanceLevels, options.ClipHistogram, - options.ClipLimitPercentage); + options.ClipLimit); break; } diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs index f8515ece6f..6e4c16de76 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs @@ -26,24 +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. + /// 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, - float clipLimitPercentage, + 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; } /// @@ -57,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. @@ -96,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); @@ -114,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) { @@ -122,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++; + } + } } /// From 03ba4c6e77969a7cdad56e5115b8f8de30650ba4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 17 Sep 2019 15:37:50 +1000 Subject: [PATCH 43/87] Refactor Image.Dispose --- src/ImageSharp/IImage.cs | 4 ++-- src/ImageSharp/Image.cs | 27 +++++++++++-------------- src/ImageSharp/ImageExtensions.cs | 13 +----------- src/ImageSharp/ImageFrame.cs | 12 ++++++++++- src/ImageSharp/ImageFrame{TPixel}.cs | 16 +++++++-------- src/ImageSharp/Image{TPixel}.cs | 30 +++++++++++++++++++++++++--- 6 files changed, 61 insertions(+), 41 deletions(-) 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..696f836628 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,7 +100,7 @@ namespace SixLabors.ImageSharp Guard.NotNull(encoder, nameof(encoder)); this.EnsureNotDisposed(); - EncodeVisitor visitor = new EncodeVisitor(encoder, stream); + var visitor = new EncodeVisitor(encoder, stream); this.AcceptVisitor(visitor); } @@ -144,9 +135,15 @@ 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. /// - protected abstract void DisposeImpl(); + internal abstract void EnsureNotDisposed(); 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/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 5c9ff489e1..0436eb9d2b 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -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; } diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index a7ea58652c..6dbfde20c6 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; @@ -18,9 +18,11 @@ namespace SixLabors.ImageSharp /// For generic -s the pixel type is known at compile time. /// /// The pixel format. - public sealed class Image : Image + public 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,7 +187,29 @@ 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."); + } + } /// internal override void AcceptVisitor(IImageVisitor visitor) From 76708ef2d605c7468d02332b96f08c77783906f2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 17 Sep 2019 15:45:58 +1000 Subject: [PATCH 44/87] Refactor IImageProcessor --- .../Processors/CloningImageProcessor{TPixel}.cs | 7 +------ .../Processing/Processors/ImageProcessor{TPixel}.cs | 8 ++------ 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs index 8b3e1eb965..c2c8690529 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs @@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.Processing.Processors internal abstract class CloningImageProcessor : ICloningImageProcessor where TPixel : struct, IPixel { - private bool isDisposed; - /// /// Initializes a new instance of the class. /// @@ -104,6 +102,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public void Dispose() { this.Dispose(true); + GC.SuppressFinalize(this); } /// @@ -160,10 +159,6 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Whether to dispose managed and unmanaged objects. protected virtual void Dispose(bool disposing) { - if (!this.isDisposed) - { - this.isDisposed = true; - } } } } diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs index 8ac8cd67bc..55b4d3dc73 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs @@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.Processing.Processors internal abstract class ImageProcessor : IImageProcessor where TPixel : struct, IPixel { - private bool isDisposed; - /// /// Initializes a new instance of the class. /// @@ -97,6 +95,8 @@ namespace SixLabors.ImageSharp.Processing.Processors /// public virtual void Dispose() { + this.Dispose(true); + GC.SuppressFinalize(this); } /// @@ -142,10 +142,6 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Whether to dispose managed and unmanaged objects. protected virtual void Dispose(bool disposing) { - if (!this.isDisposed) - { - this.isDisposed = true; - } } } } From f5c9814a00a607bd3a404fa97e3c303e0afa9353 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 17 Sep 2019 16:45:05 +1000 Subject: [PATCH 45/87] Expose visitor through advanced namespace. --- .../Processors/Drawing/DrawImageProcessor.cs | 1 + .../Advanced/AdvancedImageExtensions.cs | 9 +++++ .../{ => Advanced}/IImageVisitor.cs | 10 +++--- src/ImageSharp/Image.cs | 8 ++--- src/ImageSharp/Image{TPixel}.cs | 4 +-- .../Extensions/ProcessingExtensions.cs | 33 ++++++++++++------- .../Processors/ImageProcessorExtensions.cs | 1 + 7 files changed, 43 insertions(+), 23 deletions(-) rename src/ImageSharp/{ => Advanced}/IImageVisitor.cs (64%) diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs index dc55112c9c..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. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; 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/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/Image.cs b/src/ImageSharp/Image.cs index 57f60f2e75..dbdbfbd8f4 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -109,8 +109,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)); } /// @@ -131,11 +130,12 @@ namespace SixLabors.ImageSharp where TPixel2 : struct, IPixel; /// - /// Accept a . + /// Accepts a . /// Implemented by invoking /// with the pixel type of the image. /// - internal abstract void AcceptVisitor(IImageVisitor visitor); + /// The visitor. + protected internal abstract void Accept(IImageVisitor visitor); /// /// Update the size of the image after mutation. diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index a7ea58652c..994a2c586a 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; @@ -188,7 +188,7 @@ namespace SixLabors.ImageSharp protected override void DisposeImpl() => this.Frames.Dispose(); /// - internal override void AcceptVisitor(IImageVisitor visitor) + protected internal override void Accept(IImageVisitor visitor) { this.EnsureNotDisposed(); 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/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs index 53eedfd207..feb4c9f19d 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; From 4c2ed503d6363fd86e2204b7fade549e719d2b9a Mon Sep 17 00:00:00 2001 From: ip75 Date: Tue, 17 Sep 2019 15:20:53 +0300 Subject: [PATCH 46/87] change comment --- src/ImageSharp/Processing/Extensions/LightnessExtension.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Processing/Extensions/LightnessExtension.cs b/src/ImageSharp/Processing/Extensions/LightnessExtension.cs index b56eee1b86..688c6cd3c3 100644 --- a/src/ImageSharp/Processing/Extensions/LightnessExtension.cs +++ b/src/ImageSharp/Processing/Extensions/LightnessExtension.cs @@ -6,13 +6,12 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Defines extensions that allow the alteration of the hue component of an - /// using Mutate/Clone. + /// Defines extensions that allow to change image lightness in terms of HSL. /// public static class LightnessExtension { /// - /// Alters the hue component of the image. + /// Alters the lightness parameter of the image. /// /// The image this method extends. /// Lightness parameter of image in HSL color scheme. @@ -21,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing => source.ApplyProcessor(new LightnessProcessor(lightness)); /// - /// Alters the hue component of the image. + /// Alters the lightness parameter of the image. /// /// The image this method extends. /// Lightness parameter of image in HSL color scheme. From 28c6776884895fbe2f466c4cef3acc04ca817bbb Mon Sep 17 00:00:00 2001 From: ip75 Date: Tue, 17 Sep 2019 15:55:33 +0300 Subject: [PATCH 47/87] LightnessTest --- .../Processors/Filters/LightnessTest.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs 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..78d8af5870 --- /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); + } +} \ No newline at end of file From 8957c9c19cccb554714484168b5e5536d34fb184 Mon Sep 17 00:00:00 2001 From: ip75 Date: Tue, 17 Sep 2019 16:31:41 +0300 Subject: [PATCH 48/87] Value 1.5 must be greater than or equal to 0 and less than or equal to 1 --- .../Processing/Processors/Filters/LightnessTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs index 78d8af5870..f18642d103 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects = new TheoryData { .5F, - 1.5F + .5F }; [Theory] From 1796862302eb9a9ef478e827fca1996f8a8d9e64 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 18 Sep 2019 00:06:40 +1000 Subject: [PATCH 49/87] Make processors public, refactor cloning. --- .../DrawImageProcessor{TPixelBg,TPixelFg}.cs | 16 ++--- .../DefaultImageProcessorContext{TPixel}.cs | 22 +++---- .../Processors/CloningImageProcessor.cs | 31 ++++++++++ .../CloningImageProcessor{TPixel}.cs | 45 +++++++++----- .../ICloningImageProcessor{TPixel}.cs | 15 ++--- .../Processing/Processors/IImageProcessor.cs | 2 +- .../Processors/IImageProcessor{TPixel}.cs | 11 +--- .../Processors/ImageProcessorExtensions.cs | 2 +- .../Processors/ImageProcessor{TPixel}.cs | 9 +-- .../Transforms/AffineTransformProcessor.cs | 13 ++-- .../Processors/Transforms/CropProcessor.cs | 11 ++-- .../ProjectiveTransformProcessor.cs | 11 +--- .../Transforms/Resize/ResizeProcessor.cs | 8 +-- .../Processors/Transforms/RotateProcessor.cs | 7 +-- .../Processors/Transforms/SkewProcessor.cs | 5 +- .../Processing/ImageProcessingContextTests.cs | 59 ++++++++++++------- 16 files changed, 149 insertions(+), 118 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/CloningImageProcessor.cs 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 6cfa23cce6..76082136c7 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -91,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( @@ -102,14 +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); + } + }); } } } diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs index a6e3dc03a0..7f73166942 100644 --- a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs +++ b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs @@ -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(); } @@ -64,26 +67,25 @@ namespace SixLabors.ImageSharp.Processing { 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. - using (IImageProcessor specificProcessor = processor.CreatePixelSpecificProcessor(this.source, rectangle)) + // 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 CloningImageProcessor cloningImageProcessor) { - // TODO: if 'specificProcessor' is not an ICloningImageProcessor we are unnecessarily disposing and recreating it. - // This should be solved in a future refactor. - if (specificProcessor is ICloningImageProcessor cloningImageProcessor) + using (ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificProcessor(this.source, rectangle)) { - this.destination = cloningImageProcessor.CloneAndApply(); + this.destination = pixelProcessor.CloneAndExecute(); return this; } } + // Not a cloning processor? We need to create a clone to operate on. this.destination = this.source.Clone(); } + // Standard processing pipeline. using (IImageProcessor specificProcessor = processor.CreatePixelSpecificProcessor(this.destination, rectangle)) { - specificProcessor.Apply(); + specificProcessor.Execute(); } return this; diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs new file mode 100644 index 0000000000..6ab0fcb13b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs @@ -0,0 +1,31 @@ +// 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 +{ + /// + /// The base class for all cloning image processors. + /// + public abstract class CloningImageProcessor : IImageProcessor + { + /// + /// 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 + public abstract ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + where TPixel : struct, IPixel; + + /// + IImageProcessor IImageProcessor.CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + => this.CreatePixelSpecificProcessor(source, sourceRectangle); + } +} diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs index 8b3e1eb965..6b0329e772 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs @@ -9,10 +9,12 @@ 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 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. - internal abstract class CloningImageProcessor : ICloningImageProcessor + public abstract class CloningImageProcessor : ICloningImageProcessor where TPixel : struct, IPixel { private bool isDisposed; @@ -45,16 +47,12 @@ namespace SixLabors.ImageSharp.Processing.Processors protected Configuration Configuration { get; } /// - public Image CloneAndApply() + public Image CloneAndExecute() { try { Image clone = this.CreateDestination(); - - if (clone.Frames.Count != this.Source.Frames.Count) - { - throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames."); - } + this.CheckFrameCount(this.Source, clone); Configuration configuration = this.Source.GetConfiguration(); this.BeforeImageApply(clone); @@ -86,17 +84,24 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - public void Apply() + public void Execute() { - using (Image cloned = this.CloneAndApply()) + // 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 { - // we now need to move the pixel data/size data from one image base to another - if (cloned.Frames.Count != this.Source.Frames.Count) - { - throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames."); - } + clone = this.CloneAndExecute(); - this.Source.SwapOrCopyPixelsBuffersFrom(cloned); + // 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(); } } @@ -165,5 +170,13 @@ namespace SixLabors.ImageSharp.Processing.Processors this.isDisposed = true; } } + + 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/ICloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs index 1a21be1f93..c34bf60ae8 100644 --- a/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { @@ -10,19 +9,13 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Encapsulates methods to alter the pixels of a new image, cloned from the original image. /// /// The pixel format. - internal interface ICloningImageProcessor : IImageProcessor + public interface ICloningImageProcessor : IImageProcessor where TPixel : struct, IPixel { /// - /// Applies the process to the specified portion of the specified . + /// Clones the specified and executes the process against the clone. /// - /// - /// The target is null. - /// - /// - /// The target doesn't fit the dimension of the image. - /// - /// Returns the cloned image after there processor has been applied to it. - Image CloneAndApply(); + /// The . + Image CloneAndExecute(); } } diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index 4fff5273ac..fb7a6a4d9d 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor.cs @@ -15,7 +15,7 @@ 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. diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs index 3d6e0d765e..1b874e4b94 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs @@ -3,7 +3,6 @@ using System; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { @@ -15,14 +14,8 @@ namespace SixLabors.ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - /// Applies the process to the specified portion of the specified . + /// Executes the process against the specified . /// - /// - /// The target is null. - /// - /// - /// The target doesn't fit the dimension of the image. - /// - void Apply(); + void Execute(); } } diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs index 53eedfd207..9f41e08395 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { using (IImageProcessor processorImpl = this.processor.CreatePixelSpecificProcessor(image, this.sourceRectangle)) { - processorImpl.Apply(); + processorImpl.Execute(); } } } diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs index 8ac8cd67bc..b224adc3f4 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs @@ -9,10 +9,11 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { /// - /// Allows the application of processors to images. + /// The base class for all pixel specific image processors. + /// Allows the application of processing algorithms to the image. /// /// The pixel format. - internal abstract class ImageProcessor : IImageProcessor + public abstract class ImageProcessor : IImageProcessor where TPixel : struct, IPixel { private bool isDisposed; @@ -45,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors protected Configuration Configuration { get; } /// - public void Apply() + public void Execute() { try { @@ -71,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - /// Applies the processor to just a single ImageBase. + /// Applies the processor to a single image frame. /// /// the source image. public void Apply(ImageFrame source) diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index 6e669e7779..be5675578e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -2,8 +2,6 @@ // 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(Image source, Rectangle sourceRectangle) - where TPixel : struct, IPixel - { - return new AffineTransformProcessor(this, source, sourceRectangle); - } + /// + public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + => new AffineTransformProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 6105330df3..025592a369 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. // 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(Image source, Rectangle sourceRectangle) - where TPixel : struct, IPixel - { - return new CropProcessor(this, source, sourceRectangle); - } + public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + => new CropProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index 15a6e2d095..babdee593a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -2,8 +2,6 @@ // 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(Image source, Rectangle sourceRectangle) - where TPixel : struct, IPixel - { - return new ProjectiveTransformProcessor(this, source, sourceRectangle); - } + public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + => new ProjectiveTransformProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 35e22757c1..5390fa4a4a 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,7 +8,7 @@ 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. @@ -58,8 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public bool Compand { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - where TPixel : struct, IPixel + public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) => new ResizeProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 10d6cdc943..277cc05b22 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -2,7 +2,6 @@ // 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(Image source, Rectangle sourceRectangle) - { - return new RotateProcessor(this, source, sourceRectangle); - } + public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + => new RotateProcessor(this, source, sourceRectangle); } } 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/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs index 589d595275..afb2cbecd2 100644 --- a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs +++ b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs @@ -21,6 +21,8 @@ namespace SixLabors.ImageSharp.Tests.Processing private readonly Mock processorDefinition; + private readonly Mock cloningProcessorDefinition; + private readonly Mock> regularProcessorImpl; private readonly Mock> cloningProcessorImpl; @@ -30,18 +32,20 @@ namespace SixLabors.ImageSharp.Tests.Processing 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 } - }; + { + { false, false }, + { false, true }, + { true, false }, + { true, true } + }; + [Theory] [MemberData(nameof(ProcessorTestData))] public void Mutate_RegularProcessor(bool throwException, bool useBounds) @@ -57,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Processing this.MutateApply(useBounds); } - this.regularProcessorImpl.Verify(p => p.Apply(), Times.Once()); + this.regularProcessorImpl.Verify(p => p.Execute(), Times.Once()); this.regularProcessorImpl.Verify(p => p.Dispose(), Times.Once()); } @@ -69,16 +73,15 @@ namespace SixLabors.ImageSharp.Tests.Processing if (throwException) { - Assert.Throws(() => this.CloneApply(useBounds)); + Assert.Throws(() => this.CloneRegularApply(useBounds)); } else { - this.CloneApply(useBounds); + this.CloneRegularApply(useBounds); } - // TODO: This should be Times.Once(). See comments in DefaultImageProcessingContext.ApplyProcessor() - this.regularProcessorImpl.Verify(p => p.Apply(), Times.AtLeast(1)); - this.regularProcessorImpl.Verify(p => p.Dispose(), Times.AtLeast(1)); + this.regularProcessorImpl.Verify(p => p.Execute(), Times.Once); + this.regularProcessorImpl.Verify(p => p.Dispose(), Times.Once); } [Theory] @@ -96,7 +99,7 @@ namespace SixLabors.ImageSharp.Tests.Processing this.MutateApply(useBounds); } - this.cloningProcessorImpl.Verify(p => p.Apply(), Times.Once()); + this.cloningProcessorImpl.Verify(p => p.Execute(), Times.Once()); this.cloningProcessorImpl.Verify(p => p.Dispose(), Times.Once()); } @@ -108,14 +111,14 @@ namespace SixLabors.ImageSharp.Tests.Processing if (throwException) { - Assert.Throws(() => this.CloneApply(useBounds)); + Assert.Throws(() => this.CloneCloneApply(useBounds)); } else { - this.CloneApply(useBounds); + this.CloneCloneApply(useBounds); } - this.cloningProcessorImpl.Verify(p => p.CloneAndApply(), Times.Once()); + this.cloningProcessorImpl.Verify(p => p.CloneAndExecute(), Times.Once()); this.cloningProcessorImpl.Verify(p => p.Dispose(), Times.Once()); } @@ -131,7 +134,7 @@ namespace SixLabors.ImageSharp.Tests.Processing } } - private void CloneApply(bool useBounds) + private void CloneRegularApply(bool useBounds) { if (useBounds) { @@ -143,11 +146,23 @@ namespace SixLabors.ImageSharp.Tests.Processing } } + 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.Apply()).Throws(new ImageProcessingException("Test")); + this.regularProcessorImpl.Setup(p => p.Execute()).Throws(new ImageProcessingException("Test")); } this.processorDefinition @@ -159,13 +174,17 @@ namespace SixLabors.ImageSharp.Tests.Processing { if (throwsException) { - this.cloningProcessorImpl.Setup(p => p.Apply()).Throws(new ImageProcessingException("Test")); - this.cloningProcessorImpl.Setup(p => p.CloneAndApply()).Throws(new ImageProcessingException("Test")); + this.cloningProcessorImpl.Setup(p => p.Execute()).Throws(new ImageProcessingException("Test")); + this.cloningProcessorImpl.Setup(p => p.CloneAndExecute()).Throws(new ImageProcessingException("Test")); } this.processorDefinition .Setup(p => p.CreatePixelSpecificProcessor(It.IsAny>(), It.IsAny())) .Returns(this.cloningProcessorImpl.Object); + + this.cloningProcessorDefinition + .Setup(p => p.CreatePixelSpecificProcessor(It.IsAny>(), It.IsAny())) + .Returns(this.cloningProcessorImpl.Object); } } } From 5834a2e1400fd0b11e4102f83c15159f1ee8902d Mon Sep 17 00:00:00 2001 From: ip75 Date: Tue, 17 Sep 2019 18:37:33 +0300 Subject: [PATCH 50/87] fix value ranges for lightness --- src/ImageSharp/Processing/KnownFilterMatrices.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Processing/KnownFilterMatrices.cs b/src/ImageSharp/Processing/KnownFilterMatrices.cs index 999ecdc155..4a325503fc 100644 --- a/src/ImageSharp/Processing/KnownFilterMatrices.cs +++ b/src/ImageSharp/Processing/KnownFilterMatrices.cs @@ -436,14 +436,13 @@ namespace SixLabors.ImageSharp.Processing /// 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 makes the image completely white. - /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. + /// A value of -1 will create an image that is completely black. A value of 1 makes the image completely white. /// - /// The proportion of the conversion. Must be greater than or equal to 0. + /// The proportion of the conversion. Must be between -1 and 1. /// The public static ColorMatrix CreateLightnessFilter(float amount) { - Guard.MustBeBetweenOrEqualTo(amount, 0, 1F, nameof(amount)); + Guard.MustBeBetweenOrEqualTo(amount, -1F, 1F, nameof(amount)); return new ColorMatrix { M11 = 1F, From b0ef8a10d1fac2d9bb0cb7fb75a926b6a76cf587 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 18 Sep 2019 08:36:04 +1000 Subject: [PATCH 51/87] reseal Image{TPixel} --- src/ImageSharp/Image{TPixel}.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 6dbfde20c6..181e818ee7 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp /// For generic -s the pixel type is known at compile time. /// /// The pixel format. - public class Image : Image + public sealed class Image : Image where TPixel : struct, IPixel { private bool isDisposed; From 44692b0c1b2aea722c8a70c21b3be4e20a5356fd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 18 Sep 2019 08:45:03 +1000 Subject: [PATCH 52/87] Remove protected. --- src/ImageSharp/Image.cs | 16 ++++++++-------- src/ImageSharp/Image{TPixel}.cs | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index dbdbfbd8f4..9030ae6afd 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -129,14 +129,6 @@ namespace SixLabors.ImageSharp public abstract Image CloneAs(Configuration configuration) where TPixel2 : struct, IPixel; - /// - /// Accepts a . - /// Implemented by invoking - /// with the pixel type of the image. - /// - /// The visitor. - protected internal abstract void Accept(IImageVisitor visitor); - /// /// Update the size of the image after mutation. /// @@ -148,6 +140,14 @@ namespace SixLabors.ImageSharp /// protected abstract void DisposeImpl(); + /// + /// Accepts a . + /// Implemented by invoking + /// with the pixel type of the image. + /// + /// The visitor. + internal abstract void Accept(IImageVisitor visitor); + private class EncodeVisitor : IImageVisitor { private readonly IImageEncoder encoder; diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 994a2c586a..3d92c3be3d 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -187,17 +187,17 @@ namespace SixLabors.ImageSharp /// protected override void DisposeImpl() => this.Frames.Dispose(); + /// + public override string ToString() => $"Image<{typeof(TPixel).Name}>: {this.Width}x{this.Height}"; + /// - protected internal override void Accept(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. /// From 464598cb3fee6735ed78b8a25fa76b39ddb2c168 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 20 Sep 2019 00:24:50 +1000 Subject: [PATCH 53/87] Refactor cloning processors and tests --- src/ImageSharp/Advanced/AotCompilerTools.cs | 36 +++---- .../DefaultImageProcessorContext{TPixel}.cs | 4 +- .../Processors/CloningImageProcessor.cs | 17 +-- .../CloningImageProcessor{TPixel}.cs | 35 ++++-- .../EdgeDetector2DProcessor{TPixel}.cs | 2 +- .../EdgeDetectorCompassProcessor{TPixel}.cs | 2 +- .../EdgeDetectorProcessor{TPixel}.cs | 2 +- .../Filters/LomographProcessor{TPixel}.cs | 2 +- .../Filters/PolaroidProcessor{TPixel}.cs | 4 +- .../Processors/ICloningImageProcessor.cs | 27 +++++ .../ICloningImageProcessor{TPixel}.cs | 2 +- .../Processors/ImageProcessorExtensions.cs | 17 +-- .../Processors/ImageProcessor{TPixel}.cs | 2 +- .../Transforms/AffineTransformProcessor.cs | 2 +- .../AffineTransformProcessor{TPixel}.cs | 55 ++++------ .../Transforms/AutoOrientProcessor{TPixel}.cs | 18 ++-- .../Processors/Transforms/CropProcessor.cs | 2 +- .../Transforms/CropProcessor{TPixel}.cs | 46 +++----- .../EntropyCropProcessor{TPixel}.cs | 6 +- .../ProjectiveTransformProcessor.cs | 2 +- .../ProjectiveTransformProcessor{TPixel}.cs | 56 ++++------ .../Transforms/Resize/ResizeProcessor.cs | 2 +- .../Resize/ResizeProcessor{TPixel}.cs | 101 +++++------------- .../Processors/Transforms/RotateProcessor.cs | 2 +- .../Drawing/FillRegionProcessorTests.cs | 4 +- .../Processing/ImageProcessingContextTests.cs | 32 ++++-- 26 files changed, 221 insertions(+), 259 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 8d3a074b5f..1ceba5f90e 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, 0); + } } /// @@ -171,16 +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 genericResizeProcessor = (ResizeProcessor)new ResizeProcessor(new ResizeOptions(), default).CreatePixelSpecificProcessor(new Image(0, 0), default); - genericResizeProcessor.AotCreateDestination(); - } } } diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs index 7f73166942..328ccdf941 100644 --- a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs +++ b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs @@ -69,9 +69,9 @@ namespace SixLabors.ImageSharp.Processing { // 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 CloningImageProcessor cloningImageProcessor) + if (processor is ICloningImageProcessor cloningImageProcessor) { - using (ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificProcessor(this.source, rectangle)) + using (ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificCloningProcessor(this.source, rectangle)) { this.destination = pixelProcessor.CloneAndExecute(); return this; diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs index 6ab0fcb13b..5e9ca2e542 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs @@ -9,23 +9,14 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The base class for all cloning image processors. /// - public abstract class CloningImageProcessor : IImageProcessor + public abstract class CloningImageProcessor : ICloningImageProcessor { - /// - /// 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 - public abstract ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + /// + public abstract ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; /// IImageProcessor IImageProcessor.CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - => this.CreatePixelSpecificProcessor(source, sourceRectangle); + => this.CreatePixelSpecificCloningProcessor(source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs index 1290a1032b..42d2f0e1df 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs @@ -2,6 +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.PixelFormats; using SixLabors.Primitives; @@ -40,16 +42,16 @@ namespace SixLabors.ImageSharp.Processing.Processors protected Rectangle SourceRectangle { get; } /// - /// Gets the instance to use when performing operations. + /// Gets the instance to use when performing operations. /// protected Configuration Configuration { get; } /// - public Image CloneAndExecute() + Image ICloningImageProcessor.CloneAndExecute() { try { - Image clone = this.CreateDestination(); + Image clone = this.CreateTarget(); this.CheckFrameCount(this.Source, clone); Configuration configuration = this.Source.GetConfiguration(); @@ -82,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - public void Execute() + 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 @@ -90,7 +92,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Image clone = default; try { - clone = this.CloneAndExecute(); + 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); @@ -111,10 +113,10 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - /// Generates a deep clone of the source image that operations should be applied to. + /// Gets the size of the target image. /// - /// The cloned image. - protected virtual Image CreateDestination() => this.Source.Clone(); + /// The . + protected abstract Size GetTargetSize(); /// /// This method is called before the process is applied to prepare the processor. @@ -166,6 +168,23 @@ namespace SixLabors.ImageSharp.Processing.Processors { } + 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) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs index 7b070f99a3..8358abe7df 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(this.Source, this.SourceRectangle); + new GrayscaleBt709Processor(1F).Execute(this.Source, this.SourceRectangle); } base.BeforeImageApply(); diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index b7119ef44b..dc9974c616 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(this.Source, this.SourceRectangle); + new GrayscaleBt709Processor(1F).Execute(this.Source, this.SourceRectangle); } base.BeforeImageApply(); diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs index fc762cf1bb..5246dc3b72 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(this.Source, this.SourceRectangle); + new GrayscaleBt709Processor(1F).Execute(this.Source, this.SourceRectangle); } base.BeforeImageApply(); diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs index ab832a2759..7d3a5bbc0a 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// protected override void AfterImageApply() { - new VignetteProcessor(VeryDarkGreen).Apply(this.Source, this.SourceRectangle); + new VignetteProcessor(VeryDarkGreen).Execute(this.Source, this.SourceRectangle); base.AfterImageApply(); } } diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs index 0be5bbb0de..f7ab1a1ec2 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs @@ -31,8 +31,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// protected override void AfterImageApply() { - new VignetteProcessor(VeryDarkOrange).Apply(this.Source, this.SourceRectangle); - new GlowProcessor(LightOrange, this.Source.Width / 4F).Apply(this.Source, this.SourceRectangle); + new VignetteProcessor(VeryDarkOrange).Execute(this.Source, this.SourceRectangle); + new GlowProcessor(LightOrange, this.Source.Width / 4F).Execute(this.Source, this.SourceRectangle); base.AfterImageApply(); } } diff --git a/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs new file mode 100644 index 0000000000..554a4b8860 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs @@ -0,0 +1,27 @@ +// 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 +{ + /// + /// Defines an algorithm to alter the pixels of a cloned image. + /// + public interface ICloningImageProcessor : IImageProcessor + { + /// + /// 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 + ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) + where TPixel : struct, IPixel; + } +} diff --git a/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs index c34bf60ae8..84b1262299 100644 --- a/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors { /// - /// Encapsulates methods to alter the pixels of a new image, cloned from the original image. + /// Implements an algorithm to alter the pixels of a cloned image. /// /// The pixel format. public interface ICloningImageProcessor : IImageProcessor diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs index 19e594a324..ce8ed813b5 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -9,18 +9,21 @@ namespace SixLabors.ImageSharp.Processing.Processors { internal static class ImageProcessorExtensions { - public static void Apply(this IImageProcessor processor, Image source, Rectangle sourceRectangle) - { - source.AcceptVisitor(new ApplyVisitor(processor, sourceRectangle)); - } + /// + /// Executes the processor against the given source image and rectangle bounds. + /// + /// 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; diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs index eb1dc4ba0f..3e46e3c087 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors protected Configuration Configuration { get; } /// - public void Execute() + void IImageProcessor.Execute() { try { diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index be5675578e..6ca844fae6 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Size TargetDimensions { get; } /// - public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) => new AffineTransformProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index 7c50c04f3a..97b8b009b5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -2,10 +2,7 @@ // 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.PixelFormats; @@ -20,6 +17,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class AffineTransformProcessor : TransformProcessor where TPixel : struct, IPixel { + private Size targetSize; + private Matrix3x2 transformMatrix; + private readonly IResampler resampler; + /// /// Initializes a new instance of the class. /// @@ -29,50 +30,37 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms 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() - { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = this.Source.Frames.Select, ImageFrame>( - x => new ImageFrame(this.Configuration, this.TargetDimensions, x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(this.Configuration, this.Source.Metadata.DeepClone(), frames); - } + protected override Size GetTargetSize() => this.targetSize; /// 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); - IResampler sampler = this.Definition.Sampler; - - if (sampler is NearestNeighborResampler) + if (this.resampler is NearestNeighborResampler) { ParallelHelper.IterateRows( targetBounds, - this.Configuration, + configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) @@ -82,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 (this.SourceRectangle.Contains(point.X, point.Y)) + if (sourceBounds.Contains(point.X, point.Y)) { destRow[x] = source[point.X, point.Y]; } @@ -93,19 +81,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - var kernel = new TransformKernelMap(this.Configuration, source.Size(), destination.Size(), sampler); + var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler); + try { ParallelHelper.IterateRowsWithTempBuffer( targetBounds, - this.Configuration, + configuration, (rows, vectorBuffer) => { Span vectorSpan = vectorBuffer.Span; for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(this.Configuration, targetRowSpan, vectorSpan); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); ref float ySpanRef = ref kernel.GetYStartReference(y); ref float xSpanRef = ref kernel.GetXStartReference(y); @@ -124,7 +113,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } PixelOperations.Instance.FromVector4Destructive( - this.Configuration, + configuration, vectorSpan, targetRowSpan); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs index a5170c96a1..b9952ac8fe 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs @@ -34,33 +34,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms switch (orientation) { case OrientationMode.TopRight: - new FlipProcessor(FlipMode.Horizontal).Apply(this.Source, this.SourceRectangle); + new FlipProcessor(FlipMode.Horizontal).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.BottomRight: - new RotateProcessor((int)RotateMode.Rotate180, size).Apply(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate180, size).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.BottomLeft: - new FlipProcessor(FlipMode.Vertical).Apply(this.Source, this.SourceRectangle); + new FlipProcessor(FlipMode.Vertical).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.LeftTop: - new RotateProcessor((int)RotateMode.Rotate90, size).Apply(this.Source, this.SourceRectangle); - new FlipProcessor(FlipMode.Horizontal).Apply(this.Source, this.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(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.RightBottom: - new FlipProcessor(FlipMode.Vertical).Apply(this.Source, this.SourceRectangle); - new RotateProcessor((int)RotateMode.Rotate270, size).Apply(this.Source, this.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(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.Unknown: diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 025592a369..245a542084 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Rectangle CropRectangle { get; } /// - public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) => new CropProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index 539a11f028..1bbdd0a161 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -2,9 +2,6 @@ // 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.PixelFormats; @@ -19,7 +16,7 @@ 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. @@ -29,53 +26,42 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The source area to process for the current processor instance. public CropProcessor(CropProcessor definition, Image source, Rectangle sourceRectangle) : base(source, sourceRectangle) - { - this.definition = definition; - } - - private Rectangle CropRectangle => this.definition.CropRectangle; + => this.cropRectangle = definition.CropRectangle; /// - protected override Image CreateDestination() - { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = this.Source.Frames.Select, ImageFrame>( - x => new ImageFrame( - this.Source.GetConfiguration(), - this.CropRectangle.Width, - this.CropRectangle.Height, - x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(this.Source.GetConfiguration(), this.Source.Metadata.DeepClone(), frames); - } + protected override Size GetTargetSize() => new Size(this.cropRectangle.Width, this.cropRectangle.Height); /// protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { - // Handle resize dimensions identical to the original - if (source.Width == destination.Width && source.Height == destination.Height && this.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 = this.Configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); + ParallelExecutionSettings parallelSettings + = this.Configuration + .GetParallelSettings() + .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); } }); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs index b74fbb0ab7..2b900ee360 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs @@ -42,16 +42,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Configuration configuration = this.Source.GetConfiguration(); // Detect the edges. - new SobelProcessor(false).Apply(temp, this.SourceRectangle); + new SobelProcessor(false).Execute(temp, this.SourceRectangle); // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.definition.Threshold).Apply(temp, this.SourceRectangle); + new BinaryThresholdProcessor(this.definition.Threshold).Execute(temp, this.SourceRectangle); // Search for the first white pixels rectangle = ImageMaths.GetFilteredBoundingRectangle(temp.Frames.RootFrame, 0); } - new CropProcessor(rectangle, this.Source.Size()).Apply(this.Source, this.SourceRectangle); + new CropProcessor(rectangle, this.Source.Size()).Execute(this.Source, this.SourceRectangle); base.BeforeImageApply(); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index babdee593a..d91db9a72b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Size TargetDimensions { get; } /// - public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) => new ProjectiveTransformProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index 29dc8a070d..68bfd817e5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Linq; using System.Numerics; using SixLabors.ImageSharp.Advanced; @@ -20,7 +18,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class ProjectiveTransformProcessor : TransformProcessor where TPixel : struct, IPixel { - private readonly ProjectiveTransformProcessor definition; + private Size targetSize; + private readonly IResampler resampler; + private Matrix4x4 transformMatrix; /// /// Initializes a new instance of the class. @@ -31,52 +31,37 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms 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() - { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = this.Source.Frames.Select, ImageFrame>( - x => new ImageFrame( - this.Source.GetConfiguration(), - this.TargetDimensions.Width, - this.TargetDimensions.Height, - x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(this.Source.GetConfiguration(), this.Source.Metadata.DeepClone(), frames); - } + protected override Size GetTargetSize() => this.targetSize; /// 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); - - IResampler sampler = this.definition.Sampler; + Matrix4x4.Invert(this.transformMatrix, out Matrix4x4 matrix); - if (sampler is NearestNeighborResampler) + if (this.resampler is NearestNeighborResampler) { ParallelHelper.IterateRows( targetBounds, - this.Configuration, + configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) @@ -89,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int px = (int)MathF.Round(point.X); int py = (int)MathF.Round(point.Y); - if (this.SourceRectangle.Contains(px, py)) + if (sourceBounds.Contains(px, py)) { destRow[x] = source[px, py]; } @@ -100,19 +85,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - var kernel = new TransformKernelMap(this.Configuration, source.Size(), destination.Size(), sampler); + var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler); + try { ParallelHelper.IterateRowsWithTempBuffer( targetBounds, - this.Configuration, + configuration, (rows, vectorBuffer) => { Span vectorSpan = vectorBuffer.Span; for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(this.Configuration, targetRowSpan, vectorSpan); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); ref float ySpanRef = ref kernel.GetYStartReference(y); ref float xSpanRef = ref kernel.GetXStartReference(y); @@ -131,7 +117,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } PixelOperations.Instance.FromVector4Destructive( - this.Configuration, + configuration, vectorSpan, targetRowSpan); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 5390fa4a4a..ccaa1ef9e5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public bool Compand { get; } /// - public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) => new ResizeProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index b85983a481..78e471ad62 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Linq; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; @@ -24,74 +22,34 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class ResizeProcessor : TransformProcessor where TPixel : struct, IPixel { - private readonly ResizeProcessor parameterSource; 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; - public ResizeProcessor(ResizeProcessor parameterSource, Image source, Rectangle sourceRectangle) + 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 TargetWidth => this.parameterSource.TargetWidth; - - /// - /// Gets the target height. - /// - public int TargetHeight => this.parameterSource.TargetHeight; - - /// - /// Gets the target 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. - /// - /// The result returned from . - internal Image AotCreateDestination() - => this.CreateDestination(); - /// - protected override Image CreateDestination() - { - Image source = this.Source; - Configuration configuration = this.Configuration; - - // 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( - configuration, - this.TargetWidth, - this.TargetHeight, - x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(configuration, source.Metadata.DeepClone(), frames); - } + protected override Size GetTargetSize() => new Size(this.targetWidth, this.targetHeight); /// protected override void BeforeImageApply(Image destination) { - if (!(this.Sampler is NearestNeighborResampler)) + if (!(this.resampler is NearestNeighborResampler)) { Image source = this.Source; Rectangle sourceRectangle = this.SourceRectangle; @@ -99,14 +57,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // 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); } @@ -121,29 +79,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms 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.TargetWidth; - int height = this.TargetHeight; + 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, @@ -153,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++) @@ -169,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); @@ -183,7 +140,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.verticalKernelMap, width, targetWorkingRect, - this.TargetRectangle.Location)) + this.targetRectangle.Location)) { worker.Initialize(); diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 277cc05b22..7d6ec0e08e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public float Degrees { get; } /// - public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) => new RotateProcessor(this, source, sourceRectangle); } } 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/Processing/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs index afb2cbecd2..9d16583cd8 100644 --- a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs +++ b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs @@ -2,12 +2,10 @@ // 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 @@ -21,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Processing private readonly Mock processorDefinition; - private readonly Mock cloningProcessorDefinition; + private readonly Mock cloningProcessorDefinition; private readonly Mock> regularProcessorImpl; @@ -32,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Processing public ImageProcessingContextTests() { this.processorDefinition = new Mock(); - this.cloningProcessorDefinition = new Mock(); + this.cloningProcessorDefinition = new Mock(); this.regularProcessorImpl = new Mock>(); this.cloningProcessorImpl = new Mock>(); } @@ -54,11 +52,11 @@ namespace SixLabors.ImageSharp.Tests.Processing if (throwException) { - Assert.Throws(() => this.MutateApply(useBounds)); + Assert.Throws(() => this.MutateRegularApply(useBounds)); } else { - this.MutateApply(useBounds); + this.MutateRegularApply(useBounds); } this.regularProcessorImpl.Verify(p => p.Execute(), Times.Once()); @@ -92,11 +90,11 @@ namespace SixLabors.ImageSharp.Tests.Processing if (throwException) { - Assert.Throws(() => this.MutateApply(useBounds)); + Assert.Throws(() => this.MutateCloneApply(useBounds)); } else { - this.MutateApply(useBounds); + this.MutateCloneApply(useBounds); } this.cloningProcessorImpl.Verify(p => p.Execute(), Times.Once()); @@ -122,7 +120,7 @@ namespace SixLabors.ImageSharp.Tests.Processing this.cloningProcessorImpl.Verify(p => p.Dispose(), Times.Once()); } - private void MutateApply(bool useBounds) + private void MutateRegularApply(bool useBounds) { if (useBounds) { @@ -134,6 +132,18 @@ namespace SixLabors.ImageSharp.Tests.Processing } } + 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) @@ -178,8 +188,8 @@ namespace SixLabors.ImageSharp.Tests.Processing this.cloningProcessorImpl.Setup(p => p.CloneAndExecute()).Throws(new ImageProcessingException("Test")); } - this.processorDefinition - .Setup(p => p.CreatePixelSpecificProcessor(It.IsAny>(), It.IsAny())) + this.cloningProcessorDefinition + .Setup(p => p.CreatePixelSpecificCloningProcessor(It.IsAny>(), It.IsAny())) .Returns(this.cloningProcessorImpl.Object); this.cloningProcessorDefinition From e947ce5c5a5288a2a1ff130edb9a78b0715882d1 Mon Sep 17 00:00:00 2001 From: ip75 Date: Fri, 20 Sep 2019 15:27:41 +0300 Subject: [PATCH 54/87] remove hsl from comments --- src/ImageSharp/Processing/Extensions/LightnessExtension.cs | 6 +++--- .../Processing/Processors/Filters/LightnessProcessor.cs | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Processing/Extensions/LightnessExtension.cs b/src/ImageSharp/Processing/Extensions/LightnessExtension.cs index 688c6cd3c3..cbe4a8d789 100644 --- a/src/ImageSharp/Processing/Extensions/LightnessExtension.cs +++ b/src/ImageSharp/Processing/Extensions/LightnessExtension.cs @@ -6,7 +6,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Defines extensions that allow to change image lightness in terms of HSL. + /// Defines extensions that allow to change image lightness. /// public static class LightnessExtension { @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Processing /// Alters the lightness parameter of the image. /// /// The image this method extends. - /// Lightness parameter of image in HSL color scheme. + /// Lightness parameter of image. /// The to allow chaining of operations. public static IImageProcessingContext Lightness(this IImageProcessingContext source, float lightness) => source.ApplyProcessor(new LightnessProcessor(lightness)); @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Processing /// Alters the lightness parameter of the image. /// /// The image this method extends. - /// Lightness parameter of image in HSL color scheme. + /// Lightness parameter of image. /// /// The structure that specifies the portion of the image object to alter. /// diff --git a/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs index 35b76f5dea..4f3d332d42 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Initializes a new instance of the class. /// - /// Lightness of image in HSL color scheme + /// Lightness of image public LightnessProcessor(float lightness) : base(KnownFilterMatrices.CreateLightnessFilter(lightness)) { @@ -19,8 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - /// Gets Lightness of image in HSL color scheme. - /// The "brightness relative to the brightness of a similarly illuminated white" https://en.wikipedia.org/wiki/HSL_and_HSV#Lightness + /// Gets Lightness of image. /// public float Lightness { get; } } From 4a397dc3eb6c7d6fce2cbaf3305f1999718cc55d Mon Sep 17 00:00:00 2001 From: ip75 Date: Fri, 20 Sep 2019 15:57:15 +0300 Subject: [PATCH 55/87] Filters test --- .../Processing/Filters/LightnessTest.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs diff --git a/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs new file mode 100644 index 0000000000..16f4fd3793 --- /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(1.5F); + LightnessProcessor processor = this.Verify(); + + Assert.Equal(.5F, processor.Lightness); + } + + [Fact] + public void Lightness_amount_rect_LightnessProcessorDefaultsSet() + { + this.operations.Lightness(1.5F, this.rect); + LightnessProcessor processor = this.Verify(this.rect); + + Assert.Equal(.5F, processor.Lightness); + } + } +} From 48903b17ad03fb34ecc03124268a1e5ac8216f3a Mon Sep 17 00:00:00 2001 From: ip75 Date: Fri, 20 Sep 2019 16:06:55 +0300 Subject: [PATCH 56/87] fix test values --- tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs index 16f4fd3793..e8a378e350 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects [Fact] public void Lightness_amount_LightnessProcessorDefaultsSet() { - this.operations.Lightness(1.5F); + this.operations.Lightness(.5F); LightnessProcessor processor = this.Verify(); Assert.Equal(.5F, processor.Lightness); @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects [Fact] public void Lightness_amount_rect_LightnessProcessorDefaultsSet() { - this.operations.Lightness(1.5F, this.rect); + this.operations.Lightness(.5F, this.rect); LightnessProcessor processor = this.Verify(this.rect); Assert.Equal(.5F, processor.Lightness); From b6e0b6282005764da258fa481d3ab2bf60146853 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 Sep 2019 18:00:40 +0200 Subject: [PATCH 57/87] Change the default value of clipLimit --- .../Processors/Normalization/HistogramEqualizationOptions.cs | 4 ++-- .../Processing/Normalization/HistogramEqualizationTests.cs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs index bbb4e3bd25..58065e5f7b 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs @@ -32,9 +32,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public bool ClipHistogram { get; set; } = false; /// - /// Gets or sets the histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. Defaults to 40. + /// Gets or sets the histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. Defaults to 350. /// - public int ClipLimit { get; set; } = 40; + 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 8. diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index c712325249..ab0808f065 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -133,10 +133,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, LuminanceLevels = 256, ClipHistogram = true, + ClipLimit = 5, NumberOfTiles = 10 }; image.Mutate(x => x.HistogramEqualization(options)); image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider); } } } From a73dca0c65c9a4171f9bd1fdee4480e2dbed7cc2 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 Sep 2019 19:18:53 +0200 Subject: [PATCH 58/87] Change test images for Issue984 to be non transparent --- .../Processing/Normalization/HistogramEqualizationTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index ab0808f065..32da38621a 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -121,8 +121,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization /// See: https://github.com/SixLabors/ImageSharp/pull/984 /// [Theory] - [WithTestPatternImages(110, 110, PixelTypes.Rgba32)] - [WithTestPatternImages(170, 170, PixelTypes.Rgba32)] + [WithTestPatternImages(110, 110, PixelTypes.Rgb24)] + [WithTestPatternImages(170, 170, PixelTypes.Rgb24)] public void Issue984(TestImageProvider provider) where TPixel : struct, IPixel { From e4c44c9063edc09991297557ef34be96c232746a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 1 Oct 2019 12:26:48 +1000 Subject: [PATCH 59/87] Add code of conduct --- .github/CONTRIBUTING.md | 4 ++++ CODE_OF_CONDUCT.md | 3 +++ README.md | 4 ++++ 3 files changed, 11 insertions(+) create mode 100644 CODE_OF_CONDUCT.md 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/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/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. From 2ffe181c98e54c2a2c9feaadd98392a73f0ab1dc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 2 Oct 2019 00:01:27 +1000 Subject: [PATCH 60/87] Fix up code and tests --- .../Extensions/LightnessExtension.cs | 34 -------------- .../Extensions/LightnessExtensions.cs | 44 +++++++++++++++++++ .../Processing/KnownFilterMatrices.cs | 27 +++++++----- .../Processors/Filters/LightnessProcessor.cs | 18 +++++--- .../Processing/Filters/LightnessTest.cs | 4 +- .../Processors/Filters/LightnessTest.cs | 4 +- tests/Images/External | 2 +- 7 files changed, 75 insertions(+), 58 deletions(-) delete mode 100644 src/ImageSharp/Processing/Extensions/LightnessExtension.cs create mode 100644 src/ImageSharp/Processing/Extensions/LightnessExtensions.cs diff --git a/src/ImageSharp/Processing/Extensions/LightnessExtension.cs b/src/ImageSharp/Processing/Extensions/LightnessExtension.cs deleted file mode 100644 index cbe4a8d789..0000000000 --- a/src/ImageSharp/Processing/Extensions/LightnessExtension.cs +++ /dev/null @@ -1,34 +0,0 @@ -// 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 to change image lightness. - /// - public static class LightnessExtension - { - /// - /// Alters the lightness parameter of the image. - /// - /// The image this method extends. - /// Lightness parameter of image. - /// The to allow chaining of operations. - public static IImageProcessingContext Lightness(this IImageProcessingContext source, float lightness) - => source.ApplyProcessor(new LightnessProcessor(lightness)); - - /// - /// Alters the lightness parameter of the image. - /// - /// The image this method extends. - /// Lightness parameter of image. - /// - /// 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 lightness, Rectangle rectangle) - => source.ApplyProcessor(new LightnessProcessor(lightness), rectangle); - } -} 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/KnownFilterMatrices.cs b/src/ImageSharp/Processing/KnownFilterMatrices.cs index 4a325503fc..31b19433c7 100644 --- a/src/ImageSharp/Processing/KnownFilterMatrices.cs +++ b/src/ImageSharp/Processing/KnownFilterMatrices.cs @@ -436,23 +436,26 @@ namespace SixLabors.ImageSharp.Processing /// Create a lightness filter matrix using the given amount. /// /// - /// A value of -1 will create an image that is completely black. A value of 1 makes the image completely white. + /// 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 between -1 and 1. + /// The proportion of the conversion. Must be greater than or equal to 0. /// The public static ColorMatrix CreateLightnessFilter(float amount) { - Guard.MustBeBetweenOrEqualTo(amount, -1F, 1F, nameof(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 - }; + { + M11 = 1F, + M22 = 1F, + M33 = 1F, + M44 = 1F, + M51 = amount, + M52 = amount, + M53 = amount + }; } /// diff --git a/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs index 4f3d332d42..49be3b6a67 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LightnessProcessor.cs @@ -4,23 +4,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// - /// Applies a lightness filter matrix using + /// Applies a lightness filter matrix using the given amount. /// public sealed class LightnessProcessor : FilterProcessor { /// /// Initializes a new instance of the class. /// - /// Lightness of image - public LightnessProcessor(float lightness) - : base(KnownFilterMatrices.CreateLightnessFilter(lightness)) + /// + /// 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.Lightness = lightness; + this.Amount = amount; } /// - /// Gets Lightness of image. + /// Gets the proportion of the conversion /// - public float Lightness { get; } + public float Amount { get; } } } diff --git a/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs index e8a378e350..29d1d63e62 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/LightnessTest.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects this.operations.Lightness(.5F); LightnessProcessor processor = this.Verify(); - Assert.Equal(.5F, processor.Lightness); + Assert.Equal(.5F, processor.Amount); } [Fact] @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects this.operations.Lightness(.5F, this.rect); LightnessProcessor processor = this.Verify(this.rect); - Assert.Equal(.5F, processor.Lightness); + Assert.Equal(.5F, processor.Amount); } } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs index f18642d103..c330ed6d9a 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Filters/LightnessTest.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects = new TheoryData { .5F, - .5F + 1.5F }; [Theory] @@ -27,4 +27,4 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects public void ApplyLightnessFilter(TestImageProvider provider, float value) where TPixel : struct, IPixel => provider.RunValidatingProcessorTest(ctx => ctx.Lightness(value), value, this.imageComparer); } -} \ No newline at end of file +} diff --git a/tests/Images/External b/tests/Images/External index 99a2bc523c..58b2c01f9b 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 99a2bc523cd4eb00e37af20d1b2088fa11564c57 +Subproject commit 58b2c01f9b66dd42d2b5b040b85e6846083b5e5f From a04fc431ab1967bc99fbdf9d644c739d4efdc324 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 1 Oct 2019 19:00:16 +0200 Subject: [PATCH 61/87] Update external for histogram reference images --- tests/Images/External | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Images/External b/tests/Images/External index 99a2bc523c..468e39ad25 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 99a2bc523cd4eb00e37af20d1b2088fa11564c57 +Subproject commit 468e39ad25c9c2f38d5a16d603ec09f11d1fe0a2 From 15c572126ab580381c9ed0064e96fda983fe2dc1 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 3 Oct 2019 19:00:13 +0200 Subject: [PATCH 62/87] Add additional information about the climpLimit --- .../Normalization/HistogramEqualizationOptions.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs index 58065e5f7b..b55b725a6b 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs @@ -20,7 +20,8 @@ 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; @@ -32,12 +33,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization public bool ClipHistogram { get; set; } = false; /// - /// Gets or sets the histogram clip limit. Histogram bins which exceed this limit, will be capped at this value. Defaults to 350. + /// 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. /// + /// 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 8. + /// 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; } = 8; } From 64bba4c7d01af7609b59f9cf3fc63ddd71131043 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 4 Oct 2019 20:23:26 +0200 Subject: [PATCH 63/87] Fix issue when reading data spreading over multiple IDAT chunks. Fixes issue #1014. --- .../Formats/Png/Zlib/ZlibInflateStream.cs | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs index e4645c44ac..3eb34b8617 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs @@ -13,7 +13,7 @@ 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. /// @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib private static readonly Func GetDataNoOp = () => 0; /// - /// The inner raw memory stream + /// The inner raw memory stream. /// private readonly Stream innerStream; @@ -43,12 +43,12 @@ 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; @@ -88,14 +88,14 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib 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 { 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 + /// 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) @@ -124,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) @@ -135,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; } /// @@ -191,7 +194,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib if (disposing) { - // dispose managed resources + // Dispose managed resources. if (this.CompressedStream != null) { this.CompressedStream.Dispose(); From fdab53637626ea496d9abcfa8febc1aedae15786 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 4 Oct 2019 20:40:11 +0200 Subject: [PATCH 64/87] Add unit test for issue #1014 --- .../Formats/Png/PngDecoderTests.cs | 24 ++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 8 ++++++ .../Images/Input/Png/issues/Issue_1014_1.png | Bin 0 -> 3965 bytes .../Images/Input/Png/issues/Issue_1014_2.png | Bin 0 -> 6626 bytes .../Images/Input/Png/issues/Issue_1014_3.png | Bin 0 -> 4157 bytes .../Images/Input/Png/issues/Issue_1014_4.png | Bin 0 -> 3328 bytes .../Images/Input/Png/issues/Issue_1014_5.png | Bin 0 -> 4085 bytes .../Images/Input/Png/issues/Issue_1014_6.png | Bin 0 -> 2114 bytes 8 files changed, 32 insertions(+) create mode 100644 tests/Images/Input/Png/issues/Issue_1014_1.png create mode 100644 tests/Images/Input/Png/issues/Issue_1014_2.png create mode 100644 tests/Images/Input/Png/issues/Issue_1014_3.png create mode 100644 tests/Images/Input/Png/issues/Issue_1014_4.png create mode 100644 tests/Images/Input/Png/issues/Issue_1014_5.png create mode 100644 tests/Images/Input/Png/issues/Issue_1014_6.png diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 91b1ef2c17..2a76310fcd 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -75,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) @@ -199,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/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 163d09bdde..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 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 0000000000000000000000000000000000000000..2cbd401e61ca8754b1e43b994518602bc70b330e GIT binary patch literal 3965 zcmd^?c~nzL7Ka~3h>8T%24oXk)SzKe1I;S9Ac{y3Q4xUz1_7gFF@yjb42s<^vs`g&b)Kpd6i$?s#ov3m3wbp zJ&4+?v`lju002r3`|J(@fSeG#Etf0;6->uQbMUd0xX(QZ0ASDNZ@EMSObZmDWCy1` z&~~|%Dq9!p^WLR>k@g%T?+hlNB#?lI1r7T^X*n5`?EHd!NmwEoOTYuGmVU1X>dWT! z-2`G72^(;d4CE;{Sb{3-i%RnInKxZa#^U?{(`n_UV9(O|9#?`tImCzL2Y3->^X7_Q zCdUT(2Ks=eB!E#}8Vkxx=4Ce@5*~{W0IIbHE`u8M>-b=juOBceiyT=n+fBgX{P1L8 zsv)Tg)D-5;4r9rIe!v4^3mgOiIN0qx9GWWV&X4PQp{d|4*u;zW@cBN2@K}{(Q{dQ;P}*mkTmS)1UnND1r>C-?W$bvxN_gd_V|lb2>M*;B(dW6@*vjxZ63K@nLeJRI zB%f{7)GIv@7`J?aB2jJjhgRGI3mls9wtMr-3I8(*$+5ZbcdttGiITcTBIvZwBg^sT zN23QagyX#^x_VPi5Up17_&a=MujwMyU%0F-oD@HZ0X#^O9jNb1Y`?3N*W{Y!BnlRp zik0e>gds#9nJFb*GxtE%rK`zZ?&f8BYcH{AX(^~d0;bCP+(eEwCq?523VmCO$Xc(+?~Ec-iM-YV+ZVgqp|l{=P8ZT=1)hf;?3pC7H!P(A8jz%s$Y z`p6fV>p5cSr(;!>zV->m^%JOSuPxW!p|2?&$K6SMcMczuOb^2*G}leU_b-pkGEy`W z2G_a!6UPVNUpYu`@=bKe5{xde(8%rUIs?}mQ0ZV4Vg?36JBl{#6KB?^^DTOsZ&P2k z_s44`B_kORY9`@rx0oZ(O*6_)ENeYA(LOtjo`K11;XwH+wx}YAbq7qC~zy;S?mbS)LzSatxp*9xgk7p!`OHegZQ|3auAQsmFT}W zyCRYnr5`;U|Ip%C3)!<7&AM&M6KOZ)%vaffz0<){~ym-u6IWqWx!=Rxi^&Fj-grGq* zkQ3@@jEW|UJNMX11>D#bZ8vrJ`RCUu$GkXTVEo3|XFTr}0rxZdt?>#)zJAZpY_P zAz>#r;I#nPyyL`9v z<&&K{oSxW(y0tT{#~dr4nvI{m7ekzVI~3IUGEtqgBph_kRhN#Z2!CHF){+A?&Pi>3 zn{y(5Xg~Uq+4b^S+(zy%b|DKaWal#QC_ZkR?awC(BWVF~o$lxWJ;- zr1A2!tzr8cLe*T7tb=ELWT2ZFB8UNYrMy89c29vn>M2Lp)H!EVr7|4?_6`SBY{Tva z&dhZqs+hFZ9{qxpH#3D`Z{Tg`hb5`8hfBA2N#l-wI89e=DVf`VqlE>=Q^J@YGliH=HdwU;56YE2BKty9p!}~r4(q7tb zplWvB%Wvpi>oEK3dd6na%0qs)2V9w1HD0C8_mgd>q=09gK!E$)i}B^d_aa3%MkHq{ zi?B9IM#JiHKRu<*ecF`IHNIQL`>qCieLcoP7onSn+go+L#_}c_)}9~MXVlcZz#@6O zdTd-QHi*UZGwPz$xB`=#qD9t3kC7_r4f{Yfs*`-g3Hw5pZkb?*9WAh;YW>S9fSaym z!y2Q!u=1Tqb3(NtjSY3X#tLsyOhVKF<{?df@l2-Z=D9^KZ*!x+{|T_oQ#nF zEmje53A}FGdl^llr;UR`eB%Ic#Bz6`1azOkc}-rL*3r}(nCRhuEU@^bFl0UmIKms{ zLbW}@H7118yMmjHzI0oBaE0Jo!u6r`c`u!roms<~l2>Vc!b6O~N$K>kh#14KDfN0X z&nBsV2f5QeEUGE~jK%2_{zblM^hKAocebU-2+hS4*FEWUPxR=cmE_tBb*bVYN=jsfl$>G8L5RLw1}##?kSj zPmzBtQlw;(k!=W%6gU=^!A~Bw#fn1P)!B%F;T%^HllhQqPAH`^48=iNvKR z%vFJK&mz3GS*l|NZ@YFL0NOgIerOD9&F0KO=Ykd9j3#g7TtL=+j`7vu)Ib|UW5s7u zX?d)yATKg;PHM@&FK2+fSw811;buskr_9ZAsGI?C_hTFe6AKNL?!3C#AQ%jueQeEn zG9Jo9*pAh6w?irGync18%FfuETl<79lBmY8bDEf2a!r!AU*Oybc_HRJbnJ=E+6OV1 zi=q^qKv72|?=4p;znFp@Jei3y%ZyxLk>%c^B*?o(r&jhon`}Xf*rZ-frJshqG2-_n zT(5?Bua=T6&_6&06J4`k^W!eg;xcoW$|z<{4b002>X-mIQr-khD*jVKu20YP?E8__ zyHyqaT$O*?YmiUsU6Bx?ru3~E01qC_?ON6H&xMIrKLREJS4CvS50iW3imErVMPMOTUGFR@T~B*bvWxzpCRib);`zv;cKTzCy*5ien9H=qA~gylr?&_Kttc7X8a|%4 z+%wUOR>;bb`f8+QVy-~khL*cjcAtEa=P4w=mn-1p6pLC{pltcYHY1CPE!O1<;At&y z{%l6#Q{>$+7e-12fTOJkH!i+7rSRakg19K0bi2LsL3T?2MEgK*AMq1d+b}v^6DMXz zpCf{b04SEZIFoP(iqWvp>`H1Y6lN{EV2%8jBi~2BtNO; TdTltkZopv=%C2CS_r-q#=xJ4R literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ca1c8ea5b9777ccb3e21b36c517d7c0485b3a53e GIT binary patch literal 6626 zcmaiZcQ{;M*Y*g}qeN%a=tPO$OGXBV^3A$^9VJn?Bt zq8d7RTy&yZ)a&k*`fJZ^u`Gs+p4M+Xk%N)GI5jFl3o6dqn(T94K4`LvxiAe)TdS?b zV96AHq23mR$4>HMOWgdsb`349N1vx7FaURFQ)`KWsJ$HP1F0J*fV2(vAju?47xd$a|yh7VbVQZL>Y;Ba$fF{R!iDU#dPV&2fKc4LPglBlxXd-d*`r z=@E65v*lcHXZJf+PI9zVz&rtNBms?why73O6d}A`18Bh7`MCT2i8#Vua2yr*$a~59 zKl(vWX(KtbA-PkkAzxxS|0sgn_+4a zdYw3Y5C0fcVGZ3nA{!l_T*X2ywHqB&yBprVAeWSyy2YO=0^e{K zijDhzeX2Srok@m`vQLif?0ht-!+ADtj4$50+3?uufaByE$fMSQ97^hbdk#dqrMdCy z`TJ=Gk#{rS>-QRyfnp@i;#$X#&tNt9g`)q{|a3$(B3`8>rdLs>)m;8Fl|r3 zU_&?Zi%%e&HzPtR%#ofhs3j$4zAvs^Ocg6~op^s)v#t~JJt0Ae{7K*oAD2p?u zyNT|c;KbdL(HfJ2u>$-Od_7#vnQo!k3YHh^UT&AN)2WkBnV0cr;@fLYI$<&ZV6G1V1Mjr z6jiw-OvWkH;@0pD+LO`D6C!&UO(3+At{_%u+C$ zudJoS_;Dnxv2Nu-ixb%QUM1AhiV#q%o(H*fYo7D^i%CBTk26glv5ND(RCoB}S*Qt> zD~9r%GtG;o@vSn>WN2K~agQL`vUuOC{v~hwyp7uSr%VognLE^sg9# zUu*ch;VH>D{2heX5(J#Na>>|Oc|Ycyc;UoaSPh;L@kZnSrGA&d7P@g*NW#J?g9c!z z1W3O%5+Zxd_2bX;wz`xnhu7CGUJdD!e3kxE`@&a;LnsLLMSN2-7R_|W#)4G%6%dBl zx&W%sJIbE_0d0Zwk5A_&BxZnAtTpX0MkPqSFREl0=JPKE=#mUwdSGxP)`%IsBPFHM zV5kqZ#4n23yUg`NWGMYt9C;M7>KjKnx|QU4>qh(9>@|651)3omU>Te55r!;IM=%yG zeWh7w1m=ht?<}XhKAL3Tk$odutX^E=t<~{_l&+_p>L)sfn}(*9`_toMAGz$Qh{KDr!hWq9ER*xfUJ!)VhP=J_Qk3=l13#z3 zhz@_dQzhmBj(`x0zV3}{;VQXx6)b%-DZ;YylAW8Q1r#T26v{wms!xem>5TCq8a*`X z$Fz=cU+^{tcf)E83Sr_+04Rfjh)$kSYd#CY=U~O0 z)?8a=QjR;xj^eB+o(8sq@i`5v#xtDw^&?!r85Tw)U@F6MezkZUOkbYTrQ4aYQY<)eM$`VMUTJye4g7`s;O_SW- z4-A}chYsrM>JAQvLq!YxHB8l?df!~WzAP|d?hBP-SHojU-jl2I+g6#xQ95I#Ou!Ay zV#WU4cI+t(gfm*Mtd|O;ze43LLEVAX!X>y3B^ ztJ<7+cbQ3p%3r3}1^qH6(PuQUp;J}F3?Zk&oxSJ5Z(2v$8c=?r{KlnEPwhT;@)d?? zw`U~D%4H}~*JFQg{VYrYugo;A0w>sWb~O=~TUZ^TKaHYEzTCAyU_sPqX^pxOGN1d` z8i{2$|5X7F$+_-}AxSSw;o0wd&*NJzE>psX-0g{?1RQSa-w&62?{R+C`EC-asXDBe z<`X=P!<8&k;M*?yzv3&35nFLEUu{)qz2TsiJC271ZGvtJDcnqsaL;heAm6mv0;J1i zUXH?6uHldoZ}D|0lc!hU|G`>J<{l^Qh$cR*0E4XjM08UI8_h)rKyOPn+t`ZRTZ5>@XN3vUf#;ewzHSAHtt z!-$<}U^h5%clYX~w>|d?yJ3mel7;d?i^><|x!HF|Romej^O~fP>Jr!JF&Yl-ENBU^ z;N!PS6fM1^+LuOe&JsXd39VXO8(Gi-cV=*)d6ZCXPfEjamFp2CbQkymw?{;?w?@V^T8ksVT^8m>C7ro=a)%WZtxS zvc`7iK=MOn62h3JJHfz6l7g;dx<+!jt()sbk26|fK)|bHclFaWuj_MKI%(UR&efF2 zQ5j&{s)49KINxL5l?k|LA$?oyzT<9S_6N>#C)WD4+y7!0-L{0=4YES5k@X>mVY7am$U z41u8qNUs>ti7NtcnnT9}6TXGVl>Vf}ERyP)E;vOkJ@{e*EUiG!gC;`|;IPtl?b)iz zs-dsXfho&M%wYQ1{^Hpmci&Rfpsy7Gn?#eHm`*?TUXPmgWj8KjY!VWZL-EDo;kM)E zxjkD;%R*MWZh)UO#d0+Pwe!I;qky8If7GgtqekZMZ$Je8_F{jD>AvLGb zV8gLvB8FKo&6bOc3(nZP=D4?rxQ8d_ySMy$pZo&TB^dVRE2DkW(zs+-9=DE{V9(zS zff8fv%lno&=K#&O^LeeAy?ZWAZ@KE`sPTfupalTm@5bFNh?{#XNXLdDzp zoVzyFOJ=M|FEvfE!)Heujv$za?k#E6^$WJ9Vg3M8zY~xya6_To(<=sb@ zp5BObBXWQ$PWXy&I=8BZ#t#PXJ=U#L1{VObDOaS*USXv8gtzOqnj#ue-fIyYe(Q(?GpCj{q?+ zF)?A_(*VUW)1Uh@f_x&KSGddXH$IljXK@l7CHFPO-GqgOx&HFvri=i?K3&`JSxCW% zdkszk*A73~HreD|JwNNqd$;&PGqRHIzhbJJBwSsdp#h_ePVV2<-9JI%r>(R#disK| zDq65?05&Vh54gXwjVvU^bL+ z*H6Wqua(=VN$v{9SLtRBL&*z>toY@5ok}fM6c&cvz;V+RDd5hww3+KP?zcnUp{Pe)8nc6}1-9ktn zf9gqxjOS_Px?j{?_@XlrBBJ)w@S1LS8Al4&UIkkOlHdi?ezNy&a;pNKT>M_YIMc`X?NYTgI2CqDvMe_)y|tqLd7LWt4glzm zqSeOP*;XP#`5n$Z8Pu^f8^eOQn=ZkVA4dk%nt(o4TqUd6lj zj>`GXSww)8o6G#LNCUv{i>Qk(z>lYJTUjF6Kg|3v;(D5Y&qNjk#Cbv}ob=vps%+tqtPG?k>Re>B*LVui4OwN>Ga{6ow>|$& z?@yVmM|ywRK$7m6H%Z!kc#Q|V>4*uBqP`x{6^jPG^jKTib6J1kWc%P3_|&2~${Rym zK{Ma7x6|#KPdh5J&*werer~J@?@vtqnyz=7%Q@7BB)b?}o$4aKNA?@Qn2N8oHsFxC zs?n-Z*kgRD8mozkh6OHAQ5!6Dqv*XF1QGEL9Tu0yXfBDQu z=Rs=1(yYCCSYM9VpOrzy;L^W4CrK~4S{>6gOfPm_^mbivEB?6?$H+V4H#x!yQLtuQ z@%jjD@H0x_GT^E=p2SrnFyPXbsj#?1t-kfvJGgz}om;fNR@@a#N11ZK8ndG#l`eic zP=z>rL>3C7qNLoAybuGs6iiirUJe_kLEkSy#l!Jl*r;?zcpkNxq`h&Ji-fV>-AB(i9 z6~mixqW*NB%Lt)G3K2&gx49Q{D!e+|X}k0x=o@0-MPmnXg!N^3Fhb1bP;P-HMW`vK zUYH!Waen`r{a_m^(=GJN^EI(eLNiN}0kdc&E<_lrM+w{0pBo{Y{AM#Jb@Bf3%buL# zF6|mSpDyO~@(Q~8_jjn8nwl)HCv!a;WE}br%OSO}^2od?i5!6#jm0-|=B%s2wi`p` zk0sr=hWpDCp_+8RwFMjAmPJx?Fsr2KEZP@&T^|u|S4);r{Fr;s8p*{L7LyF|t!Ty2oB1kiUL7139L)wV`Kf+#5Cp?~0U{HpXTes+j zV%L~SN`2wu)}yVw!8y#YBiTV?-_TKfJC)&*S3Qpg6;#a5jQ1-A?Pd)1^eA!B>#P?n zmE*RZFc;$38gf}46B*geR+SbMD0(~l_OH!0wuBW4P~MkwdQkP~4_e5-T!NR*`G2PafEWl5cUMfm9ZU->S7mN#6bien`G~yR+aq%G=D4=l zu4;9HXHTa)zD(JT2xy10Mif81-IC{P zM(5&O10+(ACG=E>D?Z%U27fbhD$Kn#Zu5q3T45$9v5tZ)A+TUCxEfA%{mHp?f9VC} z3|(DUw_$fCA1fLYg$~3pn)t_@fh~IY=-!+#PWv1T1O0QJMX5M9pVQNh!r(7FL*=wS z)S)kmNii7^@Sp|Bk*>~W*|_jL)g>nDkdyGjI@rawRyE!*%d*B3aHe1<)G15b*}b|E7vOKGlQ7Qd9K^6 zC0gltTx8){7C+A#+^ji5KIXhq+pyB%SW5}C%KkU;O#J&#fOw6}tCzRzhiGmn(u2D{SSEVW$zIV2jZAV{X1m1>#Tj!g z)>{_$uTe@^)khG%Kgoi^*oS#XzqI(F@e1lr$O~KobNx-Qzl_RQ`2B~%_BRTkd@A0O m@2jUj@-%gz=7}QB(Y@jIA4uDiGIiuj0+i$+pfcI_pZ^b|vsDEE literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3a7a8ed2c003ae26420ce39acc31a38bf3757b62 GIT binary patch literal 4157 zcmds)cT`i^zQ+%sfFf84LTHWx4hX0b0U?nQMClhJ1}OoC-Vz9rkN|=@nyXSAgfIvO zM;K8sfQA;T!XOfg^oU8Qh8{|QpoH?mICrxC8)Vv%+ykT3o1je9|-${_F_5cr^k5ZV4*05wX!=e z^-*l!o)gD+1cQ$LA)R-|{%{Q&fW`nX3R^D6BfK!EK$Ji520TU-YEs|UU&DC&0?V7XrbTY%=+IDK z6c*sOlInz-3T=DhF8br^1Y41Z@n)fGhwa5Cm(QK#2!4qrGV7j`}v9X)%zDY)Rhk6$frdOw8QkXX zmV`p1QZTXZQ9};6O+TyjLxX%b<5I=yO@{O-b-TrS<+I18J&iL*n_d?tbfSoG&t>uy zC52>V^Gt^XX3tL44+qac4N4&mN+|suY4y~&6LR0}0&F^w8h&=!)BLsx z1mTi-iWo+C>z*z&(uP{e9Lr(Yj40;+YDMJvU)tj^_f`#XoZrEPMez-WC6MDC3I%{LJHsL%?8JX z6FP2W2GPuMOOmiSp~}H1t3N;xUwzN|9A)ur8q)PmxMHj2jOyG4ZgV?s9NnQ#;=DuB8 zb`_zZE9Zgz9?rTK)Zftt<6Vqx2xr$^hXhx%eY+?fSC6BhGZ}+RO83q<+wX7d9grXT z(H=jwQmIJI3!E){4u^x5SE?>YiC7etv*GT|1Glh^oh{47cS_cV=i;CVv*tDgbms&! zGtL#YChKE8W_SiVO<=^QEQXe}9=CM%j!ezowI5$%s7bA672oMBsGkpUU7|;7?JvsF zr+w~V^?=|1ji9-!$|2gn{;~$gzq7|UBkF zed9&{wySovH{6_?jr(==6ZW>d9OMuuHy&Ib=EOR4Un`;Je>zG_B}gddSH+Q{-Cy_62ADY=bneI|Da z)^q`#pVjMH;FC6CjeKqRfVP(ldM{#eXWVvGKtB7J{-JqEdnCpco%sXGzx4Gww0Cid z|8-+yque8o#!u=qJE$Pf0ijT!t#LDY9)GOOOjZZr1<1}@V;`55&>T~tiWQe&(zB&rmi`l&BKfzH86 zsF%Rw>&~nh6oVMc%%VD@Jt6J*-m>n6Sc2f?t?lZjj&gg*|Ly4S=K#Z7QPslOEb6K9 z|1jf9f8MKzQGfmx-+TJ-;o19pXa1aumze+g;rxr$Ti?}|Sy3_kg0gf_7_zkO{oe&Y zZZr9F3UK!bt-2q1M?@_C$zzBvBr;oCvi3^dRLVkxLQam<9(C-Ejyo|E5-Rj9i*vqBr90&Q_k8?)epd`xkBDpS;9> z4?H-nFNq=+qIdYOPtyMxkBN#0<*yIE=!R`cyebBoM0p+6NGr(QJk3&!XA`cy(v4l4 zuPy$}o;J*@=M=QEJ6_n9AByi7E2TU1=UK@V>%^7Ovj<=fX6KliV#^wJRrkYaq)TfLqJx=;-9z!Bx7x>CI1< ztJ__0eemFeNV$&`d^t3DDaHR$E0B6Lkr#t#J_$1uFTN`aOK`@3sSmFMnq0gZ$x1K+ zk@w?kQST1i8AFMCBCK;_PNjZ;rENGPH)gaalQZ(#XD5VVzd9BIQI>$VG7w(z41?R*B@k{FkHzLqrJtp0E=E$Cw=;%t7+MngV3-~A* z5Tq|Gy}#9+6khj1&6;rgsaVGV#DaUXTHWSSbKW5L^Cw_tkfd_V5Qbn~{<5(kaL9{e zC&PE2thDYbCD`2Q<&#z1Q?C9N-FJR_WMQ9{Ghsi4Gk(uxmn6K^c3%(J@C4l41;!rf zDGM?xEg~k{TrqYBS32EU_6bsDqx_nCv?p(kE{xOk)hZGy)hv}Yc|C678OEP?+Or@YR9iTgE{_Mo-Ga~qzsKhp9l>| zLxw;$U_h1FU1`Rkm+3_)gjb~9AFRPr4@6Eqb*z0kDQiX^E&=|C_}o`TrX*e<4A9=o zDVXS!;+sjy{8w_R#XR{w(JmeyWp1A;K?xjNl;)&sIM!NwGvcK4GJ23!sYS~?9){~4 z59YY4sFXv$r4b6(bMhJpw_nof@jKP{9?4ja9|owm%KTL}m&7E$@tPnBd!BFSBGFna zKY={PynvsxQ+#|8a&~w;>|MKU^Mn-OSyS14D$kJ#RfQcSDe4-(v`dlzTQig3vQ3>* z#7WS&Z^$5G0J65PEnq|e)q=ppQxg1U=k+(%JU0DZv(h4K^9UYa`w?By9Gzf^qb2GQ zL_B-lne1)gHXFfV)|$3OcxqKv5!lJ>y8B#n_7(i<&OD^kock|MnZl>^%}rcb3TGTq zd*_%dM;cDz&-(bmu3ay)9edl;C$`g9>5)cT2eSDTr&}JfVq^F@g7ZdKNw&MQY^>jZ z(XN$h*JXF`A9DD3*|^Kt39&-^)S{8@2gT-wU*t{7gYJn~oO6^r#W@XjUi~v0i=red zx)5{Akbc8PyZXKkok76kX~I4#RD1K&Jvq>~YJh1$)ANqkxyRPV?GQ5x*~eiY=$S4M z_q?G^*$Ru5K4+n`q};oMx>ng-`3WZj7e8!{0aD=&jIw@wWa2;%@$FhJF{C=F-RqMp zE{NRS$tgD`%Y{)R*J(VjRR>?J!umPoiXXrh_?m%)t29>ddkI#OQ!K~byh4Cbou=vm z{xaD+D+D`P&ni`pHH2_US&8ftGpjml1b28Yb_r;6mFF~6*rLz#>ANCm2%|;MFW$Aq z%2Ft6A@@(Spdujca@#z)UD&yLZuO~io-bU^0>lXqmei0sT}(+h>Y2;sQ1heNV-ipH zIo942RK8K2@D%&czCcvoY;5izH8nNq%t`_G*A+R!NX=mH20$j?%22?W6Q^-w--)Q3 S6K%qK0aoYjEeg+h-uw?P|F}8; literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..54d099427d3f9d63db566f2f2ec1bbfa1fc6a548 GIT binary patch literal 3328 zcmZWrc|4SD_a8jS*ix1nyX46(4N68B$u_njglvswEHjLmP7AVflVZ~cD$ct7tS=f3XG_d56WIp_O5*SXI<8>>tFU;Te_mHQ+!V_rf91H>pH18YR17!hm0OTf^Aue(c@repRc%HNK0SEwHbRrlM z31}Q1L@Vns1t7kC$QTvujmHEMFgPseD39z300-~ma9m&*9&;T{0F?@M=mMy~9~6)B z+;^2BU;Iu5}8goeZs@!lxV`d&2azzvQI2tZ*8pr0KN834w$|EWEO;Ew_oEq(kO z1Y!f385`P%K3;rMmn^O$9DB*|a|?UEn$pk{3$aK(F*P@Hw|3z$;N{cs6lD}dp0Q0Z zoIUZmc{DI|Q~y{?{8v}#7BSRxw+ZH|mc^MH5b5@Cs%bmqmOIz&Cn-7l7htfR+6`1g z;+xIIja1(u7V(n-<+vJ45Go=$3iu#bmCdU&Xo=+)fS0m-M+5`nR|_8!oMGty*F= zlvp~-;@9|Lq_WPcEyu|(s*0Bk9-iHN@JObUroq6L zT+x>P@`a#!z=ckdU9s|3W6@tN<(h7%90yNvl2p#C!aHkiW;5#0q+EG=+_OBRIWKW1 zt&Q|``lb8zddH%3G?PT`P~V0#PmULDX`G{X&utfI5fWB2|FUzTaC&PK+FPx2yhAWB zQe)`v^%?1j2abpneJbn`88Vw2IgAyuk@Odxm|oK?ns)7nXT9${zAHoGO3?|>lU1XO zH|nxS@8tRmLbrO@oZX6u*uHPawN$Ai&2#bv8=98Ir%J%6F_?tF3(rt4?YTJ%k+Mh|!^2AgNSE0!V zj+``vplWT@c$9EcUuS)KSE3HT%%>3_o3`8PDt2aBEFa1gO@t?dPNb6gxxuS>Vjx4JFS z6K%zM^f~isnsbV>q?0cL7pBd~T@R!$>+AYJT43M|9IW&^tw%j3Dcttx&MVnM8qwhX zeGDH_$!Yx=Pr_a-H!nD*&L(;%UK#nONx$Qe!5#Gc=|^f!JEix7U(q8WE_(`tCP^?4 zvCI{+$#Kip>L3NFA^+S@+LJbeTe2dxu|ddL&^53mkPR=^RC1Y2i5`dY|E6dtv;6uXos{k0OMl|%n#I>UWmAXqh*r7Fs-bdN|0&U#sJO(wpfQq~0o!#EHL zQ#STe%L)*$~IdCS*#qOvWfqPy!GrQkEV zdj`C5Ti}~$C-pa6LGb%R5!SwQb}o#$cPg1dnNi_DMn?1xgwS4*$>Yy3zK7vl%GqI|IJ5%u1@(O1YOTX*VL8RJY>Ap z^CLn&n8`@j^Hx-Tns;v$s;E5cn(LQG4Cy|tC3$Q9S%d_d4MxI3@TJ@-=J9Up-qzrV z-q}|+XkPjEEr^VayZ?G)gXGYU>(G$(_3{YeGo1bHM@P`X8x2N3Q2n$#XMrr{|M%aH z_vGNGT5N9R;NP5mgLR`rMWAZ54M* z?W=l>)vwTPjRg(oEB_GH20a6kSnCi6M(??ZV~>EQ-n#Vio@HkRs$(5708ak_gBu-i zv0G?8#Z0|u$MbOU$IkW!F;#wfPu-n?BIcEz%h_UPn>(%IFl9t&y)Vw$;x%d7nZ5j| z(08j$lOb6~y5IL(#flDL$;-=U1^sI6a%J9ON&J@RY)o+trcIZ!+iq)-b5ol=P^81J&V7l{ZEX>U2hlr<-KjF~WCZ|J>gXsbnJ-VBGeLb2==Ym=;-*5w`{wENL?; zJ0;g_RGT&~JVL#PhIsMO7j$AMz;UD{JBEmZ@&}-Ki$R4DVoe+((YJ}`54gbcy~pWt zAlBAkOCJ$ClpbD(0vaLQypKBI20{PXs|ZGB=^uHVyBUd`jk%0Rf&>wh6j0=me8tE< zm1HOBX=WNIJoh#KEc2U`W{E()R9j* zRT#Osm4pUY$O6pC0M^!y07eW}b-8-CPoDGmm4Fk2uZHwaWXfri=DUqJ+k0!7D+K+g zU9z=?o*8hUDthk1%`c8?U2XSJIZ*_z9|Zg4vCj5La<(8U6n67uT<6n9>_#<}m+dup zE8KpEo%VZKaKObOs1+7l+ss`|=O7<9&kNbtmDGk{8c9f|jE%jnRv0u!TBtcpu$ZqH zQ7ZlBsW^uGBqfeHwK}m=S6?on00iX)yCdmgC9Qk=sXn1$8n&aOGNWjtBjg1X?f(^V zC*n_?boB*9Bv=Y?l~u8CKpu?rt|^3u{&W2oyJ(QL=x7eE;^FoNMDXU8q{cl_*un*> zy4cBqK{dB#JLF)M)We2j{mB%~f{%lvL3F%tgz~dzV%HLm?{Zj-mLPpE^{Haf-`&&w z`5VQQ7Iu)A3;|X&8mlqxh;a6d*9%gR1j@7$b+ugIX?!({uR(8^np0duTfe%U?v*Tf zz(tk+YCv?BBAamv1qmW2w;mOy4l8y@_6(+9fGl z#F1trESSEpflUSV0@*_IFBbm?6^WFQUSS5!nE#W7n3K;$QR!*T$-OGvxZ0w6SD%pJ z%y|Q$f7jn!EoYg8Ws@plHJw9T%C_EN&YBPSGZc-_8LG1qJKs%D4}u?fze65MpZ-m$ zFNiGTCOd?y_Fp;nVsV@(KrXKXUx}IeSd9*~wYwHnRP$HZ1bTku-i-}=q{>Sn zcjK09dF`G*Jd_6DBq61+uR9 z0aN*<{Tcl4pk=bpJAN8RWFGb*HMa>T4T6Y92x4kWRjJ!nLWQE* zQZuDrf84e1``*3QUe8+J{o8x(=kxrY?{Dwe zYt|OxB8No)01&sbym%b|K=b@=C@jENU|)~v^B?>DES>xTK%#B8gNP6bdA=xwwYq91 zGzj`x>g1t=OLGT)NM?@Ma{*XSGzQ2m{A|gW#IStnBElbzLHc2lXkS2jzsga*F0!j% zK>Gz_kRF~`AYbycAzzjFp<)njyP-$0NE8ClU6tI=AKAYR?Zn)@= z#gYDSA2)s|1|T<;|CKKb@5&Bt7+<8X2hb!x8N=6v{+u6xfg=De?*VHs>;f8vLil2V zmCs3yd=0!Cb`y#9K>+XOdsX=$0IQ4VZU&{zjnqCL);kRDnb)X)VuwK3dwNJHu4yai zWA$`~9?H+cuVh|Jxo%LF_kAiw{Oh-tBe7ugg`H7@x7WGF%`S;c2L$!*vBt;8_eFkW z+|o?Wh}gKx?gU)3rzDkm6{~tcF+oj8nB6&Zd23>TX%9Pw;B9v|t}=ddwedDz*Jv{h z+bI8bGIz06($vylzbqi@)+08HmSfYa(B%`Y#^cY4(r-#gVyqasCcugYSlo~;gg`#R zwxBL!+=)?NS0~mL1kqcWH?KjtlrnN%(uqt2<(?X&0UuF)t6^(#eZKAUmUHkclO0aq zY_xKJ%eEI64zj;XWw``0zkKHjn~m?WSVII72FQ7Du1Y)UnDGjlTSj=>Cm-k}pxP(A z1A_eCPQ)fwR-3|Ww1{QhgBF<}B*|fN4FMITjwm$t>mtPT&I}7c@Q~EsH4>Aqc;uh1 zVl>BYhka%oroY(vS)?}Ys=}=UQsx5wgwCv0cUNS9J8rzL}g2GGk)D4R}+)^hkT zT>HkvmD^uj*UzWq=B3w9M7w04&2uLgFpG%F3&GaFb)44(YAWbzn8N7Vr3y}^ze1+m zYgqV=A>r$`@nw>xIQ;gD~Fq>Hvj8`H?jmpdVS2EMW|4n!mWdlWrQi zHA!c$Fhrck>%3e7L(L4%24yK6F|w7AVt5jm?ZtmLFSX1WI8H7dbX?CZBdZ&WOarzi z%1QpR6>q)>T^>*zpySx8T=E&48d*b~je?k?OQ{Xf@4-p*v;HnL7oJMVD18ZWs zmU)iH?LhH&223z@86NffF^l!9rq6_2;36~@H~B=6-QP1%Wh=9OB8!GqRNa`#o61MR zrc-**CY@bTCDC6p%-~aZUj0G2sq?z-j5|)U=}qG(7&6Jd?8zzmz)>A$PPAm`;&j(R zoDQ$9Prf0Dc)x+qYK`&am^bQL?ClR-E-m|Q9+58RN*q4C^QzZ9TBu?A4tyc~kz8toGkJ!YkFKvyDx3;#zB6b>9 zANCOJx*&}OF+?RM|L>n76T3%6#3&AHzS=PXVWFYJc=;+Lels?ntCuNn5ALg6UV|$0 zo08JX$PvID9}?0b?^r%4@DoNp^1Lb;7<lLfM3wHG&>DZ~B9ZN{Qbp+x znCx=!Hfe3P!RO5FThZnryK5EYlBxL>g-iNFJ_h~bgQ1rx&wF#T zf+uIzQ&tt#Tuwdi86=oTe>%9w0-K+Uy>xYK)~r8+GxUCnyIDx{&@Zc#l$D$p{E5>^ zW*QqCuS5Aci`Mk|2Dps~Y;{g}Q=Dg6MWzwBBaSbZ-lNFln_FYA!LpGH7ym1_)A@#J zLlBHo?Etk%PXJ@Fn138jKOot5&sDT5uX0*$(Q(<8BAe!bqUrp4_RRn7B2m`oqW4%x zrT@F^>i=gaBR6Z$?y>my{%ZevKDk$zMtdy&nXDd3D02Sa=kZOJZ|qZ@C-LpMWp8-) z!-pbULzcX}H#n53j^}7*eWOfa24d#1FQP^U6s1>SA5Jx~bB?^QUTeR@H!R0ke!VF< z5pkGx+wOE~FY&VomMvjjeOm|YVO69??3=FtPgun&U|Z-=BXU>_Zqx(25Ni<5J5a_DgpBx3yJ z9*c5fBSz8-zG#SKSfMpu;SENMvqx5jvxs%q5a3L66d^hF(&Xa4pCj$^7Z7bE`iqy# zq9>44eB+g`vM@(iH@ryRNBdI&GC<6A`H2UwLRSlxDH|M|hgHP!x&U0Fx&z41Pia_4 z7>y(h+$;eN68@87-2lrv&#FOK(&v5YCuf2dm%}MtJqDm^phd*rb&G)?Mnh{VO_*gL zDbQ^4c6Np+%ZC+k!biW*A1?KCM4(Wp@8U>(5Q2;HI*YgWbgkyPJ?HwDjyyO|iA6^~ za4ahrcD$Ry;(oPDDuN-{vf015cTdhVeoPW8N99#=d@?IIj(3xb9N;N?ETA5bqtD$h z4VMaO(<)~JgLY7DJZdYC?=v_FQ#t2iKi zE|PuZH{gx+);ZeIh8R+vOOdE&Z>4Q|pfFU^AQ$0Y{0Fo#Y5={j!>a@Sq5u84yx7(u zpB+S7$;#6&@!Dghf$LV6^_`hdw%wm@JbZ@PcS}$-t&K!Brg(Me_{K2YheFz_9P-kc zq!yaXvuQ%1lTqqPeZ0LsDy~tZu2><{E>%o;pc+rrhV*CD7c+c*A56>o&_f^-Rhk2GJmMEBHvSY)TAoI?mI{g?@~+3*<;bCmf^q! zXI|-k^rzP+4B9KjSwQZM#B$6|u>>tL@9q0>=J`<HvZFzG*GUe4#(AwF)NjTcjDj;kbTK{m+EH< zk$$~io`Sg(m8yQ%r_U1qMho3m>oy@Y-#bMW3}3OBJx(y{0$Ikmc7@M^uXSA#$p3@x zMxwo&gRa7ssbWd9T65P7(vNZH0+OevIA3QVJ?kJNWk>%mFo zze$-n%h|wicARhpGcVU^v}d4d*+RimEkZ=Ay}#l0@#b1(QLRWt8Fi9wUr+uW5@$L- zdh;!WQ;!*q2e)F23xiijFfvQNM6Aq)E9f6earIm^0WB*~D?JgXFhhUZ)cH(VW%!Yr z5DC*Il2rNM>NGt)E^WcwV1cC0{`Q#j7V9tU^Jb?+M*KKrfgn|^wR?&+v@QH}$kn36 zdqPtMNVdzW%QmBm@3D}-AF`eWyl_i&-gUd$JB{`8*>7T*SFMi>$hRBNdsB4dYBM)4$sjX_cyJ!d(zG-9c1s$ z7X+%4nbN3{Yy}Y~Gd2$k%eSckhBZPVqw^3yYr0gr`YK~Zthq{h_gw$0YITvQLfZdu=wb7iuE8N8U+(JC-dJ0!+PzhNIi0&81w(a?&!!OwEWe1;wAckW-D0rn%r! zjnV745TR(`Qn;j(VIgYa6e5F`=7NbwG1d@YXin$M`$zYjyPVJWx%ZFfe4lf30#Ep? z)ic%u0AQ`3ulFec&|<64p{xI+UVWMP=ju&2&KH&d0PA`*Q>)Z^orxOifPMi-b%wPz ztaskL=>S~t1sy#LdL)9E(HMY4Vfm`j8c>bA;R#U~WE_Y@qkxUNX1ml}Ps6>?aj6(2 z;xY)-8L$qj>AEiz1CP{5O+jQV95^5~&{bD-HI*RrB``S>0|&z5Ry1<`FOMS=qGBS| zQVh`A)l{Iyt2H{Sz*RA^A+h|8+P_t ztoCwPud(bKqd*Z9mPSCdd~msC)E~Y!Ku?uQeV@A$&>G@;d(spd|0yYm%bZRvgO-$_ zLXo$|UZD=)j2~L!oIpuspy*L=H$mlZ-y>wMvb#Azi}z*1qH3%%Sw+Efdup z>3dH&#tnkM(ra1W8L$LK(?0V_h8!Km`f(MG3gO`tyXiMP06zV$q z&_2J7cu~w2BXs2*XDrnH#H7!obHhV7;Kr5160d7SnVo|-rKrxTzcJ+#(gxFZ*T`!9RV$tk`|h z>ON*Cj1MWXK+$G%HYT(|o&_>!BOWzknj;gs;>%{8*xx8?fJ8v8s)*XkPywIM7YPaZoxf<9( zT1;-cEtHthW|>@t00Z6g(6+IQzN4_?&#c7W;^O3kN}H*_WS9%w)3_tG%GD$~?!oep zeQzjmI7xu1WN+K;t0-1ItBZCkvtwC%tv$f$#14fQi4H+5N2xlqmte-s&i%oEJ7F0=>_BE!8b93Wn6 zmu)j4oZ*C4L+JCL31Uiu^BFzwlfyh&mG2^&xT4JyQTD&NP36TkZmXeBw#)ZeChHHl zJe=Xg7CLyQP{Qd0%tPrbB27|Z*u74R5gqir_7{z#!&Q?-heu_(e++mg%qjTcY4-STpMH+aSQRYR?8c{$f_`|JdnW>Mlfb}qHm zb~k>Cb1$jSN2H*dT*4ip4OJA70zX~pFxnj1{}ZjQk!y-@Eiqi1H64_1pT97dVVt+J z``NDSZkKK=sdI=MH=5gyTz*u7<16; zzAf}{NM`GUa=xf`S^re3VYFSQQ)jlKx_}$FU1bI_dqy=+)SFa4|6KErC|i)wt%99? zEUv13Jp^ZzeRDGacKVzxOEj-Pw0Iao_Loc+x40~(2E3PU#8Z4yV0X*Li6Iee^2Sk| zmdrE1pz$ys0qfnZ2rVfVQgM2Metfk4k=JLFhH@?z-uH~wrMjaDXn|p-PhM(7+fjMM zMeGyjftXQcsg6fb`7dY-x5i}&`C(m>?^p{&R(coifKvWIxr=DdOXKKd8YB}eIp?(w l`&I~X<~aWM|L;mBLaBoks#cm!1MX-len(GuQ#>QC{SR)P_X_|3 literal 0 HcmV?d00001 From f1d4a27f949b3edd28173889793b40e3cd1e10ce Mon Sep 17 00:00:00 2001 From: Zappy Zhao Date: Fri, 4 Oct 2019 17:25:35 -0700 Subject: [PATCH 65/87] Update the hex value for DarkSeaGreen color Update the hex value for DarkSeaGreen color to 0xFF8FBC8F. Based on W3 definition, the hex value for DarkSeaGreen color needs to be fixed: https://www.w3.org/wiki/CSS/Properties/color/keywords Fix issue: https://github.com/SixLabors/ImageSharp/issues/1015 --- src/ImageSharp/Color/Color.NamedColors.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 +} From b28c81acc0bc48a22319e92480040c6c46be6163 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 9 Oct 2019 16:05:36 +1100 Subject: [PATCH 66/87] Update reference image to match new output --- tests/Images/External | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Images/External b/tests/Images/External index 58b2c01f9b..1d3d4e3652 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 58b2c01f9b66dd42d2b5b040b85e6846083b5e5f +Subproject commit 1d3d4e3652dc95bd8bd420346bfe0f189addc587 From b45ee03e847afc22c37d45fb3f68beecd6e57991 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 16 Oct 2019 23:05:56 +1100 Subject: [PATCH 67/87] Expose pixelblenders. Touch #967 --- src/ImageSharp/Color/Color.cs | 6 +- .../DefaultPixelBlenders.Generated.cs | 650 +++++-- .../DefaultPixelBlenders.Generated.tt | 8 +- .../PorterDuffFunctions.Generated.cs | 1625 ++++++++++++++++- .../PorterDuffFunctions.Generated.tt | 98 +- .../PixelBlenders/PorterDuffFunctions.cs | 154 +- .../PixelFormats/PixelBlender{TPixel}.cs | 21 +- 7 files changed, 2367 insertions(+), 195 deletions(-) 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/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index e94ea452be..c63d058081 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -22,11 +22,15 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// Note there are faster functions for when the backdrop color is known /// to be opaque /// - internal static class DefaultPixelBlenders + public static class DefaultPixelBlenders 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..7eefcad2d5 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// Note there are faster functions for when the backdrop color is known /// to be opaque /// - internal static class DefaultPixelBlenders + public static class DefaultPixelBlenders where TPixel : struct, IPixel { @@ -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..ff2d25775b 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -3,20 +3,24 @@ // - -using System; using System.Numerics; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { - internal static partial class PorterDuffFunctions + public static partial class PorterDuffFunctions { - + /// + /// 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,6 +59,13 @@ 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) { @@ -49,6 +74,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(backdrop, source, Normal(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,6 +132,13 @@ 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) { @@ -87,6 +147,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(source, backdrop, Normal(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,6 +465,13 @@ 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) { @@ -275,6 +480,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(backdrop, source, Multiply(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,6 +538,13 @@ 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) { @@ -313,6 +553,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(source, backdrop, Multiply(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,6 +871,13 @@ 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) { @@ -501,6 +886,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(backdrop, source, Add(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,6 +944,13 @@ 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) { @@ -539,6 +959,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(source, backdrop, Add(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,6 +1277,13 @@ 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) { @@ -727,6 +1292,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(backdrop, source, Subtract(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,6 +1350,13 @@ 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) { @@ -765,6 +1365,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(source, backdrop, Subtract(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,6 +1683,13 @@ 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) { @@ -953,6 +1698,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(backdrop, source, Screen(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,6 +1756,13 @@ 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) { @@ -991,6 +1771,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(source, backdrop, Screen(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,6 +2089,13 @@ 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) { @@ -1179,6 +2104,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(backdrop, source, Darken(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,6 +2162,13 @@ 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) { @@ -1217,6 +2177,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(source, backdrop, Darken(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,6 +2495,13 @@ 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) { @@ -1405,6 +2510,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(backdrop, source, Lighten(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,6 +2568,13 @@ 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) { @@ -1443,6 +2583,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(source, backdrop, Lighten(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,6 +2901,13 @@ 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) { @@ -1631,6 +2916,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(backdrop, source, Overlay(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,6 +2974,13 @@ 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) { @@ -1669,6 +2989,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(source, backdrop, Overlay(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,6 +3307,13 @@ 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) { @@ -1857,6 +3322,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(backdrop, source, HardLight(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,6 +3380,13 @@ 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) { @@ -1895,6 +3395,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(source, backdrop, HardLight(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..a7f3760891 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -17,18 +17,21 @@ // 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; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { - internal static partial class PorterDuffFunctions + public 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,6 +70,13 @@ 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) { @@ -61,6 +85,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(backdrop, source, <#=blender#>(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,6 +143,13 @@ 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) { @@ -99,6 +158,13 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return In(source, backdrop, <#=blender#>(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..c7923637c0 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; @@ -18,14 +18,14 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// Note there are faster functions for when the backdrop color is known /// to be opaque /// - internal static partial class PorterDuffFunctions + public 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; // 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; // 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,58 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders return color; } + /// + /// Returns the result of the "In" compositing equation. + /// + /// The destination vector. + /// The source vector. + /// The amount to blend. Range 0..1 + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend) + public static Vector4 In(Vector4 destination, Vector4 source, Vector4 blend) { - float alpha = dst.W * src.W; + // TODO: blend is not used? + 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 +269,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..6f3f56d3df 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,9 +22,9 @@ 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); /// @@ -36,7 +35,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// 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. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. /// protected abstract void BlendFunction( Span destination, @@ -52,7 +51,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// 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. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. /// protected abstract void BlendFunction( Span destination, @@ -69,7 +68,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// 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. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. /// public void Blend( Configuration configuration, @@ -91,7 +90,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// 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. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. /// public void Blend( Configuration configuration, @@ -134,7 +133,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// 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. + /// At amount = 0, "background" is returned, at amount = 1, "source" is returned. /// public void Blend( Configuration configuration, @@ -167,4 +166,4 @@ namespace SixLabors.ImageSharp.PixelFormats } } } -} \ No newline at end of file +} From 637dc90225de008b45bf6cd0b37df59c39e58bdd Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 22 Oct 2019 15:43:53 +1100 Subject: [PATCH 68/87] Revert exposing PortDuffFunctions --- .../PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs | 2 +- .../PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt | 2 +- .../PixelFormats/PixelBlenders/PorterDuffFunctions.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index ff2d25775b..25f3979e20 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -8,7 +8,7 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { - public static partial class PorterDuffFunctions + internal static partial class PorterDuffFunctions { diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index a7f3760891..b5af355c5f 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -22,7 +22,7 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { - public static partial class PorterDuffFunctions + internal static partial class PorterDuffFunctions { <# void GeneratePixelBlenders(string blender) { #> /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index c7923637c0..ed98de0f14 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// Note there are faster functions for when the backdrop color is known /// to be opaque /// - public static partial class PorterDuffFunctions + internal static partial class PorterDuffFunctions { /// /// Returns the result of the "Normal" compositing equation. From f2e5b8bd2448eeb06703327971c136b3f8f25cc1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 22 Oct 2019 15:57:11 +1100 Subject: [PATCH 69/87] Update PixelBlender{TPixel}.cs --- .../PixelFormats/PixelBlender{TPixel}.cs | 68 +++++++++---------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 6f3f56d3df..1d3ccf6ee3 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -27,38 +27,6 @@ namespace SixLabors.ImageSharp.PixelFormats /// 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, "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); - /// /// Blends 2 rows together /// @@ -76,9 +44,7 @@ namespace SixLabors.ImageSharp.PixelFormats ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - this.Blend(configuration, destination, background, source, amount); - } + => this.Blend(configuration, destination, background, source, amount); /// /// Blends 2 rows together @@ -165,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); } } From 33f5cee8d862774a270f99bf03bb183bb4eaca45 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 22 Oct 2019 15:59:38 +1100 Subject: [PATCH 70/87] Update PixelBlender{TPixel}.cs --- .../PixelFormats/PixelBlender{TPixel}.cs | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 1d3ccf6ee3..1ab349a73d 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -1,10 +1,10 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Memory; using System; using System.Buffers; using System.Numerics; -using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.PixelFormats { @@ -27,25 +27,6 @@ namespace SixLabors.ImageSharp.PixelFormats /// The final pixel value after composition. public abstract TPixel Blend(TPixel background, TPixel source, float 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, "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 /// @@ -55,7 +36,7 @@ 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. + /// 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( @@ -63,12 +44,12 @@ namespace SixLabors.ImageSharp.PixelFormats 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)) @@ -89,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 /// @@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// the background span /// the source span /// - /// A value between 0 and 1 indicating the weight of the second source vector. + /// 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( @@ -106,12 +106,12 @@ namespace SixLabors.ImageSharp.PixelFormats 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)) From c57012eb2fb16e6220a274f1995d383c81366039 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 22 Oct 2019 18:01:05 +1100 Subject: [PATCH 71/87] Fix ordering --- src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 1ab349a73d..c64fd3a2d9 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -1,10 +1,10 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Memory; using System; using System.Buffers; using System.Numerics; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.PixelFormats { From 7766dea28028a28aeef2405302c50f13130b6dde Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 22 Oct 2019 22:57:07 +1100 Subject: [PATCH 72/87] Remove unrequired blend function. --- .../PorterDuffFunctions.Generated.cs | 36 +++++++++---------- .../PorterDuffFunctions.Generated.tt | 4 +-- .../PixelBlenders/PorterDuffFunctions.cs | 4 +-- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index 25f3979e20..4616ce36c6 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(backdrop, source, Normal(backdrop, source)); + return In(backdrop, source); } /// @@ -144,7 +144,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(source, backdrop, Normal(source, backdrop)); + return In(source, backdrop); } /// @@ -477,7 +477,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(backdrop, source, Multiply(backdrop, source)); + return In(backdrop, source); } /// @@ -550,7 +550,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(source, backdrop, Multiply(source, backdrop)); + return In(source, backdrop); } /// @@ -883,7 +883,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(backdrop, source, Add(backdrop, source)); + return In(backdrop, source); } /// @@ -956,7 +956,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(source, backdrop, Add(source, backdrop)); + return In(source, backdrop); } /// @@ -1289,7 +1289,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(backdrop, source, Subtract(backdrop, source)); + return In(backdrop, source); } /// @@ -1362,7 +1362,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(source, backdrop, Subtract(source, backdrop)); + return In(source, backdrop); } /// @@ -1695,7 +1695,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(backdrop, source, Screen(backdrop, source)); + return In(backdrop, source); } /// @@ -1768,7 +1768,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(source, backdrop, Screen(source, backdrop)); + return In(source, backdrop); } /// @@ -2101,7 +2101,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(backdrop, source, Darken(backdrop, source)); + return In(backdrop, source); } /// @@ -2174,7 +2174,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(source, backdrop, Darken(source, backdrop)); + return In(source, backdrop); } /// @@ -2507,7 +2507,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(backdrop, source, Lighten(backdrop, source)); + return In(backdrop, source); } /// @@ -2580,7 +2580,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(source, backdrop, Lighten(source, backdrop)); + return In(source, backdrop); } /// @@ -2913,7 +2913,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(backdrop, source, Overlay(backdrop, source)); + return In(backdrop, source); } /// @@ -2986,7 +2986,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(source, backdrop, Overlay(source, backdrop)); + return In(source, backdrop); } /// @@ -3319,7 +3319,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(backdrop, source, HardLight(backdrop, source)); + return In(backdrop, source); } /// @@ -3392,7 +3392,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(source, backdrop, HardLight(source, backdrop)); + return In(source, backdrop); } /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index b5af355c5f..bc4fa17c27 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(backdrop, source, <#=blender#>(backdrop, source)); + return In(backdrop, source); } /// @@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { source.W *= opacity; - return In(source, backdrop, <#=blender#>(source, backdrop)); + return In(source, backdrop); } /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index ed98de0f14..d42518292b 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -208,12 +208,10 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// /// The destination vector. /// The source vector. - /// The amount to blend. Range 0..1 /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 In(Vector4 destination, Vector4 source, Vector4 blend) + public static Vector4 In(Vector4 destination, Vector4 source) { - // TODO: blend is not used? float alpha = destination.W * source.W; Vector4 color = source * alpha; // premultiply From 1993facee2fca7addae77eea24538191404cb2a5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 22 Oct 2019 15:38:38 +0200 Subject: [PATCH 73/87] expose limited Buffer2D internals, smplify API surface --- .../ImageFrameCollection{TPixel}.cs | 2 +- src/ImageSharp/ImageFrame{TPixel}.cs | 4 +- src/ImageSharp/Memory/Buffer2DExtensions.cs | 132 +++++++++--------- src/ImageSharp/Memory/Buffer2D{T}.cs | 18 +-- .../Transforms/Resize/ResizeKernelMap.cs | 2 +- .../Transforms/Resize/ResizeWorker.cs | 14 +- .../Helpers/ParallelHelperTests.cs | 2 +- .../Helpers/RowIntervalTests.cs | 2 +- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 22 +-- 9 files changed, 91 insertions(+), 107 deletions(-) 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 0436eb9d2b..4f0e7c1ce9 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -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/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..58c07bda59 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Memory /// interpreted as a 2D region of x elements. /// /// The value type. - internal sealed class Buffer2D : IDisposable + public sealed class Buffer2D : IDisposable where T : struct { private MemorySource memorySource; @@ -24,7 +24,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 +44,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 +52,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 +60,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 +77,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 +94,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/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/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/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs index aeadfcebb5..403ff1e404 100644 --- a/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs +++ b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs @@ -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); From 88de2d9048f634adcac932ac7a868b9194deda6f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 23 Oct 2019 00:58:37 +0200 Subject: [PATCH 74/87] publish ParallelHelper and RowInterval API-s --- .../Drawing/FillProcessor{TPixel}.cs | 2 +- .../Common/ParallelUtils/ParallelHelper.cs | 27 ++++++------ src/ImageSharp/Memory/Buffer2D{T}.cs | 6 ++- src/ImageSharp/Memory/RowInterval.cs | 43 ++++++++++++++----- 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs index a7c22f6d7b..012bda4e8e 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing int width = maxX - minX; - 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; diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs b/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs index 1e7299720d..170489c7a5 100644 --- a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs +++ b/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs @@ -17,19 +17,14 @@ namespace SixLabors.ImageSharp.ParallelUtils /// Parallel execution is optimized for image processing. /// Use this instead of direct calls! /// - 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(); @@ -40,7 +35,7 @@ namespace SixLabors.ImageSharp.ParallelUtils /// /// Iterate through the rows of a rectangle in optimized batches defined by -s. /// - public static void IterateRows( + internal static void IterateRows( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, Action body) @@ -77,11 +72,19 @@ namespace SixLabors.ImageSharp.ParallelUtils }); } + /// + /// Get the default for a + /// + internal 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 /// instantiating a temporary buffer for each invocation. /// - public static void IterateRowsWithTempBuffer( + internal static void IterateRowsWithTempBuffer( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, Action> body) @@ -133,7 +136,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, Configuration configuration, Action> body) diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index 58c07bda59..ac7de1445f 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -12,7 +12,11 @@ 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. + // TODO: Consider moving this type to the SixLabors.Memory namespace (SixLabors.Core). public sealed class Buffer2D : IDisposable where T : struct { @@ -38,7 +42,7 @@ namespace SixLabors.ImageSharp.Memory /// /// Gets the height. - /// + /// Bu public int Height { get; private set; } /// 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 +} From e6d7af071da96d5ee779f12b9b1a2d946fd734d9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 23 Oct 2019 11:56:48 +1100 Subject: [PATCH 75/87] Fix permissions --- src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs | 6 +++--- .../PixelBlenders/DefaultPixelBlenders.Generated.cs | 2 +- .../PixelBlenders/DefaultPixelBlenders.Generated.tt | 2 +- .../PixelFormats/PixelOperations{TPixel}.PixelBenders.cs | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) 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 c63d058081..51ee5d12d9 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// Note there are faster functions for when the backdrop color is known /// to be opaque /// - public static class DefaultPixelBlenders + internal static class DefaultPixelBlenders where TPixel : struct, IPixel { diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index 7eefcad2d5..55eb01df31 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders /// Note there are faster functions for when the backdrop color is known /// to be opaque /// - public static class DefaultPixelBlenders + internal static class DefaultPixelBlenders where TPixel : struct, IPixel { 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 +} From 7c45f467d54eaa386dbdb4b10f567e03ddf05509 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 24 Oct 2019 02:21:08 +0200 Subject: [PATCH 76/87] expose ParallelExecutionSettings and ParallelHelper, fix MaxDegreeOfParallelism --- .../DrawImageProcessor{TPixelBg,TPixelFg}.cs | 2 +- .../Drawing/FillProcessor{TPixel}.cs | 7 ++-- .../ParallelExecutionSettings.cs | 38 ++++++++++++++++-- .../ParallelUtils/ParallelHelper.cs | 28 ++++++------- src/ImageSharp/ImageFrame{TPixel}.cs | 2 +- .../BinaryThresholdProcessor{TPixel}.cs | 2 +- .../Convolution/BokehBlurProcessor{TPixel}.cs | 2 +- .../Convolution2DProcessor{TPixel}.cs | 2 +- .../Convolution2PassProcessor{TPixel}.cs | 3 +- .../ConvolutionProcessor{TPixel}.cs | 3 +- .../EdgeDetectorCompassProcessor{TPixel}.cs | 2 +- .../Effects/OilPaintingProcessor{TPixel}.cs | 2 +- .../Filters/FilterProcessor{TPixel}.cs | 2 +- ...lHistogramEqualizationProcessor{TPixel}.cs | 2 +- .../BackgroundColorProcessor{TPixel}.cs | 2 +- .../Overlays/GlowProcessor{TPixel}.cs | 2 +- .../Overlays/VignetteProcessor{TPixel}.cs | 2 +- .../AffineTransformProcessor{TPixel}.cs | 2 +- .../Transforms/CropProcessor{TPixel}.cs | 8 ++-- .../Transforms/FlipProcessor{TPixel}.cs | 2 +- .../ProjectiveTransformProcessor{TPixel}.cs | 2 +- .../Resize/ResizeProcessor{TPixel}.cs | 2 +- .../Transforms/RotateProcessor{TPixel}.cs | 2 +- tests/ImageSharp.Tests/ConfigurationTests.cs | 26 +++++++++--- .../Helpers/ParallelExecutionSettingsTests.cs | 40 +++++++++++++++++++ .../Helpers/ParallelHelperTests.cs | 2 +- .../TestUtilities/TestImageExtensions.cs | 2 +- 27 files changed, 138 insertions(+), 53 deletions(-) rename src/ImageSharp/{Common => Advanced}/ParallelUtils/ParallelExecutionSettings.cs (55%) rename src/ImageSharp/{Common => Advanced}/ParallelUtils/ParallelHelper.cs (84%) create mode 100644 tests/ImageSharp.Tests/Helpers/ParallelExecutionSettingsTests.cs 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 76082136c7..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; diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs index 012bda4e8e..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; @@ -53,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, 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 84% rename from src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs rename to src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs index 170489c7a5..c56337bffe 100644 --- a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs +++ b/src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs @@ -10,12 +10,13 @@ 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. /// public static class ParallelHelper { @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.ParallelUtils /// 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); } @@ -35,14 +36,19 @@ namespace SixLabors.ImageSharp.ParallelUtils /// /// Iterate through the rows of a rectangle in optimized batches defined by -s. /// - internal static void IterateRows( + /// The . + /// The . + /// The method body defining the iteration logic on a single . + public static void IterateRows( Rectangle rectangle, in ParallelExecutionSettings parallelSettings, Action body) { 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); @@ -72,14 +78,6 @@ namespace SixLabors.ImageSharp.ParallelUtils }); } - /// - /// Get the default for a - /// - internal 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 /// instantiating a temporary buffer for each invocation. @@ -142,7 +140,7 @@ namespace SixLabors.ImageSharp.ParallelUtils Action> body) where T : unmanaged { - IterateRowsWithTempBuffer(rectangle, configuration.GetParallelSettings(), body); + IterateRowsWithTempBuffer(rectangle, ParallelExecutionSettings.FromConfiguration(configuration), body); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 4f0e7c1ce9..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; diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs index 7234955edb..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; diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index 9339c8fe43..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; diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs index b38d87cd4f..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; diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs index a523f7c227..9fad8b5b73 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs @@ -4,8 +4,9 @@ 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; diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs index 5bdec738d7..f657e131dd 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs @@ -4,8 +4,9 @@ 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; diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index dc9974c616..4fa87bc98e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -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; diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index ea7ba7409d..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; diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs index 5fa233d180..731cd2a052 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -4,7 +4,7 @@ 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; diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs index 8aaa5403d4..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; diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs index 2459b47069..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; diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs index 756e8647ba..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; diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs index 8569410d22..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; diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index 97b8b009b5..e693de8f6e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -4,7 +4,7 @@ using System; using System.Numerics; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index 1bbdd0a161..2cc4a38675 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -3,7 +3,7 @@ using System; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -47,10 +47,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Rectangle bounds = this.cropRectangle; // Copying is cheap, we should process more pixels per task: - ParallelExecutionSettings parallelSettings - = this.Configuration - .GetParallelSettings() - .MultiplyMinimumPixelsPerTask(4); + ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(this.Configuration) + .MultiplyMinimumPixelsPerTask(4); ParallelHelper.IterateRows( bounds, diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs index 9374af476b..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; diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index 68bfd817e5..c6212a7d3b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -5,7 +5,7 @@ using System; using System.Numerics; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 78e471ad62..3b508032a0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -4,8 +4,8 @@ using System; 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; diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs index 1ed4c362c5..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; 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/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 403ff1e404..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; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 95f7f81fda..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; From e342d4f6e43d6219a12306c797907b0c3c7b6203 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 24 Oct 2019 02:25:36 +0200 Subject: [PATCH 77/87] fix Configuration.MaxDegreeOfParallelism error cases --- src/ImageSharp/Configuration.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 0d44db8d87..6a436187d5 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -63,9 +63,9 @@ namespace SixLabors.ImageSharp get => this.maxDegreeOfParallelism; set { - if (value <= 0) + if (value == 0 || value < -1) { - throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism)); + throw new ArgumentOutOfRangeException(nameof(MaxDegreeOfParallelism)); } this.maxDegreeOfParallelism = value; @@ -161,4 +161,4 @@ namespace SixLabors.ImageSharp new BmpConfigurationModule()); } } -} \ No newline at end of file +} From 6d566688d7ee1ec00963283eef8ce303b3492a67 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 24 Oct 2019 02:33:16 +0200 Subject: [PATCH 78/87] StyleCop --- src/ImageSharp/Configuration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 6a436187d5..ae20490c77 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp { if (value == 0 || value < -1) { - throw new ArgumentOutOfRangeException(nameof(MaxDegreeOfParallelism)); + throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism)); } this.maxDegreeOfParallelism = value; From c0a39cd647a158bf7f991e02a3df1d2c621329ab Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 24 Oct 2019 13:28:37 +1100 Subject: [PATCH 79/87] update Microsoft.Net.Compilers.Toolset to 3.3.1 --- Directory.Build.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 1551a29d8c..1dc081782a 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -25,7 +25,7 @@ - + From fb65264e87de3d68f362c7825e00c6204b82249e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 24 Oct 2019 15:59:22 +1100 Subject: [PATCH 80/87] Improve readability --- .../Components/Decoder/HuffmanScanBuffer.cs | 4 +-- .../Jpeg/Components/Decoder/HuffmanTable.cs | 26 +++++++++---------- src/ImageSharp/Formats/Jpeg/JpegConstants.cs | 10 +++++-- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs index 13c89c82cf..cabd26dc64 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs @@ -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.MinBits; + this.data = (this.data << JpegConstants.Huffman.MinBits) | this.GetBytes(); } [MethodImpl(InliningOptions.ShortMethod)] 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..53f185f01e 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,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// public const int RegisterSize = 64; + /// + /// The minimum number of bits required by the . + /// + public const int MinBits = 48; + /// /// 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 +272,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public const int LookupSize = 1 << LookupBits; } } -} \ No newline at end of file +} From f993eb6563375d7514508ce6d13ca28b88d8d682 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 24 Oct 2019 19:04:26 +1100 Subject: [PATCH 81/87] Remove extra chars --- src/ImageSharp/Memory/Buffer2D{T}.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Memory/Buffer2D{T}.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs index ac7de1445f..06cfdf5607 100644 --- a/src/ImageSharp/Memory/Buffer2D{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Memory /// /// Gets the height. - /// Bu + /// public int Height { get; private set; } /// From 5a2c5d27dbdaccb635c7d28a2814c68255f06bf4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 24 Oct 2019 20:47:47 +1100 Subject: [PATCH 82/87] Introduce a few more constants --- .../Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs | 6 +++--- src/ImageSharp/Formats/Jpeg/JpegConstants.cs | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs index cabd26dc64..a2b784c479 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 += JpegConstants.Huffman.MinBits; - this.data = (this.data << JpegConstants.Huffman.MinBits) | this.GetBytes(); + this.remainingBits += JpegConstants.Huffman.FetchBits; + this.data = (this.data << JpegConstants.Huffman.FetchBits) | this.GetBytes(); } [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs b/src/ImageSharp/Formats/Jpeg/JpegConstants.cs index 53f185f01e..6268f00db4 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegConstants.cs @@ -251,9 +251,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg public const int RegisterSize = 64; /// - /// The minimum number of bits required by the . + /// The number of bits to fetch when filling the buffer. /// - public const int MinBits = 48; + public const int FetchBits = 48; + + /// + /// 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 From c5ebc45ebb54303b5d1173f02bd185eb89996f63 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 25 Oct 2019 10:15:14 +1100 Subject: [PATCH 83/87] Introduce one more constant + cleanup --- .../Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs | 2 +- src/ImageSharp/Formats/Jpeg/JpegConstants.cs | 5 +++++ .../Processing/Processors/ImageProcessor{TPixel}.cs | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs index a2b784c479..34fe1aecbd 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanBuffer.cs @@ -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/JpegConstants.cs b/src/ImageSharp/Formats/Jpeg/JpegConstants.cs index 6268f00db4..9f50e2cab1 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegConstants.cs @@ -255,6 +255,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// 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. /// diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs index 3e46e3c087..b8bbe1e031 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs @@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - public virtual void Dispose() + public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); From 37a74ecd9809a42725c8911c2eb2f0d2e143c9ce Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 26 Oct 2019 00:34:31 +1100 Subject: [PATCH 84/87] Optimize effor diffusion. Fix #757 --- src/ImageSharp/Advanced/AotCompilerTools.cs | 2 +- .../Extensions/DiffuseExtensions.cs | 4 +- .../BinaryErrorDiffusionProcessor{TPixel}.cs | 4 +- .../Processors/Dithering/AtkinsonDiffuser.cs | 12 ++- .../Processors/Dithering/BurksDiffuser.cs | 12 ++- .../Processors/Dithering/ErrorDiffuser.cs | 96 ++++++------------- .../ErrorDiffusionPaletteProcessor{TPixel}.cs | 4 +- .../Dithering/FloydSteinbergDiffuser.cs | 12 ++- .../Processors/Dithering/IErrorDiffuser.cs | 7 +- .../Dithering/JarvisJudiceNinkeDiffuser.cs | 14 +-- .../Processors/Dithering/Sierra2Diffuser.cs | 12 ++- .../Processors/Dithering/Sierra3Diffuser.cs | 14 +-- .../Dithering/SierraLiteDiffuser.cs | 12 ++- .../Dithering/StevensonArceDiffuser.cs | 16 ++-- .../Processors/Dithering/StuckiDiffuser.cs | 14 +-- .../Processors/Dithering/error_diffusion.txt | 3 + .../OctreeFrameQuantizer{TPixel}.cs | 6 +- .../PaletteFrameQuantizer{TPixel}.cs | 6 +- .../Quantization/WuFrameQuantizer{TPixel}.cs | 2 +- .../ImageSharp.Benchmarks/Samplers/Diffuse.cs | 49 ++++++++++ 20 files changed, 168 insertions(+), 133 deletions(-) create mode 100644 tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 1ceba5f90e..5d172d93f7 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -136,7 +136,7 @@ namespace SixLabors.ImageSharp.Advanced TPixel pixel = default; using (var image = new ImageFrame(Configuration.Default, 1, 1)) { - test.Dither(image, pixel, pixel, 0, 0, 0, 0, 0, 0); + test.Dither(image, pixel, pixel, 0, 0, 0, 0, 0); } } 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/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs index 7e3458ae3e..0124575488 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs @@ -14,7 +14,7 @@ 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; @@ -76,7 +76,7 @@ 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); } } } 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..a6f666c98c 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,61 +15,32 @@ 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 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); - 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.startingOffset = 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; @@ -82,45 +53,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.startingOffset; + 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; row < matrix.Rows && targetY < maxY; row++) { - 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 offsetColor = pixel.ToVector4(); - Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor; - pixel.FromVector4(result); - } + Vector4 result = (error * coefficient) + offsetColor; + pixel.FromVector4(result); } } } } } -} \ 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 557a31c336..a14a191c69 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs @@ -13,7 +13,7 @@ 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 { /// @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } 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); } } } 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/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/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/WuFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs index 87d696dc91..64a5010a8d 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs @@ -260,7 +260,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/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs b/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs new file mode 100644 index 0000000000..c0a9d2030e --- /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 | 12.94 ms | 22.48 ms | 1.232 ms | - | - | - | 4.25 KB | +// | DoDiffuse | Core | Core | 10.95 ms | 19.31 ms | 1.058 ms | - | - | - | 4.13 KB | From 07e1fe1b8dc3af02830e3147801b4cb13112827c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 26 Oct 2019 00:35:39 +1100 Subject: [PATCH 85/87] Update External --- tests/Images/External | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Images/External b/tests/Images/External index 1d3d4e3652..54e0757856 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 1d3d4e3652dc95bd8bd420346bfe0f189addc587 +Subproject commit 54e075785697c9d6aa371282d492f16d9d916888 From 3d0a70f59209bd31840d918dbb6ed351b64c2578 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 27 Oct 2019 00:17:49 +1100 Subject: [PATCH 86/87] Fix output, cleanup and minor optimizations. --- .../Processors/Dithering/ErrorDiffuser.cs | 20 +++--- .../ErrorDiffusionPaletteProcessor{TPixel}.cs | 6 +- .../OrderedDitherPaletteProcessor{TPixel}.cs | 6 +- .../PaletteDitherProcessor{TPixel}.cs | 69 ++++++++++++------- .../Quantization/FrameQuantizer{TPixel}.cs | 55 +++++++++++---- .../Quantization/WuFrameQuantizer{TPixel}.cs | 29 +++++--- .../ImageSharp.Benchmarks/Samplers/Diffuse.cs | 8 +-- tests/Images/External | 2 +- 8 files changed, 130 insertions(+), 65 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs index a6f666c98c..7911c6ca96 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuser.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public abstract class ErrorDiffuser : IErrorDiffuser { - private readonly int startingOffset; + private readonly int offset; private readonly DenseMatrix matrix; /// @@ -24,13 +24,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// The dithering matrix. internal ErrorDiffuser(in DenseMatrix matrix) { - this.startingOffset = 0; + // Calculate the offset position of the pixel relative to + // the diffusion matrix. + this.offset = 0; for (int col = 0; col < matrix.Columns; col++) { if (matrix[0, col] != 0) { - this.startingOffset = col - 1; + this.offset = col - 1; break; } } @@ -45,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering { 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; @@ -60,18 +62,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering private void DoDither(ImageFrame image, int x, int y, int minX, int maxX, int maxY, Vector4 error) where TPixel : struct, IPixel { - int offset = this.startingOffset; + int offset = this.offset; DenseMatrix matrix = this.matrix; // Loop through and distribute the error amongst neighboring pixels. - for (int row = 0, targetY = y + row; row < matrix.Rows && targetY < maxY; row++) + for (int row = 0, targetY = y; row < matrix.Rows && targetY < maxY; row++, targetY++) { Span rowSpan = image.GetPixelRowSpan(targetY); for (int col = 0; col < matrix.Columns; col++) { int targetX = x + (col - offset); - if (targetX > minX && targetX < maxX) + if (targetX >= minX && targetX < maxX) { float coefficient = matrix[row, col]; if (coefficient == 0) @@ -80,9 +82,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } ref TPixel pixel = ref rowSpan[targetX]; - var offsetColor = pixel.ToVector4(); + var result = pixel.ToVector4(); - Vector4 result = (error * coefficient) + offsetColor; + result += error * coefficient; pixel.FromVector4(result); } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs index a14a191c69..37dcd7d5cb 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs @@ -33,8 +33,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering 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(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; @@ -49,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++) { @@ -72,7 +70,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; diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs index 08eaec503d..8cde8943e3 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs @@ -32,8 +32,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// protected override void OnFrameApply(ImageFrame source) { - bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; @@ -48,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++) { @@ -71,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; diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs index 9f817267fe..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,13 +21,10 @@ 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. @@ -37,6 +36,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering : 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; } @@ -44,28 +45,45 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// protected override void BeforeFrameApply(ImageFrame source) { - // 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(this.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( this.Configuration, - (ReadOnlySpan)this.palette, - (Span)this.paletteVector, + 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); + } + /// /// Returns the two closest colors from the palette calculated via Euclidean distance in the Rgba space. /// @@ -93,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); } } 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/WuFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs index 64a5010a8d..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(); diff --git a/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs b/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs index c0a9d2030e..6e67d11ef0 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Diffuse.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Samplers // // #### After #### // -// | Method | Job | Runtime | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | -// |---------- |----- |-------- |---------:|---------:|---------:|------:|------:|------:|----------:| -// | DoDiffuse | Clr | Clr | 12.94 ms | 22.48 ms | 1.232 ms | - | - | - | 4.25 KB | -// | DoDiffuse | Core | Core | 10.95 ms | 19.31 ms | 1.058 ms | - | - | - | 4.13 KB | +// | 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/Images/External b/tests/Images/External index 54e0757856..563ec6f777 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 54e075785697c9d6aa371282d492f16d9d916888 +Subproject commit 563ec6f7774734ba39924174c8961705a1ea6fa2 From c4bb9cbd94a02da1d4c65e096f4d0c6a6cf961ef Mon Sep 17 00:00:00 2001 From: Peter Amrehn Date: Thu, 31 Oct 2019 22:32:16 +0100 Subject: [PATCH 87/87] small improvements on the PorterDuff functions line 167: dstW + srcW + blendW = dstW + (src.W - blendW) + blendW = destination.W + source.W - blendW + blendW = destination.W + source.W line 194: dstW + blendW = (destination.W - blendW) + blendW = destination.W - blendW + blendW = destination.W --- .../PixelFormats/PixelBlenders/PorterDuffFunctions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index d42518292b..97b4458aff 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -164,7 +164,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders float srcW = source.W - blendW; // calculate final alpha - float alpha = dstW + srcW + blendW; + float alpha = dstW + source.W; // calculate final color Vector4 color = (destination * dstW) + (source * srcW) + (blend * blendW); @@ -191,7 +191,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders float dstW = destination.W - blendW; // calculate final alpha - float alpha = dstW + blendW; + float alpha = destination.W; // calculate final color Vector4 color = (destination * dstW) + (blend * blendW);