Fixed an issue with the FastBitmap, renamed methods in the BitmapBuffer to be equal to the FastBitmap implementation. In general it's now very easy to switch from the BitmapBuffer to the FastBitmap implementation, but one should consider that the BitmapBuffer checks the x,y if they are inside... and supports a "apply rectangle". Had to "refactor" the CreateBlur for this. Changed the CreateShadow to work with the BoxBlur, this changes the time to create a shadow of a 1280x1024 from ~2.6 to ~1.2 seconds on my PC... which is still slow. The resulting effect seems to be extremely similar... good enough for me.

git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@2480 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
RKrom 2013-02-12 16:36:09 +00:00
commit 3f4d93f2b6
3 changed files with 315 additions and 215 deletions

View file

@ -64,6 +64,11 @@ namespace GreenshotPlugin.Core {
}
}
public Bitmap UnlockAndReturnBitmap() {
Unlock();
return Bitmap;
}
public PixelFormat PixelFormat {
get {
return bitmap.PixelFormat;
@ -208,9 +213,9 @@ namespace GreenshotPlugin.Core {
}
}
/**
* Unlock the System Memory
*/
/// <summary>
/// Unlock the System Memory
/// </summary>
public void Unlock() {
if (bitsLocked) {
bitmap.UnlockBits(bmData);
@ -218,24 +223,31 @@ namespace GreenshotPlugin.Core {
}
}
/**
* Draw the stored bitmap to the destionation bitmap at the supplied point
*/
/// <summary>
/// Draw the stored bitmap to the destionation bitmap at the supplied point
/// </summary>
/// <param name="graphics"></param>
/// <param name="destination"></param>
public void DrawTo(Graphics graphics, Point destination) {
DrawTo(graphics, null, destination);
}
/**
* Draw the stored Bitmap on the Destination bitmap with the specified rectangle
* Be aware that the stored bitmap will be resized to the specified rectangle!!
*/
/// <summary>
/// Draw the stored Bitmap on the Destination bitmap with the specified rectangle
/// Be aware that the stored bitmap will be resized to the specified rectangle!!
/// </summary>
/// <param name="graphics"></param>
/// <param name="destinationRect"></param>
public void DrawTo(Graphics graphics, Rectangle destinationRect) {
DrawTo(graphics, destinationRect, null);
}
/**
* private helper to draw the bitmap
*/
/// <summary>
/// private helper to draw the bitmap
/// </summary>
/// <param name="graphics"></param>
/// <param name="destinationRect"></param>
/// <param name="destination"></param>
private void DrawTo(Graphics graphics, Rectangle? destinationRect, Point? destination) {
if (destinationRect.HasValue) {
// Does the rect have any pixels?
@ -288,10 +300,13 @@ namespace GreenshotPlugin.Core {
}
}
/**
* Set the color at location x,y
* Before the first time this is called the Lock() should be called once!
*/
/// <summary>
/// Set the color at location x,y
/// Before the first time this is called the Lock() should be called once!
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color"></param>
public void SetColorAt(int x, int y, Color color) {
if(x>=0 && y>=0 && x<rect.Width && y<rect.Height) {
int offset = x*bytesPerPixel+y*stride;
@ -302,10 +317,13 @@ namespace GreenshotPlugin.Core {
}
}
/**
* Retrieve the color at location x,y to a byte[]
* Before the first time this is called the Lock() should be called once!
*/
/// <summary>
/// Retrieve the color at location x,y to a byte[]
/// Before the first time this is called the Lock() should be called once!
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color"></param>
public void GetUncheckedColorIn(int x, int y, byte[] color) {
int offset = x * bytesPerPixel + y * stride;
color[0] = (aIndex == -1) ? (byte)255 : (byte)pointer[aIndex + offset];
@ -314,11 +332,14 @@ namespace GreenshotPlugin.Core {
color[3] = pointer[bIndex + offset];
}
/**
* Retrieve the color at location x,y to a byte[]
* Before the first time this is called the Lock() should be called once!
*/
public void GetColorIn(int x, int y, byte[] color) {
/// <summary>
/// Retrieve the color at location x,y to a byte[]
/// Before the first time this is called the Lock() should be called once!
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color"></param>
public void GetColorAt(int x, int y, byte[] color) {
if (x >= 0 && y >= 0 && x < rect.Width && y < rect.Height) {
int offset = x * bytesPerPixel + y * stride;
color[0] = (aIndex == -1) ? (byte)255 : (byte)pointer[aIndex + offset];
@ -333,11 +354,14 @@ namespace GreenshotPlugin.Core {
}
}
/**
* Set the color at location x,y as an array
* Before the first time this is called the Lock() should be called once!
*/
public void SetColorArrayAt(int x, int y, byte[] colors) {
/// <summary>
/// Set the color at location x,y as an array
/// Before the first time this is called the Lock() should be called once!
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="colors"></param>
public void SetColorAt(int x, int y, byte[] colors) {
if(x>=0 && y>=0 && x<rect.Width && y<rect.Height) {
int offset = x*bytesPerPixel+y*stride;
if(aIndex!=-1) pointer[aIndex+offset] = (byte)colors[0];
@ -346,20 +370,10 @@ namespace GreenshotPlugin.Core {
pointer[bIndex+offset] = (byte)colors[3];
}
}
/**
* Set the color at location x,y as an array
* Before the first time this is called the Lock() should be called once!
*/
public void SetUncheckedColorArrayAt(int x, int y, byte[] colors) {
int offset = x * bytesPerPixel + y * stride;
if (aIndex != -1) pointer[aIndex + offset] = (byte)colors[0];
pointer[rIndex + offset] = (byte)colors[1];
pointer[gIndex + offset] = (byte)colors[2];
pointer[bIndex + offset] = (byte)colors[3];
}
/**
* Set some internal values for accessing the bitmap according to the PixelFormat
*/
/// <summary>
/// Set some internal values for accessing the bitmap according to the PixelFormat
/// </summary>
private void PrepareForPixelFormat() {
// aIndex is only set if the pixel format supports "A".
aIndex = -1;

View file

@ -29,20 +29,79 @@ namespace GreenshotPlugin.Core {
/// The interface for the FastBitmap
/// </summary>
public interface IFastBitmap : IDisposable {
/// <summary>
/// Get the color at x,y
/// The returned Color object depends on the underlying pixel format
/// </summary>
/// <param name="x">int x</param>
/// <param name="y">int y</param>
/// <returns>Color</returns>
Color GetColorAt(int x, int y);
/// <summary>
/// Set the color at the specified location
/// </summary>
/// <param name="x">int x</param>
/// <param name="y">int y</param>
/// <param name="color">Color</param>
void SetColorAt(int x, int y, Color color);
/// <summary>
/// Get the color at x,y
/// The returned byte[] color depends on the underlying pixel format
/// </summary>
/// <param name="x">int x</param>
/// <param name="y">int y</par
void GetColorAt(int x, int y, byte[] color);
/// <summary>
/// Set the color at the specified location
/// </summary>
/// <param name="x">int x</param>
/// <param name="y">int y</param>
/// <param name="color">byte[] color</param>
void SetColorAt(int x, int y, byte[] color);
/// <summary>
/// Lock the bitmap
/// </summary>
void Lock();
/// <summary>
/// Unlock the bitmap
/// </summary>
void Unlock();
/// <summary>
/// Unlock the bitmap and get the underlying bitmap in one call
/// </summary>
/// <returns></returns>
Bitmap UnlockAndReturnBitmap();
/// <summary>
/// Size of the underlying image
/// </summary>
Size Size {
get;
}
/// <summary>
/// Height of the underlying image
/// </summary>
int Height {
get;
}
/// <summary>
/// Width of the underlying image
/// </summary>
int Width {
get;
}
/// <summary>
/// Does the underlying image need to be disposed
/// </summary>
bool NeedsDispose {
get;
set;
@ -55,6 +114,10 @@ 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;
/// <summary>
/// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap
/// </summary>
@ -86,6 +149,7 @@ namespace GreenshotPlugin.Core {
case PixelFormat.Format32bppRgb:
return new Fast32RGBBitmap(source);
case PixelFormat.Format32bppArgb:
case PixelFormat.Format32bppPArgb:
return new Fast32ARGBBitmap(source);
default:
throw new NotSupportedException(string.Format("Not supported Pixelformat {0}", source.PixelFormat));
@ -97,7 +161,7 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <param name="source">Bitmap to access</param>
/// <returns>IFastBitmap</returns>
public static IFastBitmap CreateDestinationFor(Bitmap source, PixelFormat pixelFormat) {
public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat) {
Bitmap destination = ImageHelper.CloneArea(source, Rectangle.Empty, pixelFormat);
IFastBitmap fastBitmap = Create(destination);
((FastBitmap)fastBitmap).NeedsDispose = true;
@ -119,8 +183,13 @@ namespace GreenshotPlugin.Core {
return fastBitmap;
}
/// <summary>
/// Constructor which stores the image and locks it when called
/// </summary>
/// <param name="bitmap"></param>
protected FastBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
Lock();
}
/// <summary>
@ -272,8 +341,13 @@ namespace GreenshotPlugin.Core {
public abstract Color GetColorAt(int x, int y);
public abstract void SetColorAt(int x, int y, Color color);
public abstract void GetColorAt(int x, int y, byte[] color);
public abstract void SetColorAt(int x, int y, byte[] color);
}
/// <summary>
/// This is the implementation of the FastBitmat for the 8BPP pixelformat
/// </summary>
public unsafe class FastChunkyBitmap : FastBitmap {
// Used for indexed images
private Color[] colorEntries;
@ -295,6 +369,26 @@ namespace GreenshotPlugin.Core {
return colorEntries[colorIndex];
}
/// <summary>
/// Get the color from the specified location into the specified array
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color">byte[4] as reference</param>
public override void GetColorAt(int x, int y, byte[] color) {
throw new NotImplementedException("No performance gain!");
}
/// <summary>
/// Set the color at the specified location from the specified array
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color">byte[4] as reference</param>
public override void SetColorAt(int x, int y, byte[] color) {
throw new NotImplementedException("No performance gain!");
}
/// <summary>
/// Get the color-index from the specified location
/// </summary>
@ -361,7 +455,7 @@ namespace GreenshotPlugin.Core {
/// <returns>Color</returns>
public override Color GetColorAt(int x, int y) {
int offset = (x * 3) + (y * stride);
return Color.FromArgb(255, pointer[2 + offset], pointer[1 + offset], pointer[offset]);
return Color.FromArgb(255, pointer[RINDEX + offset], pointer[GINDEX + offset], pointer[BINDEX + offset]);
}
/// <summary>
@ -373,10 +467,38 @@ namespace GreenshotPlugin.Core {
/// <param name="color"></param>
public override void SetColorAt(int x, int y, Color color) {
int offset = (x * 3) + (y * stride);
pointer[2 + offset] = color.R;
pointer[1 + offset] = color.G;
pointer[offset] = color.B;
pointer[RINDEX + offset] = color.R;
pointer[GINDEX + offset] = color.G;
pointer[BINDEX + offset] = color.B;
}
/// <summary>
/// Get the color from the specified location into the specified array
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
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];
}
/// <summary>
/// Set the color at the specified location from the specified array
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
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];
}
}
/// <summary>
@ -396,7 +518,7 @@ namespace GreenshotPlugin.Core {
/// <returns>Color</returns>
public override Color GetColorAt(int x, int y) {
int offset = (x * 4) + (y * stride);
return Color.FromArgb(255, pointer[2 + offset], pointer[1 + offset], pointer[offset]);
return Color.FromArgb(255, pointer[RINDEX + offset], pointer[GINDEX + offset], pointer[BINDEX + offset]);
}
/// <summary>
@ -408,9 +530,36 @@ namespace GreenshotPlugin.Core {
/// <param name="color"></param>
public override void SetColorAt(int x, int y, Color color) {
int offset = (x * 4) + (y * stride);
pointer[2 + offset] = color.R;
pointer[1 + offset] = color.G;
pointer[offset] = color.B;
pointer[RINDEX + offset] = color.R;
pointer[GINDEX + offset] = color.G;
pointer[BINDEX + offset] = color.B;
}
/// <summary>
/// Get the color from the specified location into the specified array
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
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];
}
/// <summary>
/// Set the color at the specified location from the specified array
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
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];
}
}
@ -434,7 +583,7 @@ namespace GreenshotPlugin.Core {
/// <returns>Color</returns>
public override Color GetColorAt(int x, int y) {
int offset = (x * 4) + (y * stride);
return Color.FromArgb(pointer[3 + offset], pointer[2 + offset], pointer[1 + offset], pointer[offset]);
return Color.FromArgb(pointer[AINDEX + offset], pointer[RINDEX + offset], pointer[GINDEX + offset], pointer[BINDEX + offset]);
}
/// <summary>
@ -446,10 +595,38 @@ namespace GreenshotPlugin.Core {
/// <param name="color"></param>
public override void SetColorAt(int x, int y, Color color) {
int offset = (x * 4) + (y * stride);
pointer[3 + offset] = color.A;
pointer[2 + offset] = color.R;
pointer[1 + offset] = color.G;
pointer[offset] = color.B;
pointer[AINDEX + offset] = color.A;
pointer[RINDEX + offset] = color.R;
pointer[GINDEX + offset] = color.G;
pointer[BINDEX + offset] = color.B;
}
/// <summary>
/// Get the color from the specified location into the specified array
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
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];
}
/// <summary>
/// Set the color at the specified location from the specified array
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color">byte[4] as reference (a,r,g,b)</param>
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];
}
/// <summary>
@ -459,12 +636,12 @@ namespace GreenshotPlugin.Core {
/// <param name="x">X coordinate</param>
/// <param name="y">Y Coordinate</param>
/// <returns>Color</returns>
public Color GetColorAtWithoutAlpha(int x, int y) {
public Color GetBlendedColorAt(int x, int y) {
int offset = (x * 4) + (y * stride);
int a = pointer[3 + offset];
int red = pointer[2 + offset];
int green = pointer[1 + offset];
int blue = pointer[offset];
int a = pointer[AINDEX + offset];
int red = pointer[RINDEX + offset];
int green = pointer[GINDEX + offset];
int blue = pointer[BINDEX + offset];
if (a < 255) {
// As the request is to get without alpha, we blend.

View file

@ -487,10 +487,12 @@ namespace GreenshotPlugin.Core {
byte[] settingColor = new byte[4];
byte[] readingColor = new byte[4];
using (BitmapBuffer bbbDest = new BitmapBuffer(sourceBitmap, applyRect, true)) {
bbbDest.Lock();
using (BitmapBuffer bbbSrc = new BitmapBuffer(sourceBitmap, applyRect, false)) {
bbbSrc.Lock();
using (BitmapBuffer dst = new BitmapBuffer(sourceBitmap, applyRect, true)) {
//using (IFastBitmap dst = FastBitmap.CreateEmpty(sourceBitmap.Size, sourceBitmap.PixelFormat, Color.Empty)) {
dst.Lock();
using (BitmapBuffer src = new BitmapBuffer(sourceBitmap, applyRect, false)) {
//using (IFastBitmap src = FastBitmap.Create(sourceBitmap)) {
src.Lock();
Random rand = new Random();
unchecked {
int r = blurRadius;
@ -519,12 +521,12 @@ namespace GreenshotPlugin.Core {
gSums[wx] = 0;
bSums[wx] = 0;
if (srcX >= 0 && srcX < bbbDest.Width) {
if (srcX >= 0 && srcX < src.Width) {
for (int wy = 0; wy < wlen; ++wy) {
int srcY = y + wy - r;
if (srcY >= 0 && srcY < bbbDest.Height) {
bbbSrc.GetColorIn(srcX, srcY, readingColor);
if (srcY >= 0 && srcY < src.Height) {
src.GetColorAt(srcX, srcY, readingColor);
int wp = w[wy];
waSums[wx] += wp;
@ -553,13 +555,13 @@ namespace GreenshotPlugin.Core {
if (parentBounds.Contains(applyRect.Left, applyRect.Top + y) ^ invert) {
if (waSum == 0 || wcSum == 0) {
bbbDest.SetUncheckedColorArrayAt(0, y, nullColor);
dst.SetColorAt(0, y, nullColor);
} else {
settingColor[0] = (byte)(aSum / waSum);
settingColor[1] = (byte)(rSum / wcSum);
settingColor[2] = (byte)(gSum / wcSum);
settingColor[3] = (byte)(bSum / wcSum);
bbbDest.SetUncheckedColorArrayAt(0, y, settingColor);
dst.SetColorAt(0, y, settingColor);
}
}
@ -609,7 +611,7 @@ namespace GreenshotPlugin.Core {
if ((useExportQuality || rand.NextDouble() < previewQuality) && srcY >= 0 && srcY < applyRect.Height) {
int wp = w[wy];
waSums[wx] += wp;
bbbSrc.GetColorIn(srcX, srcY, readingColor);
src.GetColorAt(srcX, srcY, readingColor);
wp *= readingColor[0] + (readingColor[0] >> 7);
wcSums[wx] += wp;
wp >>= 8;
@ -633,21 +635,20 @@ namespace GreenshotPlugin.Core {
wcSum >>= 8;
if (parentBounds.Contains(applyRect.Left + x, applyRect.Top + y) ^ invert) {
if (waSum == 0 || wcSum == 0) {
bbbDest.SetUncheckedColorArrayAt(x, y, nullColor);
dst.SetColorAt(x, y, nullColor);
} else {
settingColor[0] = (byte)(aSum / waSum);
settingColor[1] = (byte)(rSum / wcSum);
settingColor[2] = (byte)(gSum / wcSum);
settingColor[3] = (byte)(bSum / wcSum);
bbbDest.SetUncheckedColorArrayAt(x, y, settingColor);
dst.SetColorAt(x, y, settingColor);
}
}
}
}
}
}
bbbDest.Unlock();
return bbbDest.Bitmap;
return dst.UnlockAndReturnBitmap();
}
}
@ -659,17 +660,15 @@ namespace GreenshotPlugin.Core {
/// <returns>Bitmap</returns>
public static Bitmap BoxBlur(Bitmap sourceBitmap, int range) {
if ((range & 1) == 0) {
throw new InvalidOperationException("Range must be odd!");
}
using (BitmapBuffer bbbDest = new BitmapBuffer(sourceBitmap, true)) {
bbbDest.Lock();
using (BitmapBuffer bbbSrc = new BitmapBuffer(sourceBitmap, false)) {
bbbSrc.Lock();
BoxBlurHorizontal(bbbSrc, bbbDest, range);
range++;
//throw new InvalidOperationException("Range must be odd!");
}
using (IFastBitmap bbbDest = FastBitmap.CreateCloneOf(sourceBitmap, PixelFormat.Format32bppArgb)) {
BoxBlurHorizontal(bbbDest, range);
BoxBlurVertical(bbbDest, range);
bbbDest.Unlock();
return bbbDest.Bitmap;
BoxBlurHorizontal(bbbDest, range + 1);
BoxBlurVertical(bbbDest, range + 1);
return bbbDest.UnlockAndReturnBitmap();
}
}
@ -679,11 +678,11 @@ namespace GreenshotPlugin.Core {
/// <param name="bbbSrc">Source BitmapBuffer</param>
/// <param name="bbbDest">Target BitmapBuffer</param>
/// <param name="range">Range must be odd!</param>
private static void BoxBlurHorizontal(BitmapBuffer bbbSrc, BitmapBuffer bbbDest, int range) {
int w = bbbSrc.Width;
int h = bbbSrc.Height;
private static void BoxBlurHorizontal(IFastBitmap bbbDest, int range) {
int w = bbbDest.Width;
int h = bbbDest.Height;
int halfRange = range / 2;
int[] newColors = new int[w];
Color[] newColors = new Color[w];
byte[] tmpColor = new byte[4];
for (int y = 0; y < h; y++) {
int hits = 0;
@ -694,7 +693,7 @@ namespace GreenshotPlugin.Core {
for (int x = -halfRange; x < w; x++) {
int oldPixel = x - halfRange - 1;
if (oldPixel >= 0) {
bbbSrc.GetUncheckedColorIn(oldPixel, y, tmpColor);
bbbDest.GetColorAt(oldPixel, y, tmpColor);
a -= tmpColor[0];
r -= tmpColor[1];
g -= tmpColor[2];
@ -704,7 +703,7 @@ namespace GreenshotPlugin.Core {
int newPixel = x + halfRange;
if (newPixel < w) {
bbbSrc.GetUncheckedColorIn(newPixel, y, tmpColor);
bbbDest.GetColorAt(newPixel, y, tmpColor);
a += tmpColor[0];
r += tmpColor[1];
g += tmpColor[2];
@ -713,11 +712,11 @@ namespace GreenshotPlugin.Core {
}
if (x >= 0) {
newColors[x] = ((byte)(a / hits)) << 24 | ((byte)(r / hits) << 16) | ((byte)(g / hits) << 8 ) | ((byte)(b / hits));
newColors[x] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits));
}
}
for (int x = 0; x < w; x++) {
bbbDest.SetARGB(x, y, newColors[x]);
bbbDest.SetColorAt(x, y, newColors[x]);
}
}
}
@ -727,11 +726,11 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <param name="bbbDest">BitmapBuffer which previously was created with BoxBlurHorizontal</param>
/// <param name="range">Range must be odd!</param>
private static void BoxBlurVertical(BitmapBuffer bbbDest, int range) {
private static void BoxBlurVertical(IFastBitmap bbbDest, int range) {
int w = bbbDest.Width;
int h = bbbDest.Height;
int halfRange = range / 2;
int[] newColors = new int[h];
Color[] newColors = new Color[h];
int oldPixelOffset = -(halfRange + 1) * w;
int newPixelOffset = (halfRange) * w;
byte[] tmpColor = new byte[4];
@ -745,7 +744,7 @@ namespace GreenshotPlugin.Core {
int oldPixel = y - halfRange - 1;
if (oldPixel >= 0) {
//int colorg = pixels[index + oldPixelOffset];
bbbDest.GetUncheckedColorIn(x, oldPixel, tmpColor);
bbbDest.GetColorAt(x, oldPixel, tmpColor);
a -= tmpColor[0];
r -= tmpColor[1];
g -= tmpColor[2];
@ -756,7 +755,7 @@ namespace GreenshotPlugin.Core {
int newPixel = y + halfRange;
if (newPixel < h) {
//int colorg = pixels[index + newPixelOffset];
bbbDest.GetUncheckedColorIn(x, newPixel, tmpColor);
bbbDest.GetColorAt(x, newPixel, tmpColor);
a += tmpColor[0];
r += tmpColor[1];
g += tmpColor[2];
@ -765,121 +764,26 @@ namespace GreenshotPlugin.Core {
}
if (y >= 0) {
newColors[y] = ((byte)(a / hits)) << 24 | ((byte)(r / hits) << 16) | ((byte)(g / hits) << 8) | ((byte)(b / hits));
newColors[y] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits));
}
}
for (int y = 0; y < h; y++) {
bbbDest.SetARGB(x, y, newColors[y]);
bbbDest.SetColorAt(x, y, newColors[y]);
}
}
}
public static Bitmap FastBlur(Bitmap sourceBitmap, int radius) {
if (radius < 1) return null;
var rct = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height);
var dest = new int[rct.Width * rct.Height];
var source = new int[rct.Width * rct.Height];
var bits = sourceBitmap.LockBits(rct, ImageLockMode.ReadWrite, sourceBitmap.PixelFormat);
Marshal.Copy(bits.Scan0, source, 0, source.Length);
sourceBitmap.UnlockBits(bits);
int w = rct.Width;
int h = rct.Height;
int wm = w - 1;
int hm = h - 1;
int wh = w * h;
int div = radius + radius + 1;
var r = new int[wh];
var g = new int[wh];
var b = new int[wh];
int rsum, gsum, bsum, x, y, i, p1, p2, yi;
var vmin = new int[max(w, h)];
var vmax = new int[max(w, h)];
var dv = new int[256 * div];
for (i = 0; i < 256 * div; i++) {
dv[i] = (i / div);
}
int yw = yi = 0;
for (y = 0; y < h; y++) { // blur horizontal
rsum = gsum = bsum = 0;
for (i = -radius; i <= radius; i++) {
int p = source[yi + min(wm, max(i, 0))];
rsum += (p & 0xff0000) >> 16;
gsum += (p & 0x00ff00) >> 8;
bsum += p & 0x0000ff;
}
for (x = 0; x < w; x++) {
r[yi] = dv[rsum];
g[yi] = dv[gsum];
b[yi] = dv[bsum];
if (y == 0) {
vmin[x] = min(x + radius + 1, wm);
vmax[x] = max(x - radius, 0);
}
p1 = source[yw + vmin[x]];
p2 = source[yw + vmax[x]];
rsum += ((p1 & 0xff0000) - (p2 & 0xff0000)) >> 16;
gsum += ((p1 & 0x00ff00) - (p2 & 0x00ff00)) >> 8;
bsum += (p1 & 0x0000ff) - (p2 & 0x0000ff);
yi++;
}
yw += w;
}
for (x = 0; x < w; x++) { // blur vertical
rsum = gsum = bsum = 0;
int yp = -radius * w;
for (i = -radius; i <= radius; i++) {
yi = max(0, yp) + x;
rsum += r[yi];
gsum += g[yi];
bsum += b[yi];
yp += w;
}
yi = x;
for (y = 0; y < h; y++) {
dest[yi] = unchecked((int)(0xff000000u | (uint)(dv[rsum] << 16) | (uint)(dv[gsum] << 8) | (uint)dv[bsum]));
if (x == 0) {
vmin[y] = min(y + radius + 1, hm) * w;
vmax[y] = max(y - radius, 0) * w;
}
p1 = x + vmin[y];
p2 = x + vmax[y];
rsum += r[p1] - r[p2];
gsum += g[p1] - g[p2];
bsum += b[p1] - b[p2];
yi += w;
}
}
// copy back to image
Bitmap newImage = CreateEmptyLike(sourceBitmap, Color.Empty);
var bits2 = newImage.LockBits(rct, ImageLockMode.ReadWrite, newImage.PixelFormat);
Marshal.Copy(dest, 0, bits2.Scan0, dest.Length);
newImage.UnlockBits(bits);
return newImage;
}
private static int min(int a, int b) { return Math.Min(a, b); }
private static int max(int a, int b) { return Math.Max(a, b); }
/**
* 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.
* It will also account for the Invert flag.
*/
/// <summary>
/// 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.
/// It will also account for the Invert flag.
/// </summary>
/// <param name="applySize"></param>
/// <param name="rect"></param>
/// <param name="invert"></param>
/// <returns></returns>
public static Rectangle CreateIntersectRectangle(Size applySize, Rectangle rect, bool invert) {
Rectangle myRect;
if (invert) {
@ -927,10 +831,16 @@ namespace GreenshotPlugin.Core {
graphics.DrawImage(sourceBitmap, shadowRectangle, 0, 0, sourceBitmap.Width, sourceBitmap.Height, GraphicsUnit.Pixel, ia);
}
// blur "shadow", apply to whole new image
Rectangle newImageRectangle = new Rectangle(0, 0, tmpImage.Width, tmpImage.Height);
//using (Bitmap blurImage = FastBlur(newImage, shadowSize-1)) {
// Gaussian
Rectangle newImageRectangle = new Rectangle(0, 0, tmpImage.Width, tmpImage.Height);
returnImage = CreateBlur(tmpImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle);
// Box
//returnImage = BoxBlur(tmpImage, shadowSize);
//returnImage = FastBlur(tmpImage, shadowSize - 1);
}
if (returnImage != null) {
using (Graphics graphics = Graphics.FromImage(returnImage)) {
@ -976,18 +886,17 @@ namespace GreenshotPlugin.Core {
/// <param name="sourceImage">Bitmap to create a b/w off</param>
/// <returns>b/w bitmap</returns>
public static Bitmap CreateMonochrome(Image sourceImage) {
using (BitmapBuffer bb = new BitmapBuffer(sourceImage, true)) {
bb.Lock();
for (int y = 0; y < bb.Height; y++) {
for (int x = 0; x < bb.Width; x++) {
Color color = bb.GetColorAt(x, y);
int colorBrightness = (color.R+color.G+color.B > 382) ? 255 : 0;
using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(sourceImage, sourceImage.PixelFormat)) {
fastBitmap.Lock();
for (int y = 0; y < fastBitmap.Height; y++) {
for (int x = 0; x < fastBitmap.Width; x++) {
Color color = fastBitmap.GetColorAt(x, y);
int colorBrightness = (color.R + color.G + color.B > 382) ? 255 : 0;
Color monoColor = Color.FromArgb(color.A, colorBrightness, colorBrightness, colorBrightness);
bb.SetColorAt(x, y, monoColor);
fastBitmap.SetColorAt(x, y, monoColor);
}
}
bb.Unlock();
return bb.Bitmap;
return fastBitmap.UnlockAndReturnBitmap();
}
}