From 2a759eaf628abef37f1277a9f5c4688d1e70b1ee Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Wed, 12 Dec 2018 02:28:34 -0500 Subject: [PATCH 1/5] Initial gaussian blur reimplementation --- ShareX.HelpersLib/ConvolutionMatrixManager.cs | 10 -- .../Extensions/NumberExtensions.cs | 5 + ShareX.HelpersLib/Helpers/MathHelpers.cs | 18 +++ ShareX.HelpersLib/ShareX.HelpersLib.csproj | 5 + .../Filters/GaussianBlur.cs | 135 +++++++++++++++++- .../ShareX.ImageEffectsLib.csproj | 5 + 6 files changed, 167 insertions(+), 11 deletions(-) diff --git a/ShareX.HelpersLib/ConvolutionMatrixManager.cs b/ShareX.HelpersLib/ConvolutionMatrixManager.cs index d9bd522b0..6d157b6c5 100644 --- a/ShareX.HelpersLib/ConvolutionMatrixManager.cs +++ b/ShareX.HelpersLib/ConvolutionMatrixManager.cs @@ -126,16 +126,6 @@ public static ConvolutionMatrix Smooth(int weight = 1) return cm; } - public static ConvolutionMatrix GaussianBlur(int weight = 4) - { - ConvolutionMatrix cm = new ConvolutionMatrix(); - cm.SetAll(1); - cm.Matrix[1, 1] = weight; - cm.Matrix[1, 0] = cm.Matrix[0, 1] = cm.Matrix[2, 1] = cm.Matrix[1, 2] = 2; - cm.Factor = weight + 12; - return cm; - } - public static ConvolutionMatrix MeanRemoval(int weight = 9) { ConvolutionMatrix cm = new ConvolutionMatrix(); diff --git a/ShareX.HelpersLib/Extensions/NumberExtensions.cs b/ShareX.HelpersLib/Extensions/NumberExtensions.cs index ad25dd008..dd9326209 100644 --- a/ShareX.HelpersLib/Extensions/NumberExtensions.cs +++ b/ShareX.HelpersLib/Extensions/NumberExtensions.cs @@ -149,5 +149,10 @@ public static bool IsEvenNumber(this int num) { return num % 2 == 0; } + + public static void Clamp(ref this T val, T min, T max) where T : struct, IComparable + { + MathHelpers.Clamp(ref val, min, max); + } } } \ No newline at end of file diff --git a/ShareX.HelpersLib/Helpers/MathHelpers.cs b/ShareX.HelpersLib/Helpers/MathHelpers.cs index c55879d7f..d6fa72a01 100644 --- a/ShareX.HelpersLib/Helpers/MathHelpers.cs +++ b/ShareX.HelpersLib/Helpers/MathHelpers.cs @@ -185,5 +185,23 @@ public static Vector2 Lerp(Vector2 pos1, Vector2 pos2, float amount) float y = Lerp(pos1.Y, pos2.Y, amount); return new Vector2(x, y); } + + public static void Clamp(ref T val, T min, T max) where T : IComparable + { + if (val.CompareTo(min) < 0) + { + val = min; + } + else if (val.CompareTo(max) > 0) + { + val = max; + } + } + + public static T Clamp(T val, T min, T max) where T : IComparable + { + Clamp(ref val, min, max); + return val; + } } } \ No newline at end of file diff --git a/ShareX.HelpersLib/ShareX.HelpersLib.csproj b/ShareX.HelpersLib/ShareX.HelpersLib.csproj index 8be6cb052..e46d45003 100644 --- a/ShareX.HelpersLib/ShareX.HelpersLib.csproj +++ b/ShareX.HelpersLib/ShareX.HelpersLib.csproj @@ -33,6 +33,7 @@ Off false true + 7.2 none @@ -48,6 +49,7 @@ true Off false + 7.2 bin\Steam\ @@ -59,6 +61,7 @@ prompt MinimumRecommendedRules.ruleset false + 7.2 bin\WindowsStore\ @@ -70,6 +73,7 @@ prompt MinimumRecommendedRules.ruleset false + 7.2 true @@ -82,6 +86,7 @@ prompt MinimumRecommendedRules.ruleset false + 7.2 diff --git a/ShareX.ImageEffectsLib/Filters/GaussianBlur.cs b/ShareX.ImageEffectsLib/Filters/GaussianBlur.cs index fe4fb4e4b..f0f19924f 100644 --- a/ShareX.ImageEffectsLib/Filters/GaussianBlur.cs +++ b/ShareX.ImageEffectsLib/Filters/GaussianBlur.cs @@ -24,19 +24,152 @@ #endregion License Information (GPL v3) using ShareX.HelpersLib; +using System; using System.ComponentModel; using System.Drawing; +using System.Drawing.Imaging; + +// Adapted from https://stackoverflow.com/questions/33569396/correctly-implement-a-2-pass-gaussian-blur namespace ShareX.ImageEffectsLib { [Description("Gaussian blur")] internal class GaussianBlur : ImageEffect { + private double _sigma; + private int _size; + + private double[,] _cachedKernelHoriz; + private double[,] _cachedKernelVert; + + [DefaultValue(0.7955555)] + public double Sigma + { + get => _sigma; + set + { + _sigma = Math.Max(value, 0.1); + + CreateKernels(out _cachedKernelHoriz, out _cachedKernelVert); + } + } + + [DefaultValue(3)] + public int Size + { + get => _size; + set + { + _size = value.Min(1); + + if (_size.IsEvenNumber()) + { + _size++; + } + + CreateKernels(out _cachedKernelHoriz, out _cachedKernelVert); + } + } + + public GaussianBlur() + { + this.ApplyDefaultPropertyValues(); + } + + private double Gaussian(double x) + { + double left = 1.0 / (Math.Sqrt(2 * Math.PI) * _sigma); + + double exponentNumerator = -x * x; + double exponentDenominator = 2 * Math.Pow(_sigma, 2); + double right = Math.Exp(exponentNumerator / exponentDenominator); + + return left * right; + } + + private void CreateKernels(out double[,] horiz, out double[,] vert) + { + horiz = new double[1, _size]; + + double sum = 0.0; + double midpoint = (_size - 1) / 2.0; + + for (int i = 0; i < _size; i++) + { + sum += horiz[0, i] = Gaussian(i - midpoint); + } + + // Normalise kernel so that the sum of all weights equals 1 + for (int i = 0; i < _size; i++) + { + horiz[0, i] /= sum; + } + + // Copy the kernel into the vertical + vert = new double[_size, 1]; + for (int i = 0; i < _size; i++) + { + vert[i, 0] = horiz[0, i]; + } + } + + private static void ApplyKernel(UnsafeBitmap bmp, double[,] kernel) + { + int kernelHeight = kernel.GetLength(0); + int kernelWidth = kernel.GetLength(1); + + int originX = (kernelWidth - 1) / 2; + int originY = (kernelHeight - 1) / 2; + + for (int y = 0; y < bmp.Height; y++) + { + for (int x = 0; x < bmp.Width; x++) + { + double r = 0.0; + double g = 0.0; + double b = 0.0; + + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelHeight; fy++) + { + int fyr = fy - originY; + int offsetY = y + fyr; + + offsetY.Clamp(0, bmp.Height - 1); + + for (int fx = 0; fx < kernelWidth; fx++) + { + int fxr = fx - originX; + int offsetX = x + fxr; + + offsetX.Clamp(0, bmp.Width - 1); + + ColorBgra currentColor = bmp.GetPixel(offsetX, offsetY); + + r += kernel[fy, fx] * currentColor.Red; + g += kernel[fy, fx] * currentColor.Green; + b += kernel[fy, fx] * currentColor.Blue; + } + } + + bmp.SetPixel(x, y, new ColorBgra((byte)b, (byte)g, (byte)r, bmp.GetPixel(x, y).Alpha)); + } + } + } + public override Image Apply(Image img) { using (img) { - return ConvolutionMatrixManager.GaussianBlur().Apply(img); + Bitmap result = (Bitmap)img.Clone(); + + using (UnsafeBitmap dest = new UnsafeBitmap(result, true, ImageLockMode.ReadWrite)) + { + ApplyKernel(dest, _cachedKernelHoriz); + ApplyKernel(dest, _cachedKernelVert); + } + + return result; } } } diff --git a/ShareX.ImageEffectsLib/ShareX.ImageEffectsLib.csproj b/ShareX.ImageEffectsLib/ShareX.ImageEffectsLib.csproj index b6f9e196b..c7a94d00d 100644 --- a/ShareX.ImageEffectsLib/ShareX.ImageEffectsLib.csproj +++ b/ShareX.ImageEffectsLib/ShareX.ImageEffectsLib.csproj @@ -44,6 +44,7 @@ AllRules.ruleset false true + 7.2 none @@ -57,6 +58,7 @@ true Off false + 7.2 bin\Steam\ @@ -68,6 +70,7 @@ prompt AllRules.ruleset false + 7.2 bin\WindowsStore\ @@ -79,6 +82,7 @@ prompt AllRules.ruleset false + 7.2 true @@ -90,6 +94,7 @@ prompt AllRules.ruleset false + 7.2 From 1a4cc7c9d408e733744079b34839a0e233f2ce20 Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Wed, 12 Dec 2018 14:07:10 -0500 Subject: [PATCH 2/5] New matrix convolution code, add user-customizable gaussian blur effect --- ShareX.HelpersLib/ConvolutionMatrix.cs | 32 ++-- ShareX.HelpersLib/ConvolutionMatrixManager.cs | 158 ++++++++++-------- .../Filters/GaussianBlur.cs | 117 +++---------- .../Filters/MatrixConvolution.cs | 25 ++- 4 files changed, 137 insertions(+), 195 deletions(-) diff --git a/ShareX.HelpersLib/ConvolutionMatrix.cs b/ShareX.HelpersLib/ConvolutionMatrix.cs index 21e7c80a9..34f9512b7 100644 --- a/ShareX.HelpersLib/ConvolutionMatrix.cs +++ b/ShareX.HelpersLib/ConvolutionMatrix.cs @@ -27,31 +27,39 @@ namespace ShareX.HelpersLib { public class ConvolutionMatrix { - public int Size { get; private set; } - public int[,] Matrix { get; set; } - public int Factor { get; set; } - public int Offset { get; set; } + private readonly double[,] matrix; + + public int Width => matrix.GetLength(1); + public int Height => matrix.GetLength(0); + public byte Offset { get; set; } public ConvolutionMatrix() : this(3) { } - public ConvolutionMatrix(int size) + public ConvolutionMatrix(int size) : this(size, size) { - Size = size; - Matrix = new int[Size, Size]; - Factor = 1; } - public void SetAll(int value) + public ConvolutionMatrix(int height, int width) { - for (int y = 0; y < Size; y++) + matrix = new double[height, width]; + } + + public void SetAll(double value) + { + for (int y = 0; y < Height; y++) { - for (int x = 0; x < Size; x++) + for (int x = 0; x < Width; x++) { - Matrix[x, y] = value; + matrix[y, x] = value; } } } + + public ref double this[int y, int x] + { + get => ref matrix[y, x]; + } } } \ No newline at end of file diff --git a/ShareX.HelpersLib/ConvolutionMatrixManager.cs b/ShareX.HelpersLib/ConvolutionMatrixManager.cs index 6d157b6c5..ce91d17db 100644 --- a/ShareX.HelpersLib/ConvolutionMatrixManager.cs +++ b/ShareX.HelpersLib/ConvolutionMatrixManager.cs @@ -23,6 +23,7 @@ #endregion License Information (GPL v3) +// Adapted from https://stackoverflow.com/questions/33569396/correctly-implement-a-2-pass-gaussian-blur // Filters: http://www.codeproject.com/Articles/2008/Image-Processing-for-Dummies-with-C-and-GDI-Part-2 using System; @@ -33,83 +34,57 @@ namespace ShareX.HelpersLib { public static class ConvolutionMatrixManager { - public static Image Apply(this ConvolutionMatrix matrix, Image img) + public static Image Apply(this ConvolutionMatrix kernel, Image img) { - int factor = Math.Max(matrix.Factor, 1); - Bitmap result = (Bitmap)img.Clone(); using (UnsafeBitmap source = new UnsafeBitmap((Bitmap)img, true, ImageLockMode.ReadOnly)) using (UnsafeBitmap dest = new UnsafeBitmap(result, true, ImageLockMode.WriteOnly)) { - int height = source.Height - 2; - int width = source.Width - 2; - ColorBgra[,] pixelColor = new ColorBgra[3, 3]; - int pixel; - ColorBgra color = new ColorBgra(); + int originX = (kernel.Width - 1) / 2; + int originY = (kernel.Height - 1) / 2; - for (int y = 0; y < height; y++) + for (int y = 0; y < source.Height; y++) { - for (int x = 0; x < width; x++) + for (int x = 0; x < source.Width; x++) { - pixelColor[0, 0] = source.GetPixel(x, y); - pixelColor[0, 1] = source.GetPixel(x, y + 1); - pixelColor[0, 2] = source.GetPixel(x, y + 2); - pixelColor[1, 0] = source.GetPixel(x + 1, y); - pixelColor[1, 1] = source.GetPixel(x + 1, y + 1); - pixelColor[1, 2] = source.GetPixel(x + 1, y + 2); - pixelColor[2, 0] = source.GetPixel(x + 2, y); - pixelColor[2, 1] = source.GetPixel(x + 2, y + 1); - pixelColor[2, 2] = source.GetPixel(x + 2, y + 2); + double r = 0.0; + double g = 0.0; + double b = 0.0; - pixel = (((pixelColor[0, 0].Blue * matrix.Matrix[0, 0]) + - (pixelColor[1, 0].Blue * matrix.Matrix[1, 0]) + - (pixelColor[2, 0].Blue * matrix.Matrix[2, 0]) + - (pixelColor[0, 1].Blue * matrix.Matrix[0, 1]) + - (pixelColor[1, 1].Blue * matrix.Matrix[1, 1]) + - (pixelColor[2, 1].Blue * matrix.Matrix[2, 1]) + - (pixelColor[0, 2].Blue * matrix.Matrix[0, 2]) + - (pixelColor[1, 2].Blue * matrix.Matrix[1, 2]) + - (pixelColor[2, 2].Blue * matrix.Matrix[2, 2])) / factor) + matrix.Offset; + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernel.Height; fy++) + { + int fyr = fy - originY; + int offsetY = y + fyr; - if (pixel < 0) pixel = 0; - else if (pixel > 255) pixel = 255; + offsetY.Clamp(0, source.Height - 1); - color.Blue = (byte)pixel; + for (int fx = 0; fx < kernel.Width; fx++) + { + int fxr = fx - originX; + int offsetX = x + fxr; - pixel = (((pixelColor[0, 0].Green * matrix.Matrix[0, 0]) + - (pixelColor[1, 0].Green * matrix.Matrix[1, 0]) + - (pixelColor[2, 0].Green * matrix.Matrix[2, 0]) + - (pixelColor[0, 1].Green * matrix.Matrix[0, 1]) + - (pixelColor[1, 1].Green * matrix.Matrix[1, 1]) + - (pixelColor[2, 1].Green * matrix.Matrix[2, 1]) + - (pixelColor[0, 2].Green * matrix.Matrix[0, 2]) + - (pixelColor[1, 2].Green * matrix.Matrix[1, 2]) + - (pixelColor[2, 2].Green * matrix.Matrix[2, 2])) / factor) + matrix.Offset; + offsetX.Clamp(0, source.Width - 1); - if (pixel < 0) pixel = 0; - else if (pixel > 255) pixel = 255; + ColorBgra currentColor = source.GetPixel(offsetX, offsetY); - color.Green = (byte)pixel; + r += kernel[fy, fx] * currentColor.Red; + g += kernel[fy, fx] * currentColor.Green; + b += kernel[fy, fx] * currentColor.Blue; + } + } - pixel = (((pixelColor[0, 0].Red * matrix.Matrix[0, 0]) + - (pixelColor[1, 0].Red * matrix.Matrix[1, 0]) + - (pixelColor[2, 0].Red * matrix.Matrix[2, 0]) + - (pixelColor[0, 1].Red * matrix.Matrix[0, 1]) + - (pixelColor[1, 1].Red * matrix.Matrix[1, 1]) + - (pixelColor[2, 1].Red * matrix.Matrix[2, 1]) + - (pixelColor[0, 2].Red * matrix.Matrix[0, 2]) + - (pixelColor[1, 2].Red * matrix.Matrix[1, 2]) + - (pixelColor[2, 2].Red * matrix.Matrix[2, 2])) / factor) + matrix.Offset; + r += kernel.Offset; + r.Clamp(0, 255); - if (pixel < 0) pixel = 0; - else if (pixel > 255) pixel = 255; + g += kernel.Offset; + g.Clamp(0, 255); - color.Red = (byte)pixel; + b += kernel.Offset; + b.Clamp(0, 255); - color.Alpha = pixelColor[1, 1].Alpha; - - dest.SetPixel(x + 1, y + 1, color); + dest.SetPixel(x, y, new ColorBgra((byte)b, (byte)g, (byte)r, source.GetPixel(x, y).Alpha)); } } } @@ -120,28 +95,67 @@ public static Image Apply(this ConvolutionMatrix matrix, Image img) public static ConvolutionMatrix Smooth(int weight = 1) { ConvolutionMatrix cm = new ConvolutionMatrix(); - cm.SetAll(1); - cm.Matrix[1, 1] = weight; - cm.Factor = weight + 8; + double factor = weight + 8; + cm.SetAll(1 / factor); + cm[1, 1] = weight / factor; + return cm; + } + + private static double GaussianFunction(double x, double sigma) + { + double left = 1.0 / (Math.Sqrt(2 * Math.PI) * sigma); + + double exponentNumerator = -x * x; + double exponentDenominator = 2 * Math.Pow(sigma, 2); + double right = Math.Exp(exponentNumerator / exponentDenominator); + + return left * right; + } + + public static ConvolutionMatrix GaussianBlur(int height, int width, double sigma) + { + ConvolutionMatrix cm = new ConvolutionMatrix(height, width); + + double sum = 0.0; + double midpointX = (width - 1) / 2.0; + double midpointY = (height - 1) / 2.0; + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + sum += cm[y, x] = GaussianFunction(x - midpointX, sigma) * GaussianFunction(y - midpointY, sigma); + } + } + + // Normalise kernel so that the sum of all weights equals 1 + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + cm[y, x] /= sum; + } + } + return cm; } public static ConvolutionMatrix MeanRemoval(int weight = 9) { ConvolutionMatrix cm = new ConvolutionMatrix(); - cm.SetAll(-1); - cm.Matrix[1, 1] = weight; - cm.Factor = weight - 8; + double factor = weight - 8; + cm.SetAll(-1 / factor); + cm[1, 1] = weight / factor; return cm; } public static ConvolutionMatrix Sharpen(int weight = 11) { ConvolutionMatrix cm = new ConvolutionMatrix(); + double factor = weight - 8; cm.SetAll(0); - cm.Matrix[1, 1] = weight; - cm.Matrix[1, 0] = cm.Matrix[0, 1] = cm.Matrix[2, 1] = cm.Matrix[1, 2] = -2; - cm.Factor = weight - 8; + cm[1, 1] = weight / factor; + cm[1, 0] = cm[0, 1] = cm[2, 1] = cm[1, 2] = -2 / factor; return cm; } @@ -149,8 +163,8 @@ public static ConvolutionMatrix Emboss() { ConvolutionMatrix cm = new ConvolutionMatrix(); cm.SetAll(-1); - cm.Matrix[1, 1] = 4; - cm.Matrix[1, 0] = cm.Matrix[0, 1] = cm.Matrix[2, 1] = cm.Matrix[1, 2] = 0; + cm[1, 1] = 4; + cm[1, 0] = cm[0, 1] = cm[2, 1] = cm[1, 2] = 0; cm.Offset = 127; return cm; } @@ -158,9 +172,9 @@ public static ConvolutionMatrix Emboss() public static ConvolutionMatrix EdgeDetect() { ConvolutionMatrix cm = new ConvolutionMatrix(); - cm.Matrix[0, 0] = cm.Matrix[1, 0] = cm.Matrix[2, 0] = -1; - cm.Matrix[0, 1] = cm.Matrix[1, 1] = cm.Matrix[2, 1] = 0; - cm.Matrix[0, 2] = cm.Matrix[1, 2] = cm.Matrix[2, 2] = 1; + cm[0, 0] = cm[0, 1] = cm[0, 2] = -1; + cm[1, 0] = cm[1, 1] = cm[1, 2] = 0; + cm[2, 0] = cm[2, 1] = cm[2, 2] = 1; cm.Offset = 127; return cm; } diff --git a/ShareX.ImageEffectsLib/Filters/GaussianBlur.cs b/ShareX.ImageEffectsLib/Filters/GaussianBlur.cs index f0f19924f..b05c6ce2d 100644 --- a/ShareX.ImageEffectsLib/Filters/GaussianBlur.cs +++ b/ShareX.ImageEffectsLib/Filters/GaussianBlur.cs @@ -27,47 +27,44 @@ using System; using System.ComponentModel; using System.Drawing; -using System.Drawing.Imaging; - -// Adapted from https://stackoverflow.com/questions/33569396/correctly-implement-a-2-pass-gaussian-blur namespace ShareX.ImageEffectsLib { [Description("Gaussian blur")] internal class GaussianBlur : ImageEffect { - private double _sigma; - private int _size; + private double sigma; + private int size; - private double[,] _cachedKernelHoriz; - private double[,] _cachedKernelVert; + private ConvolutionMatrix cachedKernelHoriz; + private ConvolutionMatrix cachedKernelVert; [DefaultValue(0.7955555)] public double Sigma { - get => _sigma; + get => sigma; set { - _sigma = Math.Max(value, 0.1); + sigma = Math.Max(value, 0.1); - CreateKernels(out _cachedKernelHoriz, out _cachedKernelVert); + UpdateKernels(); } } [DefaultValue(3)] public int Size { - get => _size; + get => size; set { - _size = value.Min(1); + size = value.Min(1); - if (_size.IsEvenNumber()) + if (size.IsEvenNumber()) { - _size++; + size++; } - CreateKernels(out _cachedKernelHoriz, out _cachedKernelVert); + UpdateKernels(); } } @@ -76,100 +73,24 @@ public GaussianBlur() this.ApplyDefaultPropertyValues(); } - private double Gaussian(double x) + private void UpdateKernels() { - double left = 1.0 / (Math.Sqrt(2 * Math.PI) * _sigma); - - double exponentNumerator = -x * x; - double exponentDenominator = 2 * Math.Pow(_sigma, 2); - double right = Math.Exp(exponentNumerator / exponentDenominator); - - return left * right; - } - - private void CreateKernels(out double[,] horiz, out double[,] vert) - { - horiz = new double[1, _size]; - - double sum = 0.0; - double midpoint = (_size - 1) / 2.0; - - for (int i = 0; i < _size; i++) - { - sum += horiz[0, i] = Gaussian(i - midpoint); - } - - // Normalise kernel so that the sum of all weights equals 1 - for (int i = 0; i < _size; i++) - { - horiz[0, i] /= sum; - } + cachedKernelHoriz = ConvolutionMatrixManager.GaussianBlur(1, size, sigma); // Copy the kernel into the vertical - vert = new double[_size, 1]; - for (int i = 0; i < _size; i++) + cachedKernelVert = new ConvolutionMatrix(size, 1); + for (int i = 0; i < size; i++) { - vert[i, 0] = horiz[0, i]; - } - } - - private static void ApplyKernel(UnsafeBitmap bmp, double[,] kernel) - { - int kernelHeight = kernel.GetLength(0); - int kernelWidth = kernel.GetLength(1); - - int originX = (kernelWidth - 1) / 2; - int originY = (kernelHeight - 1) / 2; - - for (int y = 0; y < bmp.Height; y++) - { - for (int x = 0; x < bmp.Width; x++) - { - double r = 0.0; - double g = 0.0; - double b = 0.0; - - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelHeight; fy++) - { - int fyr = fy - originY; - int offsetY = y + fyr; - - offsetY.Clamp(0, bmp.Height - 1); - - for (int fx = 0; fx < kernelWidth; fx++) - { - int fxr = fx - originX; - int offsetX = x + fxr; - - offsetX.Clamp(0, bmp.Width - 1); - - ColorBgra currentColor = bmp.GetPixel(offsetX, offsetY); - - r += kernel[fy, fx] * currentColor.Red; - g += kernel[fy, fx] * currentColor.Green; - b += kernel[fy, fx] * currentColor.Blue; - } - } - - bmp.SetPixel(x, y, new ColorBgra((byte)b, (byte)g, (byte)r, bmp.GetPixel(x, y).Alpha)); - } + cachedKernelVert[i, 0] = cachedKernelHoriz[0, i]; } } public override Image Apply(Image img) { using (img) + using (Image horizPass = cachedKernelHoriz.Apply(img)) { - Bitmap result = (Bitmap)img.Clone(); - - using (UnsafeBitmap dest = new UnsafeBitmap(result, true, ImageLockMode.ReadWrite)) - { - ApplyKernel(dest, _cachedKernelHoriz); - ApplyKernel(dest, _cachedKernelVert); - } - - return result; + return cachedKernelVert.Apply(horizPass); } } } diff --git a/ShareX.ImageEffectsLib/Filters/MatrixConvolution.cs b/ShareX.ImageEffectsLib/Filters/MatrixConvolution.cs index d14766242..0dbcab1b0 100644 --- a/ShareX.ImageEffectsLib/Filters/MatrixConvolution.cs +++ b/ShareX.ImageEffectsLib/Filters/MatrixConvolution.cs @@ -53,11 +53,11 @@ internal class MatrixConvolution : ImageEffect [DefaultValue(0)] public int X2Y2 { get; set; } - [DefaultValue(1)] - public int Factor { get; set; } + [DefaultValue(1.0)] + public double Factor { get; set; } [DefaultValue(0)] - public int Offset { get; set; } + public byte Offset { get; set; } public MatrixConvolution() { @@ -69,16 +69,15 @@ public override Image Apply(Image img) using (img) { ConvolutionMatrix cm = new ConvolutionMatrix(); - cm.Matrix[0, 0] = X0Y0; - cm.Matrix[1, 0] = X1Y0; - cm.Matrix[2, 0] = X2Y0; - cm.Matrix[0, 1] = X0Y1; - cm.Matrix[1, 1] = X1Y1; - cm.Matrix[2, 1] = X2Y1; - cm.Matrix[0, 2] = X0Y2; - cm.Matrix[1, 2] = X1Y2; - cm.Matrix[2, 2] = X2Y2; - cm.Factor = Factor; + cm[0, 0] = X0Y0 / Factor; + cm[0, 1] = X1Y0 / Factor; + cm[0, 2] = X2Y0 / Factor; + cm[1, 0] = X0Y1 / Factor; + cm[1, 1] = X1Y1 / Factor; + cm[1, 2] = X2Y1 / Factor; + cm[2, 0] = X0Y2 / Factor; + cm[2, 1] = X1Y2 / Factor; + cm[2, 2] = X2Y2 / Factor; cm.Offset = Offset; return cm.Apply(img); } From 7a5591ebdee72b9883310ea53117a8f1c5bdaa30 Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Wed, 12 Dec 2018 14:19:26 -0500 Subject: [PATCH 3/5] Don't need C# 7.2 in ImageEffectsLib anymore --- ShareX.ImageEffectsLib/ShareX.ImageEffectsLib.csproj | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ShareX.ImageEffectsLib/ShareX.ImageEffectsLib.csproj b/ShareX.ImageEffectsLib/ShareX.ImageEffectsLib.csproj index c7a94d00d..b6f9e196b 100644 --- a/ShareX.ImageEffectsLib/ShareX.ImageEffectsLib.csproj +++ b/ShareX.ImageEffectsLib/ShareX.ImageEffectsLib.csproj @@ -44,7 +44,6 @@ AllRules.ruleset false true - 7.2 none @@ -58,7 +57,6 @@ true Off false - 7.2 bin\Steam\ @@ -70,7 +68,6 @@ prompt AllRules.ruleset false - 7.2 bin\WindowsStore\ @@ -82,7 +79,6 @@ prompt AllRules.ruleset false - 7.2 true @@ -94,7 +90,6 @@ prompt AllRules.ruleset false - 7.2 From 56b447d665f9bda4430b1039773b1e243cdecabf Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Wed, 12 Dec 2018 14:35:59 -0500 Subject: [PATCH 4/5] Use more cool features --- ShareX.HelpersLib/ConvolutionMatrix.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ShareX.HelpersLib/ConvolutionMatrix.cs b/ShareX.HelpersLib/ConvolutionMatrix.cs index 34f9512b7..4049c7d8f 100644 --- a/ShareX.HelpersLib/ConvolutionMatrix.cs +++ b/ShareX.HelpersLib/ConvolutionMatrix.cs @@ -57,9 +57,6 @@ public void SetAll(double value) } } - public ref double this[int y, int x] - { - get => ref matrix[y, x]; - } + public ref double this[int y, int x] => ref matrix[y, x]; } } \ No newline at end of file From 380653c128579ddd864bdb532efbdbae2b89b725 Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Wed, 12 Dec 2018 18:38:16 -0500 Subject: [PATCH 5/5] Remove kernel caching --- .../Filters/GaussianBlur.cs | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/ShareX.ImageEffectsLib/Filters/GaussianBlur.cs b/ShareX.ImageEffectsLib/Filters/GaussianBlur.cs index b05c6ce2d..16ba7db14 100644 --- a/ShareX.ImageEffectsLib/Filters/GaussianBlur.cs +++ b/ShareX.ImageEffectsLib/Filters/GaussianBlur.cs @@ -36,19 +36,11 @@ internal class GaussianBlur : ImageEffect private double sigma; private int size; - private ConvolutionMatrix cachedKernelHoriz; - private ConvolutionMatrix cachedKernelVert; - [DefaultValue(0.7955555)] public double Sigma { get => sigma; - set - { - sigma = Math.Max(value, 0.1); - - UpdateKernels(); - } + set => sigma = Math.Max(value, 0.1); } [DefaultValue(3)] @@ -63,8 +55,6 @@ public int Size { size++; } - - UpdateKernels(); } } @@ -73,24 +63,20 @@ public GaussianBlur() this.ApplyDefaultPropertyValues(); } - private void UpdateKernels() - { - cachedKernelHoriz = ConvolutionMatrixManager.GaussianBlur(1, size, sigma); - - // Copy the kernel into the vertical - cachedKernelVert = new ConvolutionMatrix(size, 1); - for (int i = 0; i < size; i++) - { - cachedKernelVert[i, 0] = cachedKernelHoriz[0, i]; - } - } - public override Image Apply(Image img) { - using (img) - using (Image horizPass = cachedKernelHoriz.Apply(img)) + ConvolutionMatrix kernelHoriz = ConvolutionMatrixManager.GaussianBlur(1, size, sigma); + + ConvolutionMatrix kernelVert = new ConvolutionMatrix(size, 1); + for (int i = 0; i < size; i++) { - return cachedKernelVert.Apply(horizPass); + kernelVert[i, 0] = kernelHoriz[0, i]; + } + + using (img) + using (Image horizPass = kernelHoriz.Apply(img)) + { + return kernelVert.Apply(horizPass); } } }