Bitmaps to videos
Written by Harry Fairhead   
Tuesday, 21 July 2009
Article Index
Bitmaps to videos
Bitmap Class
Making the video
Compression
Action

 

A Bitmap class

The first problem we have is reading in bitmap files. You might think that this would be easy as there is a ready made Bitmap class as part of the GDI+. The problem is that this hides all of the details of the bitmap that are needed to use the VfW API. The ideal solution would be to extend the Bitmap class using inheritance but for some reason Microsoft has decided not to let you do this and have marked the class as “sealed”.

Start a new Windows application and add a Class to the project called RawBitmap.

A bitmap file always starts in the same way, a BITMAPFILEHEADER followed by a BITMAPINFOHEADER.

Next comes an RGBQUAD array which specifies the images palette, if one is needed, and then the array of colour data that makes up the image.

To read in a bitmap all we need to do is define the necessary structs and read them in. You can find the definitions for the structures in C++ in the documentation on the Microsoft website. Translating them to C# is relatively easy but there are one or two subtle points. For example the BITMAPFILEHEADER struct is:

[StructLayout(LayoutKind.Sequential, 
Pack = 2)]
public struct BITMAPFILEHEADER
{
public Int16 bfType;
public Int32 bfSize;
public Int16 bfReserved1;
public Int16 bfReserved2;
public Int32 bfOffBits;
};

 

If you compare this to the C++ definition you will see that the variable types Int16 and Int32 have been substituted for the WORD and DWORD types. The original variable names have been retained, even though they are clumsy, because it makes interpreting the documentation easier.

If you don’t know how to declare a struct in C# you might be wondering what is the purpose of the line:

[StructLayout(LayoutKind.Sequential, 
Pack = 1)]

Normally the .NET compiler will arrange the components of the struct in memory to make it faster to access. The StructLayout attribute allows you to control its layout to be compatible with existing data structures. In this case the “Sequential” attribute tells it to place the data types in memory one after another as they occur in the definition. “Pack=1” tells it to make sure that the struct is packed so that it starts on a 1 byte address boundary. This ensures that no extra “padding” bytes are added. As the default is Pack=16 and the struct needs a minimum of 14 bytes to store, the compiler adds 2 extra bytes to make it up to 16 bytes - and this is not helpful when you are trying to work with structures stored on disk that do not have the packing bytes. To make all of this work you also need to add:

using System.Runtime.InteropServices;

The BITMAPINFOHEADER is translated to C# as:

StructLayout(LayoutKind.Sequential, 
Pack = 1)]
public struct BITMAPINFOHEADER
public int biSize;
public int biWidth;
public int biHeight;
public short biPlanes;
public short biBitCount;
public int biCompression;
public int biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public int biClrUsed;
public int biClrImportant;
};

 

Next we need a method to read the raw bytes in the bitmap file into the structs. To do this we open a FileStream and read bytes into a buffer and then transfer the bytes into the appropriate structs.

It is possible to create a function that will read a struct of any type without using advanced features such as generics but not in a type safe way. The following code can only return an object type because it doesn’t know what the struct is going to be before it is called:

private object ReadStruct(
FileStream fs,
 Type t)
{
byte[] buffer = new byte[
Marshal.SizeOf(t)];
fs.Read(
buffer,
0,
Marshal.SizeOf(t));
GCHandle handle =
GCHandle.Alloc(buffer,
GCHandleType.Pinned);
Object temp = Marshal.PtrToStructure(
handle.AddrOfPinnedObject(),t);
handle.Free();
return temp;
}

 

This is a tricky little function. First it creates a byte array of the same size as the struct using the Marshal object, which has lots of useful methods if you are interested in converting from C++ to C# types.

Next the bytes are read into the buffer. To transfer these to the struct we need to first “pin” the byte array so that the Garbage Collector (GC) doesn’t move it. Once we have a “handle” to the pinned object we can get its address and pass this to the PtrToStructure method which reads the bytes from the byte array and uses them to create a new structure of type t, in this case typed as an object. This is what is returned by the function.

 

Now it is easy to write a LoadFromFile method. First we need some public storage to hold the structs and the bitmap data:

class RawBitmap
{
public BITMAPFILEHEADER bmFH =
new BITMAPFILEHEADER();
public BITMAPINFOHEADER bmIH =
new BITMAPINFOHEADER();
public byte[] bits;

 

The new method now simply reads the structures in using the function given above:

public void LoadFromFile(
string filename)
{
FileStream fs = new FileStream(
filename,
FileMode.Open,
FileAccess.Read);
bmFH = (BITMAPFILEHEADER) ReadStruct(
fs,
typeof(BITMAPFILEHEADER));
bmIH = (BITMAPINFOHEADER)ReadStruct(
fs,typeof(BITMAPINFOHEADER));

To get the bitmap bits we use the information in the bitmap header to dimension the byte array and then simply read them:

  fs.Position = bmFH.bfOffBits;
bits = new byte[bmIH.biSizeImage];
fs.Read(
bits,
0,
bits.GetLength(0));
fs.Close();
}

 

The only complication is that the bitmap data might not start straight after the two structs because of variations in format. The solution is to use the information in the file header to position the file to the start of the data. To make it all work we also need to add:

using System.IO;

<ASIN:1578202043>

<ASIN:0809327422>



Last Updated ( Tuesday, 21 July 2009 )
 
 

   
RSS feed of all content
I Programmer - full contents
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.