// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
//
//
// Allows fast access to 's pixel data.
//
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging
{
using System;
using System.Drawing;
using System.Drawing.Imaging;
using ImageProcessor.Imaging.Colors;
///
/// Allows fast access to 's pixel data.
///
public unsafe class FastBitmap : IDisposable
{
///
/// The bitmap.
///
private readonly Bitmap bitmap;
///
/// The width of the bitmap.
///
private readonly int width;
///
/// The height of the bitmap.
///
private readonly int height;
///
/// The number of bytes in a row.
///
private int bytesInARow;
///
/// The size of the color32 structure.
///
private int color32Size;
///
/// The bitmap data.
///
private BitmapData bitmapData;
///
/// The pixel buffer for holding pixel data.
///
private byte* pixelBuffer;
///
/// A value indicating whether this instance of the given entity has been disposed.
///
/// if this instance has been disposed; otherwise, .
///
/// If the entity is disposed, it must not be disposed a second
/// time. The isDisposed field is set the first time the entity
/// is disposed. If the isDisposed field is true, then the Dispose()
/// method will not dispose again. This help not to prolong the entity's
/// life in the Garbage Collector.
///
private bool isDisposed;
///
/// Initializes a new instance of the class.
///
/// The input bitmap.
public FastBitmap(Image bitmap)
{
this.bitmap = (Bitmap)bitmap;
this.width = this.bitmap.Width;
this.height = this.bitmap.Height;
this.LockBitmap();
}
///
/// Gets the width, in pixels of the .
///
public int Width
{
get
{
return this.width;
}
}
///
/// Gets the height, in pixels of the .
///
public int Height
{
get
{
return this.height;
}
}
///
/// Gets the pixel data for the given position.
///
///
/// The x position of the pixel.
///
///
/// The y position of the pixel.
///
///
/// The .
///
private Color32* this[int x, int y]
{
get { return (Color32*)(this.pixelBuffer + (y * this.bytesInARow) + (x * this.color32Size)); }
}
///
/// Allows the implicit conversion of an instance of to a
/// .
///
///
/// The instance of to convert.
///
///
/// An instance of .
///
public static implicit operator Image(FastBitmap fastBitmap)
{
return fastBitmap.bitmap;
}
///
/// Allows the implicit conversion of an instance of to a
/// .
///
///
/// The instance of to convert.
///
///
/// An instance of .
///
public static implicit operator Bitmap(FastBitmap fastBitmap)
{
return fastBitmap.bitmap;
}
///
/// Gets the color at the specified pixel of the .
///
/// The x-coordinate of the pixel to retrieve.
/// The y-coordinate of the pixel to retrieve.
/// The at the given pixel.
public Color GetPixel(int x, int y)
{
#if DEBUG
if ((x < 0) || (x >= this.width))
{
throw new ArgumentOutOfRangeException("x", "Value cannot be less than zero or greater than the bitmap width.");
}
if ((y < 0) || (y >= this.height))
{
throw new ArgumentOutOfRangeException("y", "Value cannot be less than zero or greater than the bitmap height.");
}
#endif
Color32* data = this[x, y];
return Color.FromArgb(data->A, data->R, data->G, data->B);
}
///
/// Sets the color of the specified pixel of the .
///
/// The x-coordinate of the pixel to set.
/// The y-coordinate of the pixel to set.
///
/// A color structure that represents the
/// color to set the specified pixel.
///
public void SetPixel(int x, int y, Color color)
{
#if DEBUG
if ((x < 0) || (x >= this.width))
{
throw new ArgumentOutOfRangeException("x", "Value cannot be less than zero or greater than the bitmap width.");
}
if ((y < 0) || (y >= this.height))
{
throw new ArgumentOutOfRangeException("y", "Value cannot be less than zero or greater than the bitmap height.");
}
#endif
Color32* data = this[x, y];
data->R = color.R;
data->G = color.G;
data->B = color.B;
data->A = color.A;
}
///
/// Disposes the object and frees resources for the Garbage Collector.
///
public void Dispose()
{
this.Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
///
/// Determines whether the specified is equal to the current .
///
///
/// true if the specified object is equal to the current object; otherwise, false.
///
/// The object to compare with the current object.
public override bool Equals(object obj)
{
FastBitmap fastBitmap = obj as FastBitmap;
if (fastBitmap == null)
{
return false;
}
return this.bitmap == fastBitmap.bitmap;
}
///
/// Serves as a hash function for a particular type.
///
///
/// A hash code for the current .
///
public override int GetHashCode()
{
return this.bitmap.GetHashCode();
}
///
/// Disposes the object and frees resources for the Garbage Collector.
///
/// If true, the object gets disposed.
protected virtual void Dispose(bool disposing)
{
if (this.isDisposed)
{
return;
}
if (disposing)
{
// Dispose of any managed resources here.
this.UnlockBitmap();
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// Note disposing is done.
this.isDisposed = true;
}
///
/// Locks the bitmap into system memory.
///
private void LockBitmap()
{
Rectangle bounds = new Rectangle(Point.Empty, this.bitmap.Size);
// Figure out the number of bytes in a row. This is rounded up to be a multiple
// of 4 bytes, since a scan line in an image must always be a multiple of 4 bytes
// in length.
this.color32Size = sizeof(Color32);
this.bytesInARow = bounds.Width * this.color32Size;
if (this.bytesInARow % 4 != 0)
{
this.bytesInARow = 4 * ((this.bytesInARow / 4) + 1);
}
// Lock the bitmap
this.bitmapData = this.bitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
// Copy the bitmap data across to the array for manipulation.
this.pixelBuffer = (byte*)this.bitmapData.Scan0.ToPointer();
}
///
/// Unlocks the bitmap from system memory.
///
private void UnlockBitmap()
{
// Copy the RGB values back to the bitmap and unlock the bitmap.
this.bitmap.UnlockBits(this.bitmapData);
this.bitmapData = null;
this.pixelBuffer = null;
}
}
}