mirror of
https://github.com/greenshot/greenshot
synced 2025-07-14 00:53:51 -07:00
Did some performance improvements for the CreateShadow, without using our Gaussian (is still available) this saves a temporarily additional bitmap copy. On Vista/Windows 7 GDI+ is used, on other systems a fall-back with BoxBlur is used.
git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@2496 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
parent
9d1b0e5dc4
commit
0989012a37
3 changed files with 268 additions and 119 deletions
|
@ -107,6 +107,13 @@ namespace GreenshotPlugin.Core {
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns if this FastBitmap has an alpha channel
|
||||||
|
/// </summary>
|
||||||
|
bool hasAlphaChannel {
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draw the stored bitmap to the destionation bitmap at the supplied point
|
/// Draw the stored bitmap to the destionation bitmap at the supplied point
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -129,10 +136,16 @@ namespace GreenshotPlugin.Core {
|
||||||
public unsafe abstract class FastBitmap : IFastBitmap {
|
public unsafe abstract class FastBitmap : IFastBitmap {
|
||||||
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(FastBitmap));
|
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(FastBitmap));
|
||||||
|
|
||||||
protected const int AINDEX = 3;
|
protected const int PIXELFORMAT_INDEX_A = 3;
|
||||||
protected const int RINDEX = 2;
|
protected const int PIXELFORMAT_INDEX_R = 2;
|
||||||
protected const int GINDEX = 1;
|
protected const int PIXELFORMAT_INDEX_G = 1;
|
||||||
protected const int BINDEX = 0;
|
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;
|
protected Rectangle area = Rectangle.Empty;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap
|
/// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap
|
||||||
|
@ -289,6 +302,12 @@ namespace GreenshotPlugin.Core {
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual bool hasAlphaChannel {
|
||||||
|
get {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Destructor
|
/// Destructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -513,7 +532,7 @@ namespace GreenshotPlugin.Core {
|
||||||
/// <returns>Color</returns>
|
/// <returns>Color</returns>
|
||||||
public override Color GetColorAt(int x, int y) {
|
public override Color GetColorAt(int x, int y) {
|
||||||
int offset = (x * 3) + (y * stride);
|
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -525,9 +544,9 @@ namespace GreenshotPlugin.Core {
|
||||||
/// <param name="color"></param>
|
/// <param name="color"></param>
|
||||||
public override void SetColorAt(int x, int y, Color color) {
|
public override void SetColorAt(int x, int y, Color color) {
|
||||||
int offset = (x * 3) + (y * stride);
|
int offset = (x * 3) + (y * stride);
|
||||||
pointer[RINDEX + offset] = color.R;
|
pointer[PIXELFORMAT_INDEX_R + offset] = color.R;
|
||||||
pointer[GINDEX + offset] = color.G;
|
pointer[PIXELFORMAT_INDEX_G + offset] = color.G;
|
||||||
pointer[BINDEX + offset] = color.B;
|
pointer[PIXELFORMAT_INDEX_B + offset] = color.B;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -535,13 +554,12 @@ namespace GreenshotPlugin.Core {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="x"></param>
|
/// <param name="x"></param>
|
||||||
/// <param name="y"></param>
|
/// <param name="y"></param>
|
||||||
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
|
/// <param name="color">byte[4] as reference (r,g,b)</param>
|
||||||
public override void GetColorAt(int x, int y, byte[] color) {
|
public override void GetColorAt(int x, int y, byte[] color) {
|
||||||
int offset = (x * 3) + (y * stride);
|
int offset = (x * 3) + (y * stride);
|
||||||
color[0] = 255;
|
color[PIXELFORMAT_INDEX_R] = pointer[PIXELFORMAT_INDEX_R + offset];
|
||||||
color[1] = pointer[RINDEX + offset];
|
color[PIXELFORMAT_INDEX_G] = pointer[PIXELFORMAT_INDEX_G + offset];
|
||||||
color[2] = pointer[GINDEX + offset];
|
color[PIXELFORMAT_INDEX_B] = pointer[PIXELFORMAT_INDEX_B + offset];
|
||||||
color[3] = pointer[BINDEX + offset];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -549,12 +567,12 @@ namespace GreenshotPlugin.Core {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="x"></param>
|
/// <param name="x"></param>
|
||||||
/// <param name="y"></param>
|
/// <param name="y"></param>
|
||||||
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
|
/// <param name="color">byte[4] as reference (r,g,b)</param>
|
||||||
public override void SetColorAt(int x, int y, byte[] color) {
|
public override void SetColorAt(int x, int y, byte[] color) {
|
||||||
int offset = (x * 3) + (y * stride);
|
int offset = (x * 3) + (y * stride);
|
||||||
pointer[RINDEX + offset] = color[1];
|
pointer[PIXELFORMAT_INDEX_R + offset] = color[PIXELFORMAT_INDEX_R];
|
||||||
pointer[GINDEX + offset] = color[2];
|
pointer[PIXELFORMAT_INDEX_G + offset] = color[PIXELFORMAT_INDEX_G];
|
||||||
pointer[BINDEX + offset] = color[3];
|
pointer[PIXELFORMAT_INDEX_B + offset] = color[PIXELFORMAT_INDEX_B];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -576,7 +594,7 @@ namespace GreenshotPlugin.Core {
|
||||||
/// <returns>Color</returns>
|
/// <returns>Color</returns>
|
||||||
public override Color GetColorAt(int x, int y) {
|
public override Color GetColorAt(int x, int y) {
|
||||||
int offset = (x * 4) + (y * stride);
|
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -588,9 +606,9 @@ namespace GreenshotPlugin.Core {
|
||||||
/// <param name="color"></param>
|
/// <param name="color"></param>
|
||||||
public override void SetColorAt(int x, int y, Color color) {
|
public override void SetColorAt(int x, int y, Color color) {
|
||||||
int offset = (x * 4) + (y * stride);
|
int offset = (x * 4) + (y * stride);
|
||||||
pointer[RINDEX + offset] = color.R;
|
pointer[PIXELFORMAT_INDEX_R + offset] = color.R;
|
||||||
pointer[GINDEX + offset] = color.G;
|
pointer[PIXELFORMAT_INDEX_G + offset] = color.G;
|
||||||
pointer[BINDEX + offset] = color.B;
|
pointer[PIXELFORMAT_INDEX_B + offset] = color.B;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -601,10 +619,9 @@ namespace GreenshotPlugin.Core {
|
||||||
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
|
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
|
||||||
public override void GetColorAt(int x, int y, byte[] color) {
|
public override void GetColorAt(int x, int y, byte[] color) {
|
||||||
int offset = (x * 4) + (y * stride);
|
int offset = (x * 4) + (y * stride);
|
||||||
color[0] = 255;
|
color[COLOR_INDEX_R] = pointer[PIXELFORMAT_INDEX_R + offset];
|
||||||
color[1] = pointer[RINDEX + offset];
|
color[COLOR_INDEX_G] = pointer[PIXELFORMAT_INDEX_G + offset];
|
||||||
color[2] = pointer[GINDEX + offset];
|
color[COLOR_INDEX_B] = pointer[PIXELFORMAT_INDEX_B + offset];
|
||||||
color[3] = pointer[BINDEX + offset];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -612,12 +629,12 @@ namespace GreenshotPlugin.Core {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="x"></param>
|
/// <param name="x"></param>
|
||||||
/// <param name="y"></param>
|
/// <param name="y"></param>
|
||||||
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
|
/// <param name="color">byte[4] as reference (r,g,b)</param>
|
||||||
public override void SetColorAt(int x, int y, byte[] color) {
|
public override void SetColorAt(int x, int y, byte[] color) {
|
||||||
int offset = (x * 4) + (y * stride);
|
int offset = (x * 4) + (y * stride);
|
||||||
pointer[RINDEX + offset] = color[1]; // R
|
pointer[PIXELFORMAT_INDEX_R + offset] = color[COLOR_INDEX_R]; // R
|
||||||
pointer[GINDEX + offset] = color[2];
|
pointer[PIXELFORMAT_INDEX_G + offset] = color[COLOR_INDEX_G];
|
||||||
pointer[BINDEX + offset] = color[3];
|
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
|
/// This is the implementation of the IFastBitmap for 32 bit images with Alpha
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public unsafe class Fast32ARGBBitmap : FastBitmap {
|
public unsafe class Fast32ARGBBitmap : FastBitmap {
|
||||||
|
public override bool hasAlphaChannel {
|
||||||
|
get {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Color BackgroundBlendColor {
|
public Color BackgroundBlendColor {
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
|
@ -641,7 +664,7 @@ namespace GreenshotPlugin.Core {
|
||||||
/// <returns>Color</returns>
|
/// <returns>Color</returns>
|
||||||
public override Color GetColorAt(int x, int y) {
|
public override Color GetColorAt(int x, int y) {
|
||||||
int offset = (x * 4) + (y * stride);
|
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -653,10 +676,10 @@ namespace GreenshotPlugin.Core {
|
||||||
/// <param name="color"></param>
|
/// <param name="color"></param>
|
||||||
public override void SetColorAt(int x, int y, Color color) {
|
public override void SetColorAt(int x, int y, Color color) {
|
||||||
int offset = (x * 4) + (y * stride);
|
int offset = (x * 4) + (y * stride);
|
||||||
pointer[AINDEX + offset] = color.A;
|
pointer[PIXELFORMAT_INDEX_A + offset] = color.A;
|
||||||
pointer[RINDEX + offset] = color.R;
|
pointer[PIXELFORMAT_INDEX_R + offset] = color.R;
|
||||||
pointer[GINDEX + offset] = color.G;
|
pointer[PIXELFORMAT_INDEX_G + offset] = color.G;
|
||||||
pointer[BINDEX + offset] = color.B;
|
pointer[PIXELFORMAT_INDEX_B + offset] = color.B;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -664,13 +687,13 @@ namespace GreenshotPlugin.Core {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="x"></param>
|
/// <param name="x"></param>
|
||||||
/// <param name="y"></param>
|
/// <param name="y"></param>
|
||||||
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
|
/// <param name="color">byte[4] as reference (r,g,b,a)</param>
|
||||||
public override void GetColorAt(int x, int y, byte[] color) {
|
public override void GetColorAt(int x, int y, byte[] color) {
|
||||||
int offset = (x * 4) + (y * stride);
|
int offset = (x * 4) + (y * stride);
|
||||||
color[0] = pointer[AINDEX + offset];
|
color[COLOR_INDEX_R] = pointer[PIXELFORMAT_INDEX_R + offset];
|
||||||
color[1] = pointer[RINDEX + offset];
|
color[COLOR_INDEX_G] = pointer[PIXELFORMAT_INDEX_G + offset];
|
||||||
color[2] = pointer[GINDEX + offset];
|
color[COLOR_INDEX_B] = pointer[PIXELFORMAT_INDEX_B + offset];
|
||||||
color[3] = pointer[BINDEX + offset];
|
color[COLOR_INDEX_A] = pointer[PIXELFORMAT_INDEX_A + offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -678,13 +701,13 @@ namespace GreenshotPlugin.Core {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="x"></param>
|
/// <param name="x"></param>
|
||||||
/// <param name="y"></param>
|
/// <param name="y"></param>
|
||||||
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
|
/// <param name="color">byte[4] as reference (r,g,b,a)</param>
|
||||||
public override void SetColorAt(int x, int y, byte[] color) {
|
public override void SetColorAt(int x, int y, byte[] color) {
|
||||||
int offset = (x * 4) + (y * stride);
|
int offset = (x * 4) + (y * stride);
|
||||||
pointer[AINDEX + offset] = color[0];
|
pointer[PIXELFORMAT_INDEX_R + offset] = color[COLOR_INDEX_R]; // R
|
||||||
pointer[RINDEX + offset] = color[1]; // R
|
pointer[PIXELFORMAT_INDEX_G + offset] = color[COLOR_INDEX_G];
|
||||||
pointer[GINDEX + offset] = color[2];
|
pointer[PIXELFORMAT_INDEX_B + offset] = color[COLOR_INDEX_B];
|
||||||
pointer[BINDEX + offset] = color[3];
|
pointer[PIXELFORMAT_INDEX_A + offset] = color[COLOR_INDEX_A];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -696,10 +719,10 @@ namespace GreenshotPlugin.Core {
|
||||||
/// <returns>Color</returns>
|
/// <returns>Color</returns>
|
||||||
public Color GetBlendedColorAt(int x, int y) {
|
public Color GetBlendedColorAt(int x, int y) {
|
||||||
int offset = (x * 4) + (y * stride);
|
int offset = (x * 4) + (y * stride);
|
||||||
int a = pointer[AINDEX + offset];
|
int a = pointer[PIXELFORMAT_INDEX_A + offset];
|
||||||
int red = pointer[RINDEX + offset];
|
int red = pointer[PIXELFORMAT_INDEX_R + offset];
|
||||||
int green = pointer[GINDEX + offset];
|
int green = pointer[PIXELFORMAT_INDEX_G + offset];
|
||||||
int blue = pointer[BINDEX + offset];
|
int blue = pointer[PIXELFORMAT_INDEX_B + offset];
|
||||||
|
|
||||||
if (a < 255) {
|
if (a < 255) {
|
||||||
// As the request is to get without alpha, we blend.
|
// As the request is to get without alpha, we blend.
|
||||||
|
|
|
@ -658,29 +658,94 @@ namespace GreenshotPlugin.Core {
|
||||||
/// <param name="sourceBitmap">Bitmap to blur</param>
|
/// <param name="sourceBitmap">Bitmap to blur</param>
|
||||||
/// <param name="range">Must be ODD!</param>
|
/// <param name="range">Must be ODD!</param>
|
||||||
/// <returns>Bitmap</returns>
|
/// <returns>Bitmap</returns>
|
||||||
public static Bitmap BoxBlur(Bitmap sourceBitmap, int range) {
|
public static Bitmap ApplyBoxBlur(Bitmap destinationBitmap, int range) {
|
||||||
if ((range & 1) == 0) {
|
if ((range & 1) == 0) {
|
||||||
range++;
|
range++;
|
||||||
//throw new InvalidOperationException("Range must be odd!");
|
//throw new InvalidOperationException("Range must be odd!");
|
||||||
}
|
}
|
||||||
using (IFastBitmap bbbDest = FastBitmap.CreateCloneOf(sourceBitmap, PixelFormat.Format32bppArgb)) {
|
using (IFastBitmap fastBitmap = FastBitmap.Create(destinationBitmap)) {
|
||||||
BoxBlurHorizontal(bbbDest, range);
|
// Box blurs are frequently used to approximate a Gaussian blur.
|
||||||
BoxBlurVertical(bbbDest, range);
|
// 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.
|
||||||
BoxBlurHorizontal(bbbDest, range + 1);
|
if (fastBitmap.hasAlphaChannel) {
|
||||||
BoxBlurVertical(bbbDest, range + 1);
|
BoxBlurHorizontalAlpha(fastBitmap, range);
|
||||||
return bbbDest.UnlockAndReturnBitmap();
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// BoxBlurHorizontal is a private helper method for the BoxBlur
|
/// BoxBlurHorizontal is a private helper method for the BoxBlur
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bbbSrc">Source BitmapBuffer</param>
|
/// <param name="targetFastBitmap">Target BitmapBuffer</param>
|
||||||
/// <param name="bbbDest">Target BitmapBuffer</param>
|
|
||||||
/// <param name="range">Range must be odd!</param>
|
/// <param name="range">Range must be odd!</param>
|
||||||
private static void BoxBlurHorizontal(IFastBitmap bbbDest, int range) {
|
public static void BoxBlurHorizontal(IFastBitmap targetFastBitmap, int range) {
|
||||||
int w = bbbDest.Width;
|
if (targetFastBitmap.hasAlphaChannel) {
|
||||||
int h = bbbDest.Height;
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// BoxBlurHorizontal is a private helper method for the BoxBlur, only for IFastBitmaps with alpha channel
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bbbSrc">Source BitmapBuffer</param>
|
||||||
|
/// <param name="targetFastBitmap">Target BitmapBuffer</param>
|
||||||
|
/// <param name="range">Range must be odd!</param>
|
||||||
|
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;
|
int halfRange = range / 2;
|
||||||
Color[] newColors = new Color[w];
|
Color[] newColors = new Color[w];
|
||||||
byte[] tmpColor = new byte[4];
|
byte[] tmpColor = new byte[4];
|
||||||
|
@ -693,21 +758,21 @@ namespace GreenshotPlugin.Core {
|
||||||
for (int x = -halfRange; x < w; x++) {
|
for (int x = -halfRange; x < w; x++) {
|
||||||
int oldPixel = x - halfRange - 1;
|
int oldPixel = x - halfRange - 1;
|
||||||
if (oldPixel >= 0) {
|
if (oldPixel >= 0) {
|
||||||
bbbDest.GetColorAt(oldPixel, y, tmpColor);
|
targetFastBitmap.GetColorAt(oldPixel, y, tmpColor);
|
||||||
a -= tmpColor[0];
|
a -= tmpColor[FastBitmap.COLOR_INDEX_A];
|
||||||
r -= tmpColor[1];
|
r -= tmpColor[FastBitmap.COLOR_INDEX_R];
|
||||||
g -= tmpColor[2];
|
g -= tmpColor[FastBitmap.COLOR_INDEX_G];
|
||||||
b -= tmpColor[3];
|
b -= tmpColor[FastBitmap.COLOR_INDEX_B];
|
||||||
hits--;
|
hits--;
|
||||||
}
|
}
|
||||||
|
|
||||||
int newPixel = x + halfRange;
|
int newPixel = x + halfRange;
|
||||||
if (newPixel < w) {
|
if (newPixel < w) {
|
||||||
bbbDest.GetColorAt(newPixel, y, tmpColor);
|
targetFastBitmap.GetColorAt(newPixel, y, tmpColor);
|
||||||
a += tmpColor[0];
|
a += tmpColor[FastBitmap.COLOR_INDEX_A];
|
||||||
r += tmpColor[1];
|
r += tmpColor[FastBitmap.COLOR_INDEX_R];
|
||||||
g += tmpColor[2];
|
g += tmpColor[FastBitmap.COLOR_INDEX_G];
|
||||||
b += tmpColor[3];
|
b += tmpColor[FastBitmap.COLOR_INDEX_B];
|
||||||
hits++;
|
hits++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,7 +781,7 @@ namespace GreenshotPlugin.Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int x = 0; x < w; x++) {
|
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 {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// BoxBlurVertical is a private helper method for the BoxBlur
|
/// BoxBlurVertical is a private helper method for the BoxBlur
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bbbDest">BitmapBuffer which previously was created with BoxBlurHorizontal</param>
|
/// <param name="targetFastBitmap">BitmapBuffer which previously was created with BoxBlurHorizontal</param>
|
||||||
/// <param name="range">Range must be odd!</param>
|
/// <param name="range">Range must be odd!</param>
|
||||||
private static void BoxBlurVertical(IFastBitmap bbbDest, int range) {
|
public static void BoxBlurVertical(IFastBitmap targetFastBitmap, int range) {
|
||||||
int w = bbbDest.Width;
|
if (targetFastBitmap.hasAlphaChannel) {
|
||||||
int h = bbbDest.Height;
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// BoxBlurVertical is a private helper method for the BoxBlur
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="targetFastBitmap">BitmapBuffer which previously was created with BoxBlurHorizontal</param>
|
||||||
|
/// <param name="range">Range must be odd!</param>
|
||||||
|
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;
|
int halfRange = range / 2;
|
||||||
Color[] newColors = new Color[h];
|
Color[] newColors = new Color[h];
|
||||||
int oldPixelOffset = -(halfRange + 1) * w;
|
int oldPixelOffset = -(halfRange + 1) * w;
|
||||||
|
@ -744,22 +864,22 @@ namespace GreenshotPlugin.Core {
|
||||||
int oldPixel = y - halfRange - 1;
|
int oldPixel = y - halfRange - 1;
|
||||||
if (oldPixel >= 0) {
|
if (oldPixel >= 0) {
|
||||||
//int colorg = pixels[index + oldPixelOffset];
|
//int colorg = pixels[index + oldPixelOffset];
|
||||||
bbbDest.GetColorAt(x, oldPixel, tmpColor);
|
targetFastBitmap.GetColorAt(x, oldPixel, tmpColor);
|
||||||
a -= tmpColor[0];
|
a -= tmpColor[FastBitmap.COLOR_INDEX_A];
|
||||||
r -= tmpColor[1];
|
r -= tmpColor[FastBitmap.COLOR_INDEX_R];
|
||||||
g -= tmpColor[2];
|
g -= tmpColor[FastBitmap.COLOR_INDEX_G];
|
||||||
b -= tmpColor[3];
|
b -= tmpColor[FastBitmap.COLOR_INDEX_B];
|
||||||
hits--;
|
hits--;
|
||||||
}
|
}
|
||||||
|
|
||||||
int newPixel = y + halfRange;
|
int newPixel = y + halfRange;
|
||||||
if (newPixel < h) {
|
if (newPixel < h) {
|
||||||
//int colorg = pixels[index + newPixelOffset];
|
//int colorg = pixels[index + newPixelOffset];
|
||||||
bbbDest.GetColorAt(x, newPixel, tmpColor);
|
targetFastBitmap.GetColorAt(x, newPixel, tmpColor);
|
||||||
a += tmpColor[0];
|
a += tmpColor[FastBitmap.COLOR_INDEX_A];
|
||||||
r += tmpColor[1];
|
r += tmpColor[FastBitmap.COLOR_INDEX_R];
|
||||||
g += tmpColor[2];
|
g += tmpColor[FastBitmap.COLOR_INDEX_G];
|
||||||
b += tmpColor[3];
|
b += tmpColor[FastBitmap.COLOR_INDEX_B];
|
||||||
hits++;
|
hits++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -769,12 +889,11 @@ namespace GreenshotPlugin.Core {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < h; y++) {
|
for (int y = 0; y < h; y++) {
|
||||||
bbbDest.SetColorAt(x, y, newColors[y]);
|
targetFastBitmap.SetColorAt(x, y, newColors[y]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method fixes the problem that we can't apply a filter outside the target bitmap,
|
/// 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.
|
/// therefor the filtered-bitmap will be shifted if we try to draw it outside the target bitmap.
|
||||||
|
@ -807,41 +926,40 @@ namespace GreenshotPlugin.Core {
|
||||||
/// <returns>Bitmap with the shadow, is bigger than the sourceBitmap!!</returns>
|
/// <returns>Bitmap with the shadow, is bigger than the sourceBitmap!!</returns>
|
||||||
public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, Point shadowOffset, out Point offset, PixelFormat targetPixelformat) {
|
public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, Point shadowOffset, out Point offset, PixelFormat targetPixelformat) {
|
||||||
// Create a new "clean" image
|
// Create a new "clean" image
|
||||||
Bitmap returnImage = null;
|
|
||||||
offset = shadowOffset;
|
offset = shadowOffset;
|
||||||
offset.X += shadowSize - 1;
|
offset.X += shadowSize - 1;
|
||||||
offset.Y += 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)) {
|
Bitmap returnImage = CreateEmpty(sourceBitmap.Width + (shadowSize * 2), sourceBitmap.Height + (shadowSize * 2), targetPixelformat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
|
||||||
using (Graphics graphics = Graphics.FromImage(tmpImage)) {
|
using (Graphics graphics = Graphics.FromImage(returnImage)) {
|
||||||
// Make sure we draw with the best quality!
|
// Make sure we draw with the best quality!
|
||||||
graphics.SmoothingMode = SmoothingMode.HighQuality;
|
graphics.SmoothingMode = SmoothingMode.HighQuality;
|
||||||
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||||
graphics.CompositingQuality = CompositingQuality.HighQuality;
|
graphics.CompositingQuality = CompositingQuality.HighQuality;
|
||||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||||
|
|
||||||
// Draw "shadow" offsetted
|
// Draw "shadow" offsetted
|
||||||
ImageAttributes ia = new ImageAttributes();
|
ImageAttributes ia = new ImageAttributes();
|
||||||
ColorMatrix cm = new ColorMatrix();
|
ColorMatrix cm = new ColorMatrix();
|
||||||
cm.Matrix00 = 0;
|
cm.Matrix00 = 0;
|
||||||
cm.Matrix11 = 0;
|
cm.Matrix11 = 0;
|
||||||
cm.Matrix22 = 0;
|
cm.Matrix22 = 0;
|
||||||
cm.Matrix33 = darkness;
|
cm.Matrix33 = darkness;
|
||||||
ia.SetColorMatrix(cm);
|
ia.SetColorMatrix(cm);
|
||||||
Rectangle shadowRectangle = new Rectangle(new Point(shadowSize, shadowSize), sourceBitmap.Size);
|
Rectangle shadowRectangle = new Rectangle(new Point(shadowSize, shadowSize), sourceBitmap.Size);
|
||||||
graphics.DrawImage(sourceBitmap, shadowRectangle, 0, 0, sourceBitmap.Width, sourceBitmap.Height, GraphicsUnit.Pixel, ia);
|
graphics.DrawImage(sourceBitmap, shadowRectangle, 0, 0, sourceBitmap.Width, sourceBitmap.Height, GraphicsUnit.Pixel, ia);
|
||||||
}
|
}
|
||||||
// blur "shadow", apply to whole new image
|
// blur "shadow", apply to whole new image
|
||||||
//using (Bitmap blurImage = FastBlur(newImage, shadowSize-1)) {
|
|
||||||
|
|
||||||
// Gaussian
|
// Gaussian
|
||||||
Rectangle newImageRectangle = new Rectangle(0, 0, tmpImage.Width, tmpImage.Height);
|
Rectangle newImageRectangle = new Rectangle(0, 0, returnImage.Width, returnImage.Height);
|
||||||
returnImage = CreateBlur(tmpImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle);
|
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);
|
//returnImage = FastBlur(tmpImage, shadowSize - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnImage != null) {
|
if (returnImage != null) {
|
||||||
using (Graphics graphics = Graphics.FromImage(returnImage)) {
|
using (Graphics graphics = Graphics.FromImage(returnImage)) {
|
||||||
// Make sure we draw with the best quality!
|
// Make sure we draw with the best quality!
|
||||||
|
|
|
@ -164,7 +164,16 @@ namespace GreenshotPlugin.UnmanagedHelpers {
|
||||||
}
|
}
|
||||||
return (IntPtr)FIELD_INFO_NATIVE_IMAGEATTRIBUTES.GetValue(imageAttributes);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use the GDI+ blur effect on the bitmap
|
/// Use the GDI+ blur effect on the bitmap
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -174,7 +183,7 @@ namespace GreenshotPlugin.UnmanagedHelpers {
|
||||||
/// <param name="expandEdges">bool true if the edges are expanded with the radius</param>
|
/// <param name="expandEdges">bool true if the edges are expanded with the radius</param>
|
||||||
/// <returns>false if there is no GDI+ available or an exception occured</returns>
|
/// <returns>false if there is no GDI+ available or an exception occured</returns>
|
||||||
public static bool ApplyBlur(Bitmap destinationBitmap, Rectangle area, int radius, bool expandEdges) {
|
public static bool ApplyBlur(Bitmap destinationBitmap, Rectangle area, int radius, bool expandEdges) {
|
||||||
if (Environment.OSVersion.Version.Major < 6) {
|
if (!canApply()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
IntPtr hBlurParams = IntPtr.Zero;
|
IntPtr hBlurParams = IntPtr.Zero;
|
||||||
|
@ -228,11 +237,10 @@ namespace GreenshotPlugin.UnmanagedHelpers {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>false if there is no GDI+ available or an exception occured</returns>
|
/// <returns>false if there is no GDI+ available or an exception occured</returns>
|
||||||
public static bool DrawWithBlur(Graphics graphics, Bitmap image, Rectangle source, Matrix transform, ImageAttributes imageAttributes, int radius, bool expandEdges) {
|
public static bool DrawWithBlur(Graphics graphics, Bitmap image, Rectangle source, Matrix transform, ImageAttributes imageAttributes, int radius, bool expandEdges) {
|
||||||
if (Environment.OSVersion.Version.Major < 6) {
|
if (!canApply()) {
|
||||||
return false;
|
|
||||||
} else if ((Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 2) && radius < 20) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IntPtr hBlurParams = IntPtr.Zero;
|
IntPtr hBlurParams = IntPtr.Zero;
|
||||||
IntPtr hEffect = IntPtr.Zero;
|
IntPtr hEffect = IntPtr.Zero;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue