Click here to Skip to main content
15,891,253 members
Articles / Mobile Apps / Windows Mobile

Speaking Garmin

Rate me:
Please Sign up or sign in to vote.
4.76/5 (24 votes)
14 Dec 2005CPOL11 min read 134.7K   1.4K   79  
Covers the basics needed to get a Pocket PC and a Garmin GPS talk in their own language and to graphically display the calculated data.
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

namespace Speaking_Garmin
{
	/// <summary>
	/// BitmapFile class - Demonstrates the functionality required to save a
	/// control image to a file.
	/// </summary>
	public class BitmapFile
	{
		/// <summary>
		/// BITMAPFILEHEADER class is an implementation of the associated Windows
		/// structure required to save a bitmap to a file.
		/// </summary>
		protected class BITMAPFILEHEADER
		{
			// Constants used in the file data structure
			protected const ushort fBitmapFileDesignator = 19778;
			protected const uint fBitmapFileOffsetToData = 54;

			/// <summary>
			/// Creates a BITMAPFILEHEADER class instance from the information provided
			/// by a BITMAPINFOHEADER instance.
			/// </summary>
			/// <param name="infoHdr">Filled in bitmap info header</param>
			public BITMAPFILEHEADER(BITMAPINFOHEADER infoHdr)
			{
				// These are constant for our example
				bfType = fBitmapFileDesignator;
				bfOffBits = fBitmapFileOffsetToData;
				bfReserved1 = 0;
				bfReserved2 = 0;

				// Determine the size of the pixel data given the bit depth, width, and
				// height of the bitmap.  Note: Bitmap pixel data is always aligned to 4 byte
				// boundaries.
				uint bytesPerPixel = (uint)(infoHdr.biBitCount >> 3);
				uint extraBytes = ((uint)infoHdr.biWidth * bytesPerPixel) % 4;
				uint adjustedLineSize = bytesPerPixel * ((uint)infoHdr.biWidth + extraBytes);

				// Store the size of the pixel data
				sizeOfImageData = (uint)(infoHdr.biHeight) * adjustedLineSize;

				// Store the total file size
				bfSize = bfOffBits + sizeOfImageData;
			}

			/// <summary>
			/// Write the class data to a binary stream.
			/// </summary>
			/// <param name="bw">Target stream writer</param>
			public void Store(BinaryWriter bw)
			{
				// Must, obviously, maintain the proper order for file writing
				bw.Write(bfType);
				bw.Write(bfSize);
				bw.Write(bfReserved1);
				bw.Write(bfReserved2);
				bw.Write(bfOffBits);
			}

			public uint		sizeOfImageData;	// Size of the pixel data
			public ushort	bfType;				// File type designator "BM"
			public uint		bfSize;				// File size in bytes
			public short	bfReserved1;		// Unused
			public short	bfReserved2;		// Unused
			public uint		bfOffBits;			// Offset to get to pixel info
		}

		/// <summary>
		/// BITMAPINFOHEADER class is an implementation of the associated Windows
		/// structure required to save a bitmap to a file, as well as create a DIB
		/// section for a control.
		/// </summary>
		protected class BITMAPINFOHEADER
		{
			// Constants used in the info data structure
			const uint kBitmapInfoHeaderSize = 40;

			/// <summary>
			/// Creates a BITMAPINFOHEADER instance based on a the pixel bit depth, width,
			/// and height of the bitmap.
			/// </summary>
			/// <param name="bpp">Bits per pixel</param>
			/// <param name="w">Bitmap width (pixels)</param>
			/// <param name="h">Bitmap height (pixels)</param>
			public BITMAPINFOHEADER(short bpp, int w, int h)
			{
#if DEBUG
				// For simplicity of the example, only 16 and 24 bit direct formats
				// are supported
				if (bpp != 16 && bpp != 24)
					MessageBox.Show("Error: Non-supported bitmap pixel depth sepcified");
#endif
				biSize = kBitmapInfoHeaderSize;
				biWidth = w;			// Set the width
				biHeight = h;			// Set the height
				biPlanes = 1;			// Only use 1 color plane
				biBitCount = bpp;		// Set the bpp
				biCompression = 0;		// No compression for file bitmaps
				biSizeImage = 0;		// No compression so this can be 0
				biXPelsPerMeter = 0;	// Not used
				biYPelsPerMeter = 0;	// Not used
				biClrUsed = 0;			// Not used
				biClrImportant = 0;		// Not used
			}

			/// <summary>
			/// Write the class data to a binary stream.
			/// </summary>
			/// <param name="bw">Target stream writer</param>
			/// <param name="bFromDIB">Is this a memory DIB in memory?</param>
			/// true: Memory DIB so use compression to format pixels if 16-bit
			/// false: File DIB so do not use compression
			public void Store(BinaryWriter bw, bool bFromDIB)
			{
				// Must, obviously, maintain the proper order for file writing
				bw.Write(biSize);
				bw.Write(biWidth);
				bw.Write(biHeight);
				bw.Write(biPlanes);
				bw.Write(biBitCount);
				
				// Only use compression for memory DIB (file loads choke if this is used)
				if (bFromDIB && biBitCount == 16)
					bw.Write(Windows.BI_BITFIELDS);
				else
					bw.Write(biCompression);

				bw.Write(biSizeImage);
				bw.Write(biXPelsPerMeter);
				bw.Write(biYPelsPerMeter);
				bw.Write(biClrUsed);
				bw.Write(biClrImportant);

				// RGBQUAD bmiColors[0]
				if (bFromDIB && biBitCount == 16)
				{
					bw.Write((uint)0x7c00);		// red
					bw.Write((uint)0x03e0);		// green
					bw.Write((uint)0x001f);		// blue
				}
			}

			public uint		biSize;				// Size of this structure
			public int		biWidth;			// Width of bitmap (pixels)
			public int		biHeight;			// Height of bitmap (pixels)
			public short	biPlanes;			// Number of color planes
			public short	biBitCount;			// Pixel bit depth
			public uint		biCompression;		// Compression type
			public uint		biSizeImage;		// Size of uncompressed image
			public int		biXPelsPerMeter;	// Horizontal pixels per meter
			public int		biYPelsPerMeter;	// Vertical pixels per meter
			public uint		biClrUsed;			// Number of colors used
			public uint		biClrImportant;		// Important colors
		}

		/// <summary>
		/// Copies the pixel data from the specified control to the byte array
		/// </summary>
		/// <param name="hwnd">Handle to a control used as copy source</param>
		/// <param name="bytes">Destination buffer of entire file</param>
		/// <param name="fileHdr">Valid bitmap info header instance</param>
		/// <param name="infoHdr">Valid bitmap file header instance</param>
		/// <returns>Result: true if succeeded, false otherwise</returns>
		static protected bool CopyImageSource(IntPtr hwnd, byte[] bytes, BITMAPFILEHEADER fileHdr, BITMAPINFOHEADER infoHdr)
		{
			// Get the DeviceContext
			IntPtr hdc = Windows.GetDC(hwnd);

			// Create a compatible (memory) hDC that we can blt onto
			IntPtr hdcComp = Windows.CreateCompatibleDC(hdc);

			// Create and write out bitmap info for a 555 BI_BITFIELDS compressed image
			// BITMAPINFOHEADER = 40 bytes
			// 3 RGBQUADS for bit masking = 12 bytes
			byte[] dummyBitmapInfo = new byte[52];
			MemoryStream msDummy = new MemoryStream(dummyBitmapInfo);
			BinaryWriter bwDummy = new BinaryWriter(msDummy);
			infoHdr.Store(bwDummy, true);

			try
			{
				unsafe
				{
					// pBitmapInfo points to the locked location of the bitmap info structure
					// pPixelDest points to the pixel data destination
					fixed (byte* pBitmapInfo = &dummyBitmapInfo[0], pPixelDest = &bytes[fileHdr.bfOffBits])
					{
						// Pointer to pixel data source
						byte* pPixelSource;

						// Create a DIBSection using the dummyBitmapInfo structure. This acquires a
						// pointer (pPixelSource) to the actual bit data for the image
						IntPtr hDibSect = Windows.CreateDIBSection(hdc, pBitmapInfo, Windows.DIB_RGB_COLORS, &pPixelSource, (IntPtr)0, (uint)0);
#if DEBUG
						// if something failed creating the DIBSection, put up a MessageBox with
						// the value from GetLastError().
						if (hDibSect == (IntPtr)0)
						{
							MessageBox.Show(Windows.GetLastError().ToString());
							Windows.DeleteObject(hDibSect);
							return false;
						}
#endif
						// Select the DIBSection into the memory DC
						IntPtr hbmOld = Windows.SelectObject(hdcComp, hDibSect);

						// BitBlt from pImage's hDC into the memory DC
						Windows.BitBlt(hdcComp, 0, 0, infoHdr.biWidth, infoHdr.biHeight, hdc, 0, 0, Windows.SRCCOPY);
						
						// Copy the data from the pPixelSource pointer into the actual bitmap data (pPixelDest)
						// which will be writen to disk
						Windows.CopyMemory(pPixelDest, pPixelSource, (int)fileHdr.sizeOfImageData);
						
						// Replace the bitmap object in the memory DC with what was originally there
						Windows.SelectObject(hdcComp, hbmOld);
						
						// Delete the DIBSection because we are done with it
						Windows.DeleteObject(hDibSect);
					}
				}
			}
			finally
			{
				// Close the dummy BinaryWriter and the stream it was based on
				bwDummy.Close();
				msDummy.Close();
			}

			// Delete the compatible DC because we are done with it
			Windows.DeleteDC(hdcComp);
			
			// Release the pImage's hDC
			Windows.ReleaseDC(hwnd, hdc);

			return true;
		}

		/// <summary>
		/// Saves the image on the specified control to the specified path as a bitmap
		/// </summary>
		/// <param name="cont">Control whose image is to be saved</param>
		/// <param name="fileName">Name of destination file</param>
		/// <param name="bpp">Bits per pixel</param>
		/// <param name="width">Width of bitmap (pixels)</param>
		/// <param name="height">Height of bitmap (pixels)</param>
		/// <returns>Result: true if succeeded, false otherwise</returns>
		static public bool SaveToFile(Control cont, String fileName, short bpp, int width, int height)
		{
			// File and data streaming
			BinaryWriter bw = null;
			MemoryStream ms = null;
			FileStream fs = null;

			try
			{
				// Create the necessary bitmap file and info headers (must do info first)
				BITMAPINFOHEADER infoHdr = new BITMAPINFOHEADER(bpp, width, height);
				BITMAPFILEHEADER fileHdr = new BITMAPFILEHEADER(infoHdr);

				// Create an array of bytes to hold the actual bitmap data.
				byte[] bytes = new byte[fileHdr.bfSize];

				// Map that array of bytes to a MemoryStream and create a BinaryWriter
				ms = new MemoryStream(bytes);
				bw = new BinaryWriter(ms);

				// Write the file and info headers to the binary stream
				fileHdr.Store(bw);
				infoHdr.Store(bw, false);

				// Get the HWND of the Control
				bool captureState = cont.Capture;
				cont.Capture = true;
				IntPtr hwnd = Windows.GetCapture();
				cont.Capture = captureState;

				// Write the pixel info to the byte stream (bypassing bw)
				if (!CopyImageSource(hwnd, bytes, fileHdr, infoHdr))
				{
					return false;
				}

				// Create a file stream to the path provided
				fs = File.Create(fileName);

				// Write out our bitmap data to the file stream
				fs.Write(bytes, 0, (int)fileHdr.bfSize);
			}
			finally
			{
				// Close streams still laying around.
				if (fs != null)
					fs.Close();
				if (bw != null)
					bw.Close();
				if (ms != null)
					ms.Close();
			}

			return true;
		}

		/// <summary>
		/// Saves an Image object as a .bmp file - very slowly!
		/// </summary>
		/// <param name="image">Image object to be saved</param>
		/// <param name="fileName">Name of destination file</param>
		/// <param name="bpp">Bits per pixel of target bitmap (16,24)</param>
		/// <returns></returns>
		static public bool SaveToFile(Image image, String fileName, short bpp)
		{
			// File and data streaming
			BinaryWriter bw = null;
			MemoryStream ms = null;
			FileStream fs = null;

			try
			{
				// Create the necessary bitmap file and info headers (must do info first)
				BITMAPINFOHEADER infoHdr = new BITMAPINFOHEADER(bpp, image.Width, image.Height);
				BITMAPFILEHEADER fileHdr = new BITMAPFILEHEADER(infoHdr);

				// Create an array of bytes to hold the actual bitmap data.
				byte[] bytes = new byte[fileHdr.bfSize];

				// Map that array of bytes to a MemoryStream and create a BinaryWriter
				ms = new MemoryStream(bytes);
				bw = new BinaryWriter(ms);

				// Write the file and info headers to the binary stream
				fileHdr.Store(bw);
				infoHdr.Store(bw, false);

				// Write the pixel info to the byte stream
				// Remember that the bitmap file is stored upside-down
				Bitmap bm = (Bitmap)image;
				for (int r = image.Height - 1; r >= 0; r--)
				{
					for (int c = 0; c < image.Width; c++)
					{
						int color = bm.GetPixel(c, r).ToArgb();

						// 24 bit bmp is BGR format but color is ARGB
						uint red = (byte)((color & 0x00ff0000) >> 16);
						uint green = (byte)((color & 0x0000ff00) >> 8);
						uint blue = (byte)(color & 0x000000ff);

						if (bpp == 16)
						{
							// We are losing some resolution going to 16-bit
							// Assume 555 format for simplicity
							red = red >> 3;
							green = green >> 3;
							blue = blue >> 3;

							// Construct the pixel
							ushort dstColor = (ushort)((red << 10) | (green << 5) | blue);
							bw.Write(dstColor);
						}
						else
						{
							// 24 bit bmp is BGR format but color is ARGB
							bw.Write((byte)blue);
							bw.Write((byte)green);
							bw.Write((byte)red);
						}
					}
				}

				// Create a file stream to the path provided
				fs = File.Create(fileName);

				// Write out our bitmap data to the file stream
				fs.Write(bytes, 0, (int)fileHdr.bfSize);
			}
			finally
			{
				// Close streams still laying around.
				if (fs != null)
					fs.Close();
				if (bw != null)
					bw.Close();
				if (ms != null)
					ms.Close();
			}

			return true;
		}

	}
}

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
Web Developer
Brazil Brazil
I am currently 18 years old and since I was very little I've been into computer-programming. Back then I used to play with a Basic compiler and now I am using the most powerful development environments out there for creating games and windows applications in general.

Comments and Discussions