Custom BitmapSource
Monday, 03 May 2010
Article Index
Custom BitmapSource
Implementing a constructor
Image control
Advanced BitmapSource

Going further

There are lots of additional jobs that need to be completed to make the test card bitmap class work as a complete BitmapSource but the rest is mostly just implementation of the missing methods, properties and events. What is more interesting is to consider problems that arise in implementing more sophisticated custom BitmapSources.

No encapsulated BitmapSource

First notice that in nearly many case you are going to have to have an encapsulated BitmapSource within your custom class - this is simply the easy way of creating a bitmap that can be presented to the rest of the system. However it isn't essential.

As the only time your custom class interacts with the rest of the system is when it passes on the bits that define the bitmap and because this is a transfer to an array in the correct format you only have to provide a method that generates the bit data. For example, our test card class can return the data stored in the byte array rather than using the encapsulated BitmapSource:

public override void CopyPixels(
Int32Rect sourceRect,
Array pixels,
int stride,
int offset)
int start = sourceRect.Y * stride;

Notice that this assumes that the bits byte array is global and accessible and the CopyPixels method isn't general. It only implements the very minimum needed to copy a single stride side chunk of data starting at (0,y). A more complete solution would have to map the sourceRect to the data stored in the byte array. Not difficult but messy.

For another, extreem example, let's generate random pixel data everytime CopyPixels is called:

public override void CopyPixels(
Int32Rect sourceRect,
Array pixels,
int stride, int offset)
Random R = new Random();

This is particularly simple because all we have to do is fill the pixels array with random numbers - sizes and sourceRectangles are irrelevant! If you try this out you will see a random display



Random pixels

If you look at the example image you might be puzzled as to why the random pixels form vertical bars? The reason is that the random number generator is reset for each row of the image using a time dependent seed. Each row is transfered so quickly that the time dependent seed generally doesn't change and so you get a repeat of the pseudo random pattern until the seed does change - when you see an occational tearing of the image. If you want a more reaonable random pattern move the random number generator to be global and create it just once with a time dependent seed. 

In all of these examples the CopyPixel method has be written to be specific to the data transfer used by the Image control. There is no promise that all data transfers occur in this way so you have to implement all of the CopyPixel methods and in a completely general way.

Source properties and chaining

Another common requirement is the need to specify a source property.  A source property is simply another BitmapSource instance that you retrieve data from. To do this you make use of the CopyPixels method of choice to transfer the bitmap data in the object specified by the Source to your custom object. In fact you simply implement the mechanism that the Image control uses to get its data.

Once you have the represenation of the bits you can arrange for your custom BitmapSource to pass these on to another BitmapSource derived object via its Source property. In this way a chain of Bitmapped sources can be built up.

For example, without adding a Source property to our custom bitmap it can still be used as the start of a chain:

TestBitmapSource TBS = new 
TestBitmapSource(100, 100);
TransformedBitmap BS = new
BS.Transform = new
ScaleTransform(0.5, 2);
BS.Source = TBS;
image1.Source = BS;


A rescaled test card

For more information on how to use transformations see our explorations of the bitmap transform classes.

Asynchronous download

More advanced BitmapSource classes have to cope with the possibility that the bitmap data wont be available immediately. In this case you have little choice but to implement all of the methods and properties provided by classes such as BitmapImage. The basic idea is that you store a source URI in a suitable property and then either get on with loading it or defer until the pixels are actually needed. For example when a client accesses the bitmap data using CopyPixels your custom class has to check that the data is available and block the thread until it is. Of course blocking the UI thread isn't a good idea and in practice you have implement something more sophisticated using a worker thread to download the data while the UI gets on with its task. This rapidly becomes very complicated and difficult to get right.

If possible avoid this task by using the classes provided!


Getting Started with WPF


WPF Windows Presentation Foundation is the new way to do forms and all things graphical in .NET. You can still use  Windows Forms but don't expect anything particularly new to be added to [ ... ]

RenderTargetBitmap - Visual vector to bitmap

RenderTargetBitmap will convert any Visual to a bitmap but sometimes it isn't quite as straighforward as just calling Render().

WPF .NET Core - Inside Dependency Properties

One of the great mysteries of WPF is the strangely named "Dependency Properties". In this chapter we learn how dependency properties really work by creating a custom dependency property

BitmapImage and local files

When and how to use the BitmapImage class

ISupportInitialize and XAML

For a class to be instantiated by XAML it has to have a parameter-less constructor. This means that properties that might be essential to creating the instance can be initialized in any order and this [ ... ]

Other Articles






Last Updated ( Sunday, 02 May 2010 )