diff --git a/GreenshotPlugin/Core/FastBitmap.cs b/GreenshotPlugin/Core/FastBitmap.cs new file mode 100644 index 000000000..4a7838d37 --- /dev/null +++ b/GreenshotPlugin/Core/FastBitmap.cs @@ -0,0 +1,481 @@ +/* + * 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 . + */ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; + +namespace GreenshotPlugin.Core { + + /// + /// The interface for the FastBitmap + /// + public interface IFastBitmap : IDisposable { + Color GetColorAt(int x, int y); + void SetColorAt(int x, int y, Color color); + void Lock(); + void Unlock(); + Bitmap Bitmap { + get; + } + Size Size { + get; + } + int Height { + get; + } + int Width { + get; + } + bool NeedsDispose { + get; + } + } + + /// + /// The base class for the fast bitmap implementation + /// + public unsafe abstract class FastBitmap : IFastBitmap { + private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(FastBitmap)); + + /// + /// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap + /// + public bool NeedsDispose { + get; + protected set; + } + + /// + /// The bitmap for which the FastBitmap is creating access + /// + protected Bitmap bitmap; + protected BitmapData bmData; + protected int stride; /* bytes per pixel row */ + protected bool bitsLocked = false; + protected byte* pointer; + + /// + /// Factory for creating a FastBitmap depending on the pixelformat of the source + /// + /// Bitmap to access + /// IFastBitmap + public static IFastBitmap Create(Bitmap source) { + switch (source.PixelFormat) { + case PixelFormat.Format8bppIndexed: + return new FastChunkyBitmap(source); + case PixelFormat.Format24bppRgb: + return new Fast24RGBBitmap(source); + case PixelFormat.Format32bppRgb: + return new Fast32RGBBitmap(source); + case PixelFormat.Format32bppArgb: + return new Fast32ARGBBitmap(source); + default: + throw new NotSupportedException(string.Format("Not supported Pixelformat {0}", source.PixelFormat)); + } + } + + /// + /// Factory for creating a FastBitmap as a destination for the source + /// + /// Bitmap to access + /// IFastBitmap + public static IFastBitmap CreateDestinationFor(Bitmap source, PixelFormat pixelFormat) { + Bitmap destination = ImageHelper.CloneArea(source, Rectangle.Empty, pixelFormat); + IFastBitmap fastBitmap = Create(destination); + ((FastBitmap)fastBitmap).NeedsDispose = true; + return fastBitmap; + } + + + /// + /// Factory for creating a FastBitmap as a destination + /// + /// + /// + /// + /// IFastBitmap + public static IFastBitmap CreateEmpty(Size newSize, PixelFormat pixelFormat, Color backgroundColor) { + Bitmap destination = ImageHelper.CreateEmpty(newSize.Width, newSize.Height, pixelFormat, backgroundColor, 96f, 96f); + IFastBitmap fastBitmap = Create(destination); + ((FastBitmap)fastBitmap).NeedsDispose = true; + return fastBitmap; + } + + protected FastBitmap(Bitmap bitmap) { + this.bitmap = bitmap; + } + + /// + /// Return the size of the image + /// + public Size Size { + get { + return bitmap.Size; + } + } + + /// + /// Return the width of the image + /// + public int Width { + get { + return bitmap.Width; + } + } + + /// + /// Return the height of the image + /// + public int Height { + get { + return bitmap.Height; + } + } + + /// + /// Returns the underlying bitmap, do not dispose... if it's created for the FastBitmap it will be disposed! + /// + public Bitmap Bitmap { + get { + if (bitsLocked) { + throw new NotSupportedException("Can't get a locked bitmap!"); + } + return bitmap; + } + } + + /// + /// Destructor + /// + ~FastBitmap() { + 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 && NeedsDispose) { + bitmap.Dispose(); + } + } + bitmap = null; + bmData = null; + pointer = null; + } + + /// + /// Lock the bitmap so we have direct access to the memory + /// + public void Lock() { + if (Width > 0 && Height > 0 && !bitsLocked) { + bmData = bitmap.LockBits(new Rectangle(Point.Empty, Size), ImageLockMode.ReadWrite, bitmap.PixelFormat); + bitsLocked = true; + + IntPtr Scan0 = bmData.Scan0; + pointer = (byte*)(void*)Scan0; + stride = bmData.Stride; + } + } + + /// + /// Unlock the System Memory + /// + public void Unlock() { + if (bitsLocked) { + bitmap.UnlockBits(bmData); + bitsLocked = false; + } + } + + /// + /// Draw the stored bitmap to the destionation bitmap at the supplied point + /// + /// + /// + public void DrawTo(Graphics graphics, Point destination) { + DrawTo(graphics, null, destination); + } + + /// + /// Draw the stored Bitmap on the Destination bitmap with the specified rectangle + /// Be aware that the stored bitmap will be resized to the specified rectangle!! + /// + /// + /// + public void DrawTo(Graphics graphics, Rectangle destinationRect) { + DrawTo(graphics, destinationRect, null); + } + + /// + /// private helper to draw the bitmap + /// + /// + /// + /// + 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(); + } + } + + public abstract Color GetColorAt(int x, int y); + public abstract void SetColorAt(int x, int y, Color color); + } + + public unsafe class FastChunkyBitmap : FastBitmap { + // Used for indexed images + private Color[] colorEntries; + private Dictionary colorCache = new Dictionary(); + + public FastChunkyBitmap(Bitmap source) : base(source) { + colorEntries = bitmap.Palette.Entries; + } + + /// + /// Get the color from the specified location + /// + /// + /// + /// Color + public override Color GetColorAt(int x, int y) { + int offset = x + (y * stride); + byte colorIndex = pointer[offset]; + return colorEntries[colorIndex]; + } + + /// + /// Get the color-index from the specified location + /// + /// + /// + /// byte with index + public byte GetColorIndexAt(int x, int y) { + int offset = x + (y * stride); + return pointer[offset]; + } + + /// + /// Set the color-index at the specified location + /// + /// + /// + /// + public void SetColorIndexAt(int x, int y, byte colorIndex) { + int offset = x + (y * stride); + pointer[offset] = colorIndex; + } + + /// + /// Set the supplied color at the specified location. + /// Throws an ArgumentException if the color is not in the palette + /// + /// + /// + /// Color to set + public override void SetColorAt(int x, int y, Color color) { + int offset = x + (y * stride); + byte colorIndex; + if (!colorCache.TryGetValue(color, out colorIndex)) { + bool foundColor = false; + for (colorIndex = 0; colorIndex < colorEntries.Length; colorIndex++) { + if (color == colorEntries[colorIndex]) { + colorCache.Add(color, colorIndex); + foundColor = true; + break; + } + } + if (!foundColor) { + throw new ArgumentException("No such color!"); + } + } + pointer[offset] = colorIndex; + } + } + + /// + /// This is the implementation of the IFastBitmap for 24 bit images (no Alpha) + /// + public unsafe class Fast24RGBBitmap : FastBitmap { + + public Fast24RGBBitmap(Bitmap source) : base(source) { + } + + /// + /// Retrieve the color at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// X coordinate + /// Y Coordinate + /// Color + public override Color GetColorAt(int x, int y) { + int offset = (x * 3) + (y * stride); + return Color.FromArgb(255, pointer[2 + offset], pointer[1 + offset], pointer[offset]); + } + + /// + /// Set the color at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// + /// + /// + public override void SetColorAt(int x, int y, Color color) { + int offset = (x * 3) + (y * stride); + pointer[2 + offset] = color.R; + pointer[1 + offset] = color.G; + pointer[offset] = color.B; + } + } + + /// + /// This is the implementation of the IFastBitmap for 32 bit images (no Alpha) + /// + public unsafe class Fast32RGBBitmap : FastBitmap { + public Fast32RGBBitmap(Bitmap source) : base(source) { + + } + + /// + /// Retrieve the color at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// X coordinate + /// Y Coordinate + /// Color + public override Color GetColorAt(int x, int y) { + int offset = (x * 4) + (y * stride); + return Color.FromArgb(255, pointer[2 + offset], pointer[1 + offset], pointer[offset]); + } + + /// + /// Set the color at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// + /// + /// + public override void SetColorAt(int x, int y, Color color) { + int offset = (x * 4) + (y * stride); + pointer[2 + offset] = color.R; + pointer[1 + offset] = color.G; + pointer[offset] = color.B; + } + } + + /// + /// This is the implementation of the IFastBitmap for 32 bit images with Alpha + /// + public unsafe class Fast32ARGBBitmap : FastBitmap { + public Color BackgroundBlendColor { + get; + set; + } + public Fast32ARGBBitmap(Bitmap source) : base(source) { + BackgroundBlendColor = Color.White; + } + + /// + /// Retrieve the color at location x,y + /// + /// X coordinate + /// Y Coordinate + /// Color + public override Color GetColorAt(int x, int y) { + int offset = (x * 4) + (y * stride); + return Color.FromArgb(pointer[3 + offset], pointer[2 + offset], pointer[1 + offset], pointer[offset]); + } + + /// + /// Set the color at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// + /// + /// + public override void SetColorAt(int x, int y, Color color) { + int offset = (x * 4) + (y * stride); + pointer[3 + offset] = color.A; + pointer[2 + offset] = color.R; + pointer[1 + offset] = color.G; + pointer[offset] = color.B; + } + + /// + /// Retrieve the color, without alpha (is blended), at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// X coordinate + /// Y Coordinate + /// Color + public Color GetColorAtWithoutAlpha(int x, int y) { + int offset = (x * 4) + (y * stride); + int a = pointer[3 + offset]; + int red = pointer[2 + offset]; + int green = pointer[1 + offset]; + int blue = pointer[offset]; + + if (a < 255) { + // As the request is to get without alpha, we blend. + int rem = 255 - a; + red = (red * a + BackgroundBlendColor.R * rem) / 255; + green = (green * a + BackgroundBlendColor.G * rem) / 255; + blue = (blue * a + BackgroundBlendColor.B * rem) / 255; + } + return Color.FromArgb(255, red, green, blue); + } + + } +} diff --git a/GreenshotPlugin/GreenshotPlugin.csproj b/GreenshotPlugin/GreenshotPlugin.csproj index 6ddf4b4d2..31e9ff7f6 100644 --- a/GreenshotPlugin/GreenshotPlugin.csproj +++ b/GreenshotPlugin/GreenshotPlugin.csproj @@ -31,6 +31,7 @@ Form +