diff --git a/GreenshotPlugin/Core/FastBitmap.cs b/GreenshotPlugin/Core/FastBitmap.cs index 49bbb58b1..ec80b1906 100644 --- a/GreenshotPlugin/Core/FastBitmap.cs +++ b/GreenshotPlugin/Core/FastBitmap.cs @@ -107,6 +107,13 @@ namespace GreenshotPlugin.Core { set; } + /// + /// Returns if this FastBitmap has an alpha channel + /// + bool hasAlphaChannel { + get; + } + /// /// Draw the stored bitmap to the destionation bitmap at the supplied point /// @@ -129,10 +136,16 @@ namespace GreenshotPlugin.Core { public unsafe abstract class FastBitmap : IFastBitmap { private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(FastBitmap)); - protected const int AINDEX = 3; - protected const int RINDEX = 2; - protected const int GINDEX = 1; - protected const int BINDEX = 0; + protected const int PIXELFORMAT_INDEX_A = 3; + protected const int PIXELFORMAT_INDEX_R = 2; + protected const int PIXELFORMAT_INDEX_G = 1; + protected const int PIXELFORMAT_INDEX_B = 0; + + public const int COLOR_INDEX_R = 0; + public const int COLOR_INDEX_G = 1; + public const int COLOR_INDEX_B = 2; + public const int COLOR_INDEX_A = 3; + protected Rectangle area = Rectangle.Empty; /// /// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap @@ -289,6 +302,12 @@ namespace GreenshotPlugin.Core { return bitmap; } + public virtual bool hasAlphaChannel { + get { + return false; + } + } + /// /// Destructor /// @@ -513,7 +532,7 @@ namespace GreenshotPlugin.Core { /// Color public override Color GetColorAt(int x, int y) { int offset = (x * 3) + (y * stride); - return Color.FromArgb(255, pointer[RINDEX + offset], pointer[GINDEX + offset], pointer[BINDEX + offset]); + return Color.FromArgb(255, pointer[PIXELFORMAT_INDEX_R + offset], pointer[PIXELFORMAT_INDEX_G + offset], pointer[PIXELFORMAT_INDEX_B + offset]); } /// @@ -525,9 +544,9 @@ namespace GreenshotPlugin.Core { /// public override void SetColorAt(int x, int y, Color color) { int offset = (x * 3) + (y * stride); - pointer[RINDEX + offset] = color.R; - pointer[GINDEX + offset] = color.G; - pointer[BINDEX + offset] = color.B; + pointer[PIXELFORMAT_INDEX_R + offset] = color.R; + pointer[PIXELFORMAT_INDEX_G + offset] = color.G; + pointer[PIXELFORMAT_INDEX_B + offset] = color.B; } /// @@ -535,13 +554,12 @@ namespace GreenshotPlugin.Core { /// /// /// - /// byte[4] as reference (a,r,g,b) + /// byte[4] as reference (r,g,b) public override void GetColorAt(int x, int y, byte[] color) { int offset = (x * 3) + (y * stride); - color[0] = 255; - color[1] = pointer[RINDEX + offset]; - color[2] = pointer[GINDEX + offset]; - color[3] = pointer[BINDEX + offset]; + color[PIXELFORMAT_INDEX_R] = pointer[PIXELFORMAT_INDEX_R + offset]; + color[PIXELFORMAT_INDEX_G] = pointer[PIXELFORMAT_INDEX_G + offset]; + color[PIXELFORMAT_INDEX_B] = pointer[PIXELFORMAT_INDEX_B + offset]; } /// @@ -549,12 +567,12 @@ namespace GreenshotPlugin.Core { /// /// /// - /// byte[4] as reference (a,r,g,b) + /// byte[4] as reference (r,g,b) public override void SetColorAt(int x, int y, byte[] color) { int offset = (x * 3) + (y * stride); - pointer[RINDEX + offset] = color[1]; - pointer[GINDEX + offset] = color[2]; - pointer[BINDEX + offset] = color[3]; + pointer[PIXELFORMAT_INDEX_R + offset] = color[PIXELFORMAT_INDEX_R]; + pointer[PIXELFORMAT_INDEX_G + offset] = color[PIXELFORMAT_INDEX_G]; + pointer[PIXELFORMAT_INDEX_B + offset] = color[PIXELFORMAT_INDEX_B]; } } @@ -576,7 +594,7 @@ namespace GreenshotPlugin.Core { /// Color public override Color GetColorAt(int x, int y) { int offset = (x * 4) + (y * stride); - return Color.FromArgb(255, pointer[RINDEX + offset], pointer[GINDEX + offset], pointer[BINDEX + offset]); + return Color.FromArgb(255, pointer[PIXELFORMAT_INDEX_R + offset], pointer[PIXELFORMAT_INDEX_G + offset], pointer[PIXELFORMAT_INDEX_B + offset]); } /// @@ -588,9 +606,9 @@ namespace GreenshotPlugin.Core { /// public override void SetColorAt(int x, int y, Color color) { int offset = (x * 4) + (y * stride); - pointer[RINDEX + offset] = color.R; - pointer[GINDEX + offset] = color.G; - pointer[BINDEX + offset] = color.B; + pointer[PIXELFORMAT_INDEX_R + offset] = color.R; + pointer[PIXELFORMAT_INDEX_G + offset] = color.G; + pointer[PIXELFORMAT_INDEX_B + offset] = color.B; } /// @@ -601,10 +619,9 @@ namespace GreenshotPlugin.Core { /// byte[4] as reference (a,r,g,b) public override void GetColorAt(int x, int y, byte[] color) { int offset = (x * 4) + (y * stride); - color[0] = 255; - color[1] = pointer[RINDEX + offset]; - color[2] = pointer[GINDEX + offset]; - color[3] = pointer[BINDEX + offset]; + color[COLOR_INDEX_R] = pointer[PIXELFORMAT_INDEX_R + offset]; + color[COLOR_INDEX_G] = pointer[PIXELFORMAT_INDEX_G + offset]; + color[COLOR_INDEX_B] = pointer[PIXELFORMAT_INDEX_B + offset]; } /// @@ -612,12 +629,12 @@ namespace GreenshotPlugin.Core { /// /// /// - /// byte[4] as reference (a,r,g,b) + /// byte[4] as reference (r,g,b) public override void SetColorAt(int x, int y, byte[] color) { int offset = (x * 4) + (y * stride); - pointer[RINDEX + offset] = color[1]; // R - pointer[GINDEX + offset] = color[2]; - pointer[BINDEX + offset] = color[3]; + pointer[PIXELFORMAT_INDEX_R + offset] = color[COLOR_INDEX_R]; // R + pointer[PIXELFORMAT_INDEX_G + offset] = color[COLOR_INDEX_G]; + pointer[PIXELFORMAT_INDEX_B + offset] = color[COLOR_INDEX_B]; } } @@ -625,6 +642,12 @@ namespace GreenshotPlugin.Core { /// This is the implementation of the IFastBitmap for 32 bit images with Alpha /// public unsafe class Fast32ARGBBitmap : FastBitmap { + public override bool hasAlphaChannel { + get { + return true; + } + } + public Color BackgroundBlendColor { get; set; @@ -641,7 +664,7 @@ namespace GreenshotPlugin.Core { /// Color public override Color GetColorAt(int x, int y) { int offset = (x * 4) + (y * stride); - return Color.FromArgb(pointer[AINDEX + offset], pointer[RINDEX + offset], pointer[GINDEX + offset], pointer[BINDEX + offset]); + return Color.FromArgb(pointer[PIXELFORMAT_INDEX_A + offset], pointer[PIXELFORMAT_INDEX_R + offset], pointer[PIXELFORMAT_INDEX_G + offset], pointer[PIXELFORMAT_INDEX_B + offset]); } /// @@ -653,10 +676,10 @@ namespace GreenshotPlugin.Core { /// public override void SetColorAt(int x, int y, Color color) { int offset = (x * 4) + (y * stride); - pointer[AINDEX + offset] = color.A; - pointer[RINDEX + offset] = color.R; - pointer[GINDEX + offset] = color.G; - pointer[BINDEX + offset] = color.B; + pointer[PIXELFORMAT_INDEX_A + offset] = color.A; + pointer[PIXELFORMAT_INDEX_R + offset] = color.R; + pointer[PIXELFORMAT_INDEX_G + offset] = color.G; + pointer[PIXELFORMAT_INDEX_B + offset] = color.B; } /// @@ -664,13 +687,13 @@ namespace GreenshotPlugin.Core { /// /// /// - /// byte[4] as reference (a,r,g,b) + /// byte[4] as reference (r,g,b,a) public override void GetColorAt(int x, int y, byte[] color) { int offset = (x * 4) + (y * stride); - color[0] = pointer[AINDEX + offset]; - color[1] = pointer[RINDEX + offset]; - color[2] = pointer[GINDEX + offset]; - color[3] = pointer[BINDEX + offset]; + color[COLOR_INDEX_R] = pointer[PIXELFORMAT_INDEX_R + offset]; + color[COLOR_INDEX_G] = pointer[PIXELFORMAT_INDEX_G + offset]; + color[COLOR_INDEX_B] = pointer[PIXELFORMAT_INDEX_B + offset]; + color[COLOR_INDEX_A] = pointer[PIXELFORMAT_INDEX_A + offset]; } /// @@ -678,13 +701,13 @@ namespace GreenshotPlugin.Core { /// /// /// - /// byte[4] as reference (a,r,g,b) + /// byte[4] as reference (r,g,b,a) public override void SetColorAt(int x, int y, byte[] color) { int offset = (x * 4) + (y * stride); - pointer[AINDEX + offset] = color[0]; - pointer[RINDEX + offset] = color[1]; // R - pointer[GINDEX + offset] = color[2]; - pointer[BINDEX + offset] = color[3]; + pointer[PIXELFORMAT_INDEX_R + offset] = color[COLOR_INDEX_R]; // R + pointer[PIXELFORMAT_INDEX_G + offset] = color[COLOR_INDEX_G]; + pointer[PIXELFORMAT_INDEX_B + offset] = color[COLOR_INDEX_B]; + pointer[PIXELFORMAT_INDEX_A + offset] = color[COLOR_INDEX_A]; } /// @@ -696,10 +719,10 @@ namespace GreenshotPlugin.Core { /// Color public Color GetBlendedColorAt(int x, int y) { int offset = (x * 4) + (y * stride); - int a = pointer[AINDEX + offset]; - int red = pointer[RINDEX + offset]; - int green = pointer[GINDEX + offset]; - int blue = pointer[BINDEX + offset]; + int a = pointer[PIXELFORMAT_INDEX_A + offset]; + int red = pointer[PIXELFORMAT_INDEX_R + offset]; + int green = pointer[PIXELFORMAT_INDEX_G + offset]; + int blue = pointer[PIXELFORMAT_INDEX_B + offset]; if (a < 255) { // As the request is to get without alpha, we blend. diff --git a/GreenshotPlugin/Core/ImageHelper.cs b/GreenshotPlugin/Core/ImageHelper.cs index 83cb460c4..a7cde338c 100644 --- a/GreenshotPlugin/Core/ImageHelper.cs +++ b/GreenshotPlugin/Core/ImageHelper.cs @@ -658,29 +658,94 @@ namespace GreenshotPlugin.Core { /// Bitmap to blur /// Must be ODD! /// Bitmap - public static Bitmap BoxBlur(Bitmap sourceBitmap, int range) { + public static Bitmap ApplyBoxBlur(Bitmap destinationBitmap, int range) { if ((range & 1) == 0) { range++; //throw new InvalidOperationException("Range must be odd!"); } - using (IFastBitmap bbbDest = FastBitmap.CreateCloneOf(sourceBitmap, PixelFormat.Format32bppArgb)) { - BoxBlurHorizontal(bbbDest, range); - BoxBlurVertical(bbbDest, range); - BoxBlurHorizontal(bbbDest, range + 1); - BoxBlurVertical(bbbDest, range + 1); - return bbbDest.UnlockAndReturnBitmap(); + using (IFastBitmap fastBitmap = FastBitmap.Create(destinationBitmap)) { + // Box blurs are frequently used to approximate a Gaussian blur. + // By the central limit theorem, if applied 3 times on the same image, a box blur approximates the Gaussian kernel to within about 3%, yielding the same result as a quadratic convolution kernel. + if (fastBitmap.hasAlphaChannel) { + BoxBlurHorizontalAlpha(fastBitmap, range); + BoxBlurVerticalAlpha(fastBitmap, range); + BoxBlurHorizontalAlpha(fastBitmap, range); + BoxBlurVerticalAlpha(fastBitmap, range); + BoxBlurHorizontalAlpha(fastBitmap, range); + BoxBlurVerticalAlpha(fastBitmap, range); + } else { + BoxBlurHorizontal(fastBitmap, range); + BoxBlurVertical(fastBitmap, range); + BoxBlurHorizontal(fastBitmap, range); + BoxBlurVertical(fastBitmap, range); + BoxBlurHorizontal(fastBitmap, range); + BoxBlurVertical(fastBitmap, range); + } + + + return fastBitmap.UnlockAndReturnBitmap(); } } /// /// BoxBlurHorizontal is a private helper method for the BoxBlur /// - /// Source BitmapBuffer - /// Target BitmapBuffer + /// Target BitmapBuffer /// Range must be odd! - private static void BoxBlurHorizontal(IFastBitmap bbbDest, int range) { - int w = bbbDest.Width; - int h = bbbDest.Height; + public static void BoxBlurHorizontal(IFastBitmap targetFastBitmap, int range) { + if (targetFastBitmap.hasAlphaChannel) { + throw new NotSupportedException("BoxBlurHorizontal should NOT be called for bitmaps with alpha channel"); + } + int w = targetFastBitmap.Width; + int h = targetFastBitmap.Height; + int halfRange = range / 2; + Color[] newColors = new Color[w]; + byte[] tmpColor = new byte[3]; + for (int y = 0; y < h; y++) { + int hits = 0; + int r = 0; + int g = 0; + int b = 0; + for (int x = -halfRange; x < w; x++) { + int oldPixel = x - halfRange - 1; + if (oldPixel >= 0) { + targetFastBitmap.GetColorAt(oldPixel, y, tmpColor); + r -= tmpColor[FastBitmap.COLOR_INDEX_R]; + g -= tmpColor[FastBitmap.COLOR_INDEX_G]; + b -= tmpColor[FastBitmap.COLOR_INDEX_B]; + hits--; + } + + int newPixel = x + halfRange; + if (newPixel < w) { + targetFastBitmap.GetColorAt(newPixel, y, tmpColor); + r += tmpColor[FastBitmap.COLOR_INDEX_R]; + g += tmpColor[FastBitmap.COLOR_INDEX_G]; + b += tmpColor[FastBitmap.COLOR_INDEX_B]; + hits++; + } + + if (x >= 0) { + newColors[x] = Color.FromArgb(255, (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); + } + } + for (int x = 0; x < w; x++) { + targetFastBitmap.SetColorAt(x, y, newColors[x]); + } + } + } + /// + /// BoxBlurHorizontal is a private helper method for the BoxBlur, only for IFastBitmaps with alpha channel + /// + /// Source BitmapBuffer + /// Target BitmapBuffer + /// Range must be odd! + public static void BoxBlurHorizontalAlpha(IFastBitmap targetFastBitmap, int range) { + if (!targetFastBitmap.hasAlphaChannel) { + throw new NotSupportedException("BoxBlurHorizontalAlpha should be called for bitmaps with alpha channel"); + } + int w = targetFastBitmap.Width; + int h = targetFastBitmap.Height; int halfRange = range / 2; Color[] newColors = new Color[w]; byte[] tmpColor = new byte[4]; @@ -693,21 +758,21 @@ namespace GreenshotPlugin.Core { for (int x = -halfRange; x < w; x++) { int oldPixel = x - halfRange - 1; if (oldPixel >= 0) { - bbbDest.GetColorAt(oldPixel, y, tmpColor); - a -= tmpColor[0]; - r -= tmpColor[1]; - g -= tmpColor[2]; - b -= tmpColor[3]; + targetFastBitmap.GetColorAt(oldPixel, y, tmpColor); + a -= tmpColor[FastBitmap.COLOR_INDEX_A]; + r -= tmpColor[FastBitmap.COLOR_INDEX_R]; + g -= tmpColor[FastBitmap.COLOR_INDEX_G]; + b -= tmpColor[FastBitmap.COLOR_INDEX_B]; hits--; } int newPixel = x + halfRange; if (newPixel < w) { - bbbDest.GetColorAt(newPixel, y, tmpColor); - a += tmpColor[0]; - r += tmpColor[1]; - g += tmpColor[2]; - b += tmpColor[3]; + targetFastBitmap.GetColorAt(newPixel, y, tmpColor); + a += tmpColor[FastBitmap.COLOR_INDEX_A]; + r += tmpColor[FastBitmap.COLOR_INDEX_R]; + g += tmpColor[FastBitmap.COLOR_INDEX_G]; + b += tmpColor[FastBitmap.COLOR_INDEX_B]; hits++; } @@ -716,7 +781,7 @@ namespace GreenshotPlugin.Core { } } for (int x = 0; x < w; x++) { - bbbDest.SetColorAt(x, y, newColors[x]); + targetFastBitmap.SetColorAt(x, y, newColors[x]); } } } @@ -724,11 +789,66 @@ namespace GreenshotPlugin.Core { /// /// BoxBlurVertical is a private helper method for the BoxBlur /// - /// BitmapBuffer which previously was created with BoxBlurHorizontal + /// BitmapBuffer which previously was created with BoxBlurHorizontal /// Range must be odd! - private static void BoxBlurVertical(IFastBitmap bbbDest, int range) { - int w = bbbDest.Width; - int h = bbbDest.Height; + public static void BoxBlurVertical(IFastBitmap targetFastBitmap, int range) { + if (targetFastBitmap.hasAlphaChannel) { + throw new NotSupportedException("BoxBlurVertical should NOT be called for bitmaps with alpha channel"); + } + int w = targetFastBitmap.Width; + int h = targetFastBitmap.Height; + int halfRange = range / 2; + Color[] newColors = new Color[h]; + int oldPixelOffset = -(halfRange + 1) * w; + int newPixelOffset = (halfRange) * w; + byte[] tmpColor = new byte[4]; + for (int x = 0; x < w; x++) { + int hits = 0; + int r = 0; + int g = 0; + int b = 0; + for (int y = -halfRange; y < h; y++) { + int oldPixel = y - halfRange - 1; + if (oldPixel >= 0) { + targetFastBitmap.GetColorAt(x, oldPixel, tmpColor); + r -= tmpColor[FastBitmap.COLOR_INDEX_R]; + g -= tmpColor[FastBitmap.COLOR_INDEX_G]; + b -= tmpColor[FastBitmap.COLOR_INDEX_B]; + hits--; + } + + int newPixel = y + halfRange; + if (newPixel < h) { + targetFastBitmap.GetColorAt(x, newPixel, tmpColor); + r += tmpColor[FastBitmap.COLOR_INDEX_R]; + g += tmpColor[FastBitmap.COLOR_INDEX_G]; + b += tmpColor[FastBitmap.COLOR_INDEX_B]; + hits++; + } + + if (y >= 0) { + newColors[y] = Color.FromArgb(255, (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); + } + } + + for (int y = 0; y < h; y++) { + targetFastBitmap.SetColorAt(x, y, newColors[y]); + } + } + } + + /// + /// BoxBlurVertical is a private helper method for the BoxBlur + /// + /// BitmapBuffer which previously was created with BoxBlurHorizontal + /// Range must be odd! + public static void BoxBlurVerticalAlpha(IFastBitmap targetFastBitmap, int range) { + if (!targetFastBitmap.hasAlphaChannel) { + throw new NotSupportedException("BoxBlurVerticalAlpha should be called for bitmaps with alpha channel"); + } + + int w = targetFastBitmap.Width; + int h = targetFastBitmap.Height; int halfRange = range / 2; Color[] newColors = new Color[h]; int oldPixelOffset = -(halfRange + 1) * w; @@ -744,22 +864,22 @@ namespace GreenshotPlugin.Core { int oldPixel = y - halfRange - 1; if (oldPixel >= 0) { //int colorg = pixels[index + oldPixelOffset]; - bbbDest.GetColorAt(x, oldPixel, tmpColor); - a -= tmpColor[0]; - r -= tmpColor[1]; - g -= tmpColor[2]; - b -= tmpColor[3]; + targetFastBitmap.GetColorAt(x, oldPixel, tmpColor); + a -= tmpColor[FastBitmap.COLOR_INDEX_A]; + r -= tmpColor[FastBitmap.COLOR_INDEX_R]; + g -= tmpColor[FastBitmap.COLOR_INDEX_G]; + b -= tmpColor[FastBitmap.COLOR_INDEX_B]; hits--; } int newPixel = y + halfRange; if (newPixel < h) { //int colorg = pixels[index + newPixelOffset]; - bbbDest.GetColorAt(x, newPixel, tmpColor); - a += tmpColor[0]; - r += tmpColor[1]; - g += tmpColor[2]; - b += tmpColor[3]; + targetFastBitmap.GetColorAt(x, newPixel, tmpColor); + a += tmpColor[FastBitmap.COLOR_INDEX_A]; + r += tmpColor[FastBitmap.COLOR_INDEX_R]; + g += tmpColor[FastBitmap.COLOR_INDEX_G]; + b += tmpColor[FastBitmap.COLOR_INDEX_B]; hits++; } @@ -769,12 +889,11 @@ namespace GreenshotPlugin.Core { } for (int y = 0; y < h; y++) { - bbbDest.SetColorAt(x, y, newColors[y]); + targetFastBitmap.SetColorAt(x, y, newColors[y]); } } } - /// /// This method fixes the problem that we can't apply a filter outside the target bitmap, /// therefor the filtered-bitmap will be shifted if we try to draw it outside the target bitmap. @@ -807,41 +926,40 @@ namespace GreenshotPlugin.Core { /// Bitmap with the shadow, is bigger than the sourceBitmap!! public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, Point shadowOffset, out Point offset, PixelFormat targetPixelformat) { // Create a new "clean" image - Bitmap returnImage = null; offset = shadowOffset; offset.X += shadowSize - 1; offset.Y += shadowSize - 1; - using (Bitmap tmpImage = CreateEmpty(sourceBitmap.Width + (shadowSize * 2), sourceBitmap.Height + (shadowSize * 2), targetPixelformat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution)) { - using (Graphics graphics = Graphics.FromImage(tmpImage)) { - // Make sure we draw with the best quality! - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + Bitmap returnImage = CreateEmpty(sourceBitmap.Width + (shadowSize * 2), sourceBitmap.Height + (shadowSize * 2), targetPixelformat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution); + using (Graphics graphics = Graphics.FromImage(returnImage)) { + // Make sure we draw with the best quality! + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - // Draw "shadow" offsetted - ImageAttributes ia = new ImageAttributes(); - ColorMatrix cm = new ColorMatrix(); - cm.Matrix00 = 0; - cm.Matrix11 = 0; - cm.Matrix22 = 0; - cm.Matrix33 = darkness; - ia.SetColorMatrix(cm); - Rectangle shadowRectangle = new Rectangle(new Point(shadowSize, shadowSize), sourceBitmap.Size); - graphics.DrawImage(sourceBitmap, shadowRectangle, 0, 0, sourceBitmap.Width, sourceBitmap.Height, GraphicsUnit.Pixel, ia); - } - // blur "shadow", apply to whole new image - //using (Bitmap blurImage = FastBlur(newImage, shadowSize-1)) { + // Draw "shadow" offsetted + ImageAttributes ia = new ImageAttributes(); + ColorMatrix cm = new ColorMatrix(); + cm.Matrix00 = 0; + cm.Matrix11 = 0; + cm.Matrix22 = 0; + cm.Matrix33 = darkness; + ia.SetColorMatrix(cm); + Rectangle shadowRectangle = new Rectangle(new Point(shadowSize, shadowSize), sourceBitmap.Size); + graphics.DrawImage(sourceBitmap, shadowRectangle, 0, 0, sourceBitmap.Width, sourceBitmap.Height, GraphicsUnit.Pixel, ia); + } + // blur "shadow", apply to whole new image - // Gaussian - Rectangle newImageRectangle = new Rectangle(0, 0, tmpImage.Width, tmpImage.Height); - returnImage = CreateBlur(tmpImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle); + // Gaussian + Rectangle newImageRectangle = new Rectangle(0, 0, returnImage.Width, returnImage.Height); + if (!GDIplus.ApplyBlur(returnImage, newImageRectangle, shadowSize, false)) { + // something went wrong, try normal software blur + //returnImage = CreateBlur(returnImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle); + ApplyBoxBlur(returnImage, shadowSize); - // Box - //returnImage = BoxBlur(tmpImage, shadowSize); - //returnImage = FastBlur(tmpImage, shadowSize - 1); } + if (returnImage != null) { using (Graphics graphics = Graphics.FromImage(returnImage)) { // Make sure we draw with the best quality! diff --git a/GreenshotPlugin/UnmanagedHelpers/GDIplus.cs b/GreenshotPlugin/UnmanagedHelpers/GDIplus.cs index 0e78a3a71..d641407b4 100644 --- a/GreenshotPlugin/UnmanagedHelpers/GDIplus.cs +++ b/GreenshotPlugin/UnmanagedHelpers/GDIplus.cs @@ -164,7 +164,16 @@ namespace GreenshotPlugin.UnmanagedHelpers { } return (IntPtr)FIELD_INFO_NATIVE_IMAGEATTRIBUTES.GetValue(imageAttributes); } - + + private static bool canApply() { + if (Environment.OSVersion.Version.Major < 6) { + return false; + } else if ((Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 2) && radius < 20) { + return false; + } + return true; + } + /// /// Use the GDI+ blur effect on the bitmap /// @@ -174,7 +183,7 @@ namespace GreenshotPlugin.UnmanagedHelpers { /// bool true if the edges are expanded with the radius /// false if there is no GDI+ available or an exception occured public static bool ApplyBlur(Bitmap destinationBitmap, Rectangle area, int radius, bool expandEdges) { - if (Environment.OSVersion.Version.Major < 6) { + if (!canApply()) { return false; } IntPtr hBlurParams = IntPtr.Zero; @@ -228,11 +237,10 @@ namespace GreenshotPlugin.UnmanagedHelpers { /// /// false if there is no GDI+ available or an exception occured public static bool DrawWithBlur(Graphics graphics, Bitmap image, Rectangle source, Matrix transform, ImageAttributes imageAttributes, int radius, bool expandEdges) { - if (Environment.OSVersion.Version.Major < 6) { - return false; - } else if ((Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 2) && radius < 20) { + if (!canApply()) { return false; } + IntPtr hBlurParams = IntPtr.Zero; IntPtr hEffect = IntPtr.Zero;