Drawing Bitmaps – DrawingImage and DrawingVisual
Written by Administrator   
Wednesday, 20 January 2010
Article Index
Drawing Bitmaps – DrawingImage and DrawingVisual
DrawingContext

 

WPF provides multiple ways to convert vector drawings to bitmaps. Find out how DrawingImage and DrawingVisual work and when to use which. On the way we look at how to create 2D vector drawings.

Banner

 

WPF is a vector drawing system and this means that drawing commands are stored as commands until they are rendered by the graphics system to create a pattern of pixels. That is, if you draw a circle a command to draw a circle is stored and converted to a circle of pixels to display on the screen as and when required. Vector drawing is usually the best way to do things but there are situations when you need to work with a fixed resolution bitmap. In this case you need to render the vector commands and allow them to modify the pixels in a suitable bitmap.

WPF provides two classes derived from BitmapSource specifically for this purpose. RenderTargetBitmap allows you to render any graphical object that inherits from Visual and this includes UIElement, shapes and controls in general. Graphics that inherit from Visual are usually complex objects for a lighter implementation of vector graphics you need to turn to Drawing and classes descended from Drawing. Drawing provides the most efficient way to create vector graphics and the DrawingImage class allows you to render them to a bitmap. Even so the story is still a little complicated because to render a vector graphic to a WriteableBitmap you need yet another class DrawingVisual.

Rendering a Drawing

If you accept the fact that you can create a vector drawing in an object that inherits from Drawing then rendering this to a bitmap is easy. All you have to do is create a DrawingImage object that references the Drawing object in its constructor:

 DrawingImage MyDrawingImage = 
new DrawingImage(MyDrawing);

This creates a bitmap just big enough to contain the drawing. You can display the bitmap using an image control in the usual way:

 image1.Source = MyDrawingImage;

Of course there is nothing stopping you from doing the job in one line:

 image1.Source = 
new DrawingImage(MyDrawingGroup);

Of course the problem with using this is that you need to know how to create a drawing object so it's time to look briefly at the way WPF's 2D drawing works.

Introduction to drawing

One of the things that is confusing about WPF is the number of duplicate facilities that there seem to be. For example there is the Line object which can be used to draw a line and there is the LineGeometry object which can also be used to draw a line. The difference between them is that Line is a complex graphical object which takes part in layout and generally is more like a control than a simple graphic while LineGeometry is just what it says it is – nothing but geometry. So if you want a graphical object that is to be part of a background graphic or something basic your best choice is LineGeometry.

Unfortunately this isn't quite the end of the story – a Geometry object really is nothing but geometry. To turn it into a drawing you need to associate geometry with a pen to draw lines and a brush to fill the interior if there is one. This is where GeometryDrawing comes in. This class takes raw geometry and adds the properties needed to actually render it. So to  create a renderable line you first have to create a line between the specified points:

 LineGeometry MyLine1 = 
new LineGeometry(
new Point(0, 0),
new Point(100, 50));

This defines the geometry, now we need to add a pen and thickness specification using a GeometryDrawing object:

 GeometryDrawing MyDrawing = 
new GeometryDrawing();
MyDrawing.Pen =
new Pen(Brushes.Blue, 1);
MyDrawing.Geometry = MyLine1;

Now we have something, i.e. MyDrawing, that can be rendered as a blue line:

 image1.Source = 
new DrawingImage(MyDrawing);

Of course MyDrawing is only a single blue line and this isn't going to get us very far. The solution is the GeometryGroup object which can be used to group any number of Geometry objects and present them to the outside world as a single Geometry object:

 LineGeometry MyLine1 = 
new LineGeometry(
new Point(0, 0),
new Point(100, 50));
LineGeometry MyLine2=
new LineGeometry(
new Point(0, 0),
new Point(50, 100));
 GeometryGroup MyGroup = 
new GeometryGroup();
MyGroup.Children.Add(MyLine1);
MyGroup.Children.Add(MyLine2);

Now we have a geometry group we can add pen and brush properties by converting to a GeometryDrawing in the usual way:

GeometryDrawing MyDrawing1 = 
new GeometryDrawing();
MyDrawing1.Pen =
new Pen(Brushes.Blue, 1);
MyDrawing1.Geometry = MyGroup;

This now renders as two blue lines.

It's worth knowing that there is also the CombinedGeometry class and the Combine method of the static Geometry class that will combine geometric shapes using set or Boolean operations on their interiors.

You can see that by combining Geometry objects together into a GeometryGroup you can build up more complex shapes. However, when you convert these into a GeometryDrawing object you can only apply one pen and one brush. What this means is that you can now draw two lines but they have to be the same colour.

The solution to drawing two lines with different colours is to be found in the DrawingGroup. This groups together GeometryDrawing objects complete with their pen and brush specifications into a single GeometryDrawing object. For example, if we create two lines:

LineGeometry MyLine1 = 
new LineGeometry(
new Point(0, 0),
new Point(100, 50));
LineGeometry MyLine2=
new LineGeometry(
new Point(0, 0),
new Point(50, 100));

and then two GeometryDrawing objects, one a blue line and one a red line:

GeometryDrawing MyDrawing1 = 
new GeometryDrawing();
MyDrawing1.Pen =
new Pen(Brushes.Blue, 1);
MyDrawing1.Geometry = MyGroup;
GeometryDrawing MyDrawing2 =
new GeometryDrawing();
MyDrawing2.Pen =
new Pen(Brushes.Red, 1);
MyDrawing2.Geometry = MyLine1;

these can be combined into a single DrawingGroup.

 DrawingGroup MyDrawingGroup = 
new DrawingGroup();
MyDrawingGroup.Children.Add(MyDrawing1);
MyDrawingGroup.Children.Add(MyDrawing2);

The MyDrawingGroup object can be used as a single drawing and it will render a red and a blue line:

 image1.Source = 
new DrawingImage(MyDrawingGroup);

To summarise:

  • You create 2D drawings by creating geometry and using GeometryGroup to combine lines, circles and paths in general.
  • You add a drawing specification – pen and brush - to a Geometry object by creating a GeometryDrawing object from it.
  • To create a drawing that uses different pens and brushes you create a DrawingGroup.

<ASIN:1430272058>

<ASIN:0596510373>

<ASIN:1430210842>
<ASIN:1590599551>

Banner



Last Updated ( Friday, 19 March 2010 )