From f34923e0d737b90398799b89ac1350e8b3c90dde Mon Sep 17 00:00:00 2001 From: RKrom Date: Sun, 10 Feb 2013 14:16:57 +0000 Subject: [PATCH] Added the FastBitmap.cs, this should replace the BitmapBuffer sometime. The advantage of using this, it has fast implementations for accessing certain PixelFormats directly and doesn't need to generalize the code which would make it slower. git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@2473 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4 --- GreenshotPlugin/Core/FastBitmap.cs | 481 +++++++++++++++++++++++++ GreenshotPlugin/GreenshotPlugin.csproj | 1 + 2 files changed, 482 insertions(+) create mode 100644 GreenshotPlugin/Core/FastBitmap.cs 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 +