From 472bb850302a3e4b43870a65d7d64de4bfefc7cb Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 29 Apr 2017 23:01:51 +0100 Subject: [PATCH] initial blender implementations --- src/ImageSharp.Drawing/Brushes/IBrush.cs | 3 +- .../Brushes/ImageBrush{TPixel}.cs | 66 ++++----- .../Brushes/PatternBrush{TPixel}.cs | 43 +++--- .../Brushes/Processors/BrushApplicator.cs | 53 +++---- .../Brushes/RecolorBrush{TPixel}.cs | 55 +++----- .../Brushes/SolidBrush{TPixel}.cs | 83 ++++++----- src/ImageSharp.Drawing/FillRegion.cs | 16 ++- src/ImageSharp.Drawing/GraphicsOptions.cs | 56 ++++++-- src/ImageSharp.Drawing/Pens/IPen.cs | 3 +- src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs | 15 +- .../Processors/DrawPathProcessor.cs | 2 +- .../Processors/FillProcessor.cs | 7 +- .../Processors/FillRegionProcessor.cs | 133 +++++++++--------- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 4 +- src/ImageSharp/Image/PixelAccessor{TPixel}.cs | 2 +- src/ImageSharp/PixelFormats/Alpha8.cs | 2 +- src/ImageSharp/PixelFormats/Argb32.cs | 2 +- src/ImageSharp/PixelFormats/Bgr565.cs | 2 +- src/ImageSharp/PixelFormats/Bgra4444.cs | 2 +- src/ImageSharp/PixelFormats/Bgra5551.cs | 2 +- src/ImageSharp/PixelFormats/Byte4.cs | 2 +- src/ImageSharp/PixelFormats/HalfSingle.cs | 2 +- src/ImageSharp/PixelFormats/HalfVector2.cs | 2 +- src/ImageSharp/PixelFormats/HalfVector4.cs | 2 +- src/ImageSharp/PixelFormats/IPixel.cs | 8 +- .../PixelFormats/NormalizedByte2.cs | 2 +- .../PixelFormats/NormalizedByte4.cs | 2 +- .../PixelFormats/NormalizedShort2.cs | 2 +- .../PixelFormats/NormalizedShort4.cs | 2 +- .../PixelFormats/PixelBlenderMode.cs | 83 +++++++++++ .../DefaultBurnPixelBlender{TPixel}.cs | 42 ++++++ .../DefaultDarkenPixelBlender{TPixel}.cs | 42 ++++++ .../DefaultDifferencePixelBlender{TPixel}.cs | 42 ++++++ .../DefaultDodgePixelBlender{TPixel}.cs | 42 ++++++ .../DefaultExclusionPixelBlender{TPixel}.cs | 42 ++++++ .../DefaultHardLightPixelBlender{TPixel}.cs | 42 ++++++ .../DefaultLightenPixelBlender{TPixel}.cs | 42 ++++++ .../DefaultMultiplyPixelBlender{TPixel}.cs | 42 ++++++ .../DefaultNormalPixelBlender{TPixel}.cs | 37 +++++ .../DefaultOverlayPixelBlender{TPixel}.cs | 42 ++++++ ...ltPremultipliedLerpPixelBlender{TPixel}.cs | 42 ++++++ .../DefaultScreenPixelBlender{TPixel}.cs | 42 ++++++ .../DefaultSoftLightPixelBlender{TPixel}.cs | 42 ++++++ .../PixelFormats/PixelBlender{TPixel}.cs | 39 +++++ .../PixelOperations{TPixel}.PixelBenders.cs | 125 ++++++++++++++++ ...{TPixel}.cs => PixelOperations{TPixel}.cs} | 20 +-- src/ImageSharp/PixelFormats/Rg32.cs | 2 +- src/ImageSharp/PixelFormats/Rgba1010102.cs | 2 +- ...perations.cs => Rgba32.PixelOperations.cs} | 6 +- src/ImageSharp/PixelFormats/Rgba32.cs | 2 +- src/ImageSharp/PixelFormats/Rgba64.cs | 2 +- ...tions.cs => RgbaVector.PixelOperations.cs} | 4 +- src/ImageSharp/PixelFormats/RgbaVector.cs | 2 +- src/ImageSharp/PixelFormats/Short2.cs | 2 +- src/ImageSharp/PixelFormats/Short4.cs | 2 +- .../PixelFormats/Vector4BlendTransforms.cs | 2 +- .../ColorMatrix/PolaroidProcessor.cs | 2 +- .../Processors/Overlays/GlowProcessor.cs | 40 +++--- .../Overlays/GlowProcessorParallel.cs | 100 +++++++++++++ .../Processors/Transforms/ResizeProcessor.cs | 2 +- .../Color/Bulk/PackFromXyzw.cs | 4 +- .../Color/Bulk/ToVector4.cs | 4 +- .../ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs | 4 +- .../Color/Bulk/ToXyzw.cs | 4 +- tests/ImageSharp.Benchmarks/Samplers/Glow.cs | 48 +++++++ ...ationsTests.cs => PixelOperationsTests.cs} | 12 +- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 6 + .../PixelFormats/PixelOperations.cs | 53 +++++++ .../Processors/Filters/GlowTest.cs | 2 +- .../TestUtilities/TestPixel.cs | 62 ++++++++ 70 files changed, 1430 insertions(+), 322 deletions(-) create mode 100644 src/ImageSharp/PixelFormats/PixelBlenderMode.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultBurnPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlenders/DefaultSoftLightPixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs create mode 100644 src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs rename src/ImageSharp/PixelFormats/{BulkPixelOperations{TPixel}.cs => PixelOperations{TPixel}.cs} (91%) rename src/ImageSharp/PixelFormats/{Rgba32.BulkOperations.cs => Rgba32.PixelOperations.cs} (97%) rename src/ImageSharp/PixelFormats/{RgbaVector.BulkOperations.cs => RgbaVector.PixelOperations.cs} (81%) create mode 100644 src/ImageSharp/Processing/Processors/Overlays/GlowProcessorParallel.cs create mode 100644 tests/ImageSharp.Benchmarks/Samplers/Glow.cs rename tests/ImageSharp.Tests/Colors/{BulkPixelOperationsTests.cs => PixelOperationsTests.cs} (96%) create mode 100644 tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/TestPixel.cs diff --git a/src/ImageSharp.Drawing/Brushes/IBrush.cs b/src/ImageSharp.Drawing/Brushes/IBrush.cs index e16f22028..9534c7a88 100644 --- a/src/ImageSharp.Drawing/Brushes/IBrush.cs +++ b/src/ImageSharp.Drawing/Brushes/IBrush.cs @@ -24,6 +24,7 @@ namespace ImageSharp.Drawing /// /// The pixel source. /// The region the brush will be applied to. + /// The graphic options /// /// The brush applicator for this brush /// @@ -31,6 +32,6 @@ namespace ImageSharp.Drawing /// The when being applied to things like shapes would usually be the /// bounding box of the shape not necessarily the bounds of the whole image /// - BrushApplicator CreateApplicator(PixelAccessor pixelSource, RectangleF region); + BrushApplicator CreateApplicator(PixelAccessor pixelSource, RectangleF region, GraphicsOptions options); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs index 3e2da040f..5b1409fe2 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs @@ -31,9 +31,9 @@ namespace ImageSharp.Drawing.Brushes } /// - public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region) + public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options) { - return new ImageBrushApplicator(sourcePixels, this.image, region); + return new ImageBrushApplicator(sourcePixels, this.image, region, options); } /// @@ -57,9 +57,14 @@ namespace ImageSharp.Drawing.Brushes private readonly int xLength; /// - /// The offset. + /// The Y offset. /// - private readonly Vector2 offset; + private readonly int offsetY; + + /// + /// The X offset. + /// + private readonly int offsetX; /// /// Initializes a new instance of the class. @@ -70,16 +75,18 @@ namespace ImageSharp.Drawing.Brushes /// /// The region. /// + /// The options /// /// The sourcePixels. /// - public ImageBrushApplicator(PixelAccessor sourcePixels, IImageBase image, RectangleF region) - : base(sourcePixels) + public ImageBrushApplicator(PixelAccessor sourcePixels, IImageBase image, RectangleF region, GraphicsOptions options) + : base(sourcePixels, options) { this.source = image.Lock(); this.xLength = image.Width; this.yLength = image.Height; - this.offset = new Vector2(MathF.Max(MathF.Floor(region.Top), 0), MathF.Max(MathF.Floor(region.Left), 0)); + this.offsetY = (int)MathF.Max(MathF.Floor(region.Top), 0); + this.offsetX = (int)MathF.Max(MathF.Floor(region.Left), 0); } /// @@ -94,13 +101,8 @@ namespace ImageSharp.Drawing.Brushes { get { - Vector2 point = new Vector2(x, y); - - // Offset the requested pixel by the value in the rectangle (the shapes position) - point = point - this.offset; - int srcX = (int)point.X % this.xLength; - int srcY = (int)point.Y % this.yLength; - + int srcX = (x - this.offsetX) % this.xLength; + int srcY = (y - this.offsetY) % this.yLength; return this.source[srcX, srcY]; } } @@ -112,33 +114,27 @@ namespace ImageSharp.Drawing.Brushes } /// - internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) + internal override void Apply(BufferSpan scanline, int x, int y) { - Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - - using (Buffer buffer = new Buffer(scanlineBuffer)) + // create a span for colors + using (Buffer amountBuffer = new Buffer(scanline.Length)) + using (Buffer overlay = new Buffer(scanline.Length)) { - BufferSpan slice = buffer.Slice(offset); + int sourceY = (y - this.offsetY) % this.yLength; + int offsetX = x - this.offsetX; + BufferSpan sourceRow = this.source.GetRowSpan(sourceY); - for (int xPos = 0; xPos < scanlineWidth; xPos++) + for (int i = 0; i < scanline.Length; i++) { - int targetX = xPos + x; - int targetY = y; - - float opacity = slice[xPos]; - if (opacity > Constants.Epsilon) - { - Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); - - Vector4 sourceVector = this[targetX, targetY].ToVector4(); + amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; - Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - - TPixel packed = default(TPixel); - packed.PackFromVector4(finalColor); - this.Target[targetX, targetY] = packed; - } + int sourceX = (i + offsetX) % this.xLength; + TPixel pixel = sourceRow[sourceX]; + overlay[i] = pixel; } + + BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer); } } } diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs index ad37f4d28..4aebe00fb 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs @@ -90,9 +90,9 @@ namespace ImageSharp.Drawing.Brushes } /// - public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region) + public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options) { - return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector); + return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector, options); } /// @@ -112,8 +112,9 @@ namespace ImageSharp.Drawing.Brushes /// The sourcePixels. /// The pattern. /// The patternVector. - public PatternBrushApplicator(PixelAccessor sourcePixels, Fast2DArray pattern, Fast2DArray patternVector) - : base(sourcePixels) + /// The options + public PatternBrushApplicator(PixelAccessor sourcePixels, Fast2DArray pattern, Fast2DArray patternVector, GraphicsOptions options) + : base(sourcePixels, options) { this.pattern = pattern; this.patternVector = patternVector; @@ -146,34 +147,22 @@ namespace ImageSharp.Drawing.Brushes } /// - internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) + internal override void Apply(BufferSpan scanline, int x, int y) { - Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - - using (Buffer buffer = new Buffer(scanlineBuffer)) + int patternY = y % this.pattern.Height; + using (Buffer amountBuffer = new Buffer(scanline.Length)) + using (Buffer overlay = new Buffer(scanline.Length)) { - BufferSpan slice = buffer.Slice(offset); - - for (int xPos = 0; xPos < scanlineWidth; xPos++) + for (int i = 0; i < scanline.Length; i++) { - int targetX = xPos + x; - int targetY = y; - - float opacity = slice[xPos]; - if (opacity > Constants.Epsilon) - { - Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); + amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; - // 2d array index at row/column - Vector4 sourceVector = this.patternVector[targetY % this.patternVector.Height, targetX % this.patternVector.Width]; - - Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - - TPixel packed = default(TPixel); - packed.PackFromVector4(finalColor); - this.Target[targetX, targetY] = packed; - } + int patternX = (x + i) % this.pattern.Width; + overlay[i] = this.pattern[y, x]; } + + BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer); } } } diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 5dd6dad76..59cb0820a 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -21,16 +21,31 @@ namespace ImageSharp.Drawing.Processors /// Initializes a new instance of the class. /// /// The target. - internal BrushApplicator(PixelAccessor target) + /// The options. + internal BrushApplicator(PixelAccessor target, GraphicsOptions options) { this.Target = target; + + this.Options = options; + + this.Blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode); } + /// + /// Gets the blendder + /// + internal PixelBlender Blender { get; } + /// /// Gets the destinaion /// protected PixelAccessor Target { get; } + /// + /// Gets the blend percentage + /// + protected GraphicsOptions Options { get; private set; } + /// /// Gets the color for a single pixel. /// @@ -45,39 +60,27 @@ namespace ImageSharp.Drawing.Processors /// /// Applies the opactiy weighting for each pixel in a scanline to the target based on the pattern contained in the brush. /// - /// The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target. - /// The number of pixels effected by this scanline. - /// The offset fromthe begining of the opacity data starts. + /// The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target. /// The x position in the target pixel space that the start of the scanline data corresponds to. /// The y position in the target pixel space that whole scanline corresponds to. /// scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs. - internal virtual void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) + internal virtual void Apply(BufferSpan scanline, int x, int y) { - DebugGuard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - - using (Buffer buffer = new Buffer(scanlineBuffer)) + using (Buffer amountBuffer = new Buffer(scanline.Length)) + using (Buffer overlay = new Buffer(scanline.Length)) { - BufferSpan slice = buffer.Slice(offset); - - for (int xPos = 0; xPos < scanlineWidth; xPos++) + for (int i = 0; i < scanline.Length; i++) { - int targetX = xPos + x; - int targetY = y; - - float opacity = slice[xPos]; - if (opacity > Constants.Epsilon) + if (this.Options.BlendPercentage < 1) { - Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); - - Vector4 sourceVector = this[targetX, targetY].ToVector4(); - - Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - - TPixel packed = default(TPixel); - packed.PackFromVector4(finalColor); - this.Target[targetX, targetY] = packed; + amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; } + + overlay[i] = this[x + i, y]; } + + BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer); } } } diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs index 7c192b2d3..b8b2499ff 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs @@ -54,9 +54,9 @@ namespace ImageSharp.Drawing.Brushes public TPixel TargeTPixel { get; } /// - public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region) + public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options) { - return new RecolorBrushApplicator(sourcePixels, this.SourceColor, this.TargeTPixel, this.Threshold); + return new RecolorBrushApplicator(sourcePixels, this.SourceColor, this.TargeTPixel, this.Threshold, options); } /// @@ -86,8 +86,9 @@ namespace ImageSharp.Drawing.Brushes /// Color of the source. /// Color of the target. /// The threshold . - public RecolorBrushApplicator(PixelAccessor sourcePixels, TPixel sourceColor, TPixel targeTPixel, float threshold) - : base(sourcePixels) + /// The options + public RecolorBrushApplicator(PixelAccessor sourcePixels, TPixel sourceColor, TPixel targeTPixel, float threshold, GraphicsOptions options) + : base(sourcePixels, options) { this.sourceColor = sourceColor.ToVector4(); this.targeTPixel = targeTPixel.ToVector4(); @@ -136,42 +137,24 @@ namespace ImageSharp.Drawing.Brushes } /// - internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) + internal override void Apply(BufferSpan scanline, int x, int y) { - Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - - using (Buffer buffer = new Buffer(scanlineBuffer)) + using (Buffer amountBuffer = new Buffer(scanline.Length)) + using (Buffer overlay = new Buffer(scanline.Length)) { - BufferSpan slice = buffer.Slice(offset); - - for (int xPos = 0; xPos < scanlineWidth; xPos++) + for (int i = 0; i < scanline.Length; i++) { - int targetX = xPos + x; - int targetY = y; - - float opacity = slice[xPos]; - if (opacity > Constants.Epsilon) - { - Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); - - Vector4 sourceVector = backgroundVector; - float distance = Vector4.DistanceSquared(sourceVector, this.sourceColor); - if (distance <= this.threshold) - { - float lerpAmount = (this.threshold - distance) / this.threshold; - sourceVector = Vector4BlendTransforms.PremultipliedLerp( - sourceVector, - this.targeTPixel, - lerpAmount); - - Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - - TPixel packed = default(TPixel); - packed.PackFromVector4(finalColor); - this.Target[targetX, targetY] = packed; - } - } + amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; + + int offsetX = x + i; + + // no doubt this one can be optermised further but I can't imagine its + // actually being used and can probably be removed/interalised for now + overlay[i] = this[offsetX, y]; } + + BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + this.Blender.Compose(destinationRow, destinationRow, overlay, amountBuffer); } } } diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs index 634a2b70d..f97266c77 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs @@ -39,34 +39,61 @@ namespace ImageSharp.Drawing.Brushes public TPixel Color => this.color; /// - public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region) + public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options) { - return new SolidBrushApplicator(sourcePixels, this.color); + if (options.BlendPercentage < 0) + { + return new SolidBrushApplicator(sourcePixels, this.color, options); + } + else + { + return new SolidNoBlendBrushApplicator(sourcePixels, this.color, options); + } } /// /// The solid brush applicator. /// - private class SolidBrushApplicator : BrushApplicator + private class SolidNoBlendBrushApplicator : SolidBrushApplicator { - /// - /// The solid color. - /// - private readonly TPixel color; - private readonly Vector4 colorVector; + public SolidNoBlendBrushApplicator(PixelAccessor sourcePixels, TPixel color, GraphicsOptions options) + : base(sourcePixels, color, options) + { + } + internal override void Apply(BufferSpan scanline, int x, int y) + { + BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + this.Blender.Compose(destinationRow, destinationRow, this.Colors, scanline); + } + } + + /// + /// The solid brush applicator. + /// + private class SolidBrushApplicator : BrushApplicator + { /// /// Initializes a new instance of the class. /// /// The color. + /// The options /// The sourcePixels. - public SolidBrushApplicator(PixelAccessor sourcePixels, TPixel color) - : base(sourcePixels) + public SolidBrushApplicator(PixelAccessor sourcePixels, TPixel color, GraphicsOptions options) + : base(sourcePixels, options) { - this.color = color; - this.colorVector = color.ToVector4(); + this.Colors = new Buffer(sourcePixels.Width); + for (int i = 0; i < this.Colors.Length; i++) + { + this.Colors[i] = color; + } } + /// + /// Gets the colors. + /// + protected Buffer Colors { get; } + /// /// Gets the color for a single pixel. /// @@ -75,41 +102,27 @@ namespace ImageSharp.Drawing.Brushes /// /// The color /// - internal override TPixel this[int x, int y] => this.color; + internal override TPixel this[int x, int y] => this.Colors[x]; /// public override void Dispose() { - // noop + this.Colors.Dispose(); } /// - internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) + internal override void Apply(BufferSpan scanline, int x, int y) { - Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); + BufferSpan destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); - using (Buffer buffer = new Buffer(scanlineBuffer)) + using (Buffer amountBuffer = new Buffer(scanline.Length)) { - BufferSpan slice = buffer.Slice(offset); - - for (int xPos = 0; xPos < scanlineWidth; xPos++) + for (int i = 0; i < scanline.Length; i++) { - int targetX = xPos + x; - int targetY = y; - - float opacity = slice[xPos]; - if (opacity > Constants.Epsilon) - { - Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); - Vector4 sourceVector = this.colorVector; - - Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - - TPixel packed = default(TPixel); - packed.PackFromVector4(finalColor); - this.Target[targetX, targetY] = packed; - } + amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; } + + this.Blender.Compose(destinationRow, destinationRow, this.Colors, amountBuffer); } } } diff --git a/src/ImageSharp.Drawing/FillRegion.cs b/src/ImageSharp.Drawing/FillRegion.cs index f29c37a67..b3ee2ed99 100644 --- a/src/ImageSharp.Drawing/FillRegion.cs +++ b/src/ImageSharp.Drawing/FillRegion.cs @@ -15,6 +15,20 @@ namespace ImageSharp /// public static partial class ImageExtensions { + /// + /// Flood fills the image with the specified brush. + /// + /// The type of the color. + /// The image this method extends. + /// The details how to fill the region of interest. + /// The graphics options. + /// The . + public static Image Fill(this Image source, IBrush brush, GraphicsOptions options) + where TPixel : struct, IPixel + { + return source.Apply(new FillProcessor(brush, options)); + } + /// /// Flood fills the image with the specified brush. /// @@ -25,7 +39,7 @@ namespace ImageSharp public static Image Fill(this Image source, IBrush brush) where TPixel : struct, IPixel { - return source.Apply(new FillProcessor(brush)); + return source.Fill(brush, GraphicsOptions.Default); } /// diff --git a/src/ImageSharp.Drawing/GraphicsOptions.cs b/src/ImageSharp.Drawing/GraphicsOptions.cs index a21617ead..2ceb654d3 100644 --- a/src/ImageSharp.Drawing/GraphicsOptions.cs +++ b/src/ImageSharp.Drawing/GraphicsOptions.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Drawing { + using ImageSharp.PixelFormats; + /// /// Options for influencing the drawing functions. /// @@ -15,24 +17,60 @@ namespace ImageSharp.Drawing /// public static readonly GraphicsOptions Default = new GraphicsOptions(true); + private float? blendPercentage; + + private int? antialiasSubpixelDepth; + + private bool? antialias; + + private PixelBlenderMode blenderMode; + /// - /// Whether antialiasing should be applied. + /// Initializes a new instance of the struct. /// - public bool Antialias; + /// If set to true [enable antialiasing]. + public GraphicsOptions(bool enableAntialiasing) + { + this.blenderMode = PixelBlenderMode.Default; + this.blendPercentage = 1; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; + } /// - /// The number of subpixels to use while rendering with antialiasing enabled. + /// Gets or sets a value indicating whether antialiasing should be applied. /// - public int AntialiasSubpixelDepth; + public bool Antialias + { + get => this.antialias ?? true; + set => this.antialias = value; + } /// - /// Initializes a new instance of the struct. + /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. /// - /// If set to true [enable antialiasing]. - public GraphicsOptions(bool enableAntialiasing) + public int AntialiasSubpixelDepth + { + get => this.antialiasSubpixelDepth ?? 16; + set => this.antialiasSubpixelDepth = value; + } + + /// + /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// + public float BlendPercentage + { + get => this.blendPercentage ?? 1; + set => this.blendPercentage = value; + } + + /// + /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// + public PixelBlenderMode BlenderMode { - this.Antialias = enableAntialiasing; - this.AntialiasSubpixelDepth = 16; + get => this.blenderMode; + set => this.blenderMode = value; } } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Pens/IPen.cs b/src/ImageSharp.Drawing/Pens/IPen.cs index 31a609c8e..d488dbfb0 100644 --- a/src/ImageSharp.Drawing/Pens/IPen.cs +++ b/src/ImageSharp.Drawing/Pens/IPen.cs @@ -20,12 +20,13 @@ namespace ImageSharp.Drawing.Pens /// /// The pixel source. /// The region the pen will be applied to. + /// The currently active graphic options. /// /// Returns a the applicator for the pen. /// /// /// The when being applied to things like shapes would usually be the bounding box of the shape not necessarily the shape of the whole image. /// - PenApplicator CreateApplicator(PixelAccessor pixelSource, RectangleF region); + PenApplicator CreateApplicator(PixelAccessor pixelSource, RectangleF region, GraphicsOptions options); } } diff --git a/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs index f49d03cbc..1da50e0d6 100644 --- a/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs +++ b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs @@ -103,6 +103,7 @@ namespace ImageSharp.Drawing.Pens /// /// The source pixels. /// The region the pen will be applied to. + /// The Graphics options /// /// Returns a the applicator for the pen. /// @@ -110,16 +111,16 @@ namespace ImageSharp.Drawing.Pens /// The when being applied to things like shapes would ussually be the /// bounding box of the shape not necorserrally the shape of the whole image /// - public PenApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region) + public PenApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options) { if (this.pattern == null || this.pattern.Length < 2) { // if there is only one item in the pattern then 100% of it will // be solid so use the quicker applicator - return new SolidPenApplicator(sourcePixels, this.Brush, region, this.Width); + return new SolidPenApplicator(sourcePixels, this.Brush, region, this.Width, options); } - return new PatternPenApplicator(sourcePixels, this.Brush, region, this.Width, this.pattern); + return new PatternPenApplicator(sourcePixels, this.Brush, region, this.Width, this.pattern, options); } private class SolidPenApplicator : PenApplicator @@ -127,9 +128,9 @@ namespace ImageSharp.Drawing.Pens private readonly BrushApplicator brush; private readonly float halfWidth; - public SolidPenApplicator(PixelAccessor sourcePixels, IBrush brush, RectangleF region, float width) + public SolidPenApplicator(PixelAccessor sourcePixels, IBrush brush, RectangleF region, float width, GraphicsOptions options) { - this.brush = brush.CreateApplicator(sourcePixels, region); + this.brush = brush.CreateApplicator(sourcePixels, region, options); this.halfWidth = width / 2; this.RequiredRegion = RectangleF.Outset(region, width); } @@ -170,9 +171,9 @@ namespace ImageSharp.Drawing.Pens private readonly float[] pattern; private readonly float totalLength; - public PatternPenApplicator(PixelAccessor sourcePixels, IBrush brush, RectangleF region, float width, float[] pattern) + public PatternPenApplicator(PixelAccessor sourcePixels, IBrush brush, RectangleF region, float width, float[] pattern, GraphicsOptions options) { - this.brush = brush.CreateApplicator(sourcePixels, region); + this.brush = brush.CreateApplicator(sourcePixels, region, options); this.halfWidth = width / 2; this.totalLength = 0; diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index 62e366d2a..124722cc0 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -55,7 +55,7 @@ namespace ImageSharp.Drawing.Processors protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { using (PixelAccessor sourcePixels = source.Lock()) - using (PenApplicator applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds)) + using (PenApplicator applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds, this.Options)) { Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion); diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs index ca2dc9982..0634a06a3 100644 --- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs @@ -24,14 +24,17 @@ namespace ImageSharp.Drawing.Processors /// The brush. /// private readonly IBrush brush; + private readonly GraphicsOptions options; /// /// Initializes a new instance of the class. /// /// The brush to source pixel colors from. - public FillProcessor(IBrush brush) + /// The options + public FillProcessor(IBrush brush, GraphicsOptions options) { this.brush = brush; + this.options = options; } /// @@ -63,7 +66,7 @@ namespace ImageSharp.Drawing.Processors // for example If brush is SolidBrush then we could just get the color upfront // and skip using the IBrushApplicator?. using (PixelAccessor sourcePixels = source.Lock()) - using (BrushApplicator applicator = this.brush.CreateApplicator(sourcePixels, sourceRectangle)) + using (BrushApplicator applicator = this.brush.CreateApplicator(sourcePixels, sourceRectangle, this.options)) { Parallel.For( minY, diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index af1e6fa89..323214fb8 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -88,104 +88,105 @@ namespace ImageSharp.Drawing.Processors } using (PixelAccessor sourcePixels = source.Lock()) - using (BrushApplicator applicator = this.Brush.CreateApplicator(sourcePixels, rect)) + using (BrushApplicator applicator = this.Brush.CreateApplicator(sourcePixels, rect, this.Options)) { float[] buffer = arrayPool.Rent(maxIntersections); int scanlineWidth = maxX - minX; - float[] scanline = ArrayPool.Shared.Rent(scanlineWidth); - try + using (Buffer scanline = new Buffer(scanlineWidth)) { - bool scanlineDirty = true; - for (int y = minY; y < maxY; y++) + try { - if (scanlineDirty) + bool scanlineDirty = true; + for (int y = minY; y < maxY; y++) { - // clear the buffer - for (int x = 0; x < scanlineWidth; x++) + if (scanlineDirty) { - scanline[x] = 0; - } - - scanlineDirty = false; - } + // clear the buffer + for (int x = 0; x < scanlineWidth; x++) + { + scanline[x] = 0; + } - float subpixelFraction = 1f / subpixelCount; - float subpixelFractionPoint = subpixelFraction / subpixelCount; - for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction) - { - int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0); - if (pointsFound == 0) - { - // nothing on this line skip - continue; + scanlineDirty = false; } - QuickSort(buffer, pointsFound); - - for (int point = 0; point < pointsFound; point += 2) + float subpixelFraction = 1f / subpixelCount; + float subpixelFractionPoint = subpixelFraction / subpixelCount; + for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction) { - // points will be paired up - float scanStart = buffer[point] - minX; - float scanEnd = buffer[point + 1] - minX; - int startX = (int)MathF.Floor(scanStart); - int endX = (int)MathF.Floor(scanEnd); + int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0); + if (pointsFound == 0) + { + // nothing on this line skip + continue; + } - if (startX >= 0 && startX < scanline.Length) + QuickSort(buffer, pointsFound); + + for (int point = 0; point < pointsFound; point += 2) { - for (float x = scanStart; x < startX + 1; x += subpixelFraction) + // points will be paired up + float scanStart = buffer[point] - minX; + float scanEnd = buffer[point + 1] - minX; + int startX = (int)MathF.Floor(scanStart); + int endX = (int)MathF.Floor(scanEnd); + + if (startX >= 0 && startX < scanline.Length) { - scanline[startX] += subpixelFractionPoint; - scanlineDirty = true; + for (float x = scanStart; x < startX + 1; x += subpixelFraction) + { + scanline[startX] += subpixelFractionPoint; + scanlineDirty = true; + } } - } - if (endX >= 0 && endX < scanline.Length) - { - for (float x = endX; x < scanEnd; x += subpixelFraction) + if (endX >= 0 && endX < scanline.Length) { - scanline[endX] += subpixelFractionPoint; - scanlineDirty = true; + for (float x = endX; x < scanEnd; x += subpixelFraction) + { + scanline[endX] += subpixelFractionPoint; + scanlineDirty = true; + } } - } - int nextX = startX + 1; - endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge - if (nextX >= 0) - { - for (int x = nextX; x < endX; x++) + int nextX = startX + 1; + endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge + if (nextX >= 0) { - scanline[x] += subpixelFraction; - scanlineDirty = true; + for (int x = nextX; x < endX; x++) + { + scanline[x] += subpixelFraction; + scanlineDirty = true; + } } } } - } - if (scanlineDirty) - { - if (!this.Options.Antialias) + if (scanlineDirty) { - for (int x = 0; x < scanlineWidth; x++) + if (!this.Options.Antialias) { - if (scanline[x] > 0.5) - { - scanline[x] = 1; - } - else + for (int x = 0; x < scanlineWidth; x++) { - scanline[x] = 0; + if (scanline[x] > 0.5) + { + scanline[x] = 1; + } + else + { + scanline[x] = 0; + } } } - } - applicator.Apply(scanline, scanlineWidth, 0, minX, y); + applicator.Apply(scanline, minX, y); + } } } - } - finally - { - arrayPool.Return(buffer); - ArrayPool.Shared.Return(scanline); + finally + { + arrayPool.Return(buffer); + } } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 05ba5ee60..904aa1ff6 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -623,13 +623,13 @@ namespace ImageSharp.Formats case PngColorType.Rgb: - BulkPixelOperations.Instance.PackFromXyzBytes(scanlineBuffer, pixelBuffer, this.header.Width); + PixelOperations.Instance.PackFromXyzBytes(scanlineBuffer, pixelBuffer, this.header.Width); break; case PngColorType.RgbWithAlpha: - BulkPixelOperations.Instance.PackFromXyzwBytes(scanlineBuffer, pixelBuffer, this.header.Width); + PixelOperations.Instance.PackFromXyzwBytes(scanlineBuffer, pixelBuffer, this.header.Width); break; } diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs index 3c6763886..1e46a672e 100644 --- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -116,7 +116,7 @@ namespace ImageSharp /// BufferSpan IBuffer2D.Span => this.pixelBuffer; - private static BulkPixelOperations Operations => BulkPixelOperations.Instance; + private static PixelOperations Operations => PixelOperations.Instance; /// /// Gets or sets the pixel at the specified position. diff --git a/src/ImageSharp/PixelFormats/Alpha8.cs b/src/ImageSharp/PixelFormats/Alpha8.cs index 5e2fff5d0..5a6eebf40 100644 --- a/src/ImageSharp/PixelFormats/Alpha8.cs +++ b/src/ImageSharp/PixelFormats/Alpha8.cs @@ -60,7 +60,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/Argb32.cs b/src/ImageSharp/PixelFormats/Argb32.cs index a6db505bb..2798c4c5a 100644 --- a/src/ImageSharp/PixelFormats/Argb32.cs +++ b/src/ImageSharp/PixelFormats/Argb32.cs @@ -221,7 +221,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/Bgr565.cs b/src/ImageSharp/PixelFormats/Bgr565.cs index a3d8cbcf2..78442a3ce 100644 --- a/src/ImageSharp/PixelFormats/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/Bgr565.cs @@ -69,7 +69,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// /// Expands the packed representation into a . diff --git a/src/ImageSharp/PixelFormats/Bgra4444.cs b/src/ImageSharp/PixelFormats/Bgra4444.cs index 8d4e9b4f4..9138e25ca 100644 --- a/src/ImageSharp/PixelFormats/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/Bgra4444.cs @@ -68,7 +68,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/Bgra5551.cs b/src/ImageSharp/PixelFormats/Bgra5551.cs index 8ae1cd430..9ff571243 100644 --- a/src/ImageSharp/PixelFormats/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/Bgra5551.cs @@ -69,7 +69,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/Byte4.cs b/src/ImageSharp/PixelFormats/Byte4.cs index acb73dd1c..b27952ce4 100644 --- a/src/ImageSharp/PixelFormats/Byte4.cs +++ b/src/ImageSharp/PixelFormats/Byte4.cs @@ -71,7 +71,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/HalfSingle.cs b/src/ImageSharp/PixelFormats/HalfSingle.cs index 893667fc7..2a686f535 100644 --- a/src/ImageSharp/PixelFormats/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/HalfSingle.cs @@ -73,7 +73,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// /// Expands the packed representation into a . diff --git a/src/ImageSharp/PixelFormats/HalfVector2.cs b/src/ImageSharp/PixelFormats/HalfVector2.cs index bd3cda55d..ef9676b56 100644 --- a/src/ImageSharp/PixelFormats/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/HalfVector2.cs @@ -83,7 +83,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// /// Expands the packed representation into a . diff --git a/src/ImageSharp/PixelFormats/HalfVector4.cs b/src/ImageSharp/PixelFormats/HalfVector4.cs index 03e4326b7..8b284509f 100644 --- a/src/ImageSharp/PixelFormats/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/HalfVector4.cs @@ -86,7 +86,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index 43fe81e95..9a8d9730a 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -16,11 +16,11 @@ namespace ImageSharp.PixelFormats where TSelf : struct, IPixel { /// - /// Creates a instance for this pixel type. - /// This method is not intended to be consumed directly. Use instead. + /// Creates a instance for this pixel type. + /// This method is not intended to be consumed directly. Use instead. /// - /// The instance. - BulkPixelOperations CreateBulkOperations(); + /// The instance. + PixelOperations CreateBulkOperations(); } /// diff --git a/src/ImageSharp/PixelFormats/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/NormalizedByte2.cs index dab113ae7..c5b6eff6b 100644 --- a/src/ImageSharp/PixelFormats/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/NormalizedByte2.cs @@ -89,7 +89,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// /// Expands the packed representation into a . diff --git a/src/ImageSharp/PixelFormats/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/NormalizedByte4.cs index 0cb5c756b..9e3676660 100644 --- a/src/ImageSharp/PixelFormats/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/NormalizedByte4.cs @@ -91,7 +91,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/NormalizedShort2.cs index 86d80cbad..01a2d9954 100644 --- a/src/ImageSharp/PixelFormats/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/NormalizedShort2.cs @@ -88,7 +88,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/NormalizedShort4.cs index 8512d4131..3e4535fdf 100644 --- a/src/ImageSharp/PixelFormats/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/NormalizedShort4.cs @@ -90,7 +90,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs new file mode 100644 index 000000000..c189fefdc --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs @@ -0,0 +1,83 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + using System; + using System.Collections.Generic; + using System.Text; + + /// + /// The various blending modes. + /// + public enum PixelBlenderMode + { + /// + /// The default composition mode. + /// + /// uses PremultipliedLerpTransform + Default = 0, + + /// + /// Normal transform. + /// + Normal, + + /// + /// Multiply Transform. + /// + Multiply, + + /// + /// Screen Transform. + /// + Screen, + + /// + /// HardLight Transform. + /// + HardLight, + + /// + /// Overlay Transform. + /// + Overlay, + + /// + /// Darken Transform. + /// + Darken, + + /// + /// Lighten Transform. + /// + Lighten, + + /// + /// SoftLight Transform. + /// + SoftLight, + + /// + /// Dodge Transform. + /// + Dodge, + + /// + /// Burn Transform. + /// + Burn, + + /// + /// Difference Transform. + /// + Difference, + + /// + /// Exclusion Transform. + /// + Exclusion + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultBurnPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultBurnPixelBlender{TPixel}.cs new file mode 100644 index 000000000..f7a71e6a1 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultBurnPixelBlender{TPixel}.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultBurnPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + Vector4 result = Vector4BlendTransforms.Burn(background.ToVector4(), source.ToVector4()); + TPixel resultPixel = default(TPixel); + resultPixel.PackFromVector4(result); + return resultPixel; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + Vector4 result = Vector4BlendTransforms.Burn(background[i].ToVector4(), source[i].ToVector4()); + destination[i].PackFromVector4(result); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs new file mode 100644 index 000000000..521ae7bf1 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultDarkenPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + Vector4 result = Vector4BlendTransforms.Darken(background.ToVector4(), source.ToVector4()); + TPixel resultPixel = default(TPixel); + resultPixel.PackFromVector4(result); + return resultPixel; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + Vector4 result = Vector4BlendTransforms.Darken(background[i].ToVector4(), source[i].ToVector4()); + destination[i].PackFromVector4(result); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs new file mode 100644 index 000000000..08e1d36dd --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDifferencePixelBlender{TPixel}.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultDifferencePixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + Vector4 result = Vector4BlendTransforms.Difference(background.ToVector4(), source.ToVector4()); + TPixel resultPixel = default(TPixel); + resultPixel.PackFromVector4(result); + return resultPixel; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + Vector4 result = Vector4BlendTransforms.Difference(background[i].ToVector4(), source[i].ToVector4()); + destination[i].PackFromVector4(result); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs new file mode 100644 index 000000000..cd2c4e4b8 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDodgePixelBlender{TPixel}.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultDodgePixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + Vector4 result = Vector4BlendTransforms.Dodge(background.ToVector4(), source.ToVector4()); + TPixel resultPixel = default(TPixel); + resultPixel.PackFromVector4(result); + return resultPixel; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + Vector4 result = Vector4BlendTransforms.Dodge(background[i].ToVector4(), source[i].ToVector4()); + destination[i].PackFromVector4(result); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs new file mode 100644 index 000000000..817fe82c7 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultExclusionPixelBlender{TPixel}.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultExclusionPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + Vector4 result = Vector4BlendTransforms.Exclusion(background.ToVector4(), source.ToVector4()); + TPixel resultPixel = default(TPixel); + resultPixel.PackFromVector4(result); + return resultPixel; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + Vector4 result = Vector4BlendTransforms.Exclusion(background[i].ToVector4(), source[i].ToVector4()); + destination[i].PackFromVector4(result); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs new file mode 100644 index 000000000..41c1005f6 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultHardLightPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + Vector4 result = Vector4BlendTransforms.HardLight(background.ToVector4(), source.ToVector4()); + TPixel resultPixel = default(TPixel); + resultPixel.PackFromVector4(result); + return resultPixel; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + Vector4 result = Vector4BlendTransforms.HardLight(background[i].ToVector4(), source[i].ToVector4()); + destination[i].PackFromVector4(result); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs new file mode 100644 index 000000000..6de12372b --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultLightenPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + Vector4 result = Vector4BlendTransforms.Lighten(background.ToVector4(), source.ToVector4()); + TPixel resultPixel = default(TPixel); + resultPixel.PackFromVector4(result); + return resultPixel; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + Vector4 result = Vector4BlendTransforms.Lighten(background[i].ToVector4(), source[i].ToVector4()); + destination[i].PackFromVector4(result); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs new file mode 100644 index 000000000..8240c2b87 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultMultiplyPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + Vector4 result = Vector4BlendTransforms.Multiply(background.ToVector4(), source.ToVector4()); + TPixel resultPixel = default(TPixel); + resultPixel.PackFromVector4(result); + return resultPixel; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + Vector4 result = Vector4BlendTransforms.Multiply(background[i].ToVector4(), source[i].ToVector4()); + destination[i].PackFromVector4(result); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs new file mode 100644 index 000000000..cc58498b3 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs @@ -0,0 +1,37 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultNormalPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + return source; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + destination[i] = source[i]; + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs new file mode 100644 index 000000000..042543608 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultOverlayPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + Vector4 result = Vector4BlendTransforms.Overlay(background.ToVector4(), source.ToVector4()); + TPixel resultPixel = default(TPixel); + resultPixel.PackFromVector4(result); + return resultPixel; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + Vector4 result = Vector4BlendTransforms.Overlay(background[i].ToVector4(), source[i].ToVector4()); + destination[i].PackFromVector4(result); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs new file mode 100644 index 000000000..32ab08796 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPremultipliedLerpPixelBlender{TPixel}.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultPremultipliedLerpPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + Vector4 result = Vector4BlendTransforms.PremultipliedLerp(background.ToVector4(), source.ToVector4(), amount); + TPixel resultPixel = default(TPixel); + resultPixel.PackFromVector4(result); + return resultPixel; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + Vector4 result = Vector4BlendTransforms.PremultipliedLerp(background[i].ToVector4(), source[i].ToVector4(), amount[i]); + destination[i].PackFromVector4(result); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs new file mode 100644 index 000000000..be578af81 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultScreenPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + Vector4 result = Vector4BlendTransforms.Screen(background.ToVector4(), source.ToVector4()); + TPixel resultPixel = default(TPixel); + resultPixel.PackFromVector4(result); + return resultPixel; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + Vector4 result = Vector4BlendTransforms.Screen(background[i].ToVector4(), source[i].ToVector4()); + destination[i].PackFromVector4(result); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSoftLightPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSoftLightPixelBlender{TPixel}.cs new file mode 100644 index 000000000..357e084c9 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSoftLightPixelBlender{TPixel}.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.PixelFormats; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal class DefaultSoftLightPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + public override TPixel Compose(TPixel background, TPixel source, float amount) + { + Vector4 result = Vector4BlendTransforms.SoftLight(background.ToVector4(), source.ToVector4()); + TPixel resultPixel = default(TPixel); + resultPixel.PackFromVector4(result); + return resultPixel; + } + + /// + public override void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount) + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + Vector4 result = Vector4BlendTransforms.SoftLight(background[i].ToVector4(), source[i].ToVector4()); + destination[i].PackFromVector4(result); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs new file mode 100644 index 000000000..ee0c67396 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -0,0 +1,39 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal abstract class PixelBlender + where TPixel : struct, IPixel + { + /// + /// Composes 2 pixels together. + /// + /// The background color. + /// 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. + /// + /// The final pixel value after composition + public abstract TPixel Compose(TPixel background, TPixel source, float amount); + + /// + /// Composes 2 pixels together. + /// + /// The destination span. + /// The background span. + /// The source span. + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + public abstract void Compose(BufferSpan destination, BufferSpan background, BufferSpan source, BufferSpan amount); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs new file mode 100644 index 000000000..5ab3449f2 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs @@ -0,0 +1,125 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + using System.Numerics; + using System.Runtime.CompilerServices; + using ImageSharp.PixelFormats.PixelBlenders; + +#pragma warning disable CS1710 // XML comment has a duplicate typeparam tag + /// + /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations + /// for pixel buffers of type . + /// + /// The pixel format. + public partial class PixelOperations +#pragma warning restore CS1710 // XML comment has a duplicate typeparam tag + where TPixel : struct, IPixel + { + /// + /// Gets the NormalBlender. + /// + private PixelBlender normalBlender = new DefaultNormalPixelBlender(); + + /// + /// Gets the MultiplyBlender. + /// + private PixelBlender multiplyBlender = new DefaultMultiplyPixelBlender(); + + /// + /// Gets the ScreenBlender. + /// + private PixelBlender screenBlender = new DefaultScreenPixelBlender(); + + /// + /// Gets the HardLightBlender. + /// + private PixelBlender hardLightBlender = new DefaultHardLightPixelBlender(); + + /// + /// Gets the OverlayBlender. + /// + private PixelBlender overlayBlender = new DefaultOverlayPixelBlender(); + + /// + /// Gets the DarkenBlender. + /// + private PixelBlender darkenBlender = new DefaultDarkenPixelBlender(); + + /// + /// Gets the LightenBlender. + /// + private PixelBlender lightenBlender = new DefaultLightenPixelBlender(); + + /// + /// Gets the SoftLightBlender. + /// + private PixelBlender softLightBlender = new DefaultSoftLightPixelBlender(); + + /// + /// Gets the DodgeBlender. + /// + private PixelBlender dodgeBlender = new DefaultDodgePixelBlender(); + + /// + /// Gets the BurnBlender. + /// + private PixelBlender burnBlender = new DefaultBurnPixelBlender(); + + /// + /// Gets the DifferenceBlender. + /// + private PixelBlender differenceBlender = new DefaultDifferencePixelBlender(); + + /// + /// Gets the DifferenceBlender. + /// + private PixelBlender exclusionBlender = new DefaultExclusionPixelBlender(); + + /// + /// Gets the PremultipliedLerpBlender. + /// + private PixelBlender premultipliedLerpBlender = new DefaultPremultipliedLerpPixelBlender(); + + /// + /// Find an instance of the pixel blender. + /// + /// The blending mode to apply + /// A . + internal virtual PixelBlender GetPixelBlender(PixelBlenderMode mode) + { + switch (mode) + { + case PixelBlenderMode.Normal: + return this.normalBlender; + case PixelBlenderMode.Multiply: + return this.multiplyBlender; + case PixelBlenderMode.Screen: + return this.screenBlender; + case PixelBlenderMode.HardLight: + return this.hardLightBlender; + case PixelBlenderMode.Overlay: + return this.overlayBlender; + case PixelBlenderMode.Darken: + return this.darkenBlender; + case PixelBlenderMode.Lighten: + return this.lightenBlender; + case PixelBlenderMode.SoftLight: + return this.softLightBlender; + case PixelBlenderMode.Dodge: + return this.dodgeBlender; + case PixelBlenderMode.Burn: + return this.burnBlender; + case PixelBlenderMode.Difference: + return this.differenceBlender; + case PixelBlenderMode.Exclusion: + return this.exclusionBlender; + default: + return this.premultipliedLerpBlender; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/BulkPixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs similarity index 91% rename from src/ImageSharp/PixelFormats/BulkPixelOperations{TPixel}.cs rename to src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 9a3d266c1..11ff42222 100644 --- a/src/ImageSharp/PixelFormats/BulkPixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -8,18 +8,20 @@ namespace ImageSharp.PixelFormats using System.Numerics; using System.Runtime.CompilerServices; - /// - /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations - /// for pixel buffers of type . - /// - /// The pixel format. - public class BulkPixelOperations +#pragma warning disable CS1710 // XML comment has a duplicate typeparam tag + /// + /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations + /// for pixel buffers of type . + /// + /// The pixel format. + public partial class PixelOperations +#pragma warning restore CS1710 // XML comment has a duplicate typeparam tag where TPixel : struct, IPixel { /// - /// Gets the global instance for the pixel type + /// Gets the global instance for the pixel type /// - public static BulkPixelOperations Instance { get; } = default(TPixel).CreateBulkOperations(); + public static PixelOperations Instance { get; } = default(TPixel).CreateBulkOperations(); /// /// Bulk version of diff --git a/src/ImageSharp/PixelFormats/Rg32.cs b/src/ImageSharp/PixelFormats/Rg32.cs index a4bfc5823..4d1f0fecf 100644 --- a/src/ImageSharp/PixelFormats/Rg32.cs +++ b/src/ImageSharp/PixelFormats/Rg32.cs @@ -74,7 +74,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// /// Expands the packed representation into a . diff --git a/src/ImageSharp/PixelFormats/Rgba1010102.cs b/src/ImageSharp/PixelFormats/Rgba1010102.cs index cfd60f410..96f7af773 100644 --- a/src/ImageSharp/PixelFormats/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/Rgba1010102.cs @@ -77,7 +77,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/Rgba32.BulkOperations.cs b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs similarity index 97% rename from src/ImageSharp/PixelFormats/Rgba32.BulkOperations.cs rename to src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs index df21cdc70..ff284e625 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.BulkOperations.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -16,9 +16,9 @@ namespace ImageSharp.PixelFormats public partial struct Rgba32 { /// - /// implementation optimized for . + /// implementation optimized for . /// - internal class BulkOperations : BulkPixelOperations + internal class PixelOperations : PixelOperations { /// /// SIMD optimized bulk implementation of diff --git a/src/ImageSharp/PixelFormats/Rgba32.cs b/src/ImageSharp/PixelFormats/Rgba32.cs index d668be76f..0d5926910 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.cs @@ -202,7 +202,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/Rgba64.cs b/src/ImageSharp/PixelFormats/Rgba64.cs index 20bba9f41..b7ff143a9 100644 --- a/src/ImageSharp/PixelFormats/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/Rgba64.cs @@ -76,7 +76,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/RgbaVector.BulkOperations.cs b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs similarity index 81% rename from src/ImageSharp/PixelFormats/RgbaVector.BulkOperations.cs rename to src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs index 9dcfc9074..da0900c11 100644 --- a/src/ImageSharp/PixelFormats/RgbaVector.BulkOperations.cs +++ b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs @@ -13,9 +13,9 @@ namespace ImageSharp.PixelFormats public partial struct RgbaVector { /// - /// implementation optimized for . + /// implementation optimized for . /// - internal class BulkOperations : BulkPixelOperations + internal class PixelOperations : PixelOperations { /// internal override unsafe void ToVector4(BufferSpan sourceColors, BufferSpan destVectors, int count) diff --git a/src/ImageSharp/PixelFormats/RgbaVector.cs b/src/ImageSharp/PixelFormats/RgbaVector.cs index c59e93259..25eaa9711 100644 --- a/src/ImageSharp/PixelFormats/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/RgbaVector.cs @@ -208,7 +208,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new RgbaVector.BulkOperations(); + public PixelOperations CreateBulkOperations() => new RgbaVector.PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/Short2.cs b/src/ImageSharp/PixelFormats/Short2.cs index 60fbb5b35..d681e27b7 100644 --- a/src/ImageSharp/PixelFormats/Short2.cs +++ b/src/ImageSharp/PixelFormats/Short2.cs @@ -89,7 +89,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/Short4.cs b/src/ImageSharp/PixelFormats/Short4.cs index 65ce51eb2..f58949a72 100644 --- a/src/ImageSharp/PixelFormats/Short4.cs +++ b/src/ImageSharp/PixelFormats/Short4.cs @@ -91,7 +91,7 @@ namespace ImageSharp.PixelFormats } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/Vector4BlendTransforms.cs b/src/ImageSharp/PixelFormats/Vector4BlendTransforms.cs index 87c7a289e..f066e9a81 100644 --- a/src/ImageSharp/PixelFormats/Vector4BlendTransforms.cs +++ b/src/ImageSharp/PixelFormats/Vector4BlendTransforms.cs @@ -11,7 +11,7 @@ namespace ImageSharp.PixelFormats /// Transform algorithms that match the equations defined in the W3C Compositing and Blending Level 1 specification. /// /// - public class Vector4BlendTransforms + internal class Vector4BlendTransforms { /// /// The blending formula simply selects the source vector. diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs index ccc3c0060..74185f11f 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs @@ -42,7 +42,7 @@ namespace ImageSharp.Processing.Processors protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) { new VignetteProcessor(veryDarkOrange).Apply(source, sourceRectangle); - new GlowProcessor(lightOrange) { Radius = source.Width / 4F }.Apply(source, sourceRectangle); + new GlowProcessorParallel(lightOrange) { Radius = source.Width / 4F }.Apply(source, sourceRectangle); } } } \ 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 049378256..fba3fbb8e 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -65,25 +65,31 @@ namespace ImageSharp.Processing.Processors startY = 0; } + int width = maxX - minX; + using (Buffer rowColors = new Buffer(width)) + using (Buffer amounts = new Buffer(width)) using (PixelAccessor sourcePixels = source.Lock()) { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - 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(TPixel); - packed.PackFromVector4(Vector4BlendTransforms.PremultipliedLerp(sourceColor, glowColor.ToVector4(), 1 - (.95F * (distance / maxDistance)))); - sourcePixels[offsetX, offsetY] = packed; - } - }); + for (int i = 0; i < width; i++) + { + rowColors[i] = glowColor; + } + + // TODO move GraphicOptions into core so all processes can use it. + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(PixelBlenderMode.Default); + for (int y = minY; y < maxY; y++) + { + int offsetY = y - startY; + int offsetX = minX - startX; + for (int i = 0; i < width; i++) + { + float distance = Vector2.Distance(centre, new Vector2((i + offsetX), offsetY)); + amounts[i] = 1 - (.95F * (distance / maxDistance)); + } + + BufferSpan destination = sourcePixels.GetRowSpan(offsetY).Slice(offsetX, width); + blender.Compose(destination, destination, rowColors, amounts); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessorParallel.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessorParallel.cs new file mode 100644 index 000000000..6c2b8cfc6 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessorParallel.cs @@ -0,0 +1,100 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Processing.Processors +{ + using System; + using System.Numerics; + using System.Threading.Tasks; + + using ImageSharp.PixelFormats; + + /// + /// An that applies a radial glow effect an . + /// + /// The pixel format. + 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 OnApply(ImageBase source, Rectangle sourceRectangle) + { + 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).ToVector2(); + float maxDistance = this.Radius > 0 ? MathF.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 (Buffer rowColors = new Buffer(width)) + using (PixelAccessor sourcePixels = source.Lock()) + { + for (int i = 0; i < width; i++) + { + rowColors[i] = glowColor; + } + + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(PixelBlenderMode.Default); + + Parallel.For( + minY, + maxY, + this.ParallelOptions, + 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(TPixel); + packed.PackFromVector4(Vector4BlendTransforms.PremultipliedLerp(sourceColor, glowColor.ToVector4(), 1 - (.95F * (distance / maxDistance)))); + sourcePixels[offsetX, offsetY] = packed; + } + }); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 31328d8b7..dde79a7e4 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -123,7 +123,7 @@ namespace ImageSharp.Processing.Processors { BufferSpan sourceRow = sourcePixels.GetRowSpan(y); - BulkPixelOperations.Instance.ToVector4( + PixelOperations.Instance.ToVector4( sourceRow, tempRowBuffer, sourceRow.Length); diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index 2a370bc00..efec90c99 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -47,13 +47,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new BulkPixelOperations().PackFromXyzwBytes(this.source, this.destination, this.Count); + new PixelOperations().PackFromXyzwBytes(this.source, this.destination, this.Count); } [Benchmark] public void OptimizedBulk() { - BulkPixelOperations.Instance.PackFromXyzwBytes(this.source, this.destination, this.Count); + PixelOperations.Instance.PackFromXyzwBytes(this.source, this.destination, this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index 1234a9946..e2c1ac726 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -47,13 +47,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new BulkPixelOperations().ToVector4(this.source, this.destination, this.Count); + new PixelOperations().ToVector4(this.source, this.destination, this.Count); } [Benchmark] public void OptimizedBulk() { - BulkPixelOperations.Instance.ToVector4(this.source, this.destination, this.Count); + PixelOperations.Instance.ToVector4(this.source, this.destination, this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index fe201549b..88dac21cd 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -45,13 +45,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new BulkPixelOperations().ToXyzBytes(this.source, this.destination, this.Count); + new PixelOperations().ToXyzBytes(this.source, this.destination, this.Count); } [Benchmark] public void OptimizedBulk() { - BulkPixelOperations.Instance.ToXyzBytes(this.source, this.destination, this.Count); + PixelOperations.Instance.ToXyzBytes(this.source, this.destination, this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index f7406d0f6..11545d3d9 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -49,13 +49,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new BulkPixelOperations().ToXyzwBytes(this.source, this.destination, this.Count); + new PixelOperations().ToXyzwBytes(this.source, this.destination, this.Count); } [Benchmark] public void OptimizedBulk() { - BulkPixelOperations.Instance.ToXyzwBytes(this.source, this.destination, this.Count); + PixelOperations.Instance.ToXyzwBytes(this.source, this.destination, this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs new file mode 100644 index 000000000..9b7dbe21a --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -0,0 +1,48 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Benchmarks +{ + + using BenchmarkDotNet.Attributes; + using ImageSharp.PixelFormats; + using ImageSharp.Drawing; + using ImageSharp.Processing.Processors; + using CoreImage = ImageSharp.Image; + using CoreSize = ImageSharp.Size; + + public class Glow : BenchmarkBase + { + private GlowProcessor bulk; + private GlowProcessorParallel parallel; + + [Setup] + public void Setup() + { + this.bulk = new GlowProcessor(NamedColors.Beige) { Radius = 800 * .5f, }; + this.parallel = new GlowProcessorParallel(NamedColors.Beige) { Radius = 800 * .5f, }; + + } + [Benchmark(Description = "ImageSharp Glow - Bulk")] + public CoreSize GlowBulk() + { + using (CoreImage image = new CoreImage(800, 800)) + { + image.ApplyProcessor(bulk, image.Bounds); + return new CoreSize(image.Width, image.Height); + } + } + + [Benchmark(Description = "ImageSharp Glow - Parallel")] + public CoreSize GLowSimple() + { + using (CoreImage image = new CoreImage(800, 800)) + { + image.ApplyProcessor(parallel, image.Bounds); + return new CoreSize(image.Width, image.Height); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs similarity index 96% rename from tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs rename to tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs index b498c93ac..3d0392753 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs @@ -10,7 +10,7 @@ namespace ImageSharp.Tests.Colors using Xunit; using Xunit.Abstractions; - public class BulkPixelOperationsTests + public class PixelOperationsTests { public class Color32 : BulkPixelOperationsTests { @@ -25,7 +25,7 @@ namespace ImageSharp.Tests.Colors [Fact] public void IsSpecialImplementation() { - Assert.IsType(BulkPixelOperations.Instance); + Assert.IsType(PixelOperations.Instance); } [Fact] @@ -37,7 +37,7 @@ namespace ImageSharp.Tests.Colors TestOperation( source, expected, - (s, d) => Rgba32.BulkOperations.ToVector4SimdAligned(s, d, 64) + (s, d) => Rgba32.PixelOperations.ToVector4SimdAligned(s, d, 64) ); } @@ -54,7 +54,7 @@ namespace ImageSharp.Tests.Colors times, () => { - BulkPixelOperations.Instance.ToVector4(source, dest, count); + PixelOperations.Instance.ToVector4(source, dest, count); }); } } @@ -76,7 +76,7 @@ namespace ImageSharp.Tests.Colors public void GetGlobalInstance(TestImageProvider dummy) where TPixel : struct, IPixel { - Assert.NotNull(BulkPixelOperations.Instance); + Assert.NotNull(PixelOperations.Instance); } } @@ -90,7 +90,7 @@ namespace ImageSharp.Tests.Colors public static TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; - private static BulkPixelOperations Operations => BulkPixelOperations.Instance; + private static PixelOperations Operations => PixelOperations.Instance; internal static TPixel[] CreateExpectedPixelData(Vector4[] source) { diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index ff5eccc78..8dd2f2790 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -6,6 +6,9 @@ portable True + + + @@ -24,4 +27,7 @@ PreserveNewest + + + \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs new file mode 100644 index 000000000..6b2bd7c6b --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs @@ -0,0 +1,53 @@ + + +namespace ImageSharp.Tests.PixelFormats +{ + using System; + using System.Collections.Generic; + using System.Text; + using ImageSharp.PixelFormats; + using ImageSharp.PixelFormats.PixelBlenders; + using ImageSharp.Tests.TestUtilities; + using Xunit; + + public class PixelOperations + { + public static TheoryData blenderMappings = new TheoryData() + { + { new TestPixel(), typeof(DefaultPremultipliedLerpPixelBlender), PixelBlenderMode.Default }, + { new TestPixel(), typeof(DefaultNormalPixelBlender), PixelBlenderMode.Normal }, + { new TestPixel(), typeof(DefaultScreenPixelBlender), PixelBlenderMode.Screen }, + { new TestPixel(), typeof(DefaultHardLightPixelBlender), PixelBlenderMode.HardLight }, + { new TestPixel(), typeof(DefaultOverlayPixelBlender), PixelBlenderMode.Overlay }, + { new TestPixel(), typeof(DefaultDarkenPixelBlender), PixelBlenderMode.Darken }, + { new TestPixel(), typeof(DefaultLightenPixelBlender), PixelBlenderMode.Lighten }, + { new TestPixel(), typeof(DefaultSoftLightPixelBlender), PixelBlenderMode.SoftLight }, + { new TestPixel(), typeof(DefaultDodgePixelBlender), PixelBlenderMode.Dodge }, + { new TestPixel(), typeof(DefaultBurnPixelBlender), PixelBlenderMode.Burn }, + { new TestPixel(), typeof(DefaultDifferencePixelBlender), PixelBlenderMode.Difference }, + { new TestPixel(), typeof(DefaultExclusionPixelBlender), PixelBlenderMode.Exclusion }, + + { new TestPixel(), typeof(DefaultPremultipliedLerpPixelBlender), PixelBlenderMode.Default }, + { new TestPixel(), typeof(DefaultNormalPixelBlender), PixelBlenderMode.Normal }, + { new TestPixel(), typeof(DefaultScreenPixelBlender), PixelBlenderMode.Screen }, + { new TestPixel(), typeof(DefaultHardLightPixelBlender), PixelBlenderMode.HardLight }, + { new TestPixel(), typeof(DefaultOverlayPixelBlender), PixelBlenderMode.Overlay }, + { new TestPixel(), typeof(DefaultDarkenPixelBlender), PixelBlenderMode.Darken }, + { new TestPixel(), typeof(DefaultLightenPixelBlender), PixelBlenderMode.Lighten }, + { new TestPixel(), typeof(DefaultSoftLightPixelBlender), PixelBlenderMode.SoftLight }, + { new TestPixel(), typeof(DefaultDodgePixelBlender), PixelBlenderMode.Dodge }, + { new TestPixel(), typeof(DefaultBurnPixelBlender), PixelBlenderMode.Burn }, + { new TestPixel(), typeof(DefaultDifferencePixelBlender), PixelBlenderMode.Difference }, + { new TestPixel(), typeof(DefaultExclusionPixelBlender), PixelBlenderMode.Exclusion } + }; + + [Theory] + [MemberData(nameof(blenderMappings))] + public void ReturnsCorrectBlender(TestPixel pixel, Type type, PixelBlenderMode mode) + where TPixel : struct, IPixel + { + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(mode); + Assert.IsType(type, blender); + } + } +} diff --git a/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs index ad1048845..ebbfdd5f0 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs @@ -71,7 +71,7 @@ namespace ImageSharp.Tests using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Glow(new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2)) + image.Glow(new Rectangle(image.Width / 8, image.Height / 8, image.Width / 2, image.Height / 2)) .Save(output); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs new file mode 100644 index 000000000..93ccb0b14 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ImageSharp.PixelFormats; +using Xunit.Abstractions; + +namespace ImageSharp.Tests.TestUtilities +{ + public class TestPixel : IXunitSerializable + where TPixel : struct, IPixel + { + public TestPixel() + { + } + + public TestPixel(float red, float green, float blue, float alpha) + { + this.Red = red; + this.Green = green; + this.Blue = blue; + this.Alpha = alpha; + } + + public float Red { get; set; } + public float Green { get; set; } + public float Blue { get; set; } + public float Alpha { get; set; } + + public static implicit operator TPixel(TestPixel d) + { + return d?.AsPixel() ?? default(TPixel); + } + + public TPixel AsPixel() + { + TPixel pix = default(TPixel); + pix.PackFromVector4(new System.Numerics.Vector4( this.Red, this.Green, this.Blue, this.Alpha)); + return pix; + } + + public void Deserialize(IXunitSerializationInfo info) + { + this.Red = info.GetValue("red"); + this.Green = info.GetValue("green"); + this.Blue = info.GetValue("blue"); + this.Alpha = info.GetValue("alpha"); + } + + public void Serialize(IXunitSerializationInfo info) + { + info.AddValue("red", this.Red); + info.AddValue("green", this.Green); + info.AddValue("blue", this.Blue); + info.AddValue("alpha", this.Alpha); + } + + public override string ToString() + { + return $"{typeof(TPixel).Name}{this.AsPixel().ToString()}"; + } + } +}