Browse Source

Processors refactoring (#983)

* Added new properties to ImageProcessor<TPixel>

* Fixed constructors for convolution processors

* Fixed constructors for binarization processors

* Fixed constructor for dithering processor

* Fixed constructors for effects processors

* Fixed constructor for filter processor

* Fixed constructor for normalization processor

* Fixed constructors for overlay processors

* Fixed constructor for quantization processor

* Fixed constructors for transforms processors

* Updated CreatePixelSpecificProcessor<TPixel> definition

* Fixed convolution processors creation

* Fixed leftover dithering processor constructor

* Fixed another leftover dithering processor constructor

* Fixed dithering processors creation

* Fixed effects processors creation

* Fixed filters processor

* Fixed normalization processors creation

* Fixed overlays processors creation

* Fixed quantizer processor creation

* Fixed constructors for some remaining processors

* Fixed transform processors creation

* ImageProcessor class refactored

* Renamed some parameters

* Convolution processors refactored

* Refactored filters and dithering processors

* Refactored normalization processors

* Overlays processors refactored

* Renamed some parameters

* CloningImageProcessor class refactored

* Transforms processors refactored

* Updated DefaultImageProcessingContext class

* Src builds, tests still require updating.

* Fix tests

* Removed unnecessary local variable
af/merge-core
Sergio Pedri 7 years ago
committed by James Jackson-South
parent
commit
f25c68304d
  1. 16
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  2. 22
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
  3. 9
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  4. 9
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs
  5. 9
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  6. 6
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs
  7. 8
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
  8. 49
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs
  9. 5
      src/ImageSharp/Advanced/AotCompilerTools.cs
  10. 26
      src/ImageSharp/Processing/DefaultImageProcessorContext.cs
  11. 6
      src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs
  12. 11
      src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs
  13. 15
      src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs
  14. 11
      src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs
  15. 15
      src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs
  16. 11
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs
  17. 19
      src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs
  18. 91
      src/ImageSharp/Processing/Processors/CloningImageProcessor.cs
  19. 5
      src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs
  20. 23
      src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
  21. 5
      src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs
  22. 17
      src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs
  23. 27
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs
  24. 21
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
  25. 25
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
  26. 26
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs
  27. 29
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
  28. 7
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs
  29. 15
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs
  30. 9
      src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs
  31. 17
      src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
  32. 9
      src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs
  33. 11
      src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
  34. 12
      src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs
  35. 10
      src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs
  36. 8
      src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs
  37. 10
      src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs
  38. 10
      src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs
  39. 10
      src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs
  40. 15
      src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs
  41. 10
      src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs
  42. 10
      src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs
  43. 10
      src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs
  44. 9
      src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs
  45. 16
      src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs
  46. 9
      src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs
  47. 16
      src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs
  48. 7
      src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs
  49. 20
      src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs
  50. 9
      src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs
  51. 26
      src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs
  52. 9
      src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs
  53. 19
      src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs
  54. 9
      src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs
  55. 23
      src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs
  56. 10
      src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs
  57. 12
      src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs
  58. 10
      src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs
  59. 17
      src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs
  60. 14
      src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs
  61. 11
      src/ImageSharp/Processing/Processors/IImageProcessor.cs
  62. 12
      src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs
  63. 76
      src/ImageSharp/Processing/Processors/ImageProcessor.cs
  64. 21
      src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs
  65. 12
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs
  66. 18
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs
  67. 12
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs
  68. 30
      src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs
  69. 12
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs
  70. 19
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
  71. 7
      src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs
  72. 13
      src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs
  73. 9
      src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs
  74. 23
      src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs
  75. 9
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs
  76. 32
      src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs
  77. 9
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs
  78. 36
      src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs
  79. 9
      src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs
  80. 10
      src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs
  81. 8
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
  82. 37
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
  83. 9
      src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs
  84. 41
      src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs
  85. 8
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs
  86. 21
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs
  87. 7
      src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs
  88. 23
      src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs
  89. 9
      src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs
  90. 17
      src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs
  91. 8
      src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
  92. 35
      src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
  93. 8
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
  94. 33
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
  95. 8
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
  96. 29
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs
  97. 16
      src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs
  98. 2
      tests/ImageSharp.Benchmarks/Samplers/GaussianBlur.cs
  99. 176
      tests/ImageSharp.Benchmarks/Samplers/Glow.cs
  100. 23
      tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs

16
src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
@ -59,10 +59,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
public float Opacity { get; }
/// <inheritdoc />
public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>()
public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>(Image<TPixelBg> source, Rectangle sourceRectangle)
where TPixelBg : struct, IPixel<TPixelBg>
{
var visitor = new ProcessorFactoryVisitor<TPixelBg>(this);
var visitor = new ProcessorFactoryVisitor<TPixelBg>(this, source, sourceRectangle);
this.Image.AcceptVisitor(visitor);
return visitor.Result;
}
@ -71,10 +71,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
where TPixelBg : struct, IPixel<TPixelBg>
{
private readonly DrawImageProcessor definition;
private readonly Image<TPixelBg> source;
private readonly Rectangle sourceRectangle;
public ProcessorFactoryVisitor(DrawImageProcessor definition)
public ProcessorFactoryVisitor(DrawImageProcessor definition, Image<TPixelBg> source, Rectangle sourceRectangle)
{
this.definition = definition;
this.source = source;
this.sourceRectangle = sourceRectangle;
}
public IImageProcessor<TPixelBg> Result { get; private set; }
@ -84,6 +88,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
this.Result = new DrawImageProcessor<TPixelBg, TPixelFg>(
image,
this.source,
this.sourceRectangle,
this.definition.Location,
this.definition.ColorBlendingMode,
this.definition.AlphaCompositionMode,
@ -91,4 +97,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
}
}
}
}
}

22
src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs

@ -20,19 +20,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
where TPixelFg : struct, IPixel<TPixelFg>
{
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelDst, TPixelSrc}"/> class.
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelBg, TPixelFg}"/> class.
/// </summary>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="image">The foreground <see cref="Image{TPixelFg}"/> to blend with the currently processing image.</param>
/// <param name="source">The source <see cref="Image{TPixelBg}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
public DrawImageProcessor(
Image<TPixelFg> image,
Image<TPixelBg> source,
Rectangle sourceRectangle,
Point location,
PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode,
float opacity)
: base(source, sourceRectangle)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
@ -63,11 +68,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
public Point Location { get; }
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixelBg> source,
Rectangle sourceRectangle,
Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixelBg> source)
{
Rectangle sourceRectangle = this.SourceRectangle;
Configuration configuration = this.Configuration;
Image<TPixelFg> targetImage = this.Image;
PixelBlender<TPixelBg> blender = this.Blender;
int locationY = this.Location.Y;
@ -101,11 +106,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixelBg> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixelFg> foreground =
targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
Span<TPixelFg> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend<TPixelFg>(configuration, background, background, foreground, this.Opacity);
}
});
}
}
}
}

9
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
@ -33,10 +34,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
public GraphicsOptions Options { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new FillProcessor<TPixel>(this);
return new FillProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

9
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs

@ -21,14 +21,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
private readonly FillProcessor definition;
public FillProcessor(FillProcessor definition)
public FillProcessor(FillProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
Rectangle sourceRectangle = this.SourceRectangle;
Configuration configuration = this.Configuration;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
int startY = sourceRectangle.Y;
@ -115,4 +118,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color);
}
}
}
}

9
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs

@ -1,8 +1,9 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
@ -41,10 +42,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
public GraphicsOptions Options { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new FillRegionProcessor<TPixel>(this);
return new FillRegionProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

6
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs

@ -23,14 +23,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
private readonly FillRegionProcessor definition;
public FillRegionProcessor(FillRegionProcessor definition)
public FillRegionProcessor(FillRegionProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
Configuration configuration = this.Configuration;
GraphicsOptions options = this.definition.Options;
IBrush brush = this.definition.Brush;
Region region = this.definition.Region;

8
src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -72,10 +72,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
public PointF Location { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new DrawTextProcessor<TPixel>(this);
return new DrawTextProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

49
src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs

@ -27,7 +27,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
private readonly DrawTextProcessor definition;
public DrawTextProcessor(DrawTextProcessor definition)
public DrawTextProcessor(DrawTextProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
@ -44,35 +45,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
private IBrush Brush => this.definition.Brush;
protected override void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle)
protected override void BeforeImageApply()
{
base.BeforeImageApply(source, sourceRectangle);
base.BeforeImageApply();
// do everything at the image level as we are delegating the processing down to other processors
var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location)
{
ApplyKerning = this.Options.ApplyKerning,
TabWidth = this.Options.TabWidth,
WrappingWidth = this.Options.WrapTextWidth,
HorizontalAlignment = this.Options.HorizontalAlignment,
VerticalAlignment = this.Options.VerticalAlignment
};
this.textRenderer = new CachingGlyphRenderer(source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null);
{
ApplyKerning = this.Options.ApplyKerning,
TabWidth = this.Options.TabWidth,
WrappingWidth = this.Options.WrapTextWidth,
HorizontalAlignment = this.Options.HorizontalAlignment,
VerticalAlignment = this.Options.VerticalAlignment
};
this.textRenderer = new CachingGlyphRenderer(this.Source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null);
this.textRenderer.Options = (GraphicsOptions)this.Options;
var renderer = new TextRenderer(this.textRenderer);
renderer.RenderText(this.Text, style);
}
protected override void AfterImageApply(Image<TPixel> source, Rectangle sourceRectangle)
protected override void AfterImageApply()
{
base.AfterImageApply(source, sourceRectangle);
base.AfterImageApply();
this.textRenderer?.Dispose();
this.textRenderer = null;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
// this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome
Draw(this.textRenderer.FillOperations, this.Brush);
@ -82,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{
if (operations?.Count > 0)
{
using (BrushApplicator<TPixel> app = brush.CreateApplicator(source, sourceRectangle, this.textRenderer.Options))
using (BrushApplicator<TPixel> app = brush.CreateApplicator(source, this.SourceRectangle, this.textRenderer.Options))
{
foreach (DrawingOperation operation in operations)
{
@ -279,19 +280,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
if (this.renderFill)
{
this.FillOperations.Add(new DrawingOperation
{
Location = this.currentRenderPosition,
Map = renderData.FillMap
});
{
Location = this.currentRenderPosition,
Map = renderData.FillMap
});
}
if (this.renderOutline)
{
this.OutlineOperations.Add(new DrawingOperation
{
Location = this.currentRenderPosition,
Map = renderData.OutlineMap
});
{
Location = this.currentRenderPosition,
Map = renderData.OutlineMap
});
}
}

5
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -179,9 +179,8 @@ namespace SixLabors.ImageSharp.Advanced
private static void AotCompileResizeOperations<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var resizeProcessor = new ResizeProcessor(new ResizeOptions(), default);
var genericResizeProcessor = new ResizeProcessor<TPixel>((ResizeProcessor)resizeProcessor.CreatePixelSpecificProcessor<TPixel>());
genericResizeProcessor.AotCreateDestination(new Image<TPixel>(0, 0), default);
var genericResizeProcessor = (ResizeProcessor<TPixel>)new ResizeProcessor(new ResizeOptions(), default).CreatePixelSpecificProcessor(new Image<TPixel>(0, 0), default);
genericResizeProcessor.AotCreateDestination();
}
}
}

26
src/ImageSharp/Processing/DefaultImageProcessorContext.cs

@ -53,43 +53,33 @@ namespace SixLabors.ImageSharp.Processing
/// <inheritdoc/>
public Size GetCurrentSize() => this.GetCurrentBounds().Size;
public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle)
{
var processorImplementation = processor.CreatePixelSpecificProcessor<TPixel>();
return this.ApplyProcessor(processorImplementation, rectangle);
}
/// <inheritdoc/>
public IImageProcessingContext ApplyProcessor(IImageProcessor processor)
{
var processorImplementation = processor.CreatePixelSpecificProcessor<TPixel>();
return this.ApplyProcessor(processorImplementation);
return this.ApplyProcessor(processor, this.GetCurrentBounds());
}
private IImageProcessingContext ApplyProcessor(IImageProcessor<TPixel> processor, Rectangle rectangle)
/// <inheritdoc/>
public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle)
{
if (!this.mutate && this.destination is null)
{
// This will only work if the first processor applied is the cloning one thus
// realistically for this optimization to work the resize must the first processor
// applied any only up processors will take the double data path.
if (processor is ICloningImageProcessor<TPixel> cloningImageProcessor)
if (processor.CreatePixelSpecificProcessor(this.source, rectangle) is ICloningImageProcessor<TPixel> cloningImageProcessor)
{
this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle);
this.destination = cloningImageProcessor.CloneAndApply();
return this;
}
this.destination = this.source.Clone();
}
processor.Apply(this.destination, rectangle);
processor.CreatePixelSpecificProcessor(this.destination, rectangle).Apply();
return this;
}
private IImageProcessingContext ApplyProcessor(IImageProcessor<TPixel> processor)
{
return this.ApplyProcessor(processor, this.GetCurrentBounds());
}
private Rectangle GetCurrentBounds() => this.destination?.Bounds() ?? this.source.Bounds();
}
}
}

6
src/ImageSharp/Processing/Extensions/GrayscaleExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Processors;
@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing
{
IImageProcessor processor = mode == GrayscaleMode.Bt709
? (IImageProcessor)new GrayscaleBt709Processor(amount)
: new GrayscaleBt601Processor(1F);
: new GrayscaleBt601Processor(amount);
source.ApplyProcessor(processor);
return source;
@ -111,4 +111,4 @@ namespace SixLabors.ImageSharp.Processing
return source;
}
}
}
}

11
src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs

@ -1,8 +1,9 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Binarization
{
@ -69,10 +70,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
public Color LowerColor { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new BinaryErrorDiffusionProcessor<TPixel>(this);
}
=> new BinaryErrorDiffusionProcessor<TPixel>(this, source, sourceRectangle);
}
}
}

15
src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor{TPixel}.cs

@ -19,13 +19,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
{
private readonly BinaryErrorDiffusionProcessor definition;
public BinaryErrorDiffusionProcessor(BinaryErrorDiffusionProcessor definition)
/// <summary>
/// Initializes a new instance of the <see cref="BinaryErrorDiffusionProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="BinaryErrorDiffusionProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public BinaryErrorDiffusionProcessor(BinaryErrorDiffusionProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
TPixel upperColor = this.definition.UpperColor.ToPixel<TPixel>();
TPixel lowerColor = this.definition.LowerColor.ToPixel<TPixel>();
@ -34,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
byte threshold = (byte)MathF.Round(this.definition.Threshold * 255F);
bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
int startY = interest.Y;
int endY = interest.Bottom;
int startX = interest.X;
@ -74,4 +81,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
}
}
}
}
}

11
src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs

@ -1,10 +1,11 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Binarization
{
@ -51,10 +52,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
public Color LowerColor { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new BinaryOrderedDitherProcessor<TPixel>(this);
}
=> new BinaryOrderedDitherProcessor<TPixel>(this, source, sourceRectangle);
}
}
}

15
src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor{TPixel}.cs

@ -19,13 +19,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
{
private readonly BinaryOrderedDitherProcessor definition;
public BinaryOrderedDitherProcessor(BinaryOrderedDitherProcessor definition)
/// <summary>
/// Initializes a new instance of the <see cref="BinaryOrderedDitherProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="BinaryErrorDiffusionProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public BinaryOrderedDitherProcessor(BinaryOrderedDitherProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
IOrderedDither dither = this.definition.Dither;
TPixel upperColor = this.definition.UpperColor.ToPixel<TPixel>();
@ -33,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
int startY = interest.Y;
int endY = interest.Bottom;
int startX = interest.X;
@ -72,4 +79,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
}
}
}
}
}

11
src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Binarization
{
@ -49,10 +50,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
public Color LowerColor { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new BinaryThresholdProcessor<TPixel>(this);
}
=> new BinaryThresholdProcessor<TPixel>(this, source, sourceRectangle);
}
}
}

19
src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor{TPixel}.cs

@ -19,21 +19,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
{
private readonly BinaryThresholdProcessor definition;
public BinaryThresholdProcessor(BinaryThresholdProcessor definition)
/// <summary>
/// Initializes a new instance of the <see cref="BinaryThresholdProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="BinaryThresholdProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public BinaryThresholdProcessor(BinaryThresholdProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
byte threshold = (byte)MathF.Round(this.definition.Threshold * 255F);
TPixel upper = this.definition.UpperColor.ToPixel<TPixel>();
TPixel lower = this.definition.LowerColor.ToPixel<TPixel>();
Rectangle sourceRectangle = this.SourceRectangle;
Configuration configuration = this.Configuration;
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
int startY = interest.Y;
int endY = interest.Bottom;
@ -67,4 +74,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
});
}
}
}
}

91
src/ImageSharp/Processing/Processors/CloningImageProcessor.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -15,32 +15,58 @@ namespace SixLabors.ImageSharp.Processing.Processors
internal abstract class CloningImageProcessor<TPixel> : ICloningImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="CloningImageProcessor{TPixel}"/> class.
/// </summary>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
protected CloningImageProcessor(Image<TPixel> source, Rectangle sourceRectangle)
{
this.Source = source;
this.SourceRectangle = sourceRectangle;
this.Configuration = this.Source.GetConfiguration();
}
/// <summary>
/// Gets The source <see cref="Image{TPixel}"/> for the current processor instance.
/// </summary>
protected Image<TPixel> Source { get; }
/// <summary>
/// Gets The source area to process for the current processor instance.
/// </summary>
protected Rectangle SourceRectangle { get; }
/// <summary>
/// Gets the <see cref="ImageSharp.Configuration"/> instance to use when performing operations.
/// </summary>
protected Configuration Configuration { get; }
/// <inheritdoc/>
public Image<TPixel> CloneAndApply(Image<TPixel> source, Rectangle sourceRectangle)
public Image<TPixel> CloneAndApply()
{
try
{
Image<TPixel> clone = this.CreateDestination(source, sourceRectangle);
Image<TPixel> clone = this.CreateDestination();
if (clone.Frames.Count != source.Frames.Count)
if (clone.Frames.Count != this.Source.Frames.Count)
{
throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames.");
}
Configuration configuration = source.GetConfiguration();
this.BeforeImageApply(source, clone, sourceRectangle);
this.BeforeImageApply(clone);
for (int i = 0; i < source.Frames.Count; i++)
for (int i = 0; i < this.Source.Frames.Count; i++)
{
ImageFrame<TPixel> sourceFrame = source.Frames[i];
ImageFrame<TPixel> sourceFrame = this.Source.Frames[i];
ImageFrame<TPixel> clonedFrame = clone.Frames[i];
this.BeforeFrameApply(sourceFrame, clonedFrame, sourceRectangle, configuration);
this.OnFrameApply(sourceFrame, clonedFrame, sourceRectangle, configuration);
this.AfterFrameApply(sourceFrame, clonedFrame, sourceRectangle, configuration);
this.BeforeFrameApply(sourceFrame, clonedFrame);
this.OnFrameApply(sourceFrame, clonedFrame);
this.AfterFrameApply(sourceFrame, clonedFrame);
}
this.AfterImageApply(source, clone, sourceRectangle);
this.AfterImageApply(clone);
return clone;
}
@ -57,40 +83,31 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
/// <inheritdoc/>
public void Apply(Image<TPixel> source, Rectangle sourceRectangle)
public void Apply()
{
using (Image<TPixel> cloned = this.CloneAndApply(source, sourceRectangle))
using (Image<TPixel> cloned = this.CloneAndApply())
{
// we now need to move the pixel data/size data from one image base to another
if (cloned.Frames.Count != source.Frames.Count)
if (cloned.Frames.Count != this.Source.Frames.Count)
{
throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames.");
}
source.SwapOrCopyPixelsBuffersFrom(cloned);
this.Source.SwapOrCopyPixelsBuffersFrom(cloned);
}
}
/// <summary>
/// Generates a deep clone of the source image that operations should be applied to.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">The source rectangle.</param>
/// <returns>The cloned image.</returns>
protected virtual Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
{
return source.Clone();
}
protected virtual Image<TPixel> CreateDestination() => this.Source.Clone();
/// <summary>
/// This method is called before the process is applied to prepare the processor.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="destination">The cloned/destination image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void BeforeImageApply(Image<TPixel> source, Image<TPixel> destination, Rectangle sourceRectangle)
protected virtual void BeforeImageApply(Image<TPixel> destination)
{
}
@ -99,9 +116,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="destination">The cloned/destination image. Cannot be null.</param>
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
protected virtual void BeforeFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
protected virtual void BeforeFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
}
@ -111,31 +126,23 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="destination">The cloned/destination image. Cannot be null.</param>
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
protected abstract void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration);
protected abstract void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination);
/// <summary>
/// This method is called after the process is applied to prepare the processor.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="destination">The cloned/destination image. Cannot be null.</param>
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
protected virtual void AfterFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
protected virtual void AfterFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
}
/// <summary>
/// This method is called after the process is applied to prepare the processor.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="destination">The cloned/destination image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
protected virtual void AfterImageApply(Image<TPixel> source, Image<TPixel> destination, Rectangle sourceRectangle)
protected virtual void AfterImageApply(Image<TPixel> destination)
{
}
}
}
}

5
src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
@ -112,10 +113,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public BokehBlurExecutionMode ExecutionMode { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new BokehBlurProcessor<TPixel>(this);
return new BokehBlurProcessor<TPixel>(this, source, sourceRectangle);
}
}
}

23
src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs

@ -75,7 +75,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// Initializes a new instance of the <see cref="BokehBlurProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="BoxBlurProcessor"/> defining the processor parameters.</param>
public BokehBlurProcessor(BokehBlurProcessor definition)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public BokehBlurProcessor(BokehBlurProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.radius = definition.Radius;
this.kernelSize = (this.radius * 2) + 1;
@ -271,36 +274,36 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
// Preliminary gamma highlight pass
this.ApplyGammaExposure(source.PixelBuffer, sourceRectangle, configuration);
this.ApplyGammaExposure(source.PixelBuffer, this.SourceRectangle, this.Configuration);
// Create a 0-filled buffer to use to store the result of the component convolutions
using (Buffer2D<Vector4> processing = configuration.MemoryAllocator.Allocate2D<Vector4>(source.Size(), AllocationOptions.Clean))
using (Buffer2D<Vector4> processing = this.Configuration.MemoryAllocator.Allocate2D<Vector4>(source.Size(), AllocationOptions.Clean))
{
if (this.executionMode == BokehBlurExecutionMode.PreferLowMemoryUsage)
{
// Memory usage priority: allocate a shared buffer and execute the second convolution in sequential mode
using (Buffer2D<ComplexVector4> buffer = configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Width, source.Height + this.radius))
using (Buffer2D<ComplexVector4> buffer = this.Configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Width, source.Height + this.radius))
using (Buffer2D<ComplexVector4> firstPassBuffer = buffer.Slice(this.radius, source.Height))
using (Buffer2D<ComplexVector4> secondPassBuffer = buffer.Slice(0, source.Height))
{
this.OnFrameApplyCore(source, sourceRectangle, configuration, processing, firstPassBuffer, secondPassBuffer);
this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processing, firstPassBuffer, secondPassBuffer);
}
}
else
{
// Performance priority: allocate two independent buffers and execute both convolutions in parallel mode
using (Buffer2D<ComplexVector4> firstPassValues = configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size()))
using (Buffer2D<ComplexVector4> secondPassBuffer = configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size()))
using (Buffer2D<ComplexVector4> firstPassValues = this.Configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size()))
using (Buffer2D<ComplexVector4> secondPassBuffer = this.Configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size()))
{
this.OnFrameApplyCore(source, sourceRectangle, configuration, processing, firstPassValues, secondPassBuffer);
this.OnFrameApplyCore(source, this.SourceRectangle, this.Configuration, processing, firstPassValues, secondPassBuffer);
}
}
// Apply the inverse gamma exposure pass, and write the final pixel data
this.ApplyInverseGammaExposure(source.PixelBuffer, processing, sourceRectangle, configuration);
this.ApplyInverseGammaExposure(source.PixelBuffer, processing, this.SourceRectangle, this.Configuration);
}
}

5
src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
@ -40,10 +41,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public int Radius { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new BoxBlurProcessor<TPixel>(this);
return new BoxBlurProcessor<TPixel>(this, source, sourceRectangle);
}
}
}

17
src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs

@ -20,7 +20,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// Initializes a new instance of the <see cref="BoxBlurProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="BoxBlurProcessor"/> defining the processor parameters.</param>
public BoxBlurProcessor(BoxBlurProcessor definition)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public BoxBlurProcessor(BoxBlurProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
int kernelSize = (definition.Radius * 2) + 1;
@ -39,14 +42,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public DenseMatrix<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration) =>
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY, false).Apply(
source,
sourceRectangle,
configuration);
protected override void OnFrameApply(ImageFrame<TPixel> source) =>
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle).Apply(source);
/// <summary>
/// Create a 1 dimensional Box kernel.
@ -60,4 +57,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
return kernel;
}
}
}
}

27
src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs

@ -26,7 +26,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <param name="kernelX">The horizontal gradient operator.</param>
/// <param name="kernelY">The vertical gradient operator.</param>
/// <param name="preserveAlpha">Whether the convolution filter is applied to alpha as well as the color channels.</param>
public Convolution2DProcessor(in DenseMatrix<float> kernelX, in DenseMatrix<float> kernelY, bool preserveAlpha)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public Convolution2DProcessor(
in DenseMatrix<float> kernelX,
in DenseMatrix<float> kernelY,
bool preserveAlpha,
Image<TPixel> source,
Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same.");
this.KernelX = kernelX;
@ -50,16 +58,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public bool PreserveAlpha { get; }
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
DenseMatrix<float> matrixY = this.KernelY;
DenseMatrix<float> matrixX = this.KernelX;
bool preserveAlpha = this.PreserveAlpha;
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
int startY = interest.Y;
int endY = interest.Bottom;
int startX = interest.X;
@ -67,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
int maxY = endY - 1;
int maxX = endX - 1;
using (Buffer2D<TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D<TPixel>(source.Width, source.Height))
using (Buffer2D<TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Width, source.Height))
{
source.CopyTo(targetPixels);
@ -76,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
ParallelHelper.IterateRowsWithTempBuffer<Vector4>(
workingRectangle,
configuration,
this.Configuration,
(rows, vectorBuffer) =>
{
Span<Vector4> vectorSpan = vectorBuffer.Span;
@ -86,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX);
PixelOperations<TPixel>.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan);
PixelOperations<TPixel>.Instance.ToVector4(this.Configuration, targetRowSpan.Slice(0, length), vectorSpan);
if (preserveAlpha)
{
@ -123,7 +128,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.Configuration, vectorSpan, targetRowSpan);
}
});
@ -131,4 +136,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
}
}
}
}

21
src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -25,10 +25,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <param name="kernelX">The horizontal gradient operator.</param>
/// <param name="kernelY">The vertical gradient operator.</param>
/// <param name="preserveAlpha">Whether the convolution filter is applied to alpha as well as the color channels.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public Convolution2PassProcessor(
in DenseMatrix<float> kernelX,
in DenseMatrix<float> kernelY,
bool preserveAlpha)
bool preserveAlpha,
Image<TPixel> source,
Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.KernelX = kernelX;
this.KernelY = kernelY;
@ -51,13 +56,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public bool PreserveAlpha { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
using (Buffer2D<TPixel> firstPassPixels = configuration.MemoryAllocator.Allocate2D<TPixel>(source.Size()))
using (Buffer2D<TPixel> firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Size()))
{
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
this.ApplyConvolution(firstPassPixels, source.PixelBuffer, interest, this.KernelX, configuration);
this.ApplyConvolution(source.PixelBuffer, firstPassPixels, interest, this.KernelY, configuration);
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
this.ApplyConvolution(firstPassPixels, source.PixelBuffer, interest, this.KernelX, this.Configuration);
this.ApplyConvolution(source.PixelBuffer, firstPassPixels, interest, this.KernelY, this.Configuration);
}
}
@ -144,4 +149,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
});
}
}
}
}

25
src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -24,7 +24,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// </summary>
/// <param name="kernelXY">The 2d gradient operator.</param>
/// <param name="preserveAlpha">Whether the convolution filter is applied to alpha as well as the color channels.</param>
public ConvolutionProcessor(in DenseMatrix<float> kernelXY, bool preserveAlpha)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public ConvolutionProcessor(
in DenseMatrix<float> kernelXY,
bool preserveAlpha,
Image<TPixel> source,
Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.KernelXY = kernelXY;
this.PreserveAlpha = preserveAlpha;
@ -41,12 +48,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public bool PreserveAlpha { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
DenseMatrix<float> matrix = this.KernelXY;
bool preserveAlpha = this.PreserveAlpha;
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
int startY = interest.Y;
int endY = interest.Bottom;
int startX = interest.X;
@ -54,7 +61,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
int maxY = endY - 1;
int maxX = endX - 1;
using (Buffer2D<TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D<TPixel>(source.Size()))
using (Buffer2D<TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Size()))
{
source.CopyTo(targetPixels);
@ -63,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
ParallelHelper.IterateRowsWithTempBuffer<Vector4>(
workingRectangle,
configuration,
this.Configuration,
(rows, vectorBuffer) =>
{
Span<Vector4> vectorSpan = vectorBuffer.Span;
@ -73,7 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX);
PixelOperations<TPixel>.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan);
PixelOperations<TPixel>.Instance.ToVector4(this.Configuration, targetRowSpan.Slice(0, length), vectorSpan);
if (preserveAlpha)
{
@ -108,7 +115,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.Configuration, vectorSpan, targetRowSpan);
}
});
@ -116,4 +123,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
}
}
}
}

26
src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
@ -21,7 +21,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// <param name="kernelX">The horizontal gradient operator.</param>
/// <param name="kernelY">The vertical gradient operator.</param>
/// <param name="grayscale">Whether to convert the image to grayscale before performing edge detection.</param>
internal EdgeDetector2DProcessor(in DenseMatrix<float> kernelX, in DenseMatrix<float> kernelY, bool grayscale)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
internal EdgeDetector2DProcessor(
in DenseMatrix<float> kernelX,
in DenseMatrix<float> kernelY,
bool grayscale,
Image<TPixel> source,
Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same.");
this.KernelX = kernelX;
@ -41,17 +49,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public bool Grayscale { get; }
/// <inheritdoc />
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
=> new Convolution2DProcessor<TPixel>(this.KernelX, this.KernelY, true).Apply(source, sourceRectangle, configuration);
/// <inheritdoc/>
protected override void BeforeFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void BeforeImageApply()
{
if (this.Grayscale)
{
new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration);
new GrayscaleBt709Processor(1F).Apply(this.Source, this.SourceRectangle);
}
}
/// <inheritdoc />
protected override void OnFrameApply(ImageFrame<TPixel> source)
=> new Convolution2DProcessor<TPixel>(this.KernelX, this.KernelY, true, this.Source, this.SourceRectangle).Apply(source);
}
}
}

29
src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -27,7 +27,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// </summary>
/// <param name="kernels">Gets the kernels to use.</param>
/// <param name="grayscale">Whether to convert the image to grayscale before performing edge detection.</param>
internal EdgeDetectorCompassProcessor(CompassKernels kernels, bool grayscale)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
internal EdgeDetectorCompassProcessor(CompassKernels kernels, bool grayscale, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.Grayscale = grayscale;
this.Kernels = kernels;
@ -38,23 +41,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
private bool Grayscale { get; }
/// <inheritdoc/>
protected override void BeforeFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void BeforeImageApply()
{
if (this.Grayscale)
{
new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration);
new GrayscaleBt709Processor(1F).Apply(this.Source, this.SourceRectangle);
}
}
/// <inheritdoc />
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
DenseMatrix<float>[] kernels = this.Kernels.Flatten();
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
int startY = this.SourceRectangle.Y;
int endY = this.SourceRectangle.Bottom;
int startX = this.SourceRectangle.X;
int endX = this.SourceRectangle.Right;
// Align start/end positions.
int minX = Math.Max(0, startX);
@ -65,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
// we need a clean copy for each pass to start from
using (ImageFrame<TPixel> cleanCopy = source.Clone())
{
new ConvolutionProcessor<TPixel>(kernels[0], true).Apply(source, sourceRectangle, configuration);
new ConvolutionProcessor<TPixel>(kernels[0], true, this.Source, this.SourceRectangle).Apply(source);
if (kernels.Length == 1)
{
@ -94,14 +97,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
using (ImageFrame<TPixel> pass = cleanCopy.Clone())
{
new ConvolutionProcessor<TPixel>(kernels[i], true).Apply(pass, sourceRectangle, configuration);
new ConvolutionProcessor<TPixel>(kernels[i], true, this.Source, this.SourceRectangle).Apply(pass);
Buffer2D<TPixel> passPixels = pass.PixelBuffer;
Buffer2D<TPixel> targetPixels = source.PixelBuffer;
ParallelHelper.IterateRows(
workingRect,
configuration,
this.Configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
@ -132,4 +135,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
}
}
}
}

7
src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
@ -25,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public bool Grayscale { get; }
/// <inheritdoc />
public abstract IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public abstract IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>;
}
}
}

15
src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs

@ -20,7 +20,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// </summary>
/// <param name="kernelXY">The 2d gradient operator.</param>
/// <param name="grayscale">Whether to convert the image to grayscale before performing edge detection.</param>
public EdgeDetectorProcessor(in DenseMatrix<float> kernelXY, bool grayscale)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The target area to process for the current processor instance.</param>
public EdgeDetectorProcessor(in DenseMatrix<float> kernelXY, bool grayscale, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.KernelXY = kernelXY;
this.Grayscale = grayscale;
@ -34,16 +37,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public DenseMatrix<float> KernelXY { get; }
/// <inheritdoc/>
protected override void BeforeFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void BeforeImageApply()
{
if (this.Grayscale)
{
new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration);
new GrayscaleBt709Processor(1F).Apply(this.Source, this.SourceRectangle);
}
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
=> new ConvolutionProcessor<TPixel>(this.KernelXY, true).Apply(source, sourceRectangle, configuration);
protected override void OnFrameApply(ImageFrame<TPixel> source)
=> new ConvolutionProcessor<TPixel>(this.KernelXY, true, this.Source, this.SourceRectangle).Apply(source);
}
}
}

9
src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
@ -70,10 +71,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public int Radius { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new GaussianBlurProcessor<TPixel>(this);
return new GaussianBlurProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

17
src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs

@ -18,7 +18,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// Initializes a new instance of the <see cref="GaussianBlurProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="GaussianBlurProcessor"/> defining the processor parameters.</param>
public GaussianBlurProcessor(GaussianBlurProcessor definition)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public GaussianBlurProcessor(GaussianBlurProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
int kernelSize = (definition.Radius * 2) + 1;
this.KernelX = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma);
@ -36,13 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public DenseMatrix<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration) =>
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY, false).Apply(
source,
sourceRectangle,
configuration);
protected override void OnFrameApply(ImageFrame<TPixel> source) =>
new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle).Apply(source);
}
}
}

9
src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
@ -70,10 +71,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public int Radius { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new GaussianSharpenProcessor<TPixel>(this);
return new GaussianSharpenProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

11
src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs

@ -18,7 +18,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// Initializes a new instance of the <see cref="GaussianSharpenProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="GaussianBlurProcessor"/> defining the processor parameters.</param>
public GaussianSharpenProcessor(GaussianSharpenProcessor definition)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public GaussianSharpenProcessor(GaussianSharpenProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
int kernelSize = (definition.Radius * 2) + 1;
this.KernelX = ConvolutionProcessorHelpers.CreateGaussianSharpenKernel(kernelSize, definition.Sigma);
@ -36,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public DenseMatrix<float> KernelY { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
=> new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration);
protected override void OnFrameApply(ImageFrame<TPixel> source)
=> new Convolution2PassProcessor<TPixel>(this.KernelX, this.KernelY, false, this.Source, this.SourceRectangle).Apply(source);
}
}
}

12
src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
@ -19,12 +21,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new EdgeDetector2DProcessor<TPixel>(
KayyaliKernels.KayyaliX,
KayyaliKernels.KayyaliY,
this.Grayscale);
this.Grayscale,
source,
sourceRectangle);
}
}
}
}

10
src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new EdgeDetectorCompassProcessor<TPixel>(new KirschKernels(), this.Grayscale);
return new EdgeDetectorCompassProcessor<TPixel>(new KirschKernels(), this.Grayscale, source, sourceRectangle);
}
}
}
}

8
src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new EdgeDetectorProcessor<TPixel>(LaplacianKernels.Laplacian3x3, this.Grayscale);
return new EdgeDetectorProcessor<TPixel>(LaplacianKernels.Laplacian3x3, this.Grayscale, source, sourceRectangle);
}
}
}

10
src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new EdgeDetectorProcessor<TPixel>(LaplacianKernels.Laplacian5x5, this.Grayscale);
return new EdgeDetectorProcessor<TPixel>(LaplacianKernels.Laplacian5x5, this.Grayscale, source, sourceRectangle);
}
}
}
}

10
src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new EdgeDetectorProcessor<TPixel>(LaplacianKernels.LaplacianOfGaussianXY, this.Grayscale);
return new EdgeDetectorProcessor<TPixel>(LaplacianKernels.LaplacianOfGaussianXY, this.Grayscale, source, sourceRectangle);
}
}
}
}

10
src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new EdgeDetector2DProcessor<TPixel>(PrewittKernels.PrewittX, PrewittKernels.PrewittY, this.Grayscale);
return new EdgeDetector2DProcessor<TPixel>(PrewittKernels.PrewittX, PrewittKernels.PrewittY, this.Grayscale, source, sourceRectangle);
}
}
}
}

15
src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
@ -19,9 +21,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new EdgeDetector2DProcessor<TPixel>(RobertsCrossKernels.RobertsCrossX, RobertsCrossKernels.RobertsCrossY, this.Grayscale);
return new EdgeDetector2DProcessor<TPixel>(
RobertsCrossKernels.RobertsCrossX,
RobertsCrossKernels.RobertsCrossY,
this.Grayscale,
source,
sourceRectangle);
}
}
}
}

10
src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new EdgeDetectorCompassProcessor<TPixel>(new RobinsonKernels(), this.Grayscale);
return new EdgeDetectorCompassProcessor<TPixel>(new RobinsonKernels(), this.Grayscale, source, sourceRectangle);
}
}
}
}

10
src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new EdgeDetector2DProcessor<TPixel>(ScharrKernels.ScharrX, ScharrKernels.ScharrY, this.Grayscale);
return new EdgeDetector2DProcessor<TPixel>(ScharrKernels.ScharrX, ScharrKernels.ScharrY, this.Grayscale, source, sourceRectangle);
}
}
}
}

10
src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
/// <summary>
@ -19,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new EdgeDetector2DProcessor<TPixel>(SobelKernels.SobelX, SobelKernels.SobelY, this.Grayscale);
return new EdgeDetector2DProcessor<TPixel>(SobelKernels.SobelX, SobelKernels.SobelY, this.Grayscale, source, sourceRectangle);
}
}
}
}

9
src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Dithering
{
@ -57,9 +58,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
public float Threshold { get; }
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new ErrorDiffusionPaletteProcessor<TPixel>(this);
return new ErrorDiffusionPaletteProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

16
src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor{TPixel}.cs

@ -16,20 +16,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
internal class ErrorDiffusionPaletteProcessor<TPixel> : PaletteDitherProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
public ErrorDiffusionPaletteProcessor(ErrorDiffusionPaletteProcessor definition)
: base(definition)
/// <summary>
/// Initializes a new instance of the <see cref="ErrorDiffusionPaletteProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="ErrorDiffusionPaletteProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public ErrorDiffusionPaletteProcessor(ErrorDiffusionPaletteProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(definition, source, sourceRectangle)
{
}
private new ErrorDiffusionPaletteProcessor Definition => (ErrorDiffusionPaletteProcessor)base.Definition;
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
byte threshold = (byte)MathF.Round(this.Definition.Threshold * 255F);
bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
int startY = interest.Y;
int endY = interest.Bottom;
int startX = interest.X;
@ -78,4 +84,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
}
}
}
}
}

9
src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Dithering
{
@ -34,9 +35,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
public IOrderedDither Dither { get; }
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new OrderedDitherPaletteProcessor<TPixel>(this);
return new OrderedDitherPaletteProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

16
src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor{TPixel}.cs

@ -16,19 +16,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
internal class OrderedDitherPaletteProcessor<TPixel> : PaletteDitherProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
public OrderedDitherPaletteProcessor(OrderedDitherPaletteProcessor definition)
: base(definition)
/// <summary>
/// Initializes a new instance of the <see cref="OrderedDitherPaletteProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="OrderedDitherPaletteProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public OrderedDitherPaletteProcessor(OrderedDitherPaletteProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(definition, source, sourceRectangle)
{
}
private new OrderedDitherPaletteProcessor Definition => (OrderedDitherPaletteProcessor)base.Definition;
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
int startY = interest.Y;
int endY = interest.Bottom;
int startX = interest.X;
@ -76,4 +82,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
}
}
}
}
}

7
src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs

@ -1,9 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Dithering
{
@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
public ReadOnlyMemory<Color> Palette { get; }
/// <inheritdoc />
public abstract IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public abstract IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>;
}
}
}

20
src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs

@ -30,26 +30,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
/// <summary>
/// Initializes a new instance of the <see cref="PaletteDitherProcessor{TPixel}"/> class.
/// </summary>
protected PaletteDitherProcessor(PaletteDitherProcessor definition)
/// <param name="definition">The <see cref="PaletteDitherProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
protected PaletteDitherProcessor(PaletteDitherProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.Definition = definition;
}
protected PaletteDitherProcessor Definition { get; }
protected override void BeforeFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration)
/// <inheritdoc/>
protected override void BeforeFrameApply(ImageFrame<TPixel> source)
{
base.BeforeFrameApply(source, sourceRectangle, configuration);
base.BeforeFrameApply(source);
// Lazy init palette:
if (this.palette is null)
{
ReadOnlySpan<Color> sourcePalette = this.Definition.Palette.Span;
this.palette = new TPixel[sourcePalette.Length];
Color.ToPixel<TPixel>(configuration, sourcePalette, this.palette);
Color.ToPixel<TPixel>(this.Configuration, sourcePalette, this.palette);
}
// Lazy init paletteVector:
@ -57,7 +59,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
{
this.paletteVector = new Vector4[this.palette.Length];
PixelOperations<TPixel>.Instance.ToVector4(
configuration,
this.Configuration,
(ReadOnlySpan<TPixel>)this.palette,
(Span<Vector4>)this.paletteVector,
PixelConversionModifiers.Scale);
@ -116,4 +118,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
return pair;
}
}
}
}

9
src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Effects
{
@ -39,10 +40,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
public int BrushSize { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new OilPaintingProcessor<TPixel>(this);
return new OilPaintingProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

26
src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs

@ -22,16 +22,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
{
private readonly OilPaintingProcessor definition;
public OilPaintingProcessor(OilPaintingProcessor definition)
/// <summary>
/// Initializes a new instance of the <see cref="OilPaintingProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="OilPaintingProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public OilPaintingProcessor(OilPaintingProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
int brushSize = this.definition.BrushSize;
if (brushSize <= 0 || brushSize > source.Height || brushSize > source.Width)
@ -39,24 +43,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
throw new ArgumentOutOfRangeException(nameof(brushSize));
}
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
int startY = this.SourceRectangle.Y;
int endY = this.SourceRectangle.Bottom;
int startX = this.SourceRectangle.X;
int endX = this.SourceRectangle.Right;
int maxY = endY - 1;
int maxX = endX - 1;
int radius = brushSize >> 1;
int levels = this.definition.Levels;
using (Buffer2D<TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D<TPixel>(source.Size()))
using (Buffer2D<TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Size()))
{
source.CopyTo(targetPixels);
var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY);
ParallelHelper.IterateRows(
workingRect,
configuration,
this.Configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)

9
src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Effects
{
@ -29,10 +30,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
public int Size { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new PixelateProcessor<TPixel>(this);
return new PixelateProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

19
src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs

@ -25,7 +25,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
/// Initializes a new instance of the <see cref="PixelateProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="PixelateProcessor"/>.</param>
public PixelateProcessor(PixelateProcessor definition)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public PixelateProcessor(PixelateProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
@ -33,17 +36,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
private int Size => this.definition.Size;
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
if (this.Size <= 0 || this.Size > source.Height || this.Size > source.Width)
{
throw new ArgumentOutOfRangeException(nameof(this.Size));
}
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
int startY = this.SourceRectangle.Y;
int endY = this.SourceRectangle.Bottom;
int startX = this.SourceRectangle.X;
int endX = this.SourceRectangle.Right;
int size = this.Size;
int offset = this.Size / 2;
@ -69,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
Parallel.ForEach(
range,
configuration.GetParallelOptions(),
this.Configuration.GetParallelOptions(),
y =>
{
int offsetY = y - startY;
@ -108,4 +111,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
});
}
}
}
}

9
src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs

@ -1,8 +1,9 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Filters
{
@ -23,10 +24,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
public ColorMatrix Matrix { get; }
/// <inheritdoc />
public virtual IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public virtual IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new FilterProcessor<TPixel>(this);
return new FilterProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

23
src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -20,22 +20,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
{
private readonly FilterProcessor definition;
public FilterProcessor(FilterProcessor definition)
/// <summary>
/// Initializes a new instance of the <see cref="FilterProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="FilterProcessor"/>.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public FilterProcessor(FilterProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
int startX = interest.X;
ColorMatrix matrix = this.definition.Matrix;
ParallelHelper.IterateRowsWithTempBuffer<Vector4>(
interest,
configuration,
this.Configuration,
(rows, vectorBuffer) =>
{
for (int y = rows.Min; y < rows.Max; y++)
@ -43,13 +50,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
Span<Vector4> vectorSpan = vectorBuffer.Span;
int length = vectorSpan.Length;
Span<TPixel> rowSpan = source.GetPixelRowSpan(y).Slice(startX, length);
PixelOperations<TPixel>.Instance.ToVector4(configuration, rowSpan, vectorSpan);
PixelOperations<TPixel>.Instance.ToVector4(this.Configuration, rowSpan, vectorSpan);
Vector4Utils.Transform(vectorSpan, ref matrix);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan);
PixelOperations<TPixel>.Instance.FromVector4Destructive(this.Configuration, vectorSpan, rowSpan);
}
});
}
}
}
}

10
src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Filters
{
/// <summary>
@ -17,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>() =>
new LomographProcessor<TPixel>(this);
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle) =>
new LomographProcessor<TPixel>(this, source, sourceRectangle);
}
}
}

12
src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs

@ -19,15 +19,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
/// Initializes a new instance of the <see cref="LomographProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="LomographProcessor"/> defining the parameters.</param>
public LomographProcessor(LomographProcessor definition)
: base(definition)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public LomographProcessor(LomographProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(definition, source, sourceRectangle)
{
}
/// <inheritdoc/>
protected override void AfterFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void AfterFrameApply(ImageFrame<TPixel> source)
{
new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration);
new VignetteProcessor(VeryDarkGreen).CreatePixelSpecificProcessor(this.Source, this.SourceRectangle).Apply();
}
}
}
}

10
src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Filters
{
/// <summary>
@ -17,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>() =>
new PolaroidProcessor<TPixel>(this);
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle) =>
new PolaroidProcessor<TPixel>(this, source, sourceRectangle);
}
}
}

17
src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs

@ -21,19 +21,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
/// Initializes a new instance of the <see cref="PolaroidProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="PolaroidProcessor"/> defining the parameters.</param>
public PolaroidProcessor(PolaroidProcessor definition)
: base(definition)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public PolaroidProcessor(PolaroidProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(definition, source, sourceRectangle)
{
}
/// <inheritdoc/>
protected override void AfterFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration)
protected override void AfterImageApply()
{
new VignetteProcessor(VeryDarkOrange).Apply(source, sourceRectangle, configuration);
new GlowProcessor(LightOrange, source.Width / 4F).Apply(source, sourceRectangle, configuration);
new VignetteProcessor(VeryDarkOrange).Apply(this.Source, this.SourceRectangle);
new GlowProcessor(LightOrange, this.Source.Width / 4F).Apply(this.Source, this.SourceRectangle);
}
}
}
}

14
src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
@ -16,17 +16,13 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="source"/> is null.
/// The target <see cref="Image{TPixel}"/> is null.
/// </exception>
/// <exception cref="System.ArgumentException">
/// <paramref name="sourceRectangle"/> doesn't fit the dimension of the image.
/// The target <see cref="Rectangle"/> doesn't fit the dimension of the image.
/// </exception>
/// <returns>Returns the cloned image after there processor has been applied to it.</returns>
Image<TPixel> CloneAndApply(Image<TPixel> source, Rectangle sourceRectangle);
Image<TPixel> CloneAndApply();
}
}
}

11
src/ImageSharp/Processing/Processors/IImageProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors
{
@ -18,8 +19,12 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// the processing algorithm on an <see cref="Image{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <returns>The <see cref="IImageProcessor{TPixel}"/></returns>
IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>;
}
}
}

12
src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs

@ -16,16 +16,12 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="source"/> is null.
/// The target <see cref="Image{TPixel}"/> is null.
/// </exception>
/// <exception cref="System.ArgumentException">
/// <paramref name="sourceRectangle"/> doesn't fit the dimension of the image.
/// The target <see cref="Rectangle"/> doesn't fit the dimension of the image.
/// </exception>
void Apply(Image<TPixel> source, Rectangle sourceRectangle);
void Apply();
}
}
}

76
src/ImageSharp/Processing/Processors/ImageProcessor.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -15,20 +15,46 @@ namespace SixLabors.ImageSharp.Processing.Processors
internal abstract class ImageProcessor<TPixel> : IImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessor{TPixel}"/> class.
/// </summary>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
protected ImageProcessor(Image<TPixel> source, Rectangle sourceRectangle)
{
this.Source = source;
this.SourceRectangle = sourceRectangle;
this.Configuration = this.Source.GetConfiguration();
}
/// <summary>
/// Gets The source <see cref="Image{TPixel}"/> for the current processor instance.
/// </summary>
protected Image<TPixel> Source { get; }
/// <summary>
/// Gets The source area to process for the current processor instance.
/// </summary>
protected Rectangle SourceRectangle { get; }
/// <summary>
/// Gets the <see cref="ImageSharp.Configuration"/> instance to use when performing operations.
/// </summary>
protected Configuration Configuration { get; }
/// <inheritdoc/>
public void Apply(Image<TPixel> source, Rectangle sourceRectangle)
public void Apply()
{
try
{
Configuration config = source.GetConfiguration();
this.BeforeImageApply(source, sourceRectangle);
this.BeforeImageApply();
foreach (ImageFrame<TPixel> sourceFrame in source.Frames)
foreach (ImageFrame<TPixel> sourceFrame in this.Source.Frames)
{
this.Apply(sourceFrame, sourceRectangle, config);
this.Apply(sourceFrame);
}
this.AfterImageApply(source, sourceRectangle);
this.AfterImageApply();
}
#if DEBUG
catch (Exception)
@ -43,18 +69,16 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
/// <summary>
/// Applies the processor to just a single ImageBase
/// Applies the processor to just a single ImageBase.
/// </summary>
/// <param name="source">the source image</param>
/// <param name="sourceRectangle">the target</param>
/// <param name="configuration">The configuration.</param>
public void Apply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
/// <param name="source">the source image.</param>
public void Apply(ImageFrame<TPixel> source)
{
try
{
this.BeforeFrameApply(source, sourceRectangle, configuration);
this.OnFrameApply(source, sourceRectangle, configuration);
this.AfterFrameApply(source, sourceRectangle, configuration);
this.BeforeFrameApply(source);
this.OnFrameApply(source);
this.AfterFrameApply(source);
}
#if DEBUG
catch (Exception)
@ -71,9 +95,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <summary>
/// This method is called before the process is applied to prepare the processor.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
protected virtual void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle)
protected virtual void BeforeImageApply()
{
}
@ -81,9 +103,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// This method is called before the process is applied to prepare the processor.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
protected virtual void BeforeFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected virtual void BeforeFrameApply(ImageFrame<TPixel> source)
{
}
@ -92,27 +112,21 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// and with the specified size.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
protected abstract void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration);
protected abstract void OnFrameApply(ImageFrame<TPixel> source);
/// <summary>
/// This method is called after the process is applied to prepare the processor.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
protected virtual void AfterFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected virtual void AfterFrameApply(ImageFrame<TPixel> source)
{
}
/// <summary>
/// This method is called after the process is applied to prepare the processor.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
protected virtual void AfterImageApply(Image<TPixel> source, Rectangle sourceRectangle)
protected virtual void AfterImageApply()
{
}
}
}
}

21
src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs

@ -14,21 +14,6 @@ namespace SixLabors.ImageSharp.Processing.Processors
source.AcceptVisitor(visitor);
}
/// <summary>
/// Apply an <see cref="IImageProcessor"/> to a frame.
/// Only works from processors implemented by an <see cref="ImageProcessor{TPixel}"/> subclass.
/// </summary>
internal static void Apply<TPixel>(
this IImageProcessor processor,
ImageFrame<TPixel> frame,
Rectangle sourceRectangle,
Configuration configuration)
where TPixel : struct, IPixel<TPixel>
{
var processorImpl = (ImageProcessor<TPixel>)processor.CreatePixelSpecificProcessor<TPixel>();
processorImpl.Apply(frame, sourceRectangle, configuration);
}
private class ApplyVisitor : IImageVisitor
{
private readonly IImageProcessor processor;
@ -44,9 +29,9 @@ namespace SixLabors.ImageSharp.Processing.Processors
public void Visit<TPixel>(Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
var processorImpl = this.processor.CreatePixelSpecificProcessor<TPixel>();
processorImpl.Apply(image, this.sourceRectangle);
IImageProcessor<TPixel> processorImpl = this.processor.CreatePixelSpecificProcessor(image, this.sourceRectangle);
processorImpl.Apply();
}
}
}
}
}

12
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{
/// <summary>
@ -33,13 +35,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
public int NumberOfTiles { get; }
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new AdaptiveHistogramEqualizationProcessor<TPixel>(
this.LuminanceLevels,
this.ClipHistogram,
this.ClipLimitPercentage,
this.NumberOfTiles);
this.NumberOfTiles,
source,
sourceRectangle);
}
}
}
}

18
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs

@ -32,8 +32,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="tiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param>
public AdaptiveHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles)
: base(luminanceLevels, clipHistogram, clipLimitPercentage)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public AdaptiveHistogramEqualizationProcessor(
int luminanceLevels,
bool clipHistogram,
float clipLimitPercentage,
int tiles,
Image<TPixel> source,
Rectangle sourceRectangle)
: base(luminanceLevels, clipHistogram, clipLimitPercentage, source, sourceRectangle)
{
Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles));
Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles));
@ -47,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private int Tiles { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
int sourceWidth = source.Width;
int sourceHeight = source.Height;
@ -59,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int luminanceLevels = this.LuminanceLevels;
// The image is split up into tiles. For each tile the cumulative distribution function will be calculated.
using (var cdfData = new CdfTileData(configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels))
using (var cdfData = new CdfTileData(this.Configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels))
{
cdfData.CalculateLookupTables(source, this);
@ -74,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
Parallel.For(
0,
tileYStartPositions.Count,
new ParallelOptions { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism },
new ParallelOptions { MaxDegreeOfParallelism = this.Configuration.MaxDegreeOfParallelism },
index =>
{
int y = tileYStartPositions[index].y;

12
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{
/// <summary>
@ -32,13 +34,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
public int NumberOfTiles { get; }
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new AdaptiveHistogramEqualizationSlidingWindowProcessor<TPixel>(
this.LuminanceLevels,
this.ClipHistogram,
this.ClipLimitPercentage,
this.NumberOfTiles);
this.NumberOfTiles,
source,
sourceRectangle);
}
}
}
}

30
src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs

@ -31,8 +31,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param>
/// <param name="tiles">The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100.</param>
public AdaptiveHistogramEqualizationSlidingWindowProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles)
: base(luminanceLevels, clipHistogram, clipLimitPercentage)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public AdaptiveHistogramEqualizationSlidingWindowProcessor(
int luminanceLevels,
bool clipHistogram,
float clipLimitPercentage,
int tiles,
Image<TPixel> source,
Rectangle sourceRectangle)
: base(luminanceLevels, clipHistogram, clipLimitPercentage, source, sourceRectangle)
{
Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles));
Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles));
@ -46,11 +54,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
private int Tiles { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator;
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism };
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = this.Configuration.MaxDegreeOfParallelism };
int tileWidth = source.Width / this.Tiles;
int tileHeight = tileWidth;
int pixelInTile = tileWidth * tileHeight;
@ -58,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
int halfTileWidth = halfTileHeight;
var slidingWindowInfos = new SlidingWindowInfos(tileWidth, tileHeight, halfTileWidth, halfTileHeight, pixelInTile);
using (Buffer2D<TPixel> targetPixels = configuration.MemoryAllocator.Allocate2D<TPixel>(source.Width, source.Height))
using (Buffer2D<TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Width, source.Height))
{
// Process the inner tiles, which do not require to check the borders.
Parallel.For(
@ -73,7 +81,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
yStart: halfTileHeight,
yEnd: source.Height - halfTileHeight,
useFastPath: true,
configuration));
this.Configuration));
// Process the left border of the image.
Parallel.For(
@ -88,7 +96,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
yStart: 0,
yEnd: source.Height,
useFastPath: false,
configuration));
this.Configuration));
// Process the right border of the image.
Parallel.For(
@ -103,7 +111,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
yStart: 0,
yEnd: source.Height,
useFastPath: false,
configuration));
this.Configuration));
// Process the top border of the image.
Parallel.For(
@ -118,7 +126,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
yStart: 0,
yEnd: halfTileHeight,
useFastPath: false,
configuration));
this.Configuration));
// Process the bottom border of the image.
Parallel.For(
@ -133,7 +141,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
yStart: source.Height - halfTileHeight,
yEnd: source.Height,
useFastPath: false,
configuration));
this.Configuration));
Buffer2D<TPixel>.SwapOrCopyContent(source.PixelBuffer, targetPixels);
}

12
src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{
/// <summary>
@ -20,12 +22,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
}
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new GlobalHistogramEqualizationProcessor<TPixel>(
this.LuminanceLevels,
this.ClipHistogram,
this.ClipLimitPercentage);
this.ClipLimitPercentage,
source,
sourceRectangle);
}
}
}
}

19
src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs

@ -32,15 +32,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// </param>
/// <param name="clipHistogram">Indicating whether to clip the histogram bins at a specific value.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels. Histogram bins which exceed this limit, will be capped at this value.</param>
public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage)
: base(luminanceLevels, clipHistogram, clipLimitPercentage)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public GlobalHistogramEqualizationProcessor(
int luminanceLevels,
bool clipHistogram,
float clipLimitPercentage,
Image<TPixel> source,
Rectangle sourceRectangle)
: base(luminanceLevels, clipHistogram, clipLimitPercentage, source, sourceRectangle)
{
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator;
int numberOfPixels = source.Width * source.Height;
var workingRect = new Rectangle(0, 0, source.Width, source.Height);
@ -50,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
// Build the histogram of the grayscale levels.
ParallelHelper.IterateRows(
workingRect,
configuration,
this.Configuration,
rows =>
{
ref int histogramBase = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan());
@ -83,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
// Apply the cdf to each pixel of the image
ParallelHelper.IterateRows(
workingRect,
configuration,
this.Configuration,
rows =>
{
ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan());

7
src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{
@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
public float ClipLimitPercentage { get; }
/// <inheritdoc />
public abstract IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public abstract IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>;
/// <summary>
@ -89,4 +90,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
return processor;
}
}
}
}

13
src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs

@ -7,6 +7,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Normalization
{
@ -26,7 +27,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
/// or 65536 for 16-bit grayscale images.</param>
/// <param name="clipHistogram">Indicates, if histogram bins should be clipped.</param>
/// <param name="clipLimitPercentage">Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value.</param>
protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
protected HistogramEqualizationProcessor(
int luminanceLevels,
bool clipHistogram,
float clipLimitPercentage,
Image<TPixel> source,
Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels));
Guard.MustBeGreaterThan(clipLimitPercentage, 0F, nameof(clipLimitPercentage));
@ -136,4 +145,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
public static int GetLuminance(ref Vector4 vector, int luminanceLevels)
=> (int)MathF.Round(((.2126F * vector.X) + (.7152F * vector.Y) + (.0722F * vector.Y)) * (luminanceLevels - 1));
}
}
}

9
src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Overlays
{
@ -32,10 +33,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
public Color Color { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new BackgroundColorProcessor<TPixel>(this);
return new BackgroundColorProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

23
src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor{TPixel}.cs

@ -24,24 +24,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
/// <summary>
/// Initializes a new instance of the <see cref="BackgroundColorProcessor{TPixel}"/> class.
/// </summary>
public BackgroundColorProcessor(BackgroundColorProcessor definition)
/// <param name="definition">The <see cref="BackgroundColorProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public BackgroundColorProcessor(BackgroundColorProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
TPixel color = this.definition.Color.ToPixel<TPixel>();
GraphicsOptions graphicsOptions = this.definition.GraphicsOptions;
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
int startY = this.SourceRectangle.Y;
int endY = this.SourceRectangle.Bottom;
int startX = this.SourceRectangle.X;
int endX = this.SourceRectangle.Right;
// Align start/end positions.
int minX = Math.Max(0, startX);
@ -78,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
ParallelHelper.IterateRows(
workingRect,
configuration,
this.Configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
@ -98,4 +99,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
}
}
}
}
}

9
src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs

@ -1,8 +1,9 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Overlays
{
@ -69,10 +70,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
internal ValueSize Radius { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new GlowProcessor<TPixel>(this);
return new GlowProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

32
src/ImageSharp/Processing/Processors/Overlays/GlowProcessor{TPixel}.cs

@ -24,31 +24,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
private readonly GlowProcessor definition;
public GlowProcessor(GlowProcessor definition)
/// <summary>
/// Initializes a new instance of the <see cref="GlowProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="GlowProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public GlowProcessor(GlowProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
this.blender = PixelOperations<TPixel>.Instance.GetPixelBlender(definition.GraphicsOptions);
}
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
// TODO: can we simplify the rectangle calculation?
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
int startY = this.SourceRectangle.Y;
int endY = this.SourceRectangle.Bottom;
int startX = this.SourceRectangle.X;
int endX = this.SourceRectangle.Right;
TPixel glowColor = this.definition.GlowColor.ToPixel<TPixel>();
Vector2 center = Rectangle.Center(sourceRectangle);
Vector2 center = Rectangle.Center(this.SourceRectangle);
float finalRadius = this.definition.Radius.Calculate(source.Size());
float maxDistance = finalRadius > 0
? MathF.Min(finalRadius, sourceRectangle.Width * .5F)
: sourceRectangle.Width * .5F;
? MathF.Min(finalRadius, this.SourceRectangle.Width * .5F)
: this.SourceRectangle.Width * .5F;
// Align start/end positions.
int minX = Math.Max(0, startX);
@ -80,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
ParallelHelper.IterateRowsWithTempBuffer<float>(
workingRect,
configuration,
this.Configuration,
(rows, amounts) =>
{
Span<float> amountsSpan = amounts.Span;
@ -109,4 +113,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
}
}
}
}
}

9
src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs

@ -1,8 +1,9 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Overlays
{
@ -67,10 +68,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
internal ValueSize RadiusY { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new VignetteProcessor<TPixel>(this);
return new VignetteProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

36
src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor{TPixel}.cs

@ -24,34 +24,38 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
private readonly VignetteProcessor definition;
public VignetteProcessor(VignetteProcessor definition)
/// <summary>
/// Initializes a new instance of the <see cref="VignetteProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="VignetteProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public VignetteProcessor(VignetteProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
this.blender = PixelOperations<TPixel>.Instance.GetPixelBlender(definition.GraphicsOptions);
}
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
int startY = this.SourceRectangle.Y;
int endY = this.SourceRectangle.Bottom;
int startX = this.SourceRectangle.X;
int endX = this.SourceRectangle.Right;
TPixel vignetteColor = this.definition.VignetteColor.ToPixel<TPixel>();
Vector2 centre = Rectangle.Center(sourceRectangle);
Vector2 centre = Rectangle.Center(this.SourceRectangle);
Size sourceSize = source.Size();
float finalRadiusX = this.definition.RadiusX.Calculate(sourceSize);
float finalRadiusY = this.definition.RadiusY.Calculate(sourceSize);
float rX = finalRadiusX > 0
? MathF.Min(finalRadiusX, sourceRectangle.Width * .5F)
: sourceRectangle.Width * .5F;
? MathF.Min(finalRadiusX, this.SourceRectangle.Width * .5F)
: this.SourceRectangle.Width * .5F;
float rY = finalRadiusY > 0
? MathF.Min(finalRadiusY, sourceRectangle.Height * .5F)
: sourceRectangle.Height * .5F;
? MathF.Min(finalRadiusY, this.SourceRectangle.Height * .5F)
: this.SourceRectangle.Height * .5F;
float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY));
// Align start/end positions.
@ -83,7 +87,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
ParallelHelper.IterateRowsWithTempBuffer<float>(
workingRect,
configuration,
this.Configuration,
(rows, amounts) =>
{
Span<float> amountsSpan = amounts.Span;
@ -111,4 +115,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays
}
}
}
}
}

9
src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
@ -25,10 +26,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
public IQuantizer Quantizer { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new QuantizeProcessor<TPixel>(this.Quantizer);
return new QuantizeProcessor<TPixel>(this.Quantizer, source, sourceRectangle);
}
}
}
}

10
src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs

@ -22,15 +22,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// Initializes a new instance of the <see cref="QuantizeProcessor{TPixel}"/> class.
/// </summary>
/// <param name="quantizer">The quantizer used to reduce the color palette.</param>
public QuantizeProcessor(IQuantizer quantizer)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public QuantizeProcessor(IQuantizer quantizer, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
Guard.NotNull(quantizer, nameof(quantizer));
this.quantizer = quantizer;
}
/// <inheritdoc />
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
Configuration configuration = this.Configuration;
using (IFrameQuantizer<TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer<TPixel>(configuration))
using (IQuantizedFrame<TPixel> quantized = frameQuantizer.QuantizeFrame(source))
{
@ -57,4 +61,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
}
}
}
}

8
src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
@ -43,10 +43,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public Size TargetDimensions { get; }
/// <inheritdoc />
public virtual IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public virtual IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new AffineTransformProcessor<TPixel>(this);
return new AffineTransformProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

37
src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs

@ -20,7 +20,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
internal class AffineTransformProcessor<TPixel> : TransformProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
public AffineTransformProcessor(AffineTransformProcessor definition)
/// <summary>
/// Initializes a new instance of the <see cref="AffineTransformProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="AffineTransformProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public AffineTransformProcessor(AffineTransformProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.Definition = definition;
}
@ -32,22 +39,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private Matrix3x2 TransformMatrix => this.Definition.TransformMatrix;
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
protected override Image<TPixel> CreateDestination()
{
// We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
x => new ImageFrame<TPixel>(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone()));
IEnumerable<ImageFrame<TPixel>> frames = this.Source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
x => new ImageFrame<TPixel>(this.Configuration, this.TargetDimensions, x.Metadata.DeepClone()));
// Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.Metadata.DeepClone(), frames);
return new Image<TPixel>(this.Configuration, this.Source.Metadata.DeepClone(), frames);
}
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Rectangle sourceRectangle,
Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
// Handle transforms that result in output identical to the original.
if (this.TransformMatrix.Equals(default) || this.TransformMatrix.Equals(Matrix3x2.Identity))
@ -63,13 +66,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// Convert from screen to world space.
Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 matrix);
var sampler = this.Definition.Sampler;
IResampler sampler = this.Definition.Sampler;
if (sampler is NearestNeighborResampler)
{
ParallelHelper.IterateRows(
targetBounds,
configuration,
this.Configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
@ -79,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
for (int x = 0; x < width; x++)
{
var point = Point.Transform(new Point(x, y), matrix);
if (sourceRectangle.Contains(point.X, point.Y))
if (this.SourceRectangle.Contains(point.X, point.Y))
{
destRow[x] = source[point.X, point.Y];
}
@ -90,19 +93,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return;
}
var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), sampler);
var kernel = new TransformKernelMap(this.Configuration, source.Size(), destination.Size(), sampler);
try
{
ParallelHelper.IterateRowsWithTempBuffer<Vector4>(
targetBounds,
configuration,
this.Configuration,
(rows, vectorBuffer) =>
{
Span<Vector4> vectorSpan = vectorBuffer.Span;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> targetRowSpan = destination.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(configuration, targetRowSpan, vectorSpan);
PixelOperations<TPixel>.Instance.ToVector4(this.Configuration, targetRowSpan, vectorSpan);
ref float ySpanRef = ref kernel.GetYStartReference(y);
ref float xSpanRef = ref kernel.GetXStartReference(y);
@ -121,7 +124,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(
configuration,
this.Configuration,
vectorSpan,
targetRowSpan);
}

9
src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
@ -11,10 +12,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public sealed class AutoOrientProcessor : IImageProcessor
{
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new AutoOrientProcessor<TPixel>();
return new AutoOrientProcessor<TPixel>(source, sourceRectangle);
}
}
}
}

41
src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs

@ -16,41 +16,51 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
internal class AutoOrientProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="AutoOrientProcessor{TPixel}"/> class.
/// </summary>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public AutoOrientProcessor(Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
}
/// <inheritdoc/>
protected override void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle)
protected override void BeforeImageApply()
{
OrientationMode orientation = GetExifOrientation(source);
Size size = sourceRectangle.Size;
OrientationMode orientation = GetExifOrientation(this.Source);
Size size = this.SourceRectangle.Size;
switch (orientation)
{
case OrientationMode.TopRight:
new FlipProcessor(FlipMode.Horizontal).Apply(source, sourceRectangle);
new FlipProcessor(FlipMode.Horizontal).Apply(this.Source, this.SourceRectangle);
break;
case OrientationMode.BottomRight:
new RotateProcessor((int)RotateMode.Rotate180, size).Apply(source, sourceRectangle);
new RotateProcessor((int)RotateMode.Rotate180, size).Apply(this.Source, this.SourceRectangle);
break;
case OrientationMode.BottomLeft:
new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle);
new FlipProcessor(FlipMode.Vertical).Apply(this.Source, this.SourceRectangle);
break;
case OrientationMode.LeftTop:
new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle);
new FlipProcessor(FlipMode.Horizontal).Apply(source, sourceRectangle);
new RotateProcessor((int)RotateMode.Rotate90, size).Apply(this.Source, this.SourceRectangle);
new FlipProcessor(FlipMode.Horizontal).Apply(this.Source, this.SourceRectangle);
break;
case OrientationMode.RightTop:
new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle);
new RotateProcessor((int)RotateMode.Rotate90, size).Apply(this.Source, this.SourceRectangle);
break;
case OrientationMode.RightBottom:
new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle);
new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle);
new FlipProcessor(FlipMode.Vertical).Apply(this.Source, this.SourceRectangle);
new RotateProcessor((int)RotateMode.Rotate270, size).Apply(this.Source, this.SourceRectangle);
break;
case OrientationMode.LeftBottom:
new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle);
new RotateProcessor((int)RotateMode.Rotate270, size).Apply(this.Source, this.SourceRectangle);
break;
case OrientationMode.Unknown:
@ -61,10 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> sourceBase,
Rectangle sourceRectangle,
Configuration config)
protected override void OnFrameApply(ImageFrame<TPixel> sourceBase)
{
// All processing happens at the image level within BeforeImageApply();
}
@ -103,4 +110,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return orientation;
}
}
}
}

8
src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
@ -32,10 +32,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public Rectangle CropRectangle { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new CropProcessor<TPixel>(this);
return new CropProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

21
src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs

@ -25,7 +25,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Initializes a new instance of the <see cref="CropProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="CropProcessor"/>.</param>
public CropProcessor(CropProcessor definition)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public CropProcessor(CropProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
@ -33,25 +36,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private Rectangle CropRectangle => this.definition.CropRectangle;
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
protected override Image<TPixel> CreateDestination()
{
// We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
IEnumerable<ImageFrame<TPixel>> frames = this.Source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
x => new ImageFrame<TPixel>(
source.GetConfiguration(),
this.Source.GetConfiguration(),
this.CropRectangle.Width,
this.CropRectangle.Height,
x.Metadata.DeepClone()));
// Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.Metadata.DeepClone(), frames);
return new Image<TPixel>(this.Source.GetConfiguration(), this.Source.Metadata.DeepClone(), frames);
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
// Handle resize dimensions identical to the original
if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.CropRectangle)
if (source.Width == destination.Width && source.Height == destination.Height && this.SourceRectangle == this.CropRectangle)
{
// the cloned will be blank here copy all the pixel data over
source.GetPixelSpan().CopyTo(destination.GetPixelSpan());
@ -61,7 +64,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Rectangle rect = this.CropRectangle;
// Copying is cheap, we should process more pixels per task:
ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4);
ParallelExecutionSettings parallelSettings = this.Configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4);
ParallelHelper.IterateRows(
rect,
@ -77,4 +80,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
});
}
}
}
}

7
src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
@ -37,10 +38,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public float Threshold { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new EntropyCropProcessor<TPixel>(this);
return new EntropyCropProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

23
src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs

@ -22,38 +22,41 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Initializes a new instance of the <see cref="EntropyCropProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="EntropyCropProcessor"/>.</param>
public EntropyCropProcessor(EntropyCropProcessor definition)
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public EntropyCropProcessor(EntropyCropProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle)
protected override void BeforeImageApply()
{
Rectangle rectangle;
// All frames have be the same size so we only need to calculate the correct dimensions for the first frame
using (ImageFrame<TPixel> temp = source.Frames.RootFrame.Clone())
using (var temp = new Image<TPixel>(this.Configuration, this.Source.Metadata.DeepClone(), new[] { this.Source.Frames.RootFrame.Clone() }))
{
Configuration configuration = source.GetConfiguration();
Configuration configuration = this.Source.GetConfiguration();
// Detect the edges.
new SobelProcessor(false).Apply(temp, sourceRectangle, configuration);
new SobelProcessor(false).Apply(temp, this.SourceRectangle);
// Apply threshold binarization filter.
new BinaryThresholdProcessor(this.definition.Threshold).Apply(temp, sourceRectangle, configuration);
new BinaryThresholdProcessor(this.definition.Threshold).Apply(temp, this.SourceRectangle);
// Search for the first white pixels
rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0);
rectangle = ImageMaths.GetFilteredBoundingRectangle(temp.Frames.RootFrame, 0);
}
new CropProcessor(rectangle, source.Size()).Apply(source, sourceRectangle);
new CropProcessor(rectangle, this.Source.Size()).Apply(this.Source, this.SourceRectangle);
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
// All processing happens at the image level within BeforeImageApply();
}
}
}
}

9
src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs

@ -1,7 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
@ -25,10 +26,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public FlipMode FlipMode { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new FlipProcessor<TPixel>(this);
return new FlipProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

17
src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs

@ -20,22 +20,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
private readonly FlipProcessor definition;
public FlipProcessor(FlipProcessor definition)
/// <summary>
/// Initializes a new instance of the <see cref="FlipProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="FlipProcessor"/>.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public FlipProcessor(FlipProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
switch (this.definition.FlipMode)
{
// No default needed as we have already set the pixels.
case FlipMode.Vertical:
this.FlipX(source, configuration);
this.FlipX(source, this.Configuration);
break;
case FlipMode.Horizontal:
this.FlipY(source, configuration);
this.FlipY(source, this.Configuration);
break;
}
}
@ -84,4 +91,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
});
}
}
}
}

8
src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
@ -43,10 +43,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public Size TargetDimensions { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new ProjectiveTransformProcessor<TPixel>(this);
return new ProjectiveTransformProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

35
src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs

@ -22,7 +22,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
private readonly ProjectiveTransformProcessor definition;
public ProjectiveTransformProcessor(ProjectiveTransformProcessor definition)
/// <summary>
/// Initializes a new instance of the <see cref="ProjectiveTransformProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="ProjectiveTransformProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public ProjectiveTransformProcessor(ProjectiveTransformProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
@ -30,26 +37,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private Size TargetDimensions => this.definition.TargetDimensions;
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
protected override Image<TPixel> CreateDestination()
{
// We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
IEnumerable<ImageFrame<TPixel>> frames = this.Source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
x => new ImageFrame<TPixel>(
source.GetConfiguration(),
this.Source.GetConfiguration(),
this.TargetDimensions.Width,
this.TargetDimensions.Height,
x.Metadata.DeepClone()));
// Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.Metadata.DeepClone(), frames);
return new Image<TPixel>(this.Source.GetConfiguration(), this.Source.Metadata.DeepClone(), frames);
}
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Rectangle sourceRectangle,
Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
Matrix4x4 transformMatrix = this.definition.TransformMatrix;
@ -73,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
ParallelHelper.IterateRows(
targetBounds,
configuration,
this.Configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
@ -86,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int px = (int)MathF.Round(point.X);
int py = (int)MathF.Round(point.Y);
if (sourceRectangle.Contains(px, py))
if (this.SourceRectangle.Contains(px, py))
{
destRow[x] = source[px, py];
}
@ -97,19 +100,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return;
}
var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), sampler);
var kernel = new TransformKernelMap(this.Configuration, source.Size(), destination.Size(), sampler);
try
{
ParallelHelper.IterateRowsWithTempBuffer<Vector4>(
targetBounds,
configuration,
this.Configuration,
(rows, vectorBuffer) =>
{
Span<Vector4> vectorSpan = vectorBuffer.Span;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> targetRowSpan = destination.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(configuration, targetRowSpan, vectorSpan);
PixelOperations<TPixel>.Instance.ToVector4(this.Configuration, targetRowSpan, vectorSpan);
ref float ySpanRef = ref kernel.GetYStartReference(y);
ref float xSpanRef = ref kernel.GetXStartReference(y);
@ -128,7 +131,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(
configuration,
this.Configuration,
vectorSpan,
targetRowSpan);
}

8
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs

@ -130,10 +130,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public bool Compand { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>
{
return new ResizeProcessor<TPixel>(this);
}
=> new ResizeProcessor<TPixel>(this, source, sourceRectangle);
}
}
}

33
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs

@ -30,7 +30,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly ResizeProcessor parameterSource;
public ResizeProcessor(ResizeProcessor parameterSource)
public ResizeProcessor(ResizeProcessor parameterSource, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.parameterSource = parameterSource;
}
@ -64,31 +65,36 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// This is a shim for tagging the CreateDestination virtual generic method for the AoT iOS compiler.
/// This method should never be referenced outside of the AotCompiler code.
/// </summary>
/// <param name="source">Passed through as source to the CreateDestination method.</param>
/// <param name="sourceRectangle">Passed through as sourceRectangle to the CreateDestination method.</param>
/// <returns>The result returned from CreateDestination.</returns>
internal Image<TPixel> AotCreateDestination(Image<TPixel> source, Rectangle sourceRectangle) => this.CreateDestination(source, sourceRectangle);
/// <returns>The result returned from <see cref="M:CreateDestination"/>.</returns>
internal Image<TPixel> AotCreateDestination()
=> this.CreateDestination();
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
protected override Image<TPixel> CreateDestination()
{
Image<TPixel> source = this.Source;
Configuration configuration = this.Configuration;
// We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
x => new ImageFrame<TPixel>(
source.GetConfiguration(),
configuration,
this.Width,
this.Height,
x.Metadata.DeepClone()));
// Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.Metadata.DeepClone(), frames);
return new Image<TPixel>(configuration, source.Metadata.DeepClone(), frames);
}
/// <inheritdoc/>
protected override void BeforeImageApply(Image<TPixel> source, Image<TPixel> destination, Rectangle sourceRectangle)
protected override void BeforeImageApply(Image<TPixel> destination)
{
if (!(this.Sampler is NearestNeighborResampler))
{
Image<TPixel> source = this.Source;
Rectangle sourceRectangle = this.SourceRectangle;
// Since all image frame dimensions have to be the same we can calculate this for all frames.
MemoryAllocator memoryAllocator = source.GetMemoryAllocator();
this.horizontalKernelMap = ResizeKernelMap.Calculate(
@ -106,8 +112,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
Rectangle sourceRectangle = this.SourceRectangle;
Configuration configuration = this.Configuration;
// Handle resize dimensions identical to the original
if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.TargetRectangle)
{
@ -180,9 +189,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
}
protected override void AfterImageApply(Image<TPixel> source, Image<TPixel> destination, Rectangle sourceRectangle)
protected override void AfterImageApply(Image<TPixel> destination)
{
base.AfterImageApply(source, destination, sourceRectangle);
base.AfterImageApply(destination);
// TODO: An exception in the processing chain can leave these buffers undisposed. We should consider making image processors IDisposable!
this.horizontalKernelMap?.Dispose();

8
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
@ -47,9 +47,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public float Degrees { get; }
/// <inheritdoc />
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
public override IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
{
return new RotateProcessor<TPixel>(this);
return new RotateProcessor<TPixel>(this, source, sourceRectangle);
}
}
}
}

29
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs

@ -18,8 +18,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
internal class RotateProcessor<TPixel> : AffineTransformProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
public RotateProcessor(RotateProcessor definition)
: base(definition)
/// <summary>
/// Initializes a new instance of the <see cref="RotateProcessor{TPixel}"/> class.
/// </summary>
/// <param name="definition">The <see cref="RotateProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public RotateProcessor(RotateProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(definition, source, sourceRectangle)
{
this.Degrees = definition.Degrees;
}
@ -27,10 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private float Degrees { get; }
/// <inheritdoc/>
protected override void AfterImageApply(
Image<TPixel> source,
Image<TPixel> destination,
Rectangle sourceRectangle)
protected override void AfterImageApply(Image<TPixel> destination)
{
ExifProfile profile = destination.Metadata.ExifProfile;
if (profile is null)
@ -46,22 +49,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
profile.RemoveValue(ExifTag.Orientation);
base.AfterImageApply(source, destination, sourceRectangle);
base.AfterImageApply(destination);
}
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Rectangle sourceRectangle,
Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
if (this.OptimizedApply(source, destination, configuration))
if (this.OptimizedApply(source, destination, this.Configuration))
{
return;
}
base.OnFrameApply(source, destination, sourceRectangle, configuration);
base.OnFrameApply(source, destination);
}
/// <summary>
@ -222,4 +221,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
});
}
}
}
}

16
src/ImageSharp/Processing/Processors/Transforms/TransformProcessor.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
@ -13,8 +13,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
internal abstract class TransformProcessor<TPixel> : CloningImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="TransformProcessor{TPixel}"/> class.
/// </summary>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
protected TransformProcessor(Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
}
/// <inheritdoc/>
protected override void AfterImageApply(Image<TPixel> source, Image<TPixel> destination, Rectangle sourceRectangle)
protected override void AfterImageApply(Image<TPixel> destination)
=> TransformProcessorHelpers.UpdateDimensionalMetadata(destination);
}
}
}

2
tests/ImageSharp.Benchmarks/Samplers/GaussianBlur.cs

@ -1,4 +1,4 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;

176
tests/ImageSharp.Benchmarks/Samplers/Glow.cs

@ -1,176 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Numerics;
using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using SixLabors.ImageSharp.Processing.Processors.Overlays;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Benchmarks
{
using CoreSize = SixLabors.Primitives.Size;
public class Glow : BenchmarkBase
{
private GlowProcessor bulk;
private GlowProcessorParallel<Rgba32> parallel;
[GlobalSetup]
public void Setup()
{
this.bulk = new GlowProcessor(Color.Beige, 800 * .5f, GraphicsOptions.Default);
this.parallel = new GlowProcessorParallel<Rgba32>(Color.Beige) { Radius = 800 * .5f, };
}
[Benchmark(Description = "ImageSharp Glow - Bulk")]
public CoreSize GlowBulk()
{
using (var image = new Image<Rgba32>(800, 800))
{
this.bulk.Apply(image, image.Bounds());
return new CoreSize(image.Width, image.Height);
}
}
[Benchmark(Description = "ImageSharp Glow - Parallel")]
public CoreSize GLowSimple()
{
using (var image = new Image<Rgba32>(800, 800))
{
this.parallel.Apply(image, image.Bounds());
return new CoreSize(image.Width, image.Height);
}
}
internal class GlowProcessorParallel<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="GlowProcessorParallel{TPixel}" /> class.
/// </summary>
/// <param name="color">The color or the glow.</param>
public GlowProcessorParallel(TPixel color)
{
this.GlowColor = color;
}
/// <summary>
/// Gets or sets the glow color to apply.
/// </summary>
public TPixel GlowColor { get; set; }
/// <summary>
/// Gets or sets the the radius.
/// </summary>
public float Radius { get; set; }
/// <inheritdoc/>
protected override void OnFrameApply(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration)
{
int startY = sourceRectangle.Y;
int endY = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
TPixel glowColor = this.GlowColor;
Vector2 centre = Rectangle.Center(sourceRectangle);
float maxDistance = this.Radius > 0
? Math.Min(this.Radius, sourceRectangle.Width * .5F)
: sourceRectangle.Width * .5F;
// Align start/end positions.
int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}
if (minY > 0)
{
startY = 0;
}
int width = maxX - minX;
using (IMemoryOwner<TPixel> rowColors = Configuration.Default.MemoryAllocator.Allocate<TPixel>(width))
{
Buffer2D<TPixel> sourcePixels = source.PixelBuffer;
rowColors.GetSpan().Fill(glowColor);
var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);
ParallelHelper.IterateRows(
workingRect,
configuration,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
int offsetY = y - startY;
for (int x = minX; x < maxX; x++)
{
int offsetX = x - startX;
float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
TPixel packed = default;
packed.FromVector4(
PremultipliedLerp(
sourceColor,
glowColor.ToVector4(),
1 - (.95F * (distance / maxDistance))));
sourcePixels[offsetX, offsetY] = packed;
}
}
});
}
}
public static Vector4 PremultipliedLerp(Vector4 backdrop, Vector4 source, float amount)
{
amount = amount.Clamp(0, 1);
// Sanitize on zero alpha
if (Math.Abs(backdrop.W) < Constants.Epsilon)
{
source.W *= amount;
return source;
}
if (Math.Abs(source.W) < Constants.Epsilon)
{
return backdrop;
}
// Premultiply the source vector.
// Oddly premultiplying the background vector creates dark outlines when pixels
// Have low alpha values.
source = new Vector4(source.X, source.Y, source.Z, 1) * (source.W * amount);
// This should be implementing the following formula
// https://en.wikipedia.org/wiki/Alpha_compositing
// Vout = Vs + Vb (1 - Vsa)
// Aout = Vsa + Vsb (1 - Vsa)
var inverseW = new Vector3(1 - source.W);
var xyzB = new Vector3(backdrop.X, backdrop.Y, backdrop.Z);
var xyzS = new Vector3(source.X, source.Y, source.Z);
return new Vector4(xyzS + (xyzB * inverseW), source.W + (backdrop.W * (1 - source.W)));
}
}
}
}

23
tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs

@ -56,17 +56,20 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
}
// Make sure the kernel components are the same
var definition = new BokehBlurProcessor(10, BokehBlurProcessor.DefaultComponents, BokehBlurProcessor.DefaultGamma);
var processor = new BokehBlurProcessor<Rgb24>(definition);
Assert.Equal(components.Count, processor.Kernels.Count);
foreach ((Complex64[] a, Complex64[] b) in components.Zip(processor.Kernels, (a, b) => (a, b)))
using (var image = new Image<Rgb24>(1, 1))
{
Span<Complex64> spanA = a.AsSpan(), spanB = b.AsSpan();
Assert.Equal(spanA.Length, spanB.Length);
for (int i = 0; i < spanA.Length; i++)
var definition = new BokehBlurProcessor(10, BokehBlurProcessor.DefaultComponents, BokehBlurProcessor.DefaultGamma);
var processor = (BokehBlurProcessor<Rgb24>)definition.CreatePixelSpecificProcessor(image, image.Bounds());
Assert.Equal(components.Count, processor.Kernels.Count);
foreach ((Complex64[] a, Complex64[] b) in components.Zip(processor.Kernels, (a, b) => (a, b)))
{
Assert.True(Math.Abs(Math.Abs(spanA[i].Real) - Math.Abs(spanB[i].Real)) < 0.0001f);
Assert.True(Math.Abs(Math.Abs(spanA[i].Imaginary) - Math.Abs(spanB[i].Imaginary)) < 0.0001f);
Span<Complex64> spanA = a.AsSpan(), spanB = b.AsSpan();
Assert.Equal(spanA.Length, spanB.Length);
for (int i = 0; i < spanA.Length; i++)
{
Assert.True(Math.Abs(Math.Abs(spanA[i].Real) - Math.Abs(spanB[i].Real)) < 0.0001f);
Assert.True(Math.Abs(Math.Abs(spanA[i].Imaginary) - Math.Abs(spanB[i].Imaginary)) < 0.0001f);
}
}
}
}
@ -122,7 +125,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
{
provider.RunValidatingProcessorTest(
x => x.BokehBlur(value.Radius, value.Components, value.Gamma),
testOutputDetails: value.ToString(),
testOutputDetails: value.ToString(),
appendPixelTypeToFileName: false);
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save