BitmapSource: WPF Bitmaps
Written by Administrator   
Friday, 04 December 2009
Article Index
BitmapSource: WPF Bitmaps
Paletted bitmaps

Although bitmap graphics are not a main focus of WPF it does offer some useful facilities

 

Banner

WPF is based on DirectX a 3D drawing system and hence it is very good at vector graphics in 2D and 3D but what about bitmaps?

In the main bitmap graphics in WPF are a second-class citizen and often relegated to simply providing texture maps for 3D solids. You will even find bitmaps referred to as textures in the documentation – something that doesn’t make a lot of sense until you realise that WPF is essentially about vector graphics.

The good news is that that despite the emphasis on vector graphics WPF can do bitmaps and with the WriteableBitmap object you can even do bitmap manipulation.The WPF bitmap classes are essentially wrappers for the Windows Imaging Component - a COM based system that provides bitmap facilities to general Windows applications.

But first let's start off with something more fundamental. Notice that if you are looking to work with bitmaps in Silverlight which is based on WPF then you need know that it does things differently.

BitmapSource – the base class

The fundamental bitmap class is BitmapSource which, as its name suggests, represents the source of a bitmap. This is a subtle distinction and one that can be confusing.

The key idea is that BitmapSource represents a set of immutable bits that you have specified in some way. It might even be that the bits haven’t yet been retrieved as a BitmapSource doesn’t necessarily load the bitmap until it is needed. However once the bits have been loaded they are locked and you cannot modify them without destroying the existing object and creating a new object. A BitmapSource really is a source of bitmaps for other objects to use rather than being a bitmap in its own right.

This description applies to classes that derive from BitmapSouce. The base class, i.e. BitmapSource is comparatively limited in what it can do. Indeed being an abstract class you might even expect it to do nothing at all! In fact BitmapSource provides some useful if low level facilities.

BitmapSource is the basic class that creates bitmaps from raw arrays of bits.

In most cases you are probably going to use Bitmap which inherits from BitmapSource and can source bitmaps from a wider range of possibilities. Primitive though BitmapSource might be it is useful when you are trying to do something out of the ordinary.

As already mentioned being an abstract class you can't actually create an instance of BitmapSource using its constructor. However you can create instances of the class because it has a static "instance factory" that can be used in place of the constructor. What this approach gains us is difficult to see but it provides an abstract class to base other derived classes on and a class you can actually use at the same time.

Also notice that the inability to use a constructor to create instances means that you can't create BitmapSource objects using XAML - this is code only. You can of course create instances of classes derived from BitmapSource using XAML, providing they have a suitable constructor.

BitmapSource has a static Create method which allows you to convert bit arrays into a valid bitmap either stored in a byte array or in unmanaged memory. Create actually returns an instance of type CachedBitmap - which is derived from BitmapSource - but this usually causes no problems. The one complication of using this method is that it creates an effectively frozen instance of BitmapSource. For a BitmapSource the model is that the bitmap you create is immutable.

Creating a BitmapSource

The best way to understand BitmapSource is to see a simple example in action. First we need to define the height and width of our bitmap:

 int width=10;
int height=20;

Easy so far but next we need to define the “stride” of the bitmap.

This exciting sounding parameter is quite simple to understand. Each pixel takes a given number of bits or bytes to store, b Bytes say, so a row of the image takes width*b Bytes to store and this is the stride – or it would be except for the rule that every row of a bitmap has to start aligned on a 32-bit address boundary. What this means is that every row has to be a multiple of four bytes and the stride is the size of the row after rounding up to be a multiple of four bytes.

You can calculate the stride in a number of ways depending on the way that the storage needed for each pixel is specified – bits or bytes.

If we assume that the bitmap needs three bytes per pixel, e.g. it uses an RGB format then the stride is:

 int stride=width*3 + (width %4);

That is, the size of a row in bytes plus the remainder after dividing this by four – which has the effect of rounding it up to the nearest multiple of four.

Given each row takes stride Bytes to store the byte array we need to store the entire bitmap image is just:

byte[] bits=new byte[height*stride];

You could set these bytes directly to a group of three bytes that specifies the RGB colour of the pixel but it is much easier to write a small method that will set the pixel at x,y to the colour specified.

The pixel at x,y is stored in the array in three bytes starting at x*3+y*stride in the order Red, Green and Blue. Using this information the function is:

 private void setpixel(ref byte[] bits,
int x, int y,int stride, Color c)
{
bits[x * 3 + y * stride] = c.R;
bits[x*3 + y * stride + 1] = c.G;
bits[x*3 + y * stride + 2] = c.B
}

Now we can set some bits in the array to demonstrate that it all works:

 setpixel(ref bits, 0,0,stride, 
Colors.Blue);
setpixel(ref bits, 2, 19, stride,
Colors.Red);
setpixel(ref bits, 3, 19, stride,
Colors.Green);

Finally, and at long last, we can create the BitmapSource:

 BitmapSource bs = BitmapSource.Create(
width, height,
300, 300,
PixelFormats.Rgb24,
null,
bits,
stride);

The parameters are all fairly obvious, apart from the 300,300 which sets the resolution of the image in the horizontal and vertical to 300dpi. The PixelFormats class has a range of different possibilities for how the pixel data is stored including an indexed option which then needs a palette of colours to be defined as the next parameter – null in this example.

Notice that we have to specify the pixel format and the stride to agree with the structure of the byte array we have created. If you get this wrong then the result is usually a mixed up image with rows the wrong length.

Now that we have a BitmapSource we can set it as the Source property of an Image control which will finally display it:

 image1.Source = bs;

If you run the entire program you will see three coloured pixels at the specified locations.

The BitmapSource contains the information that the Image control needs to render the pixels. That is, the Image control interprets the pixelformat, height, width and stride. Notice also that the BitmapSource is still a raw format in the sense that is has none of the usual bitmap headers, formatting or compression inherent in a format such as JPEG, TIFF, BMP and so on. More sophisticated classes derived from BitmapSource deal with the problem of getting from a format such as JPEG to the raw pixel data. However, if you have a class that can read in a bitmap format and decode it to the point where you have an array of bytes representing the pixels then you can use a BitmapSource to encapsulate it.

Banner

<ASIN:0672329859>

<ASIN:1590599551>

<ASIN:0596510373>



Last Updated ( Monday, 01 August 2011 )