Click here to Skip to main content
15,896,063 members
Articles / Programming Languages / C#

Managed Bitmaps 2

Rate me:
Please Sign up or sign in to vote.
4.77/5 (17 votes)
19 Mar 2010CPOL5 min read 51.6K   991   36  
This article presents classes that represent bitmaps in full managed code.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using Pfz.Drawing.Extensions.ManagedBitmapIntExtensions;
using Pfz.Threading;

namespace Pfz.Drawing
{
	/// <summary>
	/// A ManagedBitmap that uses argb 32-bit ints as the pixel values and also presents a
	/// System.Drawing.Bitmap wrapper that shows/edits it's content without copy.
	/// </summary>
	[Serializable]
	public class Argb32Bitmap:
		ManagedBitmap<int>,
		ISerializable,
		IDisposable
	{
		private GCHandle fPinnedHandle;
		
		/// <summary>
		/// Creates a new Argb32Bitmap with the given Width and Height.
		/// </summary>
		public Argb32Bitmap(int width, int height):
			base(width, height)
		{
			p_InitializeSystemBitmap();
		}
		
		/// <summary>
		/// Creates a new Argb32Bitmap using the given baseArray and width.
		/// </summary>
		public Argb32Bitmap(int[] baseArray, int width):
			base(baseArray, width)
		{
			p_InitializeSystemBitmap();
		}
		
		/// <summary>
		/// Creates a new Argb32Bitmap as a copy of the given System.Drawing.Bitmap, using the given
		/// convert delegate.
		/// </summary>
		public Argb32Bitmap(Bitmap bitmap, Func<byte, int> convertFromIndexed8Delegate):
			base(bitmap, convertFromIndexed8Delegate)
		{
			p_InitializeSystemBitmap();
		}

		/// <summary>
		/// Creates a new Argb32Bitmap as a copy of the given System.Drawing.Bitmap, using the given
		/// convert delegate.
		/// </summary>
		public Argb32Bitmap(Bitmap bitmap, Func<int, int> convertFromArgbDelegate):
			base(bitmap, convertFromArgbDelegate)
		{
			p_InitializeSystemBitmap();
		}
		
		/// <summary>
		/// Creates a clone of the given System.Drawing.Bitmap.
		/// </summary>
		public Argb32Bitmap(Bitmap sourceBitmap):
			this(p_GetSourceBitmapWidth(sourceBitmap), sourceBitmap.Height)
		{
			if (sourceBitmap.PixelFormat == PixelFormat.Format32bppArgb)
				this.CopyFrom(sourceBitmap);
			else
			{
				Bitmap newBitmap = null;
				AbortSafe.Run
				(
					() => newBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height, PixelFormat.Format32bppArgb),
					() =>
					{
						Graphics graphics = null;
						AbortSafe.Run
						(
							() => graphics = Graphics.FromImage(newBitmap),
							() => 
							{
								graphics.DrawImage(sourceBitmap, 0, 0, sourceBitmap.Width, sourceBitmap.Height);
								this.CopyFrom(newBitmap);
							},
							() =>
							{
								if (graphics != null)
									graphics.Dispose();
							}
						);
					},
					() =>
					{
						if (newBitmap != null)
							newBitmap.Dispose();
					}
				);
			}
		}
		
		private static int p_GetSourceBitmapWidth(Bitmap sourceBitmap)
		{
			if (sourceBitmap == null)
				throw new ArgumentNullException("sourceBitmap");
			
			return sourceBitmap.Width;
		}
		
		/// <summary>
		/// Disposes the System.Drawing.Bitmap generated by this bitmap.
		/// </summary>
		~Argb32Bitmap()
		{
			Dispose(false);
		}
		
		/// <summary>
		/// Disposes the System.Drawing.Bitmap generated by this bitmap.
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}
		
		/// <summary>
		/// Must be implemented by child classes to free any additional resource.
		/// </summary>
		protected virtual void Dispose(bool disposing)
		{
			// even if this Dispose is called from the destructor, we need to free
			// our systemBitmap. It's better that someone gets an exception because
			// he is using a disposed bitmap than to free the PinnedHandle and later
			// corrupt the memory of some other object.
			var systemBitmap = AsSystemBitmap;
			if (systemBitmap != null)
			{
				AsSystemBitmap = null;
				systemBitmap.Dispose();
			}

			if (fPinnedHandle.IsAllocated)
				fPinnedHandle.Free();
		}

		private void p_InitializeSystemBitmap()
		{
			AbortSafe.Run
			(
				() =>
				{
					fPinnedHandle = GCHandle.Alloc(PixelArray, GCHandleType.Pinned);
					AsSystemBitmap = new Bitmap(Width, Height, Width * 4, PixelFormat.Format32bppArgb, fPinnedHandle.AddrOfPinnedObject());
				}
			);
		}
		
		/// <summary>
		/// Gets a System.Drawing.Bitmap object that uses the same pixels as this ManagedBitmap.
		/// </summary>
		public Bitmap AsSystemBitmap { get; private set; }
		
		/// <summary>
		/// Gets the color from the given coordinate.
		/// </summary>
		public Color GetColor(int x, int y)
		{
			return Color.FromArgb(this[x, y]);
		}
		
		/// <summary>
		/// Sets the color at a given coordinate.
		/// </summary>
		public void SetColor(int x, int y, Color color)
		{
			this[x, y] = color.ToArgb();
		}

		#region ISerializable Members
			/// <summary>
			/// Creates the Argb32Bitmap from serialization.
			/// </summary>
			protected Argb32Bitmap(SerializationInfo info, StreamingContext context):
				base((int[])info.GetValue("PixelArray", typeof(int[])), info.GetInt32("Width"))
			{
				p_InitializeSystemBitmap();
			}
			void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
			{
				info.AddValue("PixelArray", PixelArray);
				info.AddValue("Width", Width);
			}
		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Microsoft
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).

Comments and Discussions