mirror of
https://github.com/greenshot/greenshot
synced 2025-07-16 10:03:44 -07:00
Changed logic of the DWM capture which tries to keep the window to capture on it's location, so less movement is visible. Also added a BoxBlur which might be a faster replacement of the CreateBlur (which is Gaussian), but the quality needs to be checked. For the BoxBlur the BitmapBuffer is extended with a few new tricks.
git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@2245 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
parent
15d5bb58e4
commit
f63d4ca06e
5 changed files with 200 additions and 25 deletions
|
@ -797,7 +797,7 @@ namespace Greenshot.Drawing {
|
||||||
break;
|
break;
|
||||||
case Effects.TornEdge:
|
case Effects.TornEdge:
|
||||||
using (Bitmap tmpImage = ImageHelper.CreateTornEdge((Bitmap)Image)) {
|
using (Bitmap tmpImage = ImageHelper.CreateTornEdge((Bitmap)Image)) {
|
||||||
newImage = ImageHelper.CreateShadow(tmpImage, 1f, 6, ref offset, PixelFormat.Format32bppArgb); //Image.PixelFormat);
|
newImage = ImageHelper.CreateShadow(tmpImage, 1f, 7, ref offset, PixelFormat.Format32bppArgb); //Image.PixelFormat);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Effects.Border:
|
case Effects.Border:
|
||||||
|
|
|
@ -62,6 +62,12 @@ namespace GreenshotPlugin.Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PixelFormat PixelFormat {
|
||||||
|
get {
|
||||||
|
return bitmap.PixelFormat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[NonSerialized]
|
[NonSerialized]
|
||||||
private BitmapData bmData;
|
private BitmapData bmData;
|
||||||
[NonSerialized]
|
[NonSerialized]
|
||||||
|
@ -69,6 +75,8 @@ namespace GreenshotPlugin.Core {
|
||||||
[NonSerialized]
|
[NonSerialized]
|
||||||
private byte* pointer;
|
private byte* pointer;
|
||||||
[NonSerialized]
|
[NonSerialized]
|
||||||
|
private int* intPointer;
|
||||||
|
[NonSerialized]
|
||||||
private int stride; /* bytes per pixel row */
|
private int stride; /* bytes per pixel row */
|
||||||
[NonSerialized]
|
[NonSerialized]
|
||||||
private int aIndex = -1;
|
private int aIndex = -1;
|
||||||
|
@ -219,7 +227,8 @@ namespace GreenshotPlugin.Core {
|
||||||
bitsLocked = true;
|
bitsLocked = true;
|
||||||
|
|
||||||
IntPtr Scan0 = bmData.Scan0;
|
IntPtr Scan0 = bmData.Scan0;
|
||||||
pointer = (byte*)(void*)Scan0;
|
pointer = (byte*)(void*)Scan0;
|
||||||
|
intPointer = (int*)(void*)Scan0;
|
||||||
|
|
||||||
PrepareForPixelFormat();
|
PrepareForPixelFormat();
|
||||||
stride = bmData.Stride;
|
stride = bmData.Stride;
|
||||||
|
@ -300,6 +309,28 @@ namespace GreenshotPlugin.Core {
|
||||||
pointer[offset] = color;
|
pointer[offset] = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use only when 32-bit bitmap!
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">x</param>
|
||||||
|
/// <param name="y">y</param>
|
||||||
|
/// <returns>int with argb value</returns>
|
||||||
|
public int GetARGB(int x, int y) {
|
||||||
|
int offset = (y * (stride >> 2)) + x;
|
||||||
|
return intPointer[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use only when 32-bit bitmap!
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">x</param>
|
||||||
|
/// <param name="y">y</param>
|
||||||
|
/// <param name="argb">argb value</param>
|
||||||
|
public void SetARGB(int x, int y, int argb) {
|
||||||
|
int offset = (y * (stride>>2)) + x;
|
||||||
|
intPointer[offset] = argb;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve the color at location x,y
|
/// Retrieve the color at location x,y
|
||||||
/// Before the first time this is called the Lock() should be called once!
|
/// Before the first time this is called the Lock() should be called once!
|
||||||
|
@ -372,6 +403,18 @@ 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!
|
||||||
|
*/
|
||||||
|
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];
|
||||||
|
color[1] = pointer[rIndex + offset];
|
||||||
|
color[2] = pointer[gIndex + offset];
|
||||||
|
color[3] = pointer[bIndex + offset];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the color at location x,y to a byte[]
|
* Retrieve the color at location x,y to a byte[]
|
||||||
* Before the first time this is called the Lock() should be called once!
|
* Before the first time this is called the Lock() should be called once!
|
||||||
|
|
|
@ -593,6 +593,130 @@ namespace GreenshotPlugin.Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new Bitmap with the BoxBlur result of the sourceBitmap
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourceBitmap">Bitmap to blur</param>
|
||||||
|
/// <param name="range">Must be ODD!</param>
|
||||||
|
/// <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);
|
||||||
|
}
|
||||||
|
BoxBlurVertical(bbbDest, range);
|
||||||
|
bbbDest.Unlock();
|
||||||
|
return bbbDest.Bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// BoxBlurHorizontal is a private helper method for the BoxBlur
|
||||||
|
/// </summary>
|
||||||
|
/// <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;
|
||||||
|
int halfRange = range / 2;
|
||||||
|
int[] newColors = new int[w];
|
||||||
|
byte[] tmpColor = new byte[4];
|
||||||
|
for (int y = 0; y < h; y++) {
|
||||||
|
int hits = 0;
|
||||||
|
int a = 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) {
|
||||||
|
bbbSrc.GetUncheckedColorIn(oldPixel, y, tmpColor);
|
||||||
|
a -= tmpColor[0];
|
||||||
|
r -= tmpColor[1];
|
||||||
|
g -= tmpColor[2];
|
||||||
|
b -= tmpColor[3];
|
||||||
|
hits--;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newPixel = x + halfRange;
|
||||||
|
if (newPixel < w) {
|
||||||
|
bbbSrc.GetUncheckedColorIn(newPixel, y, tmpColor);
|
||||||
|
a += tmpColor[0];
|
||||||
|
r += tmpColor[1];
|
||||||
|
g += tmpColor[2];
|
||||||
|
b += tmpColor[3];
|
||||||
|
hits++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x >= 0) {
|
||||||
|
newColors[x] = ((byte)(a / hits)) << 24 | ((byte)(r / hits) << 16) | ((byte)(g / hits) << 8 ) | ((byte)(b / hits));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int x = 0; x < w; x++) {
|
||||||
|
bbbDest.SetARGB(x, y, newColors[x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// BoxBlurVertical is a private helper method for the BoxBlur
|
||||||
|
/// </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) {
|
||||||
|
int w = bbbDest.Width;
|
||||||
|
int h = bbbDest.Height;
|
||||||
|
int halfRange = range / 2;
|
||||||
|
int[] newColors = new int[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 a = 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) {
|
||||||
|
//int colorg = pixels[index + oldPixelOffset];
|
||||||
|
bbbDest.GetUncheckedColorIn(x, oldPixel, tmpColor);
|
||||||
|
a -= tmpColor[0];
|
||||||
|
r -= tmpColor[1];
|
||||||
|
g -= tmpColor[2];
|
||||||
|
b -= tmpColor[3];
|
||||||
|
hits--;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newPixel = y + halfRange;
|
||||||
|
if (newPixel < h) {
|
||||||
|
//int colorg = pixels[index + newPixelOffset];
|
||||||
|
bbbDest.GetUncheckedColorIn(x, newPixel, tmpColor);
|
||||||
|
a += tmpColor[0];
|
||||||
|
r += tmpColor[1];
|
||||||
|
g += tmpColor[2];
|
||||||
|
b += tmpColor[3];
|
||||||
|
hits++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y >= 0) {
|
||||||
|
newColors[y] = ((byte)(a / hits)) << 24 | ((byte)(r / hits) << 16) | ((byte)(g / hits) << 8) | ((byte)(b / hits));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < h; y++) {
|
||||||
|
bbbDest.SetARGB(x, y, newColors[y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Bitmap FastBlur(Bitmap sourceBitmap, int radius) {
|
public static Bitmap FastBlur(Bitmap sourceBitmap, int radius) {
|
||||||
if (radius < 1) return null;
|
if (radius < 1) return null;
|
||||||
|
|
||||||
|
@ -747,6 +871,7 @@ namespace GreenshotPlugin.Core {
|
||||||
Rectangle newImageRectangle = new Rectangle(0, 0, tmpImage.Width, tmpImage.Height);
|
Rectangle newImageRectangle = new Rectangle(0, 0, tmpImage.Width, tmpImage.Height);
|
||||||
//using (Bitmap blurImage = FastBlur(newImage, shadowSize-1)) {
|
//using (Bitmap blurImage = FastBlur(newImage, shadowSize-1)) {
|
||||||
returnImage = CreateBlur(tmpImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle);
|
returnImage = CreateBlur(tmpImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle);
|
||||||
|
//returnImage = BoxBlur(tmpImage, shadowSize);
|
||||||
}
|
}
|
||||||
if (returnImage != null) {
|
if (returnImage != null) {
|
||||||
using (Graphics graphics = Graphics.FromImage(returnImage)) {
|
using (Graphics graphics = Graphics.FromImage(returnImage)) {
|
||||||
|
|
|
@ -776,32 +776,25 @@ namespace GreenshotPlugin.Core {
|
||||||
Size borderSize = new Size();
|
Size borderSize = new Size();
|
||||||
|
|
||||||
if (!Maximised) {
|
if (!Maximised) {
|
||||||
Screen displayScreen = null;
|
// Assume using it's own location
|
||||||
|
formLocation = windowRectangle.Location;
|
||||||
// Find the screen where the window is and check if it fits
|
using (Region workingArea = new Region(Screen.PrimaryScreen.WorkingArea)) {
|
||||||
foreach(Screen screen in Screen.AllScreens) {
|
// Find the screen where the window is and check if it fits
|
||||||
if (screen.WorkingArea.Contains(windowRectangle)) {
|
foreach (Screen screen in Screen.AllScreens) {
|
||||||
displayScreen = screen;
|
workingArea.Union(screen.WorkingArea);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (displayScreen == null) {
|
// If the formLocation is not inside the visible area
|
||||||
// If none found we find the biggest screen
|
if (!workingArea.AreRectangleCornersVisisble(windowRectangle)) {
|
||||||
foreach(Screen screen in Screen.AllScreens) {
|
// If none found we find the biggest screen
|
||||||
if (displayScreen == null || (screen.Bounds.Width >= displayScreen.Bounds.Width && screen.Bounds.Height >= displayScreen.Bounds.Height)) {
|
foreach (Screen screen in Screen.AllScreens) {
|
||||||
displayScreen = screen;
|
Rectangle newWindowRectangle = new Rectangle(screen.WorkingArea.Location, windowRectangle.Size);
|
||||||
|
if (workingArea.AreRectangleCornersVisisble(newWindowRectangle)) {
|
||||||
|
formLocation = screen.WorkingArea.Location;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check if the window will fit
|
|
||||||
if (displayScreen != null && displayScreen.Bounds.Contains(new Rectangle(Point.Empty, windowRectangle.Size))) {
|
|
||||||
formLocation = new Point(displayScreen.Bounds.X, displayScreen.Bounds.Y);
|
|
||||||
} else {
|
|
||||||
// No, just use the primary screen
|
|
||||||
formLocation = new Point(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The window actually fits, so while capturing create the copy over the original
|
|
||||||
formLocation = new Point(windowRectangle.X, windowRectangle.Y);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//GetClientRect(out windowRectangle);
|
//GetClientRect(out windowRectangle);
|
||||||
|
|
|
@ -23,6 +23,20 @@ using System.Drawing;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace GreenshotPlugin.UnmanagedHelpers {
|
namespace GreenshotPlugin.UnmanagedHelpers {
|
||||||
|
public static class GDIExtensions {
|
||||||
|
public static bool AreRectangleCornersVisisble(this Region region, Rectangle rectangle) {
|
||||||
|
Point topLeft = new Point(rectangle.X, rectangle.Y);
|
||||||
|
Point topRight = new Point(rectangle.X + rectangle.Width, rectangle.Y);
|
||||||
|
Point bottomLeft = new Point(rectangle.X, rectangle.Y + rectangle.Height);
|
||||||
|
Point bottomRight = new Point(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height);
|
||||||
|
bool topLeftVisible = region.IsVisible(topLeft);
|
||||||
|
bool topRightVisible = region.IsVisible(topRight);
|
||||||
|
bool bottomLeftVisible = region.IsVisible(bottomLeft);
|
||||||
|
bool bottomRightVisible = region.IsVisible(bottomRight);
|
||||||
|
|
||||||
|
return topLeftVisible && topRightVisible && bottomLeftVisible && bottomRightVisible;
|
||||||
|
}
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GDI32 Helpers
|
/// GDI32 Helpers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue