diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 43a4a17084..16f8ebb065 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -15,59 +15,38 @@ jobs:
matrix:
options:
- os: ubuntu-latest
- framework: net6.0
- sdk: 6.0.x
+ framework: net7.0
+ sdk: 7.0.x
sdk-preview: true
runtime: -x64
codecov: false
- os: macos-latest
- framework: net6.0
- sdk: 6.0.x
+ framework: net7.0
+ sdk: 7.0.x
sdk-preview: true
runtime: -x64
codecov: false
- os: windows-latest
- framework: net6.0
- sdk: 6.0.x
+ framework: net7.0
+ sdk: 7.0.x
sdk-preview: true
runtime: -x64
codecov: false
- os: ubuntu-latest
- framework: net5.0
- runtime: -x64
- codecov: false
- - os: macos-latest
- framework: net5.0
- runtime: -x64
- codecov: false
- - os: windows-latest
- framework: net5.0
- runtime: -x64
- codecov: false
- - os: ubuntu-latest
- framework: netcoreapp3.1
+ framework: net6.0
+ sdk: 6.0.x
runtime: -x64
codecov: false
- os: macos-latest
- framework: netcoreapp3.1
- runtime: -x64
- codecov: false
- - os: windows-latest
- framework: netcoreapp3.1
- runtime: -x64
- codecov: false
- - os: windows-latest
- framework: netcoreapp2.1
+ framework: net6.0
+ sdk: 6.0.x
runtime: -x64
codecov: false
- os: windows-latest
- framework: net472
+ framework: net6.0
+ sdk: 6.0.x
runtime: -x64
codecov: false
- - os: windows-latest
- framework: net472
- runtime: -x86
- codecov: false
runs-on: ${{matrix.options.os}}
@@ -112,11 +91,10 @@ jobs:
- name: DotNet Setup
uses: actions/setup-dotnet@v1
with:
+ include-prerelease: true
dotnet-version: |
+ 7.0.x
6.0.x
- 5.0.x
- 3.1.x
- 2.1.x
- name: DotNet Build
if: ${{ matrix.options.sdk-preview != true }}
diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml
index 2b14f2a4b7..3f8a820313 100644
--- a/.github/workflows/code-coverage.yml
+++ b/.github/workflows/code-coverage.yml
@@ -10,7 +10,7 @@ jobs:
matrix:
options:
- os: ubuntu-latest
- framework: netcoreapp3.1
+ framework: net6.0
runtime: -x64
codecov: true
@@ -54,6 +54,12 @@ jobs:
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
restore-keys: ${{ runner.os }}-nuget-
+ - name: DotNet Setup
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: |
+ 6.0.x
+
- name: DotNet Build
shell: pwsh
run: ./ci-build.ps1 "${{matrix.options.framework}}"
diff --git a/ImageSharp.sln b/ImageSharp.sln
index 5428f3394d..fbf1ca24ba 100644
--- a/ImageSharp.sln
+++ b/ImageSharp.sln
@@ -654,43 +654,25 @@ Global
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
- Debug-InnerLoop|Any CPU = Debug-InnerLoop|Any CPU
Release|Any CPU = Release|Any CPU
- Release-InnerLoop|Any CPU = Release-InnerLoop|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU
- {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU
{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release|Any CPU.Build.0 = Release|Any CPU
- {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release-InnerLoop|Any CPU.ActiveCfg = Release-InnerLoop|Any CPU
- {2AA31A1F-142C-43F4-8687-09ABCA4B3A26}.Release-InnerLoop|Any CPU.Build.0 = Release-InnerLoop|Any CPU
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU
- {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release|Any CPU.Build.0 = Release|Any CPU
- {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release-InnerLoop|Any CPU.ActiveCfg = Release-InnerLoop|Any CPU
- {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Release-InnerLoop|Any CPU.Build.0 = Release-InnerLoop|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU
- {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|Any CPU.Build.0 = Release|Any CPU
- {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release-InnerLoop|Any CPU.ActiveCfg = Release-InnerLoop|Any CPU
- {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release-InnerLoop|Any CPU.Build.0 = Release-InnerLoop|Any CPU
{FC527290-2F22-432C-B77B-6E815726B02C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC527290-2F22-432C-B77B-6E815726B02C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|Any CPU.ActiveCfg = Debug-InnerLoop|Any CPU
- {FC527290-2F22-432C-B77B-6E815726B02C}.Debug-InnerLoop|Any CPU.Build.0 = Debug-InnerLoop|Any CPU
{FC527290-2F22-432C-B77B-6E815726B02C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC527290-2F22-432C-B77B-6E815726B02C}.Release|Any CPU.Build.0 = Release|Any CPU
- {FC527290-2F22-432C-B77B-6E815726B02C}.Release-InnerLoop|Any CPU.ActiveCfg = Release-InnerLoop|Any CPU
- {FC527290-2F22-432C-B77B-6E815726B02C}.Release-InnerLoop|Any CPU.Build.0 = Release-InnerLoop|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj
index 39c85c4f22..7e58607dd1 100644
--- a/src/ImageSharp/ImageSharp.csproj
+++ b/src/ImageSharp/ImageSharp.csproj
@@ -12,33 +12,23 @@
$(RepositoryUrl)
Image Resize Crop Gif Jpg Jpeg Bitmap Pbm Png Tga Tiff WebP NetCore
A new, fully featured, fully managed, cross-platform, 2D graphics API for .NET
- Debug;Release;Debug-InnerLoop;Release-InnerLoop
+ Debug;Release
-
- 2.0
+
+ 3.0
- net6.0;net5.0;netcoreapp3.1;netcoreapp2.1;netstandard2.1;netstandard2.0;net472
-
-
-
-
- net5.0;netcoreapp3.1;netcoreapp2.1;netstandard2.1;netstandard2.0;net472
-
-
-
-
- netcoreapp3.1
+ net7.0;net6.0
- netcoreapp3.1;netcoreapp2.1;netstandard2.1;netstandard2.0;net472
+ net6.0
@@ -47,17 +37,6 @@
-
-
-
-
-
-
-
-
-
-
-
True
diff --git a/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs
index f891ffa97c..a7f93e23ec 100644
--- a/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Convolution/BoxBlurExtensions.cs
@@ -39,5 +39,26 @@ namespace SixLabors.ImageSharp.Processing
/// The to allow chaining of operations.
public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle)
=> source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle);
+
+ ///
+ /// Applies a box blur to the image.
+ ///
+ /// The image this method extends.
+ /// The 'radius' value representing the size of the area to sample.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ ///
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ ///
+ ///
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ /// The to allow chaining of operations.
+ public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
+ {
+ var processor = new BoxBlurProcessor(radius, borderWrapModeX, borderWrapModeY);
+ return source.ApplyProcessor(processor, rectangle);
+ }
}
}
diff --git a/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs
index bd4fb716d4..49af591e98 100644
--- a/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Convolution/GaussianBlurExtensions.cs
@@ -39,5 +39,26 @@ namespace SixLabors.ImageSharp.Processing
/// The to allow chaining of operations.
public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle)
=> source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle);
+
+ ///
+ /// Applies a Gaussian blur to the image.
+ ///
+ /// The image this method extends.
+ /// The 'sigma' value representing the weight of the blur.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ ///
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ ///
+ ///
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ /// The to allow chaining of operations.
+ public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
+ {
+ var processor = new GaussianBlurProcessor(sigma, borderWrapModeX, borderWrapModeY);
+ return source.ApplyProcessor(processor, rectangle);
+ }
}
}
diff --git a/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs b/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs
index f5b8798f46..5ac9d6909c 100644
--- a/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs
+++ b/src/ImageSharp/Processing/Extensions/Convolution/GaussianSharpenExtensions.cs
@@ -42,5 +42,26 @@ namespace SixLabors.ImageSharp.Processing
float sigma,
Rectangle rectangle) =>
source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle);
+
+ ///
+ /// Applies a Gaussian sharpening filter to the image.
+ ///
+ /// The image this method extends.
+ /// The 'sigma' value representing the weight of the blur.
+ ///
+ /// The structure that specifies the portion of the image object to alter.
+ ///
+ ///
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ ///
+ ///
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ /// The to allow chaining of operations.
+ public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma, Rectangle rectangle, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
+ {
+ var processor = new GaussianSharpenProcessor(sigma, borderWrapModeX, borderWrapModeY);
+ return source.ApplyProcessor(processor, rectangle);
+ }
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/BorderWrappingMode.cs b/src/ImageSharp/Processing/Processors/Convolution/BorderWrappingMode.cs
new file mode 100644
index 0000000000..e835fc748c
--- /dev/null
+++ b/src/ImageSharp/Processing/Processors/Convolution/BorderWrappingMode.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Processing.Processors.Convolution
+{
+ ///
+ /// Wrapping mode for the border pixels in convolution processing.
+ ///
+ public enum BorderWrappingMode : byte
+ {
+ /// Repeat the border pixel value: aaaaaa|abcdefgh|hhhhhhh
+ Repeat = 0,
+
+ /// Take values from the opposite edge: cdefgh|abcdefgh|abcdefg
+ Wrap = 1,
+
+ /// Mirror the last few border values: fedcba|abcdefgh|hgfedcb
+ /// This Mode is similar to , but here the very border pixel is repeated.
+ Mirror = 2,
+
+ /// Bounce off the border: fedcb|abcdefgh|gfedcb
+ /// This Mode is similar to , but here the very border pixel is not repeated.
+ Bounce = 3
+ }
+}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs
index da6b967181..a622739fd4 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs
@@ -21,9 +21,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
/// The 'radius' value representing the size of the area to sample.
///
- public BoxBlurProcessor(int radius)
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ public BoxBlurProcessor(int radius, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
{
this.Radius = radius;
+ this.BorderWrapModeX = borderWrapModeX;
+ this.BorderWrapModeY = borderWrapModeY;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The 'radius' value representing the size of the area to sample.
+ ///
+ public BoxBlurProcessor(int radius)
+ : this(radius, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat)
+ {
}
///
@@ -39,9 +54,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
public int Radius { get; }
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in X direction.
+ ///
+ public BorderWrappingMode BorderWrapModeX { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public BorderWrappingMode BorderWrapModeY { get; }
+
///
public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
where TPixel : unmanaged, IPixel
- => new BoxBlurProcessor(configuration, this, source, sourceRectangle);
+ => new BoxBlurProcessor(configuration, this, source, sourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs
index 5beadb0cee..ceebdf15aa 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs
@@ -27,15 +27,49 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
this.Kernel = CreateBoxKernel(kernelSize);
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The configuration which allows altering default behaviour or extending the library.
+ /// The defining the processor parameters.
+ /// The source for the current processor instance.
+ /// The source area to process for the current processor instance.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ public BoxBlurProcessor(
+ Configuration configuration,
+ BoxBlurProcessor definition,
+ Image source,
+ Rectangle sourceRectangle,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
+ : base(configuration, source, sourceRectangle)
+ {
+ int kernelSize = (definition.Radius * 2) + 1;
+ this.Kernel = CreateBoxKernel(kernelSize);
+ this.BorderWrapModeX = borderWrapModeX;
+ this.BorderWrapModeY = borderWrapModeY;
+ }
+
///
/// Gets the 1D convolution kernel.
///
public float[] Kernel { get; }
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in X direction.
+ ///
+ public BorderWrappingMode BorderWrapModeX { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public BorderWrappingMode BorderWrapModeY { get; }
+
///
protected override void OnFrameApply(ImageFrame source)
{
- using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle);
+ using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
processor.Apply(source);
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
index fa58422dc6..2fc0a5fe87 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
@@ -26,16 +26,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// Whether the convolution filter is applied to alpha as well as the color channels.
/// The source for the current processor instance.
/// The source area to process for the current processor instance.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
public Convolution2PassProcessor(
Configuration configuration,
float[] kernel,
bool preserveAlpha,
Image source,
- Rectangle sourceRectangle)
+ Rectangle sourceRectangle,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
: base(configuration, source, sourceRectangle)
{
this.Kernel = kernel;
this.PreserveAlpha = preserveAlpha;
+ this.BorderWrapModeX = borderWrapModeX;
+ this.BorderWrapModeY = borderWrapModeY;
}
///
@@ -48,6 +54,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
public bool PreserveAlpha { get; }
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in X direction.
+ ///
+ public BorderWrappingMode BorderWrapModeX { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public BorderWrappingMode BorderWrapModeY { get; }
+
///
protected override void OnFrameApply(ImageFrame source)
{
@@ -63,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
// the two 1D kernels represent, and reuse it across both convolution steps, like in the bokeh blur.
using var mapXY = new KernelSamplingMap(this.Configuration.MemoryAllocator);
- mapXY.BuildSamplingOffsetMap(this.Kernel.Length, this.Kernel.Length, interest);
+ mapXY.BuildSamplingOffsetMap(this.Kernel.Length, this.Kernel.Length, interest, this.BorderWrapModeX, this.BorderWrapModeY);
// Horizontal convolution
var horizontalOperation = new HorizontalConvolutionRowOperation(
diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs
index 1fa65b62cd..3af9791dcb 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs
@@ -32,6 +32,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The 'sigma' value representing the weight of the blur.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ public GaussianBlurProcessor(float sigma, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
+ : this(sigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(sigma), borderWrapModeX, borderWrapModeY)
+ {
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -54,9 +65,32 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// This should be at least twice the sigma value.
///
public GaussianBlurProcessor(float sigma, int radius)
+ : this(sigma, radius, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The 'sigma' value representing the weight of the blur.
+ ///
+ ///
+ /// The 'radius' value representing the size of the area to sample.
+ /// This should be at least twice the sigma value.
+ ///
+ ///
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ ///
+ ///
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public GaussianBlurProcessor(float sigma, int radius, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
{
this.Sigma = sigma;
this.Radius = radius;
+ this.BorderWrapModeX = borderWrapModeX;
+ this.BorderWrapModeY = borderWrapModeY;
}
///
@@ -69,9 +103,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
public int Radius { get; }
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in X direction.
+ ///
+ public BorderWrappingMode BorderWrapModeX { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public BorderWrappingMode BorderWrapModeY { get; }
+
///
public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
where TPixel : unmanaged, IPixel
- => new GaussianBlurProcessor(configuration, this, source, sourceRectangle);
+ => new GaussianBlurProcessor(configuration, this, source, sourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
index 4ade01f914..16b05b8bb0 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs
@@ -30,15 +30,49 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
this.Kernel = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma);
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The configuration which allows altering default behaviour or extending the library.
+ /// The defining the processor parameters.
+ /// The source for the current processor instance.
+ /// The source area to process for the current processor instance.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ public GaussianBlurProcessor(
+ Configuration configuration,
+ GaussianBlurProcessor definition,
+ Image source,
+ Rectangle sourceRectangle,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
+ : base(configuration, source, sourceRectangle)
+ {
+ int kernelSize = (definition.Radius * 2) + 1;
+ this.Kernel = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma);
+ this.BorderWrapModeX = borderWrapModeX;
+ this.BorderWrapModeY = borderWrapModeY;
+ }
+
///
/// Gets the 1D convolution kernel.
///
public float[] Kernel { get; }
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in X direction.
+ ///
+ public BorderWrappingMode BorderWrapModeX { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public BorderWrappingMode BorderWrapModeY { get; }
+
///
protected override void OnFrameApply(ImageFrame source)
{
- using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle);
+ using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
processor.Apply(source);
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs
index 7e1f029066..98c897c21e 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs
@@ -32,6 +32,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The 'sigma' value representing the weight of the blur.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ public GaussianSharpenProcessor(float sigma, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
+ : this(sigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(sigma), borderWrapModeX, borderWrapModeY)
+ {
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -54,9 +65,32 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// This should be at least twice the sigma value.
///
public GaussianSharpenProcessor(float sigma, int radius)
+ : this(sigma, radius, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The 'sigma' value representing the weight of the blur.
+ ///
+ ///
+ /// The 'radius' value representing the size of the area to sample.
+ /// This should be at least twice the sigma value.
+ ///
+ ///
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ ///
+ ///
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public GaussianSharpenProcessor(float sigma, int radius, BorderWrappingMode borderWrapModeX, BorderWrappingMode borderWrapModeY)
{
this.Sigma = sigma;
this.Radius = radius;
+ this.BorderWrapModeX = borderWrapModeX;
+ this.BorderWrapModeY = borderWrapModeY;
}
///
@@ -69,9 +103,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
public int Radius { get; }
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in X direction.
+ ///
+ public BorderWrappingMode BorderWrapModeX { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public BorderWrappingMode BorderWrapModeY { get; }
+
///
public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
where TPixel : unmanaged, IPixel
- => new GaussianSharpenProcessor(configuration, this, source, sourceRectangle);
+ => new GaussianSharpenProcessor(configuration, this, source, sourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
index 73aaaec188..bddaab233b 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs
@@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// Initializes a new instance of the class.
///
/// The configuration which allows altering default behaviour or extending the library.
- /// The defining the processor parameters.
+ /// The defining the processor parameters.
/// The source for the current processor instance.
/// The source area to process for the current processor instance.
public GaussianSharpenProcessor(
@@ -24,10 +24,32 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
GaussianSharpenProcessor definition,
Image source,
Rectangle sourceRectangle)
+ : this(configuration, definition, source, sourceRectangle, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The configuration which allows altering default behaviour or extending the library.
+ /// The defining the processor parameters.
+ /// The source for the current processor instance.
+ /// The source area to process for the current processor instance.
+ /// The to use when mapping the pixels outside of the border, in X direction.
+ /// The to use when mapping the pixels outside of the border, in Y direction.
+ public GaussianSharpenProcessor(
+ Configuration configuration,
+ GaussianSharpenProcessor definition,
+ Image source,
+ Rectangle sourceRectangle,
+ BorderWrappingMode borderWrapModeX,
+ BorderWrappingMode borderWrapModeY)
: base(configuration, source, sourceRectangle)
{
int kernelSize = (definition.Radius * 2) + 1;
this.Kernel = ConvolutionProcessorHelpers.CreateGaussianSharpenKernel(kernelSize, definition.Sigma);
+ this.BorderWrapModeX = borderWrapModeX;
+ this.BorderWrapModeY = borderWrapModeY;
}
///
@@ -35,10 +57,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
///
public float[] Kernel { get; }
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in X direction.
+ ///
+ public BorderWrappingMode BorderWrapModeX { get; }
+
+ ///
+ /// Gets the to use when mapping the pixels outside of the border, in Y direction.
+ ///
+ public BorderWrappingMode BorderWrapModeY { get; }
+
///
protected override void OnFrameApply(ImageFrame source)
{
- using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle);
+ using var processor = new Convolution2PassProcessor(this.Configuration, this.Kernel, false, this.Source, this.SourceRectangle, this.BorderWrapModeX, this.BorderWrapModeY);
processor.Apply(source);
}
diff --git a/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs b/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs
index 904b599f7c..98a4ca3574 100644
--- a/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs
+++ b/src/ImageSharp/Processing/Processors/Convolution/KernelSamplingMap.cs
@@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// The convolution kernel.
/// The source bounds.
public void BuildSamplingOffsetMap(DenseMatrix kernel, Rectangle bounds)
- => this.BuildSamplingOffsetMap(kernel.Rows, kernel.Columns, bounds);
+ => this.BuildSamplingOffsetMap(kernel.Rows, kernel.Columns, bounds, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat);
///
/// Builds a map of the sampling offsets for the kernel clamped by the given bounds.
@@ -40,6 +40,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// The width (number of columns) of the convolution kernel to use.
/// The source bounds.
public void BuildSamplingOffsetMap(int kernelHeight, int kernelWidth, Rectangle bounds)
+ => this.BuildSamplingOffsetMap(kernelHeight, kernelWidth, bounds, BorderWrappingMode.Repeat, BorderWrappingMode.Repeat);
+
+ ///
+ /// Builds a map of the sampling offsets for the kernel clamped by the given bounds.
+ ///
+ /// The height (number of rows) of the convolution kernel to use.
+ /// The width (number of columns) of the convolution kernel to use.
+ /// The source bounds.
+ /// The wrapping mode on the horizontal borders.
+ /// The wrapping mode on the vertical borders.
+ public void BuildSamplingOffsetMap(int kernelHeight, int kernelWidth, Rectangle bounds, BorderWrappingMode xBorderMode, BorderWrappingMode yBorderMode)
{
this.yOffsets = this.allocator.Allocate(bounds.Height * kernelHeight);
this.xOffsets = this.allocator.Allocate(bounds.Width * kernelWidth);
@@ -49,43 +60,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
int minX = bounds.X;
int maxX = bounds.Right - 1;
- int radiusY = kernelHeight >> 1;
- int radiusX = kernelWidth >> 1;
-
- // Calculate the y and x sampling offsets clamped to the given rectangle.
- // While this isn't a hotpath we still dip into unsafe to avoid the span bounds
- // checks as the can potentially be looping over large arrays.
- Span ySpan = this.yOffsets.GetSpan();
- ref int ySpanBase = ref MemoryMarshal.GetReference(ySpan);
- for (int row = 0; row < bounds.Height; row++)
- {
- int rowBase = row * kernelHeight;
- for (int y = 0; y < kernelHeight; y++)
- {
- Unsafe.Add(ref ySpanBase, rowBase + y) = row + y + minY - radiusY;
- }
- }
-
- if (kernelHeight > 1)
- {
- Numerics.Clamp(ySpan, minY, maxY);
- }
-
- Span xSpan = this.xOffsets.GetSpan();
- ref int xSpanBase = ref MemoryMarshal.GetReference(xSpan);
- for (int column = 0; column < bounds.Width; column++)
- {
- int columnBase = column * kernelWidth;
- for (int x = 0; x < kernelWidth; x++)
- {
- Unsafe.Add(ref xSpanBase, columnBase + x) = column + x + minX - radiusX;
- }
- }
-
- if (kernelWidth > 1)
- {
- Numerics.Clamp(xSpan, minX, maxX);
- }
+ this.BuildOffsets(this.yOffsets, bounds.Height, kernelHeight, minY, maxY, yBorderMode);
+ this.BuildOffsets(this.xOffsets, bounds.Width, kernelWidth, minX, maxX, xBorderMode);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -105,5 +81,105 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
this.isDisposed = true;
}
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void BuildOffsets(IMemoryOwner offsets, int boundsSize, int kernelSize, int min, int max, BorderWrappingMode borderMode)
+ {
+ int radius = kernelSize >> 1;
+ Span span = offsets.GetSpan();
+ ref int spanBase = ref MemoryMarshal.GetReference(span);
+ for (int chunk = 0; chunk < boundsSize; chunk++)
+ {
+ int chunkBase = chunk * kernelSize;
+ for (int i = 0; i < kernelSize; i++)
+ {
+ Unsafe.Add(ref spanBase, chunkBase + i) = chunk + i + min - radius;
+ }
+ }
+
+ this.CorrectBorder(span, kernelSize, min, max, borderMode);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void CorrectBorder(Span span, int kernelSize, int min, int max, BorderWrappingMode borderMode)
+ {
+ var affectedSize = (kernelSize >> 1) * kernelSize;
+ ref int spanBase = ref MemoryMarshal.GetReference(span);
+ if (affectedSize > 0)
+ {
+ switch (borderMode)
+ {
+ case BorderWrappingMode.Repeat:
+ Numerics.Clamp(span.Slice(0, affectedSize), min, max);
+ Numerics.Clamp(span.Slice(span.Length - affectedSize), min, max);
+ break;
+ case BorderWrappingMode.Mirror:
+ var min2dec = min + min - 1;
+ for (int i = 0; i < affectedSize; i++)
+ {
+ var value = span[i];
+ if (value < min)
+ {
+ span[i] = min2dec - value;
+ }
+ }
+
+ var max2inc = max + max + 1;
+ for (int i = span.Length - affectedSize; i < span.Length; i++)
+ {
+ var value = span[i];
+ if (value > max)
+ {
+ span[i] = max2inc - value;
+ }
+ }
+
+ break;
+ case BorderWrappingMode.Bounce:
+ var min2 = min + min;
+ for (int i = 0; i < affectedSize; i++)
+ {
+ var value = span[i];
+ if (value < min)
+ {
+ span[i] = min2 - value;
+ }
+ }
+
+ var max2 = max + max;
+ for (int i = span.Length - affectedSize; i < span.Length; i++)
+ {
+ var value = span[i];
+ if (value > max)
+ {
+ span[i] = max2 - value;
+ }
+ }
+
+ break;
+ case BorderWrappingMode.Wrap:
+ var diff = max - min + 1;
+ for (int i = 0; i < affectedSize; i++)
+ {
+ var value = span[i];
+ if (value < min)
+ {
+ span[i] = diff + value;
+ }
+ }
+
+ for (int i = span.Length - affectedSize; i < span.Length; i++)
+ {
+ var value = span[i];
+ if (value > max)
+ {
+ span[i] = value - diff;
+ }
+ }
+
+ break;
+ }
+ }
+ }
}
}
diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
index 9a92741997..24f618d11b 100644
--- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
+++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj
@@ -9,7 +9,7 @@
portable
false
- Debug;Release;Debug-InnerLoop;Release-InnerLoop
+ Debug;Release
@@ -17,17 +17,12 @@
- net6.0;net5.0;netcoreapp3.1;netcoreapp2.1;net472
-
-
-
-
- netcoreapp3.1
+ net7.0;net6.0
- net5.0;netcoreapp3.1;netcoreapp2.1;net472
+ net6.0
diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj
index 6ff5a4cc7f..492ce36b81 100644
--- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj
+++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj
@@ -12,24 +12,19 @@
false
false
- Debug;Release;Debug-InnerLoop;Release-InnerLoop
+ Debug;Release
false
- net6.0;net5.0;netcoreapp3.1;netcoreapp2.1;net472
-
-
-
-
- netcoreapp3.1
+ net7.0;net6.0
- net5.0;netcoreapp3.1;netcoreapp2.1;net472
+ net6.0
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs
index 9c467a1cc9..a40ae2af50 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs
@@ -149,11 +149,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// 1. AllowAll - call avx/fma implementation
// 2. DisableFMA - call avx without fma implementation
// 3. DisableAvx - call sse implementation
- // 4. DisableSIMD - call Vector4 fallback implementation
+ // 4. DisableHWIntrinsic - call Vector4 fallback implementation
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
seed,
- HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSIMD);
+ HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
}
// Forward transform
@@ -200,11 +200,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// 1. AllowAll - call avx/fma implementation
// 2. DisableFMA - call avx without fma implementation
// 3. DisableAvx - call Vector4 implementation
- // 4. DisableSIMD - call scalar fallback implementation
+ // 4. DisableHWIntrinsic - call scalar fallback implementation
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
seed,
- HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSIMD);
+ HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic);
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs
index 11e3fbb230..90fa5777b5 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs
@@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
- HwIntrinsics.DisableSIMD);
+ HwIntrinsics.DisableHWIntrinsic);
}
[Fact]
@@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
- HwIntrinsics.DisableSIMD);
+ HwIntrinsics.DisableHWIntrinsic);
}
[Fact]
@@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
- HwIntrinsics.DisableSIMD);
+ HwIntrinsics.DisableHWIntrinsic);
}
[Fact]
@@ -179,7 +179,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
- HwIntrinsics.DisableSIMD);
+ HwIntrinsics.DisableHWIntrinsic);
}
[Fact]
diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
index 28c778787a..a4f1de17be 100644
--- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
+++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
@@ -6,23 +6,18 @@
SixLabors.ImageSharp.Tests
AnyCPU;x64;x86
SixLabors.ImageSharp.Tests
- Debug;Release;Debug-InnerLoop;Release-InnerLoop
+ Debug;Release
- net6.0;net5.0;netcoreapp3.1;netcoreapp2.1;net472
-
-
-
-
- netcoreapp3.1
+ net7.0;net6.0
- net5.0;netcoreapp3.1;netcoreapp2.1;net472
+ net6.0
@@ -47,7 +42,6 @@
-
diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs
index f58136f738..5b6938df11 100644
--- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs
+++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.SwapOrCopyContent.cs
@@ -13,13 +13,13 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
public class SwapOrCopyContent
{
- private readonly TestMemoryAllocator MemoryAllocator = new TestMemoryAllocator();
+ private readonly TestMemoryAllocator memoryAllocator = new TestMemoryAllocator();
[Fact]
public void SwapOrCopyContent_WhenBothAllocated()
{
- using (Buffer2D a = this.MemoryAllocator.Allocate2D(10, 5, AllocationOptions.Clean))
- using (Buffer2D b = this.MemoryAllocator.Allocate2D(3, 7, AllocationOptions.Clean))
+ using (Buffer2D a = this.memoryAllocator.Allocate2D(10, 5, AllocationOptions.Clean))
+ using (Buffer2D b = this.memoryAllocator.Allocate2D(3, 7, AllocationOptions.Clean))
{
a[1, 3] = 666;
b[1, 3] = 444;
@@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
using var destData = MemoryGroup.Wrap(new int[100]);
using var dest = new Buffer2D(destData, 10, 10);
- using (Buffer2D source = this.MemoryAllocator.Allocate2D(10, 10, AllocationOptions.Clean))
+ using (Buffer2D source = this.memoryAllocator.Allocate2D(10, 10, AllocationOptions.Clean))
{
source[0, 0] = 1;
dest[0, 0] = 2;
@@ -68,9 +68,9 @@ namespace SixLabors.ImageSharp.Tests.Memory
[Fact]
public void WhenBothAreMemoryOwners_ShouldSwap()
{
- this.MemoryAllocator.BufferCapacityInBytes = sizeof(int) * 50;
- using Buffer2D a = this.MemoryAllocator.Allocate2D(48, 2);
- using Buffer2D b = this.MemoryAllocator.Allocate2D(50, 2);
+ this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * 50;
+ using Buffer2D a = this.memoryAllocator.Allocate2D(48, 2);
+ using Buffer2D b = this.memoryAllocator.Allocate2D(50, 2);
Memory a0 = a.FastMemoryGroup[0];
Memory a1 = a.FastMemoryGroup[1];
@@ -90,8 +90,8 @@ namespace SixLabors.ImageSharp.Tests.Memory
[Fact]
public void WhenBothAreMemoryOwners_ShouldReplaceViews()
{
- using Buffer2D a = this.MemoryAllocator.Allocate2D(100, 1);
- using Buffer2D b = this.MemoryAllocator.Allocate2D(100, 2);
+ using Buffer2D a = this.memoryAllocator.Allocate2D(100, 1);
+ using Buffer2D b = this.memoryAllocator.Allocate2D(100, 2);
a.FastMemoryGroup[0].Span[42] = 1;
b.FastMemoryGroup[0].Span[33] = 2;
@@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
using var destOwner = new TestMemoryManager(data);
using var dest = new Buffer2D(MemoryGroup.Wrap(destOwner.Memory), 21, 1);
- using Buffer2D source = this.MemoryAllocator.Allocate2D(21, 1);
+ using Buffer2D source = this.memoryAllocator.Allocate2D(21, 1);
source.FastMemoryGroup[0].Span[10] = color;
@@ -145,7 +145,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
using var destOwner = new TestMemoryManager(data);
using var dest = new Buffer2D(MemoryGroup.Wrap(destOwner.Memory), 21, 1);
- using Buffer2D source = this.MemoryAllocator.Allocate2D(22, 1);
+ using Buffer2D source = this.memoryAllocator.Allocate2D(22, 1);
source.FastMemoryGroup[0].Span[10] = color;
diff --git a/tests/ImageSharp.Tests/Processing/Convolution/KernelSamplingMapTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/KernelSamplingMapTest.cs
new file mode 100644
index 0000000000..64e1ea2cbc
--- /dev/null
+++ b/tests/ImageSharp.Tests/Processing/Convolution/KernelSamplingMapTest.cs
@@ -0,0 +1,421 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Processing.Processors.Convolution;
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Processing.Convolution
+{
+ [Trait("Category", "Processors")]
+ public class KernelSamplingMapTest
+ {
+ [Fact]
+ public void KernalSamplingMap_Kernel5Image7x7RepeatBorder()
+ {
+ var kernelSize = new Size(5, 5);
+ var bounds = new Rectangle(0, 0, 7, 7);
+ var mode = BorderWrappingMode.Repeat;
+ int[] expected =
+ {
+ 0, 0, 0, 1, 2,
+ 0, 0, 1, 2, 3,
+ 0, 1, 2, 3, 4,
+ 1, 2, 3, 4, 5,
+ 2, 3, 4, 5, 6,
+ 3, 4, 5, 6, 6,
+ 4, 5, 6, 6, 6,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel5Image7x7BounceBorder()
+ {
+ var kernelSize = new Size(5, 5);
+ var bounds = new Rectangle(0, 0, 7, 7);
+ var mode = BorderWrappingMode.Bounce;
+ int[] expected =
+ {
+ 2, 1, 0, 1, 2,
+ 1, 0, 1, 2, 3,
+ 0, 1, 2, 3, 4,
+ 1, 2, 3, 4, 5,
+ 2, 3, 4, 5, 6,
+ 3, 4, 5, 6, 5,
+ 4, 5, 6, 5, 4,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel5Image7x7MirrorBorder()
+ {
+ var kernelSize = new Size(5, 5);
+ var bounds = new Rectangle(0, 0, 7, 7);
+ var mode = BorderWrappingMode.Mirror;
+ int[] expected =
+ {
+ 1, 0, 0, 1, 2,
+ 0, 0, 1, 2, 3,
+ 0, 1, 2, 3, 4,
+ 1, 2, 3, 4, 5,
+ 2, 3, 4, 5, 6,
+ 3, 4, 5, 6, 6,
+ 4, 5, 6, 6, 5,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel5Image7x7WrapBorder()
+ {
+ var kernelSize = new Size(5, 5);
+ var bounds = new Rectangle(0, 0, 7, 7);
+ var mode = BorderWrappingMode.Wrap;
+ int[] expected =
+ {
+ 5, 6, 0, 1, 2,
+ 6, 0, 1, 2, 3,
+ 0, 1, 2, 3, 4,
+ 1, 2, 3, 4, 5,
+ 2, 3, 4, 5, 6,
+ 3, 4, 5, 6, 0,
+ 4, 5, 6, 0, 1,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel5Image9x9BounceBorder()
+ {
+ var kernelSize = new Size(5, 5);
+ var bounds = new Rectangle(1, 1, 9, 9);
+ var mode = BorderWrappingMode.Bounce;
+ int[] expected =
+ {
+ 3, 2, 1, 2, 3,
+ 2, 1, 2, 3, 4,
+ 1, 2, 3, 4, 5,
+ 2, 3, 4, 5, 6,
+ 3, 4, 5, 6, 7,
+ 4, 5, 6, 7, 8,
+ 5, 6, 7, 8, 9,
+ 6, 7, 8, 9, 8,
+ 7, 8, 9, 8, 7,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel5Image9x9MirrorBorder()
+ {
+ var kernelSize = new Size(5, 5);
+ var bounds = new Rectangle(1, 1, 9, 9);
+ var mode = BorderWrappingMode.Mirror;
+ int[] expected =
+ {
+ 2, 1, 1, 2, 3,
+ 1, 1, 2, 3, 4,
+ 1, 2, 3, 4, 5,
+ 2, 3, 4, 5, 6,
+ 3, 4, 5, 6, 7,
+ 4, 5, 6, 7, 8,
+ 5, 6, 7, 8, 9,
+ 6, 7, 8, 9, 9,
+ 7, 8, 9, 9, 8,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel5Image9x9WrapBorder()
+ {
+ var kernelSize = new Size(5, 5);
+ var bounds = new Rectangle(1, 1, 9, 9);
+ var mode = BorderWrappingMode.Wrap;
+ int[] expected =
+ {
+ 8, 9, 1, 2, 3,
+ 9, 1, 2, 3, 4,
+ 1, 2, 3, 4, 5,
+ 2, 3, 4, 5, 6,
+ 3, 4, 5, 6, 7,
+ 4, 5, 6, 7, 8,
+ 5, 6, 7, 8, 9,
+ 6, 7, 8, 9, 1,
+ 7, 8, 9, 1, 2,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel5Image7x7RepeatBorderTile()
+ {
+ var kernelSize = new Size(5, 5);
+ var bounds = new Rectangle(2, 2, 7, 7);
+ var mode = BorderWrappingMode.Repeat;
+ int[] expected =
+ {
+ 2, 2, 2, 3, 4,
+ 2, 2, 3, 4, 5,
+ 2, 3, 4, 5, 6,
+ 3, 4, 5, 6, 7,
+ 4, 5, 6, 7, 8,
+ 5, 6, 7, 8, 8,
+ 6, 7, 8, 8, 8,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel5Image7x7BounceBorderTile()
+ {
+ var kernelSize = new Size(5, 5);
+ var bounds = new Rectangle(2, 2, 7, 7);
+ var mode = BorderWrappingMode.Bounce;
+ int[] expected =
+ {
+ 4, 3, 2, 3, 4,
+ 3, 2, 3, 4, 5,
+ 2, 3, 4, 5, 6,
+ 3, 4, 5, 6, 7,
+ 4, 5, 6, 7, 8,
+ 5, 6, 7, 8, 7,
+ 6, 7, 8, 7, 6,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel5Image7x7MirrorBorderTile()
+ {
+ var kernelSize = new Size(5, 5);
+ var bounds = new Rectangle(2, 2, 7, 7);
+ var mode = BorderWrappingMode.Mirror;
+ int[] expected =
+ {
+ 3, 2, 2, 3, 4,
+ 2, 2, 3, 4, 5,
+ 2, 3, 4, 5, 6,
+ 3, 4, 5, 6, 7,
+ 4, 5, 6, 7, 8,
+ 5, 6, 7, 8, 8,
+ 6, 7, 8, 8, 7,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel5Image7x7WrapBorderTile()
+ {
+ var kernelSize = new Size(5, 5);
+ var bounds = new Rectangle(2, 2, 7, 7);
+ var mode = BorderWrappingMode.Wrap;
+ int[] expected =
+ {
+ 7, 8, 2, 3, 4,
+ 8, 2, 3, 4, 5,
+ 2, 3, 4, 5, 6,
+ 3, 4, 5, 6, 7,
+ 4, 5, 6, 7, 8,
+ 5, 6, 7, 8, 2,
+ 6, 7, 8, 2, 3,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel3Image7x7RepeatBorder()
+ {
+ var kernelSize = new Size(3, 3);
+ var bounds = new Rectangle(0, 0, 7, 7);
+ var mode = BorderWrappingMode.Repeat;
+ int[] expected =
+ {
+ 0, 0, 1,
+ 0, 1, 2,
+ 1, 2, 3,
+ 2, 3, 4,
+ 3, 4, 5,
+ 4, 5, 6,
+ 5, 6, 6,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel3Image7x7BounceBorder()
+ {
+ var kernelSize = new Size(3, 3);
+ var bounds = new Rectangle(0, 0, 7, 7);
+ var mode = BorderWrappingMode.Bounce;
+ int[] expected =
+ {
+ 1, 0, 1,
+ 0, 1, 2,
+ 1, 2, 3,
+ 2, 3, 4,
+ 3, 4, 5,
+ 4, 5, 6,
+ 5, 6, 5,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel3Image7x7MirrorBorder()
+ {
+ var kernelSize = new Size(3, 3);
+ var bounds = new Rectangle(0, 0, 7, 7);
+ var mode = BorderWrappingMode.Mirror;
+ int[] expected =
+ {
+ 0, 0, 1,
+ 0, 1, 2,
+ 1, 2, 3,
+ 2, 3, 4,
+ 3, 4, 5,
+ 4, 5, 6,
+ 5, 6, 6,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel3Image7x7WrapBorder()
+ {
+ var kernelSize = new Size(3, 3);
+ var bounds = new Rectangle(0, 0, 7, 7);
+ var mode = BorderWrappingMode.Wrap;
+ int[] expected =
+ {
+ 6, 0, 1,
+ 0, 1, 2,
+ 1, 2, 3,
+ 2, 3, 4,
+ 3, 4, 5,
+ 4, 5, 6,
+ 5, 6, 0,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel3Image7x7RepeatBorderTile()
+ {
+ var kernelSize = new Size(3, 3);
+ var bounds = new Rectangle(2, 2, 7, 7);
+ var mode = BorderWrappingMode.Repeat;
+ int[] expected =
+ {
+ 2, 2, 3,
+ 2, 3, 4,
+ 3, 4, 5,
+ 4, 5, 6,
+ 5, 6, 7,
+ 6, 7, 8,
+ 7, 8, 8,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel3Image7BounceBorderTile()
+ {
+ var kernelSize = new Size(3, 3);
+ var bounds = new Rectangle(2, 2, 7, 7);
+ var mode = BorderWrappingMode.Bounce;
+ int[] expected =
+ {
+ 3, 2, 3,
+ 2, 3, 4,
+ 3, 4, 5,
+ 4, 5, 6,
+ 5, 6, 7,
+ 6, 7, 8,
+ 7, 8, 7,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel3Image7MirrorBorderTile()
+ {
+ var kernelSize = new Size(3, 3);
+ var bounds = new Rectangle(2, 2, 7, 7);
+ var mode = BorderWrappingMode.Mirror;
+ int[] expected =
+ {
+ 2, 2, 3,
+ 2, 3, 4,
+ 3, 4, 5,
+ 4, 5, 6,
+ 5, 6, 7,
+ 6, 7, 8,
+ 7, 8, 8,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel3Image7x7WrapBorderTile()
+ {
+ var kernelSize = new Size(3, 3);
+ var bounds = new Rectangle(2, 2, 7, 7);
+ var mode = BorderWrappingMode.Wrap;
+ int[] expected =
+ {
+ 8, 2, 3,
+ 2, 3, 4,
+ 3, 4, 5,
+ 4, 5, 6,
+ 5, 6, 7,
+ 6, 7, 8,
+ 7, 8, 2,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, expected, expected);
+ }
+
+ [Fact]
+ public void KernalSamplingMap_Kernel3Image7x5WrapBorderTile()
+ {
+ var kernelSize = new Size(3, 3);
+ var bounds = new Rectangle(2, 2, 7, 5);
+ var mode = BorderWrappingMode.Wrap;
+ int[] xExpected =
+ {
+ 8, 2, 3,
+ 2, 3, 4,
+ 3, 4, 5,
+ 4, 5, 6,
+ 5, 6, 7,
+ 6, 7, 8,
+ 7, 8, 2,
+ };
+ int[] yExpected =
+ {
+ 6, 2, 3,
+ 2, 3, 4,
+ 3, 4, 5,
+ 4, 5, 6,
+ 5, 6, 2,
+ };
+ this.AssertOffsets(kernelSize, bounds, mode, mode, xExpected, yExpected);
+ }
+
+ private void AssertOffsets(Size kernelSize, Rectangle bounds, BorderWrappingMode xBorderMode, BorderWrappingMode yBorderMode, int[] xExpected, int[] yExpected)
+ {
+ // Arrange
+ var map = new KernelSamplingMap(Configuration.Default.MemoryAllocator);
+
+ // Act
+ map.BuildSamplingOffsetMap(kernelSize.Height, kernelSize.Width, bounds, xBorderMode, yBorderMode);
+
+ // Assert
+ var xOffsets = map.GetColumnOffsetSpan().ToArray();
+ Assert.Equal(xExpected, xOffsets);
+ var yOffsets = map.GetRowOffsetSpan().ToArray();
+ Assert.Equal(yExpected, yOffsets);
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs
index 0d2f3fcefb..fc0374bbd7 100644
--- a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs
@@ -356,10 +356,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities
var key = (HwIntrinsics)Enum.Parse(typeof(HwIntrinsics), intrinsic);
switch (intrinsic)
{
- case nameof(HwIntrinsics.DisableSIMD):
- features.Add(key, "FeatureSIMD");
- break;
-
case nameof(HwIntrinsics.AllowAll):
// Not a COMPlus value. We filter in calling method.
@@ -390,23 +386,22 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities
{
// Use flags so we can pass multiple values without using params.
// Don't base on 0 or use inverse for All as that doesn't translate to string values.
- DisableSIMD = 1 << 0,
- DisableHWIntrinsic = 1 << 1,
- DisableSSE = 1 << 2,
- DisableSSE2 = 1 << 3,
- DisableAES = 1 << 4,
- DisablePCLMULQDQ = 1 << 5,
- DisableSSE3 = 1 << 6,
- DisableSSSE3 = 1 << 7,
- DisableSSE41 = 1 << 8,
- DisableSSE42 = 1 << 9,
- DisablePOPCNT = 1 << 10,
- DisableAVX = 1 << 11,
- DisableFMA = 1 << 12,
- DisableAVX2 = 1 << 13,
- DisableBMI1 = 1 << 14,
- DisableBMI2 = 1 << 15,
- DisableLZCNT = 1 << 16,
- AllowAll = 1 << 17
+ DisableHWIntrinsic = 1 << 0,
+ DisableSSE = 1 << 1,
+ DisableSSE2 = 1 << 2,
+ DisableAES = 1 << 3,
+ DisablePCLMULQDQ = 1 << 4,
+ DisableSSE3 = 1 << 5,
+ DisableSSSE3 = 1 << 6,
+ DisableSSE41 = 1 << 7,
+ DisableSSE42 = 1 << 8,
+ DisablePOPCNT = 1 << 9,
+ DisableAVX = 1 << 10,
+ DisableFMA = 1 << 11,
+ DisableAVX2 = 1 << 12,
+ DisableBMI1 = 1 << 13,
+ DisableBMI2 = 1 << 14,
+ DisableLZCNT = 1 << 15,
+ AllowAll = 1 << 16
}
}
diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs
index a2f36c85a8..6dd59a750d 100644
--- a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs
@@ -16,10 +16,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
public class FeatureTestRunnerTests
{
public static TheoryData Intrinsics =>
- new TheoryData
+ new()
{
{ HwIntrinsics.DisableAES | HwIntrinsics.AllowAll, new string[] { "EnableAES", "AllowAll" } },
- { HwIntrinsics.DisableSIMD | HwIntrinsics.DisableHWIntrinsic, new string[] { "FeatureSIMD", "EnableHWIntrinsic" } },
+ { HwIntrinsics.DisableHWIntrinsic, new string[] { "EnableHWIntrinsic" } },
{ HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableAVX, new string[] { "EnableSSE42", "EnableAVX" } }
};
@@ -55,14 +55,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
HwIntrinsics.AllowAll);
}
- [Fact]
- public void CanLimitHwIntrinsicSIMDFeatures()
- {
- FeatureTestRunner.RunWithHwIntrinsicsFeature(
- () => Assert.False(Vector.IsHardwareAccelerated),
- HwIntrinsics.DisableSIMD);
- }
-
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void CanLimitHwIntrinsicBaseFeatures()
@@ -101,9 +93,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
switch ((HwIntrinsics)Enum.Parse(typeof(HwIntrinsics), intrinsic))
{
- case HwIntrinsics.DisableSIMD:
- Assert.False(Vector.IsHardwareAccelerated);
- break;
#if SUPPORTS_RUNTIME_INTRINSICS
case HwIntrinsics.DisableHWIntrinsic:
Assert.False(Sse.IsSupported);
@@ -206,9 +195,6 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
switch ((HwIntrinsics)Enum.Parse(typeof(HwIntrinsics), intrinsic))
{
- case HwIntrinsics.DisableSIMD:
- Assert.False(Vector.IsHardwareAccelerated, nameof(Vector.IsHardwareAccelerated));
- break;
#if SUPPORTS_RUNTIME_INTRINSICS
case HwIntrinsics.DisableHWIntrinsic:
Assert.False(Sse.IsSupported);