Click here to Skip to main content
15,891,372 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.Windows.Forms;
using Pfz.Drawing;
using Pfz.Drawing.Extensions.BitmapExtensions;
using Pfz.Threading;
using Pfz.Drawing.Extensions.ManagedBitmapIntExtensions;

namespace PfzDrawingSample
{
	public partial class Form1 : Form
	{
		private ArgbBitmap fOriginalBitmap;
		private ArgbBitmap fBitmap;
		private bool fGenerateNew;
		private int fGenerationCount;
	
		public Form1()
		{
			InitializeComponent();
			comboMethod.SelectedIndex = 0;
		}
		protected override void OnPaintBackground(PaintEventArgs e)
		{
		}

		private void buttonLoad_Click(object sender, EventArgs e)
		{
			if (openFileDialog.ShowDialog() == DialogResult.OK)
			{
				try
				{
					using(var systemBitmapOnly = (Bitmap)Image.FromFile(openFileDialog.FileName))
					{
						var originalBitmap = new ArgbBitmap(systemBitmapOnly);
						var bitmap = new ArgbBitmap(originalBitmap.Width, originalBitmap.Height);
						
						// if boths bitmaps where allocated with success, there is no more chance
						// of exceptions, so we can dispose the old bitmaps (if they existed) and
						// replace with the new ones.
						if (fBitmap != null)
							fBitmap.Dispose();
						
						if (fOriginalBitmap != null)
							fOriginalBitmap.Dispose();
						
						fOriginalBitmap = originalBitmap;
						fBitmap = bitmap;
					}
					
					fGenerateNew = true;

					buttonSave.Enabled = true;
					Invalidate();
				}
				catch(Exception exception)
				{
					MessageBox.Show(exception.Message, exception.GetType().FullName);
				}
			}
		}

		private void Form1_Paint(object sender, PaintEventArgs e)
		{
			// If an image was not loaded we clear and return.
			if (fOriginalBitmap == null)
			{
				e.Graphics.FillRectangle(Brushes.Black, e.ClipRectangle);
				return;
			}

			int width = fOriginalBitmap.Width;
			int height = fOriginalBitmap.Height;

			var originalBitmap = fOriginalBitmap.AsSystemBitmap;
			var bitmap = fBitmap.AsSystemBitmap;

			// If we need to generate a new image, we will get the pixels from
			// the original image, modify the color by the tracks and then apply the
			// changed to the "managedbitmap" that we already pre-allocated with the
			// same width and height.
			if (fGenerateNew)
			{
				fGenerateNew = false;
				
				// it is REALLY important to get these values before starting the
				// processment. In my first version everything was really slow because
				// I was re-reading the value properties at each pixel.
				int trackBarRedValue = trackBarRed.Value;
				int trackBarGreenValue = trackBarGreen.Value;
				int trackBarBlueValue = trackBarBlue.Value;
				
				fGenerationCount++;
				DateTime begin = DateTime.Now;
				
				switch(comboMethod.SelectedIndex)
				{
					case 0:
						int[] sourceArray = fOriginalBitmap.PixelArray;
						int[] destArray = fBitmap.PixelArray;
						
						int count = sourceArray.Length;
						for(int i=0; i<count; i++)
						{
							Color color = Color.FromArgb(sourceArray[i]);
							
							int r = p_Calculate(color.R, trackBarRedValue);
							int g = p_Calculate(color.G, trackBarGreenValue);
							int b = p_Calculate(color.B, trackBarBlueValue);

							destArray[i] = Color.FromArgb(r, g, b).ToArgb();
						}
						break;
					
					case 1:
						for (int y=0; y<height; y++)
						{
							for(int x=0; x<width; x++)
							{
								Color color = originalBitmap.GetPixel(x, y);
								
								int r = p_Calculate(color.R, trackBarRedValue);
								int g = p_Calculate(color.G, trackBarGreenValue);
								int b = p_Calculate(color.B, trackBarBlueValue);
								
								bitmap.SetPixel(x, y, Color.FromArgb(r, g, b));
							}
						}
						break;
						
					case 2:
						BitmapData sourceData = null;
						BitmapData destData = null;
						AbortSafe.Run
						(
							() =>
							{
								sourceData = originalBitmap.LockBits(new Rectangle(new Point(), originalBitmap.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
								destData = bitmap.LockBits(new Rectangle(new Point(), bitmap.Size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
							},
							() =>
							{
								Size size = originalBitmap.Size;
								unsafe
								{
									byte *sourceScanlineBytes = (byte *)sourceData.Scan0;
									byte *destScanlineBytes = (byte *)destData.Scan0;
									for(int y=0; y<size.Height; y++)
									{
										int *sourceScanline = (int *)sourceScanlineBytes;
										int *destScanline = (int *)destScanlineBytes;
									
										for(int x=0; x<size.Width; x++)
										{
											int color = sourceScanline[x];

											int r = p_Calculate((color>>16) & 0xFF, trackBarRedValue);
											int g = p_Calculate((color>>8) & 0xFF, trackBarGreenValue);
											int b = p_Calculate(color & 0xFF, trackBarBlueValue);
								
											color = (0xFF<<24) | (r <<16) | (g<<8) | b;
											destScanline[x] = color;
										}
										
										sourceScanlineBytes += sourceData.Stride;
										destScanlineBytes += destData.Stride;
									}
								}
							},
							() =>
							{
								if (sourceData != null)
									originalBitmap.UnlockBits(sourceData);
								
								if (destData != null)
									bitmap.UnlockBits(destData);
							}
						);
						break;
				}
				
				DateTime end = DateTime.Now;
				Text = "Generated image #" + fGenerationCount + " - Time:" + (end-begin);
			}
			
			// Here we write the bitmap to the form and clear the remaining area.
			e.Graphics.DrawImage(bitmap, 0, 0);
			e.Graphics.FillRectangle(Brushes.Black, width, 0, Width, Height);
			e.Graphics.FillRectangle(Brushes.Black, 0, height, Width, Height);
		}

		private int p_Calculate(int colorValue, int modifier)
		{
			int result = colorValue + modifier;
			if (result < 0)
				return 0;
			
			if (result > 255)
				return 255;
			
			return result;
		}

		private void trackBarRed_ValueChanged(object sender, EventArgs e)
		{
			numericUpDownRed.ValueChanged -= numericUpDownRed_ValueChanged;
			numericUpDownRed.Value = trackBarRed.Value;
			numericUpDownRed.ValueChanged += numericUpDownRed_ValueChanged;
		
			fGenerateNew = true;
			Invalidate();
		}
		private void trackBarGreen_Scroll(object sender, EventArgs e)
		{
			numericUpDownGreen.ValueChanged -= numericUpDownGreen_ValueChanged;
			numericUpDownGreen.Value = trackBarGreen.Value;
			numericUpDownGreen.ValueChanged += numericUpDownGreen_ValueChanged;

			fGenerateNew = true;
			Invalidate();
		}
		private void trackBarBlue_Scroll(object sender, EventArgs e)
		{
			numericUpDownBlue.ValueChanged -= numericUpDownBlue_ValueChanged;
			numericUpDownBlue.Value = trackBarBlue.Value;
			numericUpDownBlue.ValueChanged += numericUpDownBlue_ValueChanged;

			fGenerateNew = true;
			Invalidate();
		}

		private void buttonSave_Click(object sender, EventArgs e)
		{
			if (saveFileDialog.ShowDialog() == DialogResult.OK)
			{
				try
				{
					fBitmap.AsSystemBitmap.Save(saveFileDialog.FileName);
				}
				catch(Exception exception)
				{
					MessageBox.Show(exception.Message, exception.GetType().FullName);
				}
			}
		}

		private void numericUpDownRed_ValueChanged(object sender, EventArgs e)
		{
			trackBarRed.Value = (int)numericUpDownRed.Value;
		}

		private void numericUpDownGreen_ValueChanged(object sender, EventArgs e)
		{
			trackBarGreen.Value = (int)numericUpDownGreen.Value;
		}

		private void numericUpDownBlue_ValueChanged(object sender, EventArgs e)
		{
			trackBarBlue.Value = (int)numericUpDownBlue.Value;
		}


	}
}

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