0057: Cairo I – The Basics of Drawing
Today we’re going to put the MVC series aside for a few weeks while we dive into another series on a topic near and dear to my heart… graphics. We’ll start simple and get more complicated as we go. But first, we need to look at some foundation stuff…
The Imports
To do anything with Cairo, you need to add these two lines to your list of import statements:
import cairo.Context;
import gtk.DrawingArea;
Perhaps surprisingly, you won’t need to add a lot more to this list for most Cairo operations.
The DrawingArea
Yup, it’s like an overhead projector. You prep whatever you’re going to show off to one side, then slap it onto the projector to shine it on the wall. Same thing here, and setting them up? Nothing to it:
class MyDrawingArea : DrawingArea
{
// R G B Alpha
float[] rgba = [0.3, 0.6, 0.2, 0.9];
this()
{
addOnDraw(&onDraw);
} // this()
Ignoring the rgbaColor
array for the moment, to prepare a DrawingArea
, you just:
- instantiate it,
- write a callback function and
- hook up the
onDraw
signal to call the callback.
That’s it. But anything you plan to show with the DrawingArea
‘projector’ you’ll need to prep first. And you do that off-screen with the DrawingArea
’s constant companion…
The Context
This is like a paste-up board. You take bits and pieces of images, shapes, lines, etc., mix in some color and build the image you want to show on the DrawingArea
. When it’s ready, you call one of the Context
’s many functions to slap your results onto the DrawingArea
projector.
But to show this, we need an example, so let’s start with something simple like…
A Simple Fill
Let’s start by looking at the callback function… which is where the fill is done:
bool onDraw(Scoped!Context context, Widget w)
{
context.setSourceRgba(rgba[0], rgba[1], rgba[2], rgba[3]);
context.paint();
return(true);
} // onDraw()
Here, all we do is:
- set the color, and
- fill the entire
DrawingArea
.
And that is Cairo drawing at its simplest.
A Technical Note: You’ll notice the onDraw()
function’s first argument is Scoped!Context
. Scoped!
is a GtkD reflection of D
’s scoped()
function and all it means is that the Context
will be destroyed automatically when we’re done with it. It has to be done this way because it’s created automatically.
Now let’s look at a second simple example…
Drawing a Line
The only thing different about this line-drawing example compared to the fill example is the callback function:
bool onDraw(Scoped!Context context, Widget w)
{
context.setLineWidth(3);
context.moveTo(100, 45);
context.lineTo(249, 249);
context.stroke();
return(true);
} // onDraw()
In this operation, we do four things:
- set the width of the line to draw,
- move the ‘pen’ to where the line starts,
- decide where the line will end, and
- stroke it.
But what if you want to draw…
Multiple Lines with Rounded Ends
In this third, multi-line example, we just add a few preparatory commands to get the Context set up:
bool onDraw(Scoped!Context context, Widget w)
{
context.setLineWidth(10);
context.setLineCap(CairoLineCap.ROUND);
context.setLineJoin(CairoLineJoin.ROUND);
context.moveTo(10, 10);
context.lineTo(249, 249);
context.lineTo(450, 15);
context.lineTo(450, 249);
context.stroke();
return(true);
} // onDraw()
It’s mostly just more of what we’ve already been doing, but there are two new lines in there that make the ends and angles of the lines rounded.
So you know what all the options are for setLineCap()
, here they are:
CairoLineCap.BUTT
,CairoLineCap.ROUND
, andCairoLineCap.SQUARE
.
And the options for setLineJoin()
are:
CairoLineJoin.MITER
,CairoLineJoin.ROUND
, andCairoLineJoin.BEVEL
.
One other note: The set of lineTo()
calls will draw a continuous line with three legs. If instead you want two separate lines, you would change the second lineTo()
call to a moveTo()
like this:
context.moveTo(10, 10);
context.lineTo(249, 249);
context.moveTo(450, 15);
context.lineTo(450, 249);
context.stroke();
Conclusion
So, to use a DrawingArea
, we now know we need to instantiate it and hook up the onDraw
signal. Also, we need a behind-the-scenes Context
(automatically supplied in most cases) for preparing any drawing before it hits the DrawingArea
.
Next time we continue with Cairo and look at rectangles. Until then…
Comments? Questions? Observations?
Did we miss a tidbit of information that would make this post even more informative? Let's talk about it in the comments.
- come on over to the D Language Forum and look for one of the gtkDcoding announcement posts,
- drop by the GtkD Forum,
- follow the link below to email me, or
- go to the gtkDcoding Facebook page.
You can also subscribe via RSS so you won't miss anything. Thank you very much for dropping by.
© Copyright 2024 Ron Tarrant