/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2012 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 .
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using Greenshot.IniFile;
using GreenshotPlugin.UnmanagedHelpers;
namespace GreenshotPlugin.Core {
///
/// Description of ImageHelper.
///
public static class ImageHelper {
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ImageHelper));
private static CoreConfiguration conf = IniConfig.GetIniSection();
public static Image CreateThumbnail(Image image, int thumbWidth, int thumbHeight) {
return CreateThumbnail(image, thumbWidth, thumbHeight, -1, -1);
}
public static Image CreateThumbnail(Image image, int thumbWidth, int thumbHeight, int maxWidth, int maxHeight) {
int srcWidth=image.Width;
int srcHeight=image.Height;
if (thumbHeight < 0) {
thumbHeight = (int)(thumbWidth * ((float)srcHeight / (float)srcWidth));
}
if (thumbWidth < 0) {
thumbWidth = (int)(thumbHeight * ((float)srcWidth / (float)srcHeight));
}
if (maxWidth > 0 && thumbWidth > maxWidth) {
thumbWidth = Math.Min(thumbWidth, maxWidth);
thumbHeight = (int)(thumbWidth * ((float)srcHeight / (float)srcWidth));
}
if (maxHeight > 0 && thumbHeight > maxHeight) {
thumbHeight = Math.Min(thumbHeight, maxHeight);
thumbWidth = (int)(thumbHeight * ((float)srcWidth / (float)srcHeight));
}
Bitmap bmp = new Bitmap(thumbWidth, thumbHeight);
using (Graphics graphics = System.Drawing.Graphics.FromImage(bmp)) {
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle rectDestination = new Rectangle(0, 0, thumbWidth, thumbHeight);
graphics.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel);
}
return bmp;
}
///
/// Crops the image to the specified rectangle
///
/// Image to crop
/// Rectangle with bitmap coordinates, will be "intersected" to the bitmap
public static bool Crop(ref Image image, ref Rectangle cropRectangle) {
Image returnImage = null;
if (image != null && image is Bitmap && ((image.Width * image.Height) > 0)) {
cropRectangle.Intersect(new Rectangle(0,0, image.Width, image.Height));
if (cropRectangle.Width != 0 || cropRectangle.Height != 0) {
returnImage = CloneArea(image, cropRectangle, PixelFormat.DontCare);
image.Dispose();
image = returnImage;
return true;
}
}
LOG.Warn("Can't crop a null/zero size image!");
return false;
}
///
/// Helper method for the FindAutoCropRectangle
///
///
///
///
private static Rectangle FindAutoCropRectangle(BitmapBuffer buffer, Point colorPoint, int cropDifference) {
Rectangle cropRectangle = Rectangle.Empty;
Color referenceColor = buffer.GetColorAtWithoutAlpha(colorPoint.X,colorPoint.Y);
Point min = new Point(int.MaxValue, int.MaxValue);
Point max = new Point(int.MinValue, int.MinValue);
if (cropDifference > 0) {
for(int y = 0; y < buffer.Height; y++) {
for(int x = 0; x < buffer.Width; x++) {
Color currentColor = buffer.GetColorAt(x, y);
int diffR = Math.Abs(currentColor.R - referenceColor.R);
int diffG = Math.Abs(currentColor.G - referenceColor.G);
int diffB = Math.Abs(currentColor.B - referenceColor.B);
if (((diffR + diffG + diffB) / 3) > cropDifference) {
if (x < min.X) min.X = x;
if (y < min.Y) min.Y = y;
if (x > max.X) max.X = x;
if (y > max.Y) max.Y = y;
}
}
}
} else {
for(int y = 0; y < buffer.Height; y++) {
for(int x = 0; x < buffer.Width; x++) {
Color currentColor = buffer.GetColorAtWithoutAlpha(x, y);
if (referenceColor.Equals(currentColor)) {
if (x < min.X) min.X = x;
if (y < min.Y) min.Y = y;
if (x > max.X) max.X = x;
if (y > max.Y) max.Y = y;
}
}
}
}
if (!(Point.Empty.Equals(min) && max.Equals(new Point(buffer.Width-1, buffer.Height-1)))) {
if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue)) {
cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1);
}
}
return cropRectangle;
}
///
/// Get a rectangle for the image which crops the image of all colors equal to that on 0,0
///
///
/// Rectangle
public static Rectangle FindAutoCropRectangle(Image image, int cropDifference) {
Rectangle cropRectangle = Rectangle.Empty;
using (BitmapBuffer buffer = new BitmapBuffer((Bitmap)image, false)) {
buffer.Lock();
Rectangle currentRectangle = Rectangle.Empty;
List checkPoints = new List();
// Top Left
checkPoints.Add(new Point(0, 0));
// Bottom Left
checkPoints.Add(new Point(0, image.Height-1));
// Top Right
checkPoints.Add(new Point(image.Width-1, 0));
// Bottom Right
checkPoints.Add( new Point(image.Width-1, image.Height-1));
// find biggest area
foreach(Point checkPoint in checkPoints) {
currentRectangle = FindAutoCropRectangle(buffer, checkPoint, cropDifference);
if (currentRectangle.Width * currentRectangle.Height > cropRectangle.Width * cropRectangle.Height) {
cropRectangle = currentRectangle;
}
}
}
return cropRectangle;
}
///
/// Load an image from file
///
///
///
public static Bitmap LoadBitmap(string filename) {
if (string.IsNullOrEmpty(filename)) {
return null;
}
Bitmap fileBitmap = null;
LOG.InfoFormat("Loading image from file {0}", filename);
// Fixed lock problem Bug #3431881
using (Stream imageFileStream = File.OpenRead(filename)) {
// And fixed problem that the bitmap stream is disposed... by Cloning the image
// This also ensures the bitmap is correctly created
if (filename.EndsWith(".ico")) {
// Icon logic, try to get the Vista icon, else the biggest possible
try {
using (Image tmpImage = ExtractVistaIcon(imageFileStream)) {
if (tmpImage != null) {
fileBitmap = Clone(tmpImage);
}
}
} catch (Exception vistaIconException) {
LOG.Warn("Can't read icon from " + filename, vistaIconException);
}
if (fileBitmap == null) {
try {
// No vista icon, try normal icon
imageFileStream.Position = 0;
// We create a copy of the bitmap, so everything else can be disposed
using (Icon tmpIcon = new Icon(imageFileStream, new Size(1024,1024))) {
using (Image tmpImage = tmpIcon.ToBitmap()) {
fileBitmap = Clone(tmpImage);
}
}
} catch (Exception iconException) {
LOG.Warn("Can't read icon from " + filename, iconException);
}
}
}
if (fileBitmap == null) {
// We create a copy of the bitmap, so everything else can be disposed
imageFileStream.Position = 0;
using (Image tmpImage = Image.FromStream(imageFileStream, true, true)) {
LOG.DebugFormat("Loaded {0} with Size {1}x{2} and PixelFormat {3}", filename, tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
fileBitmap = Clone(tmpImage);
}
}
}
if (fileBitmap != null) {
LOG.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileBitmap.Width, fileBitmap.Height, fileBitmap.PixelFormat, fileBitmap.HorizontalResolution, fileBitmap.VerticalResolution);
}
return fileBitmap;
}
/**
* Checks if we support the supplied PixelFormat
*/
private static bool isSupported(PixelFormat pixelformat) {
return (PixelFormat.Format32bppArgb.Equals(pixelformat)||
PixelFormat.Format32bppRgb.Equals(pixelformat) ||
PixelFormat.Format24bppRgb.Equals(pixelformat));
}
// Based on: http://www.codeproject.com/KB/cs/IconExtractor.aspx
// And a hint from: http://www.codeproject.com/KB/cs/IconLib.aspx
public static Bitmap ExtractVistaIcon(Stream iconStream) {
const int SizeICONDIR = 6;
const int SizeICONDIRENTRY = 16;
Bitmap bmpPngExtracted = null;
try {
byte[] srcBuf = new byte[iconStream.Length];
iconStream.Read(srcBuf, 0, (int)iconStream.Length);
int iCount = BitConverter.ToInt16(srcBuf, 4);
for (int iIndex=0; iIndex
/// See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx
///
/// The icon to
/// The file (EXE or DLL) to get the icon from
/// Index of the icon
/// true if the large icon is wanted
/// Icon
public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge) {
IntPtr large;
IntPtr small;
Shell32.ExtractIconEx(location, index, out large, out small, 1);
Icon returnIcon = null;
bool isLarge = false;
bool isSmall = false;
try {
if (takeLarge && !IntPtr.Zero.Equals(large)) {
returnIcon = Icon.FromHandle(large);
isLarge = true;
} else if (!IntPtr.Zero.Equals(small)) {
returnIcon = Icon.FromHandle(small);
isSmall = true;
} else if (!IntPtr.Zero.Equals(large)) {
returnIcon = Icon.FromHandle(large);
isLarge = true;
}
} finally {
if (isLarge && !IntPtr.Zero.Equals(small)) {
User32.DestroyIcon(small);
}
if (isSmall && !IntPtr.Zero.Equals(large)) {
User32.DestroyIcon(large);
}
}
return returnIcon;
}
///
/// Get the number of icon in the file
///
///
/// Location of the EXE or DLL
///
public static int CountAssociatedIcons(string location) {
IntPtr large = IntPtr.Zero;
IntPtr small = IntPtr.Zero;
return Shell32.ExtractIconEx(location, -1, out large, out small, 0);
}
///
/// Make the picture look like it's torn
///
/// Bitmap to make torn edge off
/// Changed bitmap
public static Bitmap CreateTornEdge(Bitmap sourceBitmap) {
Bitmap returnImage = CreateEmpty(sourceBitmap.Width, sourceBitmap.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
using (GraphicsPath path = new GraphicsPath()) {
Random random = new Random();
int regionWidth = 20;
int regionHeight = 20;
int HorizontalRegions = (int)(sourceBitmap.Width / regionWidth);
int VerticalRegions = (int)(sourceBitmap.Height / regionHeight);
int distance = 12;
// Start
Point previousEndingPoint = new Point(regionWidth, random.Next(1, distance));
Point newEndingPoint;
// Top
for (int i = 0; i < HorizontalRegions; i++) {
int x = (int)previousEndingPoint.X + regionWidth;
int y = random.Next(1, distance);
newEndingPoint = new Point(x, y);
path.AddLine(previousEndingPoint, newEndingPoint);
previousEndingPoint = newEndingPoint;
}
// Right
for (int i = 0; i < VerticalRegions; i++) {
int x = sourceBitmap.Width - random.Next(1, distance);
int y = (int)previousEndingPoint.Y + regionHeight;
newEndingPoint = new Point(x, y);
path.AddLine(previousEndingPoint, newEndingPoint);
previousEndingPoint = newEndingPoint;
}
// Bottom
for (int i = 0; i < HorizontalRegions; i++) {
int x = (int)previousEndingPoint.X - regionWidth;
int y = sourceBitmap.Height - random.Next(1, distance);
newEndingPoint = new Point(x, y);
path.AddLine(previousEndingPoint, newEndingPoint);
previousEndingPoint = newEndingPoint;
}
// Left
for (int i = 0; i < VerticalRegions; i++) {
int x = random.Next(1, distance);
int y = (int)previousEndingPoint.Y - regionHeight;
newEndingPoint = new Point(x, y);
path.AddLine(previousEndingPoint, newEndingPoint);
previousEndingPoint = newEndingPoint;
}
path.CloseFigure();
// Draw the created figure with the original image by using a TextureBrush so we have anti-aliasing
using (Graphics graphics = Graphics.FromImage(returnImage)) {
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
using (Brush brush = new TextureBrush(sourceBitmap)) {
// Imporant note: If the target wouldn't be at 0,0 we need to translate-transform!!
graphics.FillPath(brush, path);
}
}
}
return returnImage;
}
///
/// Helper Method for the ApplyBlur
///
///
///
private static int[] CreateGaussianBlurRow(int amount) {
int size = 1 + (amount * 2);
int[] weights = new int[size];
for (int i = 0; i <= amount; ++i) {
// 1 + aa - aa + 2ai - ii
weights[i] = 16 * (i + 1);
weights[weights.Length - i - 1] = weights[i];
}
return weights;
}
///
/// Create a new bitmap with the sourceBitmap blurred
///
/// Source to blur
/// Area to blur
/// Use best quality
/// Radius of the blur
/// Quality, use 1d for normal anything less skipps calculations
/// true if the blur needs to occur outside of the area
/// Rectangle limiting the area when using invert
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 bbbDest = new BitmapBuffer(sourceBitmap, applyRect, true)) {
bbbDest.Lock();
using (BitmapBuffer bbbSrc = new BitmapBuffer(sourceBitmap, applyRect, false)) {
bbbSrc.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 < bbbDest.Width) {
for (int wy = 0; wy < wlen; ++wy) {
int srcY = y + wy - r;
if (srcY >= 0 && srcY < bbbDest.Height) {
bbbSrc.GetColorIn(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) {
bbbDest.SetUncheckedColorArrayAt(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);
}
}
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;
bbbSrc.GetColorIn(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) {
bbbDest.SetUncheckedColorArrayAt(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);
}
}
}
}
}
}
bbbDest.Unlock();
return bbbDest.Bitmap;
}
}
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.
*/
public static Rectangle CreateIntersectRectangle(Size applySize, Rectangle rect, bool invert) {
Rectangle myRect;
if (invert) {
myRect = new Rectangle(0, 0, applySize.Width, applySize.Height);
} else {
Rectangle applyRect = new Rectangle(0, 0, applySize.Width, applySize.Height);
myRect = new Rectangle(rect.X, rect.Y, rect.Width, rect.Height);
myRect.Intersect(applyRect);
}
return myRect;
}
///
/// Create a new bitmap where the sourceBitmap has a shadow
///
/// Bitmap to make a shadow on
/// How dark is the shadow
/// Size of the shadow
/// What pixel format must the returning bitmap have
/// How many pixels is the original image moved?
/// Bitmap with the shadow, is bigger than the sourceBitmap!!
public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, ref Point offset, PixelFormat targetPixelformat) {
// Create a new "clean" image
Bitmap returnImage = null;
offset.X += shadowSize - 1;
offset.Y += shadowSize - 1;
using (Bitmap tmpImage = CreateEmpty(sourceBitmap.Width + (shadowSize * 2), sourceBitmap.Height + (shadowSize * 2), targetPixelformat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution)) {
using (Graphics graphics = Graphics.FromImage(tmpImage)) {
// Make sure we draw with the best quality!
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Draw "shadow" offsetted
ImageAttributes ia = new ImageAttributes();
ColorMatrix cm = new ColorMatrix();
cm.Matrix00 = 0;
cm.Matrix11 = 0;
cm.Matrix22 = 0;
cm.Matrix33 = darkness;
ia.SetColorMatrix(cm);
Rectangle shadowRectangle = new Rectangle(new Point(shadowSize, shadowSize), sourceBitmap.Size);
graphics.DrawImage(sourceBitmap, shadowRectangle, 0, 0, sourceBitmap.Width, sourceBitmap.Height, GraphicsUnit.Pixel, ia);
}
// blur "shadow", apply to whole new image
Rectangle newImageRectangle = new Rectangle(0, 0, tmpImage.Width, tmpImage.Height);
//using (Bitmap blurImage = FastBlur(newImage, shadowSize-1)) {
returnImage = CreateBlur(tmpImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle);
}
if (returnImage != null) {
using (Graphics graphics = Graphics.FromImage(returnImage)) {
// Make sure we draw with the best quality!
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
// draw original with a TextureBrush so we have nice antialiasing!
using (Brush textureBrush = new TextureBrush(sourceBitmap, WrapMode.Clamp)) {
// We need to do a translate-tranform otherwise the image is wrapped
graphics.TranslateTransform(offset.X, offset.Y);
graphics.FillRectangle(textureBrush, 0, 0, sourceBitmap.Width, sourceBitmap.Height);
}
}
}
return returnImage;
}
///
/// Return negative of Bitmap
///
/// Bitmap to create a negative off
/// Negative bitmap
public static Bitmap CreateNegative(Bitmap sourceBitmap) {
using (BitmapBuffer bb = new BitmapBuffer(sourceBitmap, 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);
Color invertedColor = Color.FromArgb(color.A, color.R ^ 255, color.G ^ 255, color.B ^ 255);
bb.SetColorAt(x, y, invertedColor);
}
}
bb.Unlock();
return bb.Bitmap;
}
}
///
/// Create a new bitmap where the sourceBitmap has a Simple border around it
///
/// Bitmap to make a border on
/// Size of the border
/// Color of the border
/// What pixel format must the returning bitmap have
/// How many pixels is the original image moved?
/// Bitmap with the shadow, is bigger than the sourceBitmap!!
public static Bitmap CreateBorder(Bitmap sourceBitmap, int borderSize, Color borderColor, PixelFormat targetPixelformat, out Point offset) {
// "return" the shifted offset, so the caller can e.g. move elements
offset = new Point(borderSize, borderSize);
// Create a new "clean" image
Bitmap newImage = CreateEmpty(sourceBitmap.Width + (borderSize * 2), sourceBitmap.Height + (borderSize * 2), targetPixelformat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
using (Graphics graphics = Graphics.FromImage(newImage)) {
// Make sure we draw with the best quality!
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
using (GraphicsPath path = new GraphicsPath()) {
path.AddRectangle(new Rectangle(borderSize >> 1, borderSize >> 1, newImage.Width - (borderSize), newImage.Height - (borderSize)));
using (Pen pen = new Pen(borderColor, borderSize)) {
pen.LineJoin = LineJoin.Round;
pen.StartCap = LineCap.Round;
pen.EndCap = LineCap.Round;
graphics.DrawPath(pen, path);
}
}
// draw original with a TextureBrush so we have nice antialiasing!
using (Brush textureBrush = new TextureBrush(sourceBitmap, WrapMode.Clamp)) {
// We need to do a translate-tranform otherwise the image is wrapped
graphics.TranslateTransform(offset.X, offset.Y);
graphics.FillRectangle(textureBrush, 0, 0, sourceBitmap.Width, sourceBitmap.Height);
}
}
return newImage;
}
///
/// Create a new bitmap where the sourceBitmap is in grayscale
///
/// Original bitmap
/// Bitmap with grayscale
public static Bitmap CreateGrayscale(Bitmap sourceBitmap) {
//create a blank bitmap the same size as original
Bitmap newBitmap = CreateEmptyLike(sourceBitmap, Color.Empty);
//get a graphics object from the new image
using (Graphics graphics = Graphics.FromImage(newBitmap)) {
// create the grayscale ColorMatrix
ColorMatrix colorMatrix = 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}
});
//create some image attributes
ImageAttributes attributes = new ImageAttributes();
//set the color matrix attribute
attributes.SetColorMatrix(colorMatrix);
//draw the original image on the new image using the grayscale color matrix
graphics.DrawImage(sourceBitmap, new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), 0, 0, sourceBitmap.Width, sourceBitmap.Height, GraphicsUnit.Pixel, attributes);
}
return newBitmap;
}
///
/// Checks if the supplied Bitmap has a PixelFormat we support
///
/// bitmap to check
/// bool if we support it
public static bool SupportsPixelFormat(Bitmap bitmap) {
return SupportsPixelFormat(bitmap.PixelFormat);
}
///
/// Checks if we support the pixel format
///
/// PixelFormat to check
/// bool if we support it
public static bool SupportsPixelFormat(PixelFormat pixelformat) {
return (pixelformat.Equals(PixelFormat.Format32bppArgb) ||
pixelformat.Equals(PixelFormat.Format32bppPArgb) ||
pixelformat.Equals(PixelFormat.Format32bppRgb) ||
pixelformat.Equals(PixelFormat.Format24bppRgb));
}
///
/// Wrapper for just cloning which calls the CloneArea
///
/// Image to clone
/// Bitmap with clone image data
public static Bitmap Clone(Image sourceBitmap) {
return CloneArea(sourceBitmap, Rectangle.Empty, PixelFormat.DontCare);
}
///
/// Wrapper for just cloning & TargetFormat which calls the CloneArea
///
/// Image to clone
/// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported)
/// Bitmap with clone image data
public static Bitmap Clone(Image sourceBitmap, PixelFormat targetFormat) {
return CloneArea(sourceBitmap, Rectangle.Empty, targetFormat);
}
///
/// Clone an image, taking some rules into account:
/// 1) When sourceRect is the whole bitmap there is a GDI+ bug in Clone
/// Clone will than return the same PixelFormat as the source
/// a quick workaround is using new Bitmap which uses a default of Format32bppArgb
/// 2) When going from a transparent to a non transparent bitmap, we draw the background white!
///
/// Source bitmap to clone
/// Rectangle to copy from the source, use Rectangle.Empty for all
/// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported)
///
public static Bitmap CloneArea(Image sourceBitmap, Rectangle sourceRect, PixelFormat targetFormat) {
Bitmap newImage = null;
Rectangle bitmapRect = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height);
// Make sure the source is not Rectangle.Empty
if (Rectangle.Empty.Equals(sourceRect)) {
sourceRect = new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height);
}
// If no pixelformat is supplied
if (PixelFormat.DontCare == targetFormat || PixelFormat.Undefined == targetFormat) {
if (SupportsPixelFormat(sourceBitmap.PixelFormat)) {
targetFormat = sourceBitmap.PixelFormat;
} else if (Image.IsAlphaPixelFormat(sourceBitmap.PixelFormat)) {
targetFormat = PixelFormat.Format32bppArgb;
} else {
targetFormat = PixelFormat.Format24bppRgb;
}
}
// check the target format
if (!SupportsPixelFormat(targetFormat)) {
if (Image.IsAlphaPixelFormat(targetFormat)) {
targetFormat = PixelFormat.Format32bppArgb;
} else {
targetFormat = PixelFormat.Format24bppRgb;
}
}
bool destinationIsTransparent = Image.IsAlphaPixelFormat(targetFormat);
bool sourceIsTransparent = Image.IsAlphaPixelFormat(sourceBitmap.PixelFormat);
bool fromTransparentToNon = !destinationIsTransparent && sourceIsTransparent;
bool isBitmap = sourceBitmap is Bitmap;
bool isAreaEqual = sourceRect.Equals(bitmapRect);
if (isAreaEqual || fromTransparentToNon || !isBitmap) {
// Rule 1: if the areas are equal, always copy ourselves
newImage = new Bitmap(bitmapRect.Width, bitmapRect.Height, targetFormat);
// Make sure both images have the same resolution
newImage.SetResolution(sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
using (Graphics graphics = Graphics.FromImage(newImage)) {
if (fromTransparentToNon) {
// Rule 2: Make sure the background color is white
graphics.Clear(Color.White);
}
// decide fastest copy method
if (isAreaEqual) {
graphics.DrawImageUnscaled(sourceBitmap, 0, 0);
} else {
graphics.DrawImage(sourceBitmap, 0, 0, sourceRect, GraphicsUnit.Pixel);
}
}
} else {
// Let GDI+ decide how to convert, need to test what is quicker...
newImage = (sourceBitmap as Bitmap).Clone(sourceRect, targetFormat);
// Make sure both images have the same resolution
newImage.SetResolution(sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
}
return newImage;
}
///
/// Rotate the bitmap
///
///
///
///
public static Bitmap RotateFlip(Bitmap sourceBitmap, RotateFlipType rotateFlipType) {
Bitmap returnBitmap = Clone(sourceBitmap);
returnBitmap.RotateFlip(rotateFlipType);
return returnBitmap;
}
///
/// A generic way to create an empty image
///
/// the source bitmap as the specifications for the new bitmap
/// The color to fill with, or Color.Empty to take the default depending on the pixel format
///
public static Bitmap CreateEmptyLike(Bitmap sourceBitmap, Color backgroundColor) {
return CreateEmpty(sourceBitmap.Width, sourceBitmap.Height, sourceBitmap.PixelFormat, backgroundColor, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
}
///
/// A generic way to create an empty image
///
///
///
///
/// The color to fill with, or Color.Empty to take the default depending on the pixel format
///
///
///
public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution, float verticalResolution) {
// Create a new "clean" image
Bitmap newImage = new Bitmap(width, height, format);
newImage.SetResolution(horizontalResolution, verticalResolution);
using (Graphics graphics = Graphics.FromImage(newImage)) {
// Make sure the background color is what we want (transparent or white, depending on the pixel format)
if (!Color.Empty.Equals(backgroundColor)) {
graphics.Clear(backgroundColor);
} else if (Image.IsAlphaPixelFormat(format)) {
graphics.Clear(Color.Transparent);
} else {
graphics.Clear(Color.White);
}
}
return newImage;
}
///
/// Get a scaled version of the sourceBitmap
///
///
/// 1-99 to make smaller, use 101 and more to make the picture bigger
///
public static Bitmap ScaleByPercent(Bitmap sourceBitmap, int percent) {
float nPercent = ((float)percent/100);
int sourceWidth = sourceBitmap.Width;
int sourceHeight = sourceBitmap.Height;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
Bitmap scaledBitmap = CreateEmpty(destWidth, destHeight, sourceBitmap.PixelFormat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
using (Graphics graphics = Graphics.FromImage(scaledBitmap)) {
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.DrawImage(sourceBitmap, new Rectangle(0, 0, destWidth, destHeight), new Rectangle(0, 0, sourceWidth, sourceHeight), GraphicsUnit.Pixel);
}
return scaledBitmap;
}
///
/// Grow canvas with pixel to the left, right, top and bottom
///
///
/// The color to fill with, or Color.Empty to take the default depending on the pixel format
///
///
///
///
/// a new bitmap with the source copied on it
public static Bitmap GrowCanvas(Bitmap sourceBitmap, Color backgroundColor, int left, int right, int top, int bottom) {
Bitmap newBitmap = CreateEmpty(sourceBitmap.Width + left + right, sourceBitmap.Height + top + bottom, sourceBitmap.PixelFormat, backgroundColor, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
using (Graphics graphics = Graphics.FromImage(newBitmap)) {
graphics.DrawImageUnscaled(sourceBitmap, left, top);
}
return newBitmap;
}
///
/// Wrapper for the more complex Resize, this resize could be used for e.g. Thumbnails
///
///
/// true to maintain the aspect ratio
///
///
///
public static Bitmap ResizeBitmap(Bitmap sourceBitmap, bool maintainAspectRatio, int newWidth, int newHeight) {
Point throwAway;
return ResizeBitmap(sourceBitmap, maintainAspectRatio, false, Color.Empty, newWidth, newHeight, out throwAway);
}
///
/// Scale the bitmap, keeping aspect ratio, but the canvas will always have the specified size.
///
/// Bitmap to scale
/// true to maintain the aspect ratio
/// The color to fill with, or Color.Empty to take the default depending on the pixel format
/// new width
/// new height
/// a new bitmap with the specified size, the source-bitmap scaled to fit with aspect ratio locked
public static Bitmap ResizeBitmap(Bitmap sourceBitmap, bool maintainAspectRatio, bool canvasUseNewSize, Color backgroundColor, int newWidth, int newHeight, out Point offset) {
int destX = 0;
int destY = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)newWidth / (float)sourceBitmap.Width);
nPercentH = ((float)newHeight / (float)sourceBitmap.Height);
if (maintainAspectRatio) {
if (nPercentH != 0 && nPercentH < nPercentW) {
nPercentW = nPercentH;
if (canvasUseNewSize) {
destX = Math.Max(0, System.Convert.ToInt32((newWidth - (sourceBitmap.Width * nPercentW)) / 2));
}
} else {
nPercentH = nPercentW;
if (canvasUseNewSize) {
destY = Math.Max(0, System.Convert.ToInt32((newHeight - (sourceBitmap.Height * nPercentH)) / 2));
}
}
}
offset = new Point(destX, destY);
int destWidth = (int)(sourceBitmap.Width * nPercentW);
int destHeight = (int)(sourceBitmap.Height * nPercentH);
if (newWidth == 0) {
newWidth = destWidth;
}
if (newHeight == 0) {
newHeight = destHeight;
}
Bitmap newBitmap = null;
if (maintainAspectRatio && canvasUseNewSize) {
newBitmap = CreateEmpty(newWidth, newHeight, sourceBitmap.PixelFormat, backgroundColor, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
} else {
newBitmap = CreateEmpty(destWidth, destHeight, sourceBitmap.PixelFormat, backgroundColor, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
}
using (Graphics graphics = Graphics.FromImage(newBitmap)) {
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.DrawImage(sourceBitmap, new Rectangle(destX, destY, destWidth, destHeight), new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), GraphicsUnit.Pixel);
}
return newBitmap;
}
}
}