Drawing Complex Figures
Drawing lines and rectangles works very well when you need to create custom Windows Forms controls. If you want to create more complex and artistic drawings such as diagrams, GDI+ lets you draw more complex shapes. In GDI+, this is accomplished using graphics paths.
GraphicsPath objects encapsulate a number of line segments. You add individual segments via drawing primitives, such as AddElipse() and AddLine(). GraphicsPath objects make it relatively simple to generate complex shapes by automatically connecting line segments. Consider the following code:
Dim Person As New GraphicsPath()
Person.AddEllipse(23, 1, 14, 14)
Person.AddLine(18, 16, 42, 16)
Person.AddLine(50, 40, 44, 42)
Person.AddLine(38, 25, 37, 42)
Person.AddLine(45, 75, 37, 75)
Person.AddLine(30, 50, 23, 75)
Person.AddLine(16, 75, 23, 42)
Person.AddLine(22, 25, 16, 42)
Person.AddLine(10, 40, 18, 16)
This simple example generates the shape of a human (well... as close as I can get with my limited artistic abilities) and renders it on the screen as shown in Figure 3
|Figure 3: Drawing a simple person using a GraphicsPath.|
Note: The GraphicsPath
class is a member of System.Drawing.Drawing2D
. Make sure to import that namespace or reference the class by its fully qualified name.
At this point it is important to discuss the quality of the graphics you render. When you draw vertical and horizontal lines, quality is not a big concern because GDI+ draws lines simply by setting the colors of pixels that are all lined up in a row. When you draw lines at an angle (or curves), things get a bit more tricky. The pixels on your monitor do not correlate with the pixels that should be set based on the mathematical calculation of the drawn line. So the rendering system needs to decide what pixels to use and which ones to leave out. This process is known as aliasing
Aliasing leads to poor looking drawingsyou can clearly see a "step" or "jagged" effect. One solution to this problem is a technique known as anti-aliasing.
Using this technique, the rendering engine uses different color variations for pixels that should only be partially included, leading to a much smoother appearance to the human eye.
You can tell GDI+ how you would like it to optimize a drawing. Consider the following code for instance:
Dim oPen As New Pen(Color.Blue, 3)
g.SmoothingMode = _ SmoothingMode.HighSpeed
g.DrawBezier(oPen, _ 10, 10, 90, 90, 10, 90, 90, 180)
g.SmoothingMode = _ SmoothingMode.AntiAlias
g.DrawBezier(oPen, _ 50, 10, 130, 90, 50, 90, 130, 180)
g.SmoothingMode = _ SmoothingMode.HighQuality
g.DrawBezier(oPen, _ 90, 10, 170, 90, 90, 90, 170, 180)
This renders three similar Bezier Splines at different quality settings. Figure 4
shows a magnified version of the result. Naturally, you want the high-quality version, but quality comes with a cost: performance. Which method you choose will depend on the performance requirements for your application.
|Figure 4: Bezier splines drawn at different quality levels.|
As mentioned before, GDI+ also offers ways to fill shapes. The techniques you use to fill shapes is very similar to drawing shapes, except for fill operations you use Brushes
. A GDI+ Brush is similar to a GDI+ Pen, but Brushes are often much more powerful.
The following example shows how to draw an ellipse filled with a green brush:
g.FillEllipse(Brushes.Green, _ 10, 10, 150, 80)
In a slightly more complex operation you can fill a shape with a pattern using something called a Hatch Brush.
In this example, you can create a diagonal brick effect:
Dim oBrush As New HatchBrush( _
HatchStyle.DiagonalBrick, _ Color.Blue, Color.Firebrick)
g.FillEllipse(oBrush, _ 10, 100, 150, 80)
You can also choose to use a bitmap as a Brush. In this code snippet you see that I load one of the default images that ships with Windows into a Bitmap object, then create a TextureBrush
based on that image, and use it to fill the ellipse:
Dim oBmp As New _ Bitmap("C:\WINDOWS\GREENSTONE.BMP")
Dim oBrush2 As New TextureBrush(oBmp)
g.FillEllipse(oBrush2, _ 200, 10, 150, 80)
Furthermore, you can create gradient brushes as in the following example:
Dim oRect As New _ Rectangle(200, 100, 150, 80)
Dim oBrush3 As New _ LinearGradientBrush(oRect, _
Color.Red, Color.Blue, _ LinearGradientMode.Vertical)
g.FillEllipse(oBrush3, _ 200, 100, 150, 80)
I used a Rectangle object to first specify an area that I wanted to confine the gradient to. I then defined two colors, as well as an angle ("Vertical" in this case, but you could also use a numeric value).
|Author's Note: The LinearGradientBrush class is a member of System.Drawing.Drawing2D.
shows a combined result for the last 4 examples.
|Figure 5: Ellipses filled using different brushes.|
I personally favor gradient brushes. I think shapes filled with a gradient look more professional than shapes filled with a single color. Consider Figure 6,
which shows the human shape filled with two different brushes (solid and gradient). Here's the code that fills the "person" path I created before:
|Figure 6: Shapes filled with gradients often look more professional than shapes filled with solid brushes.|
Dim oRect As New _ Rectangle(0, 0, 100, 100)
Dim oBrush As New _ LinearGradientBrush(oRect, _
Color.White, Color.Red, _ LinearGradientMode.Vertical)
Whenever you want to create a shape with a fill color as well as an outline (a technique sometimes also referred to as "cell shading"), you need to perform both actions separately (unlike in conventional GDI). Perform the fill operation first and render the outline second to make sure potential drawing inaccuracies do not "cut" through the outline.