Optimizing: Replaced BitmapBuffer with FastBitmap, also changed some filters to use more GDI "hardware" instead of home-made pixel processing. Only on Windows 8 the blur is "slow", when radius < 20.

git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@2531 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
RKrom 2013-03-11 16:44:34 +00:00
parent ea20ead07e
commit 5018f67ff7
12 changed files with 481 additions and 814 deletions

View file

@ -1,30 +1,37 @@
/// <summary> /*
/// Parts of this class were taken from BlurEffect.cs of Paint.NET 3.0.1, * Greenshot - a free and open source screenshot tool
/// which was released under MIT license. * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom
/// http://www.getpaint.net *
/// Some of this code has been adapted for integration with Greenshot. * For more information see: http://getgreenshot.org/
/// See Paint.NET copyright notice below. * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
/// </summary> *
* This program is free software: you can redistribute it and/or modify
///////////////////////////////////////////////////////////////////////////////// * it under the terms of the GNU General Public License as published by
// Paint.NET // * the Free Software Foundation, either version 1 of the License, or
// Copyright (C) Rick Brewster, Tom Jackson, and past contributors. // * (at your option) any later version.
// Portions Copyright (C) Microsoft Corporation. All Rights Reserved. // *
// See src/Resources/Files/License.txt for full licensing and attribution // * This program is distributed in the hope that it will be useful,
// details. // * but WITHOUT ANY WARRANTY; without even the implied warranty of
// . // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
///////////////////////////////////////////////////////////////////////////////// * GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System; using System;
using System.Drawing; using System.Drawing;
using Greenshot.Drawing.Fields; using Greenshot.Drawing.Fields;
using Greenshot.Plugin.Drawing; using Greenshot.Plugin.Drawing;
using GreenshotPlugin.Core; using GreenshotPlugin.Core;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using GreenshotPlugin.UnmanagedHelpers;
using System.Drawing.Drawing2D;
namespace Greenshot.Drawing.Filters { namespace Greenshot.Drawing.Filters {
[Serializable()] [Serializable()]
public class BlurFilter : AbstractFilter { public class BlurFilter : AbstractFilter {
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(BlurFilter));
public double previewQuality; public double previewQuality;
public double PreviewQuality { public double PreviewQuality {
get { return previewQuality; } get { return previewQuality; }
@ -40,11 +47,20 @@ namespace Greenshot.Drawing.Filters {
int blurRadius = GetFieldValueAsInt(FieldType.BLUR_RADIUS); int blurRadius = GetFieldValueAsInt(FieldType.BLUR_RADIUS);
double previewQuality = GetFieldValueAsDouble(FieldType.PREVIEW_QUALITY); double previewQuality = GetFieldValueAsDouble(FieldType.PREVIEW_QUALITY);
Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert);
using (Bitmap blurImage = ImageHelper.CreateBlur(applyBitmap, applyRect, renderMode == RenderMode.EXPORT, blurRadius, previewQuality, Invert, parent.Bounds)) { GraphicsState state = graphics.Save();
if (blurImage != null) { if (Invert) {
graphics.DrawImageUnscaled(blurImage, applyRect.Location); graphics.SetClip(applyRect);
graphics.ExcludeClip(rect);
}
if (GDIplus.isBlurPossible(blurRadius)) {
GDIplus.DrawWithBlur(graphics, applyBitmap, applyRect, null, null, blurRadius, false);
} else {
using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) {
ImageHelper.ApplyBoxBlur(fastBitmap, blurRadius);
fastBitmap.DrawTo(graphics, applyRect);
} }
} }
graphics.Restore(state);
return; return;
} }
} }

View file

@ -24,6 +24,7 @@ using Greenshot.Drawing.Fields;
using Greenshot.Plugin.Drawing; using Greenshot.Plugin.Drawing;
using GreenshotPlugin.Core; using GreenshotPlugin.Core;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
namespace Greenshot.Drawing.Filters { namespace Greenshot.Drawing.Filters {
[Serializable()] [Serializable()]
@ -48,24 +49,14 @@ namespace Greenshot.Drawing.Filters {
return; return;
} }
using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) { GraphicsState state = graphics.Save();
double brightness = GetFieldValueAsDouble(FieldType.BRIGHTNESS); if (Invert) {
for (int y = 0; y < fastBitmap.Height; y++) { graphics.SetClip(applyRect);
for (int x = 0; x < fastBitmap.Width; x++) { graphics.ExcludeClip(rect);
if (parent.Contains(applyRect.Left + x, applyRect.Top + y) ^ Invert) {
Color color = fastBitmap.GetColorAt(x, y);
int r = Convert.ToInt16(color.R * brightness);
int g = Convert.ToInt16(color.G * brightness);
int b = Convert.ToInt16(color.B * brightness);
r = (r > 255) ? 255 : r;
g = (g > 255) ? 255 : g;
b = (b > 255) ? 255 : b;
fastBitmap.SetColorAt(x, y, Color.FromArgb(color.A, r, g, b));
}
}
}
fastBitmap.DrawTo(graphics, applyRect.Location);
} }
ImageAttributes ia = ImageHelper.CreateAdjustAttributes(0.9f, 1f, 1f);
graphics.DrawImage(applyBitmap, applyRect, applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height, GraphicsUnit.Pixel, ia);
graphics.Restore(state);
} }
} }
} }

View file

@ -22,6 +22,8 @@ using System;
using System.Drawing; using System.Drawing;
using GreenshotPlugin.Core; using GreenshotPlugin.Core;
using Greenshot.Plugin.Drawing; using Greenshot.Plugin.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
namespace Greenshot.Drawing.Filters { namespace Greenshot.Drawing.Filters {
/// <summary> /// <summary>
@ -39,20 +41,23 @@ namespace Greenshot.Drawing.Filters {
// nothing to do // nothing to do
return; return;
} }
GraphicsState state = graphics.Save();
if (Invert) {
graphics.SetClip(applyRect);
graphics.ExcludeClip(rect);
}
ColorMatrix grayscaleMatrix = new ColorMatrix(new float[][] {
new float[] {.3f, .3f, .3f, 0, 0},
new float[] {.59f, .59f, .59f, 0, 0},
new float[] {.11f, .11f, .11f, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1}
});
ImageAttributes ia = new ImageAttributes();
ia.SetColorMatrix(grayscaleMatrix);
graphics.DrawImage(applyBitmap, applyRect, applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height, GraphicsUnit.Pixel, ia);
graphics.Restore(state);
using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) {
for (int y = 0; y < fastBitmap.Height; y++) {
for (int x = 0; x < fastBitmap.Width; x++) {
if (parent.Contains(applyRect.Left + x, applyRect.Top + y) ^ Invert) {
Color color = fastBitmap.GetColorAt(x, y);
int luma = (int)((0.3 * color.R) + (0.59 * color.G) + (0.11 * color.B));
color = Color.FromArgb(luma, luma, luma);
fastBitmap.SetColorAt(x, y, color);
}
}
}
fastBitmap.DrawTo(graphics, applyRect.Location);
}
} }
} }
} }

View file

@ -24,6 +24,7 @@ using Greenshot.Drawing.Fields;
using Greenshot.Plugin.Drawing; using Greenshot.Plugin.Drawing;
using GreenshotPlugin.Core; using GreenshotPlugin.Core;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
namespace Greenshot.Drawing.Filters { namespace Greenshot.Drawing.Filters {
[Serializable()] [Serializable()]
@ -46,20 +47,23 @@ namespace Greenshot.Drawing.Filters {
// nothing to do // nothing to do
return; return;
} }
GraphicsState state = graphics.Save();
if (Invert) {
graphics.SetClip(applyRect);
graphics.ExcludeClip(rect);
}
using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) { using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) {
Color highlightColor = GetFieldValueAsColor(FieldType.FILL_COLOR); Color highlightColor = GetFieldValueAsColor(FieldType.FILL_COLOR);
for (int y = 0; y < fastBitmap.Height; y++) { for (int y = fastBitmap.Top; y < fastBitmap.Bottom; y++) {
for (int x = 0; x < fastBitmap.Width; x++) { for (int x = fastBitmap.Left; x < fastBitmap.Right; x++) {
if (parent.Contains(applyRect.Left + x, applyRect.Top + y) ^ Invert) {
Color color = fastBitmap.GetColorAt(x, y); Color color = fastBitmap.GetColorAt(x, y);
color = Color.FromArgb(color.A, Math.Min(highlightColor.R, color.R), Math.Min(highlightColor.G, color.G), Math.Min(highlightColor.B, color.B)); color = Color.FromArgb(color.A, Math.Min(highlightColor.R, color.R), Math.Min(highlightColor.G, color.G), Math.Min(highlightColor.B, color.B));
fastBitmap.SetColorAt(x, y, color); fastBitmap.SetColorAt(x, y, color);
} }
} }
}
fastBitmap.DrawTo(graphics, applyRect.Location); fastBitmap.DrawTo(graphics, applyRect.Location);
} }
graphics.Restore(state);
} }
} }
} }

View file

@ -24,6 +24,7 @@ using Greenshot.Drawing.Fields;
using Greenshot.Plugin.Drawing; using Greenshot.Plugin.Drawing;
using GreenshotPlugin.Core; using GreenshotPlugin.Core;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
namespace Greenshot.Drawing.Filters { namespace Greenshot.Drawing.Filters {
[Serializable] [Serializable]
@ -40,24 +41,22 @@ namespace Greenshot.Drawing.Filters {
return; return;
} }
int magnificationFactor = GetFieldValueAsInt(FieldType.MAGNIFICATION_FACTOR); int magnificationFactor = GetFieldValueAsInt(FieldType.MAGNIFICATION_FACTOR);
GraphicsState state = graphics.Save();
using (IFastBitmap destFastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) { if (Invert) {
int halfWidth = destFastBitmap.Size.Width / 2; graphics.SetClip(applyRect);
int halfHeight = destFastBitmap.Size.Height / 2; graphics.ExcludeClip(rect);
using (IFastBitmap sourceFastBitmap = FastBitmap.Create(applyBitmap, applyRect)) {
for (int y = 0; y < destFastBitmap.Height; y++) {
int yDistanceFromCenter = halfHeight - y;
for (int x = 0; x < destFastBitmap.Width; x++) {
int xDistanceFromCenter = halfWidth - x;
if (parent.Contains(applyRect.Left + x, applyRect.Top + y) ^ Invert) {
Color color = sourceFastBitmap.GetColorAt(halfWidth - xDistanceFromCenter / magnificationFactor, halfHeight - yDistanceFromCenter / magnificationFactor);
destFastBitmap.SetColorAt(x, y, color);
}
}
}
}
destFastBitmap.DrawTo(graphics, applyRect.Location);
} }
graphics.SmoothingMode = SmoothingMode.None;
graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.None;
int halfWidth = rect.Width / 2;
int halfHeight = rect.Height / 2;
int newWidth = rect.Width / magnificationFactor;
int newHeight = rect.Width / magnificationFactor;
Rectangle source = new Rectangle(rect.X + halfWidth - (newWidth / 2), rect.Y + halfHeight - (newHeight / 2), newWidth, newHeight);
graphics.DrawImage(applyBitmap, rect, source, GraphicsUnit.Pixel);
graphics.Restore(state);
} }
} }
} }

View file

@ -48,35 +48,36 @@ namespace Greenshot.Drawing.Filters {
if (rect.Height < pixelSize) { if (rect.Height < pixelSize) {
pixelSize = rect.Height; pixelSize = rect.Height;
} }
using (IFastBitmap dest = FastBitmap.CreateCloneOf(applyBitmap, rect)) {
using (BitmapBuffer bbbDest = new BitmapBuffer(applyBitmap, rect)) { using (IFastBitmap src = FastBitmap.Create(applyBitmap, rect)) {
bbbDest.Lock();
using (BitmapBuffer bbbSrc = new BitmapBuffer(applyBitmap, rect, false)) {
bbbSrc.Lock();
List<Color> colors = new List<Color>(); List<Color> colors = new List<Color>();
int halbPixelSize = pixelSize / 2; int halbPixelSize = pixelSize / 2;
for (int y = -halbPixelSize; y < bbbSrc.Height + halbPixelSize; y = y + pixelSize) { for (int y = src.Top - halbPixelSize; y < src.Bottom + halbPixelSize; y = y + pixelSize) {
for (int x = -halbPixelSize; x <= bbbSrc.Width + halbPixelSize; x = x + pixelSize) { for (int x = src.Left - halbPixelSize; x <= src.Right + halbPixelSize; x = x + pixelSize) {
colors.Clear(); colors.Clear();
for (int yy = y; yy < y + pixelSize; yy++) { for (int yy = y; yy < y + pixelSize; yy++) {
if (yy >= 0 && yy < bbbSrc.Height) { if (yy >= src.Top && yy < src.Bottom) {
for (int xx = x; xx < x + pixelSize; xx++) { for (int xx = x; xx < x + pixelSize; xx++) {
colors.Add(bbbSrc.GetColorAt(xx, yy)); if (xx >= src.Left && xx < src.Right) {
colors.Add(src.GetColorAt(xx, yy));
}
} }
} }
} }
Color currentAvgColor = Colors.Mix(colors); Color currentAvgColor = Colors.Mix(colors);
for (int yy = y; yy <= y + pixelSize; yy++) { for (int yy = y; yy <= y + pixelSize; yy++) {
if (yy >= 0 && yy < bbbSrc.Height) { if (yy >= src.Top && yy < src.Bottom) {
for (int xx = x; xx <= x + pixelSize; xx++) { for (int xx = x; xx <= x + pixelSize; xx++) {
bbbDest.SetColorAt(xx, yy, currentAvgColor); if (xx >= src.Left && xx < src.Right) {
dest.SetColorAt(xx, yy, currentAvgColor);
} }
} }
} }
} }
} }
} }
bbbDest.DrawTo(graphics, rect.Location); }
dest.DrawTo(graphics, rect.Location);
} }
} }
} }

View file

@ -1,409 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.Serialization;
namespace GreenshotPlugin.Core {
/// <summary>
/// The BitmapBuffer is exactly what it says, it buffers a Bitmap.
/// And it is possible to Draw on the Bitmap with direct memory access for better performance
/// </summary>
public unsafe class BitmapBuffer : IDisposable {
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(BitmapBuffer));
private bool clone;
private Bitmap bitmap;
// Used for indexed images
private Color[] colorEntries;
public static Color BackgroundBlendColor {
get;
set;
}
static BitmapBuffer() {
BackgroundBlendColor = Color.White;
}
/// <summary>
/// Get the bitmap, you will always need to dispose the returned bitmap!!
/// Only works, and makes sense, if cloned and not locked!
/// </summary>
public Bitmap Bitmap {
get {
if (bitsLocked) {
throw new NotSupportedException("Can't get a locked bitmap!");
}
if (!clone) {
throw new NotSupportedException("Can't return a not cloned bitmap!");
} else {
// Make sure the bitmap isn't disposed when this object is closed
clone = false;
return bitmap;
}
}
}
public Bitmap UnlockAndReturnBitmap() {
Unlock();
return Bitmap;
}
public PixelFormat PixelFormat {
get {
return bitmap.PixelFormat;
}
}
private BitmapData bmData;
private Rectangle rect;
private byte* pointer;
private int* intPointer;
private int stride; /* bytes per pixel row */
private int aIndex = -1;
private int rIndex = -1;
private int gIndex = -1;
private int bIndex = -1;
private int bytesPerPixel;
private bool bitsLocked = false;
public Size Size {
get {return rect.Size;}
}
public int Width {
get {return rect.Width;}
}
public int Height {
get {return rect.Height;}
}
/// <summary>
/// Create a BitmapBuffer from a Bitmap
/// </summary>
/// <param name="sourceBmp">Bitmap</param>
public BitmapBuffer(Image sourceBmp) : this(sourceBmp, Rectangle.Empty) {
}
/// <summary>
/// Create a BitmapBuffer from a Bitmap a flag if we need a clone
/// </summary>
/// <param name="sourceBmp">Bitmap</param>
/// <param name="clone">bool specifying if the bitmap needs to be cloned</param>
public BitmapBuffer(Image sourceBmp, bool clone) : this(sourceBmp, Rectangle.Empty, clone) {
}
/// <summary>
/// Create a BitmapBuffer from a Bitmap and a Rectangle specifying what part from the Bitmap to take.
/// </summary>
/// <param name="sourceBmp">Bitmap</param>
/// <param name="applyRect">Rectangle</param>
public BitmapBuffer(Image sourceBmp, Rectangle applyRect) : this(sourceBmp, applyRect, true) {
}
/// <summary>
/// Create a BitmapBuffer from a Bitmap, a Rectangle specifying what part from the Bitmap to take and a flag if we need a clone
/// </summary>
/// <param name="sourceImage">Image, will be cloned if it's not a bitmap!!!</param>
/// <param name="applyRect">Rectangle</param>
/// <param name="clone">bool specifying if the bitmap needs to be cloned</param>
public BitmapBuffer(Image sourceImage, Rectangle applyRect, bool clone) {
this.clone = clone;
Rectangle sourceRect = new Rectangle(applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height);
Rectangle bitmapRect = new Rectangle(0,0, sourceImage.Width, sourceImage.Height);
if (sourceRect.IsEmpty) {
sourceRect = bitmapRect;
} else {
sourceRect.Intersect(bitmapRect);
}
// Does the rect have any pixels?
if (sourceRect.Height <= 0 || sourceRect.Width <= 0) {
return;
}
if (!(sourceImage is Bitmap) && !clone) {
LOG.Warn("Chancing clone to true, as the image is not a bitmap");
clone = true;
}
if (clone) {
this.bitmap = ImageHelper.CloneArea(sourceImage, sourceRect, PixelFormat.DontCare);
// Set "this" rect to location 0,0
// as the Cloned Bitmap is only the part we want to work with
this.rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
} else if (!ImageHelper.SupportsPixelFormat(sourceImage) && PixelFormat.Format8bppIndexed != sourceImage.PixelFormat) {
throw new ArgumentException("Unsupported pixel format: " + sourceImage.PixelFormat + " set clone to true!");
} else {
this.bitmap = sourceImage as Bitmap;
this.rect = sourceRect;
}
if (bitmap.PixelFormat == PixelFormat.Format8bppIndexed) {
colorEntries = bitmap.Palette.Entries;
}
}
/**
* Destructor
*/
~BitmapBuffer() {
Dispose(false);
}
/**
* The public accessible Dispose
* Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
*/
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
// The bulk of the clean-up code is implemented in Dispose(bool)
/**
* This Dispose is called from the Dispose and the Destructor.
* When disposing==true all non-managed resources should be freed too!
*/
protected virtual void Dispose(bool disposing) {
Unlock();
if (disposing) {
if (bitmap != null && clone) {
bitmap.Dispose();
}
}
bitmap = null;
bmData = null;
pointer = null;
}
/**
* Lock the bitmap so we have direct access to the memory
*/
public void Lock() {
if(rect.Width > 0 && rect.Height > 0 && !bitsLocked) {
bmData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
bitsLocked = true;
IntPtr Scan0 = bmData.Scan0;
pointer = (byte*)(void*)Scan0;
intPointer = (int*)(void*)Scan0;
PrepareForPixelFormat();
stride = bmData.Stride;
}
}
/// <summary>
/// Unlock the System Memory
/// </summary>
public void Unlock() {
if (bitsLocked) {
bitmap.UnlockBits(bmData);
bitsLocked = false;
}
}
/// <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);
}
/// <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);
}
/// <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?
if (destinationRect.Value.Height <= 0 || destinationRect.Value.Width <= 0) {
return;
}
}
// Make sure this.bitmap is unlocked, if it was locked
bool isLocked = bitsLocked;
if (isLocked) {
Unlock();
}
if (destinationRect.HasValue) {
graphics.DrawImage(this.bitmap, destinationRect.Value);
} else if (destination.HasValue) {
graphics.DrawImageUnscaled(this.bitmap, destination.Value);
}
// If it was locked, lock it again
if (isLocked) {
Lock();
}
}
/// <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>
/// Retrieve the color at location x,y
/// Before the first time this is called the Lock() should be called once!
/// </summary>
/// <param name="x">X coordinate</param>
/// <param name="y">Y Coordinate</param>
/// <returns>Color</returns>
public Color GetColorAt(int x, int y) {
if(x>=0 && y>=0 && x<rect.Width && y<rect.Height) {
int offset = x*bytesPerPixel+y*stride;
int a = (aIndex==-1) ? 255 : (int)pointer[aIndex+offset];
return Color.FromArgb(a, pointer[rIndex+offset], pointer[gIndex+offset], pointer[bIndex+offset]);
} else {
return Color.Empty;
}
}
/// <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;
if(aIndex!=-1) pointer[aIndex+offset] = color.A;
pointer[rIndex+offset] = color.R;
pointer[gIndex+offset] = color.G;
pointer[bIndex+offset] = color.B;
}
}
/// <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];
color[1] = pointer[rIndex + offset];
color[2] = pointer[gIndex + offset];
color[3] = pointer[bIndex + offset];
}
/// <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];
color[1] = pointer[rIndex + offset];
color[2] = pointer[gIndex + offset];
color[3] = pointer[bIndex + offset];
} else {
color[0] = 0;
color[1] = 0;
color[2] = 0;
color[3] = 0;
}
}
/// <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];
pointer[rIndex+offset] = (byte)colors[1];
pointer[gIndex+offset] = (byte)colors[2];
pointer[bIndex+offset] = (byte)colors[3];
}
}
/// <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;
switch(bitmap.PixelFormat) {
case PixelFormat.Format32bppPArgb:
case PixelFormat.Format32bppArgb:
bIndex = 0;
gIndex = 1;
rIndex = 2;
aIndex = 3;
bytesPerPixel = 4;
break;
case PixelFormat.Format32bppRgb:
bIndex = 0;
gIndex = 1;
rIndex = 2;
bytesPerPixel = 4;
break;
case PixelFormat.Format24bppRgb:
bIndex = 0;
gIndex = 1;
rIndex = 2;
bytesPerPixel = 3;
break;
case PixelFormat.Format8bppIndexed:
bytesPerPixel = 1;
break;
default:
throw new FormatException("Bitmap.Pixelformat."+bitmap.PixelFormat+" is currently not supported. Supported: Format32bpp(A)Rgb, Format24bppRgb");
}
}
}
}

View file

@ -86,19 +86,47 @@ namespace GreenshotPlugin.Core {
} }
/// <summary> /// <summary>
/// Height of the underlying image /// Height of the image area that this fastbitmap covers
/// </summary> /// </summary>
int Height { int Height {
get; get;
} }
/// <summary> /// <summary>
/// Width of the underlying image /// Width of the image area that this fastbitmap covers
/// </summary> /// </summary>
int Width { int Width {
get; get;
} }
/// <summary>
/// Top of the image area that this fastbitmap covers
/// </summary>
int Top {
get;
}
/// <summary>
/// Left of the image area that this fastbitmap covers
/// </summary>
int Left {
get;
}
/// <summary>
/// Right of the image area that this fastbitmap covers
/// </summary>
int Right {
get;
}
/// <summary>
/// Bottom of the image area that this fastbitmap covers
/// </summary>
int Bottom {
get;
}
/// <summary> /// <summary>
/// Does the underlying image need to be disposed /// Does the underlying image need to be disposed
/// </summary> /// </summary>
@ -136,6 +164,108 @@ namespace GreenshotPlugin.Core {
/// <param name="y"></param> /// <param name="y"></param>
/// <returns></returns> /// <returns></returns>
bool Contains(int x, int y); bool Contains(int x, int y);
/// <summary>
/// Set the bitmap resolution
/// </summary>
/// <param name="horizontal"></param>
/// <param name="vertical"></param>
void SetResolution(float horizontal, float vertical);
}
/// <summary>
/// This interface can be used for when offsetting is needed
/// </summary>
public interface IFastBitmapWithOffset : IFastBitmap {
/// <summary>
/// Return true if the coordinates are inside the FastBitmap
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
bool Contains(int x, int y);
/// <summary>
/// Set the color at the specified location, using offsetting so the original coordinates can be used
/// </summary>
/// <param name="x">int x</param>
/// <param name="y">int y</param>
/// <param name="color">Color color</param>
new void SetColorAt(int x, int y, Color color);
/// <summary>
/// Set the color at the specified location, using offsetting so the original coordinates can be used
/// </summary>
/// <param name="x">int x</param>
/// <param name="y">int y</param>
/// <param name="color">byte[] color</param>
new void SetColorAt(int x, int y, byte[] color);
/// <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>
new Color GetColorAt(int x, int y);
/// <summary>
/// Get the color at x,y, using offsetting so the original coordinates can be used
/// The returned byte[] color depends on the underlying pixel format
/// </summary>
/// <param name="x">int x</param>
/// <param name="y">int y</par
new void GetColorAt(int x, int y, byte[] color);
new int Left {
get;
set;
}
new int Top {
get;
set;
}
}
/// <summary>
/// This interface can be used for when clipping is needed
/// </summary>
public interface IFastBitmapWithClip : IFastBitmap {
Rectangle Clip {
get;
set;
}
bool InvertClip {
get;
set;
}
/// <summary>
/// Set the color at the specified location, this doesn't do anything if the location is excluded due to clipping
/// </summary>
/// <param name="x">int x</param>
/// <param name="y">int y</param>
/// <param name="color">Color color</param>
new void SetColorAt(int x, int y, Color color);
/// <summary>
/// Set the color at the specified location, this doesn't do anything if the location is excluded due to clipping
/// </summary>
/// <param name="x">int x</param>
/// <param name="y">int y</param>
/// <param name="color">byte[] color</param>
new void SetColorAt(int x, int y, byte[] color);
/// <summary>
/// Return true if the coordinates are inside the FastBitmap and not clipped
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
new bool Contains(int x, int y);
} }
/// <summary> /// <summary>
@ -152,7 +282,7 @@ namespace GreenshotPlugin.Core {
/// <summary> /// <summary>
/// The base class for the fast bitmap implementation /// The base class for the fast bitmap implementation
/// </summary> /// </summary>
public unsafe abstract class FastBitmap : IFastBitmap { public unsafe abstract class FastBitmap : IFastBitmap, IFastBitmapWithClip, IFastBitmapWithOffset {
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(FastBitmap)); private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(FastBitmap));
protected const int PIXELFORMAT_INDEX_A = 3; protected const int PIXELFORMAT_INDEX_A = 3;
@ -174,6 +304,16 @@ namespace GreenshotPlugin.Core {
set; set;
} }
public Rectangle Clip {
get;
set;
}
public bool InvertClip {
get;
set;
}
/// <summary> /// <summary>
/// The bitmap for which the FastBitmap is creating access /// The bitmap for which the FastBitmap is creating access
/// </summary> /// </summary>
@ -187,6 +327,11 @@ namespace GreenshotPlugin.Core {
public static IFastBitmap Create(Bitmap source) { public static IFastBitmap Create(Bitmap source) {
return Create(source, Rectangle.Empty); return Create(source, Rectangle.Empty);
} }
public void SetResolution(float horizontal, float vertical) {
bitmap.SetResolution(horizontal, vertical);
}
/// <summary> /// <summary>
/// Factory for creating a FastBitmap depending on the pixelformat of the source /// Factory for creating a FastBitmap depending on the pixelformat of the source
/// The supplied rectangle specifies the area for which the FastBitmap does its thing /// The supplied rectangle specifies the area for which the FastBitmap does its thing
@ -247,8 +392,10 @@ namespace GreenshotPlugin.Core {
/// <returns>IFastBitmap</returns> /// <returns>IFastBitmap</returns>
public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat, Rectangle area) { public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat, Rectangle area) {
Bitmap destination = ImageHelper.CloneArea(source, area, pixelFormat); Bitmap destination = ImageHelper.CloneArea(source, area, pixelFormat);
IFastBitmap fastBitmap = Create(destination); FastBitmap fastBitmap = Create(destination) as FastBitmap;
((FastBitmap)fastBitmap).NeedsDispose = true; fastBitmap.NeedsDispose = true;
fastBitmap.Left = area.Left;
fastBitmap.Top = area.Top;
return fastBitmap; return fastBitmap;
} }
@ -279,6 +426,13 @@ namespace GreenshotPlugin.Core {
} else { } else {
this.area = bitmapArea; this.area = bitmapArea;
} }
// As the lock takes care that only the specified area is made available we need to calculate the offset
this.Left = area.Left;
this.Top = area.Top;
// Default cliping is done to the area without invert
this.Clip = this.area;
this.InvertClip = false;
// Always lock, so we don't need to do this ourselves
Lock(); Lock();
} }
@ -318,12 +472,79 @@ namespace GreenshotPlugin.Core {
} }
} }
private int left;
/// <summary>
/// Return the left of the fastbitmap, this is also used as an offset
/// </summary>
public int Left {
get {
return 0;
}
set {
left = value;
}
}
/// <summary>
/// Return the left of the fastbitmap, this is also used as an offset
/// </summary>
int IFastBitmapWithOffset.Left {
get {
return left;
}
set {
left = value;
}
}
private int top;
/// <summary>
/// Return the top of the fastbitmap, this is also used as an offset
/// </summary>
public int Top {
get {
return 0;
}
set {
top = value;
}
}
/// <summary>
/// Return the top of the fastbitmap, this is also used as an offset
/// </summary>
int IFastBitmapWithOffset.Top {
get {
return top;
}
set {
top = value;
}
}
/// <summary>
/// Return the right of the fastbitmap
/// </summary>
public int Right {
get {
return Left + Width;
}
}
/// <summary>
/// Return the bottom of the fastbitmap
/// </summary>
public int Bottom {
get {
return Top + Height;
}
}
/// <summary> /// <summary>
/// Returns the underlying bitmap, unlocks it and prevents that it will be disposed /// Returns the underlying bitmap, unlocks it and prevents that it will be disposed
/// </summary> /// </summary>
public Bitmap UnlockAndReturnBitmap() { public Bitmap UnlockAndReturnBitmap() {
if (bitsLocked) { if (bitsLocked) {
LOG.Warn("Unlocking the bitmap");
Unlock(); Unlock();
} }
NeedsDispose = false; NeedsDispose = false;
@ -401,7 +622,7 @@ namespace GreenshotPlugin.Core {
/// <param name="graphics"></param> /// <param name="graphics"></param>
/// <param name="destination"></param> /// <param name="destination"></param>
public void DrawTo(Graphics graphics, Point destination) { public void DrawTo(Graphics graphics, Point destination) {
DrawTo(graphics, null, destination); DrawTo(graphics, new Rectangle(destination, area.Size));
} }
/// <summary> /// <summary>
@ -410,38 +631,15 @@ namespace GreenshotPlugin.Core {
/// </summary> /// </summary>
/// <param name="graphics"></param> /// <param name="graphics"></param>
/// <param name="destinationRect"></param> /// <param name="destinationRect"></param>
public void DrawTo(Graphics graphics, Rectangle destinationRect) {
DrawTo(graphics, destinationRect, null);
}
/// <summary>
/// private helper to draw the bitmap
/// </summary>
/// <param name="graphics"></param>
/// <param name="destinationRect"></param>
/// <param name="destination"></param> /// <param name="destination"></param>
private void DrawTo(Graphics graphics, Rectangle? destinationRect, Point? destination) { public void DrawTo(Graphics graphics, Rectangle destinationRect) {
if (destinationRect.HasValue) {
// Does the rect have any pixels?
if (destinationRect.Value.Height <= 0 || destinationRect.Value.Width <= 0) {
return;
}
}
// Make sure this.bitmap is unlocked, if it was locked // Make sure this.bitmap is unlocked, if it was locked
bool isLocked = bitsLocked; bool isLocked = bitsLocked;
if (isLocked) { if (isLocked) {
Unlock(); Unlock();
} }
if (destinationRect.HasValue) { graphics.DrawImage(this.bitmap, destinationRect, area, GraphicsUnit.Pixel);
graphics.DrawImage(this.bitmap, destinationRect.Value);
} else if (destination.HasValue) {
graphics.DrawImageUnscaled(this.bitmap, destination.Value);
}
// If it was locked, lock it again
if (isLocked) {
Lock();
}
} }
/// <summary> /// <summary>
@ -451,13 +649,75 @@ namespace GreenshotPlugin.Core {
/// <param name="y"></param> /// <param name="y"></param>
/// <returns>true if x & y are inside the FastBitmap</returns> /// <returns>true if x & y are inside the FastBitmap</returns>
public bool Contains(int x, int y) { public bool Contains(int x, int y) {
return x >= 0 && x < Width && y >= 0 && y < Height; return area.Contains(x - Left, y - Top);
} }
public abstract Color GetColorAt(int x, int y); public abstract Color GetColorAt(int x, int y);
public abstract void SetColorAt(int x, int y, Color color); public abstract void SetColorAt(int x, int y, Color color);
public abstract void GetColorAt(int x, int y, byte[] color); public abstract void GetColorAt(int x, int y, byte[] color);
public abstract void SetColorAt(int x, int y, byte[] color); public abstract void SetColorAt(int x, int y, byte[] color);
#region IFastBitmapWithClip
bool IFastBitmapWithClip.Contains(int x, int y) {
bool contains = Clip.Contains(x, y);
if (InvertClip) {
return !contains;
} else {
return contains;
}
}
void IFastBitmapWithClip.SetColorAt(int x, int y, byte[] color) {
bool contains = Clip.Contains(x, y);
if ((InvertClip && contains) || (!InvertClip && !contains)) {
return;
}
SetColorAt(x, y, color);
}
void IFastBitmapWithClip.SetColorAt(int x, int y, Color color) {
bool contains = Clip.Contains(x, y);
if ((InvertClip && contains) || (!InvertClip && !contains)) {
return;
}
SetColorAt(x, y, color);
}
#endregion
#region IFastBitmapWithOffset
/// <summary>
/// returns true if x & y are inside the FastBitmap
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns>true if x & y are inside the FastBitmap</returns>
bool IFastBitmapWithOffset.Contains(int x, int y) {
return area.Contains(x - Left, y - Top);
}
Color IFastBitmapWithOffset.GetColorAt(int x, int y) {
x -= left;
y -= top;
return GetColorAt(x, y);
}
void IFastBitmapWithOffset.GetColorAt(int x, int y, byte[] color) {
x -= left;
y -= top;
GetColorAt(x, y, color);
}
void IFastBitmapWithOffset.SetColorAt(int x, int y, byte[] color) {
x -= left;
y -= top;
SetColorAt(x, y, color);
}
void IFastBitmapWithOffset.SetColorAt(int x, int y, Color color) {
x -= left;
y -= top;
SetColorAt(x, y, color);
}
#endregion
} }
/// <summary> /// <summary>
@ -771,6 +1031,5 @@ namespace GreenshotPlugin.Core {
} }
return Color.FromArgb(255, red, green, blue); return Color.FromArgb(255, red, green, blue);
} }
} }
} }

View file

@ -175,8 +175,6 @@ namespace GreenshotPlugin.Core {
// Bottom Right // Bottom Right
checkPoints.Add(new Point(image.Width - 1, image.Height - 1)); checkPoints.Add(new Point(image.Width - 1, image.Height - 1));
using (IFastBitmap fastBitmap = FastBitmap.Create((Bitmap)image)) { using (IFastBitmap fastBitmap = FastBitmap.Create((Bitmap)image)) {
fastBitmap.Lock();
// find biggest area // find biggest area
foreach(Point checkPoint in checkPoints) { foreach(Point checkPoint in checkPoints) {
currentRectangle = FindAutoCropRectangle(fastBitmap, checkPoint, cropDifference); currentRectangle = FindAutoCropRectangle(fastBitmap, checkPoint, cropDifference);
@ -461,197 +459,6 @@ namespace GreenshotPlugin.Core {
return weights; return weights;
} }
/// <summary>
/// Create a new bitmap with the sourceBitmap blurred
/// </summary>
/// <param name="sourceBitmap">Source to blur</param>
/// <param name="applyRect">Area to blur</param>
/// <param name="useExportQuality">Use best quality</param>
/// <param name="blurRadius">Radius of the blur</param>
/// <param name="previewQuality">Quality, use 1d for normal anything less skipps calculations</param>
/// <param name="invert">true if the blur needs to occur outside of the area</param>
/// <param name="parentBounds">Rectangle limiting the area when using invert</param>
public static unsafe Bitmap CreateBlur(Bitmap sourceBitmap, Rectangle applyRect, bool useExportQuality, int blurRadius, double previewQuality, bool invert, Rectangle parentBounds) {
if (applyRect.Height <= 0 || applyRect.Width <= 0) {
return null;
}
// do nothing when nothing can be done!
if (blurRadius < 1) {
return null;
}
byte[] nullColor = new byte[] { 255, 255, 255, 255 };
if (sourceBitmap.PixelFormat == PixelFormat.Format32bppArgb || sourceBitmap.PixelFormat == PixelFormat.Format32bppPArgb) {
nullColor = new byte[] { 0, 0, 0, 0 };
}
byte[] settingColor = new byte[4];
byte[] readingColor = new byte[4];
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;
int[] w = CreateGaussianBlurRow(r);
int wlen = w.Length;
long* waSums = stackalloc long[wlen];
long* wcSums = stackalloc long[wlen];
long* aSums = stackalloc long[wlen];
long* rSums = stackalloc long[wlen];
long* gSums = stackalloc long[wlen];
long* bSums = stackalloc long[wlen];
for (int y = 0; y < applyRect.Height; ++y) {
long waSum = 0;
long wcSum = 0;
long aSum = 0;
long rSum = 0;
long gSum = 0;
long bSum = 0;
for (int wx = 0; wx < wlen; ++wx) {
int srcX = wx - r;
waSums[wx] = 0;
wcSums[wx] = 0;
aSums[wx] = 0;
rSums[wx] = 0;
gSums[wx] = 0;
bSums[wx] = 0;
if (srcX >= 0 && srcX < src.Width) {
for (int wy = 0; wy < wlen; ++wy) {
int srcY = y + wy - r;
if (srcY >= 0 && srcY < src.Height) {
src.GetColorAt(srcX, srcY, readingColor);
int wp = w[wy];
waSums[wx] += wp;
wp *= readingColor[0] + (readingColor[0] >> 7);
wcSums[wx] += wp;
wp >>= 8;
aSums[wx] += wp * readingColor[0];
rSums[wx] += wp * readingColor[1];
gSums[wx] += wp * readingColor[2];
bSums[wx] += wp * readingColor[3];
}
}
int wwx = w[wx];
waSum += wwx * waSums[wx];
wcSum += wwx * wcSums[wx];
aSum += wwx * aSums[wx];
rSum += wwx * rSums[wx];
gSum += wwx * gSums[wx];
bSum += wwx * bSums[wx];
}
}
wcSum >>= 8;
if (parentBounds.Contains(applyRect.Left, applyRect.Top + y) ^ invert) {
if (waSum == 0 || wcSum == 0) {
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);
dst.SetColorAt(0, y, settingColor);
}
}
for (int x = 1; x < applyRect.Width; ++x) {
for (int i = 0; i < wlen - 1; ++i) {
waSums[i] = waSums[i + 1];
wcSums[i] = wcSums[i + 1];
aSums[i] = aSums[i + 1];
rSums[i] = rSums[i + 1];
gSums[i] = gSums[i + 1];
bSums[i] = bSums[i + 1];
}
waSum = 0;
wcSum = 0;
aSum = 0;
rSum = 0;
gSum = 0;
bSum = 0;
int wx;
for (wx = 0; wx < wlen - 1; ++wx) {
long wwx = (long)w[wx];
waSum += wwx * waSums[wx];
wcSum += wwx * wcSums[wx];
aSum += wwx * aSums[wx];
rSum += wwx * rSums[wx];
gSum += wwx * gSums[wx];
bSum += wwx * bSums[wx];
}
wx = wlen - 1;
waSums[wx] = 0;
wcSums[wx] = 0;
aSums[wx] = 0;
rSums[wx] = 0;
gSums[wx] = 0;
bSums[wx] = 0;
int srcX = x + wx - r;
if (srcX >= 0 && srcX < applyRect.Width) {
for (int wy = 0; wy < wlen; ++wy) {
int srcY = y + wy - r;
// only when in EDIT mode, ignore some pixels depending on preview quality
if ((useExportQuality || rand.NextDouble() < previewQuality) && srcY >= 0 && srcY < applyRect.Height) {
int wp = w[wy];
waSums[wx] += wp;
src.GetColorAt(srcX, srcY, readingColor);
wp *= readingColor[0] + (readingColor[0] >> 7);
wcSums[wx] += wp;
wp >>= 8;
aSums[wx] += wp * readingColor[0];
rSums[wx] += wp * readingColor[1];
gSums[wx] += wp * readingColor[2];
bSums[wx] += wp * readingColor[3];
}
}
int wr = w[wx];
waSum += wr * waSums[wx];
wcSum += wr * wcSums[wx];
aSum += wr * aSums[wx];
rSum += wr * rSums[wx];
gSum += wr * gSums[wx];
bSum += wr * bSums[wx];
}
wcSum >>= 8;
if (parentBounds.Contains(applyRect.Left + x, applyRect.Top + y) ^ invert) {
if (waSum == 0 || wcSum == 0) {
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);
dst.SetColorAt(x, y, settingColor);
}
}
}
}
}
}
return dst.UnlockAndReturnBitmap();
}
}
/// <summary> /// <summary>
/// Apply BoxBlur to the destinationBitmap /// Apply BoxBlur to the destinationBitmap
/// </summary> /// </summary>
@ -674,6 +481,9 @@ namespace GreenshotPlugin.Core {
if ((range & 1) == 0) { if ((range & 1) == 0) {
range++; range++;
} }
if (range <= 1) {
return;
}
// Box blurs are frequently used to approximate a Gaussian blur. // 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. // 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.
// This might be true, but the GDI+ BlurEffect doesn't look the same, a 2x blur is more simular and we only make 2x Box-Blur. // This might be true, but the GDI+ BlurEffect doesn't look the same, a 2x blur is more simular and we only make 2x Box-Blur.
@ -700,19 +510,17 @@ namespace GreenshotPlugin.Core {
if (targetFastBitmap.hasAlphaChannel) { if (targetFastBitmap.hasAlphaChannel) {
throw new NotSupportedException("BoxBlurHorizontal should NOT be called for bitmaps with alpha channel"); 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; int halfRange = range / 2;
Color[] newColors = new Color[w]; Color[] newColors = new Color[targetFastBitmap.Width];
byte[] tmpColor = new byte[3]; byte[] tmpColor = new byte[3];
for (int y = 0; y < h; y++) { for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) {
int hits = 0; int hits = 0;
int r = 0; int r = 0;
int g = 0; int g = 0;
int b = 0; int b = 0;
for (int x = -halfRange; x < w; x++) { for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++) {
int oldPixel = x - halfRange - 1; int oldPixel = x - halfRange - 1;
if (oldPixel >= 0) { if (oldPixel >= targetFastBitmap.Left) {
targetFastBitmap.GetColorAt(oldPixel, y, tmpColor); targetFastBitmap.GetColorAt(oldPixel, y, tmpColor);
r -= tmpColor[FastBitmap.COLOR_INDEX_R]; r -= tmpColor[FastBitmap.COLOR_INDEX_R];
g -= tmpColor[FastBitmap.COLOR_INDEX_G]; g -= tmpColor[FastBitmap.COLOR_INDEX_G];
@ -721,7 +529,7 @@ namespace GreenshotPlugin.Core {
} }
int newPixel = x + halfRange; int newPixel = x + halfRange;
if (newPixel < w) { if (newPixel < targetFastBitmap.Right) {
targetFastBitmap.GetColorAt(newPixel, y, tmpColor); targetFastBitmap.GetColorAt(newPixel, y, tmpColor);
r += tmpColor[FastBitmap.COLOR_INDEX_R]; r += tmpColor[FastBitmap.COLOR_INDEX_R];
g += tmpColor[FastBitmap.COLOR_INDEX_G]; g += tmpColor[FastBitmap.COLOR_INDEX_G];
@ -729,12 +537,12 @@ namespace GreenshotPlugin.Core {
hits++; hits++;
} }
if (x >= 0) { if (x >= targetFastBitmap.Left) {
newColors[x] = Color.FromArgb(255, (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); newColors[x - targetFastBitmap.Left] = Color.FromArgb(255, (byte)(r / hits), (byte)(g / hits), (byte)(b / hits));
} }
} }
for (int x = 0; x < w; x++) { for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) {
targetFastBitmap.SetColorAt(x, y, newColors[x]); targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]);
} }
} }
} }
@ -748,20 +556,18 @@ namespace GreenshotPlugin.Core {
if (!targetFastBitmap.hasAlphaChannel) { if (!targetFastBitmap.hasAlphaChannel) {
throw new NotSupportedException("BoxBlurHorizontalAlpha should be called for bitmaps with alpha channel"); 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[targetFastBitmap.Width];
byte[] tmpColor = new byte[4]; byte[] tmpColor = new byte[4];
for (int y = 0; y < h; y++) { for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) {
int hits = 0; int hits = 0;
int a = 0; int a = 0;
int r = 0; int r = 0;
int g = 0; int g = 0;
int b = 0; int b = 0;
for (int x = -halfRange; x < w; x++) { for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++) {
int oldPixel = x - halfRange - 1; int oldPixel = x - halfRange - 1;
if (oldPixel >= 0) { if (oldPixel >= targetFastBitmap.Left) {
targetFastBitmap.GetColorAt(oldPixel, y, tmpColor); targetFastBitmap.GetColorAt(oldPixel, y, tmpColor);
a -= tmpColor[FastBitmap.COLOR_INDEX_A]; a -= tmpColor[FastBitmap.COLOR_INDEX_A];
r -= tmpColor[FastBitmap.COLOR_INDEX_R]; r -= tmpColor[FastBitmap.COLOR_INDEX_R];
@ -771,7 +577,7 @@ namespace GreenshotPlugin.Core {
} }
int newPixel = x + halfRange; int newPixel = x + halfRange;
if (newPixel < w) { if (newPixel < targetFastBitmap.Right) {
targetFastBitmap.GetColorAt(newPixel, y, tmpColor); targetFastBitmap.GetColorAt(newPixel, y, tmpColor);
a += tmpColor[FastBitmap.COLOR_INDEX_A]; a += tmpColor[FastBitmap.COLOR_INDEX_A];
r += tmpColor[FastBitmap.COLOR_INDEX_R]; r += tmpColor[FastBitmap.COLOR_INDEX_R];
@ -780,12 +586,12 @@ namespace GreenshotPlugin.Core {
hits++; hits++;
} }
if (x >= 0) { if (x >= targetFastBitmap.Left) {
newColors[x] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); newColors[x - targetFastBitmap.Left] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits));
} }
} }
for (int x = 0; x < w; x++) { for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) {
targetFastBitmap.SetColorAt(x, y, newColors[x]); targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]);
} }
} }
} }
@ -800,20 +606,19 @@ namespace GreenshotPlugin.Core {
throw new NotSupportedException("BoxBlurVertical should NOT be called for bitmaps with alpha channel"); throw new NotSupportedException("BoxBlurVertical should NOT be called for bitmaps with alpha channel");
} }
int w = targetFastBitmap.Width; 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[targetFastBitmap.Height];
int oldPixelOffset = -(halfRange + 1) * w; int oldPixelOffset = -(halfRange + 1) * w;
int newPixelOffset = (halfRange) * w; int newPixelOffset = (halfRange) * w;
byte[] tmpColor = new byte[4]; byte[] tmpColor = new byte[4];
for (int x = 0; x < w; x++) { for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) {
int hits = 0; int hits = 0;
int r = 0; int r = 0;
int g = 0; int g = 0;
int b = 0; int b = 0;
for (int y = -halfRange; y < h; y++) { for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++) {
int oldPixel = y - halfRange - 1; int oldPixel = y - halfRange - 1;
if (oldPixel >= 0) { if (oldPixel >= targetFastBitmap.Top) {
targetFastBitmap.GetColorAt(x, oldPixel, tmpColor); targetFastBitmap.GetColorAt(x, oldPixel, tmpColor);
r -= tmpColor[FastBitmap.COLOR_INDEX_R]; r -= tmpColor[FastBitmap.COLOR_INDEX_R];
g -= tmpColor[FastBitmap.COLOR_INDEX_G]; g -= tmpColor[FastBitmap.COLOR_INDEX_G];
@ -822,7 +627,7 @@ namespace GreenshotPlugin.Core {
} }
int newPixel = y + halfRange; int newPixel = y + halfRange;
if (newPixel < h) { if (newPixel < targetFastBitmap.Bottom) {
targetFastBitmap.GetColorAt(x, newPixel, tmpColor); targetFastBitmap.GetColorAt(x, newPixel, tmpColor);
r += tmpColor[FastBitmap.COLOR_INDEX_R]; r += tmpColor[FastBitmap.COLOR_INDEX_R];
g += tmpColor[FastBitmap.COLOR_INDEX_G]; g += tmpColor[FastBitmap.COLOR_INDEX_G];
@ -830,13 +635,13 @@ namespace GreenshotPlugin.Core {
hits++; hits++;
} }
if (y >= 0) { if (y >= targetFastBitmap.Top) {
newColors[y] = Color.FromArgb(255, (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); newColors[y - targetFastBitmap.Top] = Color.FromArgb(255, (byte)(r / hits), (byte)(g / hits), (byte)(b / hits));
} }
} }
for (int y = 0; y < h; y++) { for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) {
targetFastBitmap.SetColorAt(x, y, newColors[y]); targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]);
} }
} }
} }
@ -852,22 +657,20 @@ namespace GreenshotPlugin.Core {
} }
int w = targetFastBitmap.Width; 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[targetFastBitmap.Height];
int oldPixelOffset = -(halfRange + 1) * w; int oldPixelOffset = -(halfRange + 1) * w;
int newPixelOffset = (halfRange) * w; int newPixelOffset = (halfRange) * w;
byte[] tmpColor = new byte[4]; byte[] tmpColor = new byte[4];
for (int x = 0; x < w; x++) { for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) {
int hits = 0; int hits = 0;
int a = 0; int a = 0;
int r = 0; int r = 0;
int g = 0; int g = 0;
int b = 0; int b = 0;
for (int y = -halfRange; y < h; y++) { for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++) {
int oldPixel = y - halfRange - 1; int oldPixel = y - halfRange - 1;
if (oldPixel >= 0) { if (oldPixel >= targetFastBitmap.Top) {
//int colorg = pixels[index + oldPixelOffset];
targetFastBitmap.GetColorAt(x, oldPixel, tmpColor); targetFastBitmap.GetColorAt(x, oldPixel, tmpColor);
a -= tmpColor[FastBitmap.COLOR_INDEX_A]; a -= tmpColor[FastBitmap.COLOR_INDEX_A];
r -= tmpColor[FastBitmap.COLOR_INDEX_R]; r -= tmpColor[FastBitmap.COLOR_INDEX_R];
@ -877,7 +680,7 @@ namespace GreenshotPlugin.Core {
} }
int newPixel = y + halfRange; int newPixel = y + halfRange;
if (newPixel < h) { if (newPixel < targetFastBitmap.Bottom) {
//int colorg = pixels[index + newPixelOffset]; //int colorg = pixels[index + newPixelOffset];
targetFastBitmap.GetColorAt(x, newPixel, tmpColor); targetFastBitmap.GetColorAt(x, newPixel, tmpColor);
a += tmpColor[FastBitmap.COLOR_INDEX_A]; a += tmpColor[FastBitmap.COLOR_INDEX_A];
@ -887,13 +690,13 @@ namespace GreenshotPlugin.Core {
hits++; hits++;
} }
if (y >= 0) { if (y >= targetFastBitmap.Top) {
newColors[y] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); newColors[y - targetFastBitmap.Top] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits));
} }
} }
for (int y = 0; y < h; y++) { for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) {
targetFastBitmap.SetColorAt(x, y, newColors[y]); targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]);
} }
} }
} }
@ -1068,7 +871,6 @@ namespace GreenshotPlugin.Core {
/// <returns>b/w bitmap</returns> /// <returns>b/w bitmap</returns>
public static Bitmap CreateMonochrome(Image sourceImage, byte threshold) { public static Bitmap CreateMonochrome(Image sourceImage, byte threshold) {
using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(sourceImage, sourceImage.PixelFormat)) { using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(sourceImage, sourceImage.PixelFormat)) {
fastBitmap.Lock();
for (int y = 0; y < fastBitmap.Height; y++) { for (int y = 0; y < fastBitmap.Height; y++) {
for (int x = 0; x < fastBitmap.Width; x++) { for (int x = 0; x < fastBitmap.Width; x++) {
Color color = fastBitmap.GetColorAt(x, y); Color color = fastBitmap.GetColorAt(x, y);
@ -1122,15 +924,13 @@ namespace GreenshotPlugin.Core {
} }
/// <summary> /// <summary>
/// Adjust the brightness, contract or gamma of an image. /// Create ImageAttributes to modify
/// Use the value "1.0f" for no changes.
/// </summary> /// </summary>
/// <param name="sourceImage">Original bitmap</param> /// <param name="brightness"></param>
/// <returns>Bitmap with grayscale</returns> /// <param name="contrast"></param>
public static Image Adjust(Image sourceImage, float brightness, float contrast, float gamma) { /// <param name="gamma"></param>
//create a blank bitmap the same size as original /// <returns>ImageAttributes</returns>
// If using 8bpp than the following exception comes: A Graphics object cannot be created from an image that has an indexed pixel format. public static ImageAttributes CreateAdjustAttributes(float brightness, float contrast, float gamma) {
Bitmap newBitmap = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format24bppRgb, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
float adjustedBrightness = brightness - 1.0f; float adjustedBrightness = brightness - 1.0f;
ColorMatrix applyColorMatrix = new ColorMatrix( ColorMatrix applyColorMatrix = new ColorMatrix(
new float[][] { new float[][] {
@ -1146,8 +946,19 @@ namespace GreenshotPlugin.Core {
attributes.ClearColorMatrix(); attributes.ClearColorMatrix();
attributes.SetColorMatrix(applyColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); attributes.SetColorMatrix(applyColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
attributes.SetGamma(gamma, ColorAdjustType.Bitmap); attributes.SetGamma(gamma, ColorAdjustType.Bitmap);
ApplyImageAttributes((Bitmap)sourceImage, Rectangle.Empty, newBitmap, Rectangle.Empty, attributes); return attributes;
}
/// <summary>
/// Adjust the brightness, contract or gamma of an image.
/// Use the value "1.0f" for no changes.
/// </summary>
/// <param name="sourceImage">Original bitmap</param>
/// <returns>Bitmap with grayscale</returns>
public static Image Adjust(Image sourceImage, float brightness, float contrast, float gamma) {
//create a blank bitmap the same size as original
// If using 8bpp than the following exception comes: A Graphics object cannot be created from an image that has an indexed pixel format.
Bitmap newBitmap = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format24bppRgb, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
ApplyImageAttributes((Bitmap)sourceImage, Rectangle.Empty, newBitmap, Rectangle.Empty, CreateAdjustAttributes(brightness, contrast, gamma));
return newBitmap; return newBitmap;
} }
@ -1407,8 +1218,7 @@ namespace GreenshotPlugin.Core {
if (!includeAlpha) { if (!includeAlpha) {
toCount = toCount & 0xffffff; toCount = toCount & 0xffffff;
} }
using (BitmapBuffer bb = new BitmapBuffer(sourceImage, false)) { using (IFastBitmap bb = FastBitmap.Create((Bitmap)sourceImage)) {
bb.Lock();
for (int y = 0; y < bb.Height; y++) { for (int y = 0; y < bb.Height; y++) {
for (int x = 0; x < bb.Width; x++) { for (int x = 0; x < bb.Width; x++) {
int bitmapcolor = bb.GetColorAt(x, y).ToArgb(); int bitmapcolor = bb.GetColorAt(x, y).ToArgb();
@ -1420,7 +1230,6 @@ namespace GreenshotPlugin.Core {
} }
} }
} }
bb.Unlock();
return colors; return colors;
} }
} }

View file

@ -332,21 +332,19 @@ namespace GreenshotPlugin.Core {
LOG.Info("Starting bitmap reconstruction..."); LOG.Info("Starting bitmap reconstruction...");
using (FastChunkyBitmap bbbDest = FastBitmap.Create(resultBitmap) as FastChunkyBitmap) { using (FastChunkyBitmap dest = FastBitmap.Create(resultBitmap) as FastChunkyBitmap) {
bbbDest.Lock(); using (IFastBitmap src = FastBitmap.Create(sourceBitmap)) {
using (IFastBitmap bbbSrc = FastBitmap.Create(sourceBitmap)) { IFastBitmapWithBlend srcBlend = src as IFastBitmapWithBlend;
IFastBitmapWithBlend bbbSrcBlend = bbbSrc as IFastBitmapWithBlend;
bbbSrc.Lock();
Dictionary<Color, byte> lookup = new Dictionary<Color, byte>(); Dictionary<Color, byte> lookup = new Dictionary<Color, byte>();
byte bestMatch; byte bestMatch;
for (int y = 0; y < bbbSrc.Height; y++) { for (int y = 0; y < src.Height; y++) {
for (int x = 0; x < bbbSrc.Width; x++) { for (int x = 0; x < src.Width; x++) {
Color color; Color color;
if (bbbSrcBlend != null) { if (srcBlend != null) {
// WithoutAlpha, this makes it possible to ignore the alpha // WithoutAlpha, this makes it possible to ignore the alpha
color = bbbSrcBlend.GetBlendedColorAt(x, y); color = srcBlend.GetBlendedColorAt(x, y);
} else { } else {
color = bbbSrc.GetColorAt(x, y); color = src.GetColorAt(x, y);
} }
// Check if we already matched the color // Check if we already matched the color
@ -354,7 +352,7 @@ namespace GreenshotPlugin.Core {
// If not we need to find the best match // If not we need to find the best match
// First get initial match // First get initial match
bestMatch = bbbDest.GetColorIndexAt(x, y); bestMatch = dest.GetColorIndexAt(x, y);
bestMatch = tag[bestMatch]; bestMatch = tag[bestMatch];
Int32 bestDistance = 100000000; Int32 bestDistance = 100000000;
@ -384,7 +382,7 @@ namespace GreenshotPlugin.Core {
blues[bestMatch] += color.B; blues[bestMatch] += color.B;
sums[bestMatch]++; sums[bestMatch]++;
bbbDest.SetColorIndexAt(x, y, bestMatch); dest.SetColorIndexAt(x, y, bestMatch);
} }
} }
} }

View file

@ -1117,14 +1117,13 @@ namespace GreenshotPlugin.Core {
/// </summary> /// </summary>
/// <param name="image">The bitmap to remove the corners from.</param> /// <param name="image">The bitmap to remove the corners from.</param>
private void RemoveCorners(Bitmap image) { private void RemoveCorners(Bitmap image) {
using (BitmapBuffer buffer = new BitmapBuffer((Bitmap)image, false)) { using (IFastBitmap fastBitmap = FastBitmap.Create(image)) {
buffer.Lock();
for (int y = 0; y < conf.WindowCornerCutShape.Count; y++) { for (int y = 0; y < conf.WindowCornerCutShape.Count; y++) {
for (int x = 0; x < conf.WindowCornerCutShape[y]; x++) { for (int x = 0; x < conf.WindowCornerCutShape[y]; x++) {
buffer.SetColorAt(x, y, Color.Transparent); fastBitmap.SetColorAt(x, y, Color.Transparent);
buffer.SetColorAt(image.Width-1-x, y, Color.Transparent); fastBitmap.SetColorAt(image.Width-1-x, y, Color.Transparent);
buffer.SetColorAt(image.Width-1-x, image.Height-1-y, Color.Transparent); fastBitmap.SetColorAt(image.Width-1-x, image.Height-1-y, Color.Transparent);
buffer.SetColorAt(x, image.Height-1-y, Color.Transparent); fastBitmap.SetColorAt(x, image.Height-1-y, Color.Transparent);
} }
} }
} }
@ -1139,18 +1138,14 @@ namespace GreenshotPlugin.Core {
/// <param name="whiteBitmap">Bitmap with the black image</param> /// <param name="whiteBitmap">Bitmap with the black image</param>
/// <returns>Bitmap with transparency</returns> /// <returns>Bitmap with transparency</returns>
private Bitmap ApplyTransparency(Bitmap blackBitmap, Bitmap whiteBitmap) { private Bitmap ApplyTransparency(Bitmap blackBitmap, Bitmap whiteBitmap) {
Bitmap returnBitmap = new Bitmap(blackBitmap.Width, blackBitmap.Height, PixelFormat.Format32bppArgb); using (IFastBitmap targetBuffer = FastBitmap.CreateEmpty(blackBitmap.Size, PixelFormat.Format32bppArgb, Color.Transparent)) {
returnBitmap.SetResolution(blackBitmap.HorizontalResolution, blackBitmap.VerticalResolution); targetBuffer.SetResolution(blackBitmap.HorizontalResolution, blackBitmap.VerticalResolution);
using (BitmapBuffer blackBuffer = new BitmapBuffer(blackBitmap, false)) { using (IFastBitmap blackBuffer = FastBitmap.Create(blackBitmap)) {
blackBuffer.Lock(); using (IFastBitmap whiteBuffer = FastBitmap.Create(whiteBitmap)) {
using (BitmapBuffer whiteBuffer = new BitmapBuffer(whiteBitmap, false)) { for (int y = 0; y < blackBuffer.Height; y++) {
whiteBuffer.Lock(); for (int x = 0; x < blackBuffer.Width; x++) {
using (BitmapBuffer targetBuffer = new BitmapBuffer(returnBitmap, false)) { Color c0 = blackBuffer.GetColorAt(x, y);
targetBuffer.Lock(); Color c1 = whiteBuffer.GetColorAt(x, y);
for(int y=0; y<blackBuffer.Height;y++) {
for(int x=0; x<blackBuffer.Width;x++) {
Color c0 = blackBuffer.GetColorAt(x,y);
Color c1 = whiteBuffer.GetColorAt(x,y);
// Calculate alpha as double in range 0-1 // Calculate alpha as double in range 0-1
double alpha = (c0.R - c1.R + 255) / 255d; double alpha = (c0.R - c1.R + 255) / 255d;
if (alpha == 1) { if (alpha == 1) {
@ -1174,8 +1169,8 @@ namespace GreenshotPlugin.Core {
} }
} }
} }
return targetBuffer.UnlockAndReturnBitmap();
} }
return returnBitmap;
} }
/// <summary> /// <summary>

View file

@ -146,7 +146,6 @@
<Compile Include="Core\AccessibleHelper.cs" /> <Compile Include="Core\AccessibleHelper.cs" />
<Compile Include="Core\AnimationHelpers.cs" /> <Compile Include="Core\AnimationHelpers.cs" />
<Compile Include="Core\BinaryStructHelper.cs" /> <Compile Include="Core\BinaryStructHelper.cs" />
<Compile Include="Core\BitmapBuffer.cs" />
<Compile Include="Core\Cache.cs" /> <Compile Include="Core\Cache.cs" />
<Compile Include="Core\ClipboardHelper.cs" /> <Compile Include="Core\ClipboardHelper.cs" />
<Compile Include="Core\CoreConfiguration.cs" /> <Compile Include="Core\CoreConfiguration.cs" />