[cairo] What is the c# code to draw an image as a background then draw other stuff over it?

Maarten Bosmans mkbosmans at gmail.com
Fri Nov 13 07:26:46 PST 2009

Hi Alan,

2009/11/12 alan battersby <alan.battersby at ntlworld.com>:
> Hi,
> My experience with cairo so far is just confined to drawing lines etc
> and setting transformations. I have a gtk drawing area where I want to
> draw a background image on it and then draw lines over that image.
> I understand that I should be drawing the background onto an image
> surface.

Yes, it is certainly possible to draw your background image to a
temporary surface once and use that whenever you need the background
But if performance allows, you can also skip the _grid ImageSurface
and call scaleView(cg), drawCentre(cg) and drawGrid(cg) directly on
the Cairo.Context of the Gdk.Window.

> I am assuming that in order to display this you should set the
> context source property as shown in the DrawProfile() and then call
> paint()!! is this correct.

Yes, you're doing it right.

> What do I then set the source to before
> calling drawOutline(). When the code below runs everything seems to hang
> up, I am very unsure about what is going on here so would be very
> grateful for any information / suggestions / corrections.

I'm not sure what's happening here. Can you give some more details please?

> Also when should I call the code to draw the grid, it needs to be after
> the Gdk window is created but is there a signal to mark this event?

I think the Widget.Realized event on your Gtk.Window is what you need.
I'm not certain though. The way I would do it is initialize _grid to
null and check for that at the start of DrawProfile and call
drawGridToSurface if necessary. This way you can just set _grid to
null again when the window resizes and it would be automatically
regenerated on the next expose event.

> thanks
> Alan

I commented on the code below.

Have fun with Cairo and Gtk!


> Example code
> ...
> ImageSurface  _grid;            // grid drawn as an image
> ...
> // called on expose event and when some properties are changed
> void DrawProfile()

Be sure to only call this from within the ExposeEvent handler. If a
property has changed, call Gtk.Window.QueueDraw() instead of
DrawProfile directly.

> {
>        if ((_profile == null) || (_profile.Count == 0)) return;
>        ct = Gdk.CairoHelper.Create (this.GdkWindow);
>        GdkWindow.Clear(); // is there a better way than this?

No need to clear, the _grid surface covers the whole window, doesn't it?

>        ct.SetSource(_grid);
>        ct.Paint();
>        ****** do I need to set source back to original here? ****

No, setting ct.Color below sets a new source. For extra fun, try
reversing the SetSource call above with ct.Color below. You'll see
your background drawn in lines on a solid colored background.

>        ct.Save();
>        ct.Color = outlineColr;
>        drawOutline(ct);
>        ct.Restore();

Save/Restore unnecessary here, you don't do anything with the Context
after the Restore.

> }
> // create grid image on imagesurface
> // called once to create original image
> // thereafter only when certain properties / window size etc change.
> void drawGridToSurface()
> {
>        // Calculate desired size here.
>        int width,height;
>        if (GdkWindow == null)
>                return;
>        GdkWindow.GetSize(out width,out height);
>        _grid = new ImageSurface(Format.ARGB32,width,height);

Instead of creating a new ImageSurface, you can also obtain a surface
for your background by calling Surface.CreateSimilar() on the
GdkWindow surface. This typically is faster, because it creates an
XlibSurface for you. In my experience an ImageSurface is faster on
win32 though.

>        Context cg = new Context(_grid);
>        scaleView(cg);
>        drawCentre(cg);
>        drawGrid(cg);
> }
> // draw a profile over the grid image
> protected void drawOutline(Context ct)
> {
>        double lw = 1.0, lh=1.0 , ld=5.0;
>        ct.InverseTransformDistance(ref lw,ref lh);
>        ct.LineWidth = Math.Min(lw,lh);
>        ct.InverseTransformDistance(ref ld,ref lh);
>        double ld2 = ld / 2;
>        bool first = true;
>        foreach (float a in _profile.Profile.Keys) {
>                float r = _profile.Value(a);
>                double ar = Utilities.toRadians(a);
>                float x = (float)(r * Math.Cos(ar));
>                float y = (float)(r * Math.Sin(ar));
>                if (first) {
>                        ct.MoveTo(x,y);
>                        first = false;
>                } else {
>                        ct.LineTo(x,y);
>                }
>        }
>        ct.Stroke();
>        foreach (float a in _profile.Profile.Keys) {
>                float r = _profile.Value(a);
>                double ar = Utilities.toRadians(a);
>                float x = (float)(r * Math.Cos(ar));
>                float y = (float)(r * Math.Sin(ar));
>                ct.Rectangle(x-ld2,y-ld2,ld,ld);
>                ct.Stroke();
>        }
> }

More information about the cairo mailing list