/* * 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); } } }