I’m just starting to play with combining a vector-graphics library with OpenGL. I also wanted to take advantage of all the great features of C# and .NET, as well as using a solution that would port easily between Mac OS X, Windows, and Linux. In the end, I decided that using cairo for vector graphics and the Tao Framework for OpenGL was the way to go.

The Mono Project has C# bindings for cairo, but unfortunately the latest version still didn’t support everything I needed. I was fortunate, however, to come across cleaned-up cairo-sharp library at NDesk. I took the latest snapshot, and because I don’t have make installed on my computer, I simply created a Microsoft Visual C# Express class library project, put all the files inside of it, and built the Mono.Cairo.dll assembly.

There were several links that were especially pertinent to what I was trying to do. Cairo has a guide to using cairo and OpenGL, and the Mono Project had some information about using the Mono.Cairo bindings. Unfortunately, their links to more samples did not seem to work.

In the end, I was able to get a very simple example working. The steps are after the jump:

  1. If you haven’t already, install the Tao Framework.
  2. Make a copy of the cleaned-up cairo-sharp (i.e. Mono.Cairo) library from NDesk. You will also need the cairo dlls; in my case, they were already installed as part of Mono.
  3. We will be modifying a Tao Framework / NeHe sample project. Go to your Tao Framework installation, and go to the source\examples\NeHe directory.
  4. Create a new C# Windows Application in Visual Studio and replace all the code in the file Program.cs with the text from the file Lesson06.cs in the Tao Framework NeHe samples directory.
  5. Add a reference to the Tao Framework OpenGL Binding and Windows Platform API Binding. Add a reference to the Mono.Cairo assembly as well. Add the line “using Cairo;” at the top of the source file. A few classes will now be ambiguous when you recompile; fix them to use the standard class library (not Cairo).
  6. Change the name of the method Run to Main. In case you’re curious, you can compile but the program will crash because it cannot find a texture that it tries to preload.
  7. At the beginning of the LoadGLTexture method, add the following code (taken from the Mono website). This code generates an in-memory bitmap for Cairo and draws something interesting on it.

    Cairo.ImageSurface i = new Cairo.ImageSurface(Format.Argb32, 256, 256);
    Cairo.Context gr = new Cairo.Context(i);

    // Shape
    gr.MoveTo(new PointD(100, 200));
    gr.CurveTo(new PointD(100, 100), new PointD(100, 100),
    new PointD(200, 100));
    gr.CurveTo(new PointD(200, 200), new PointD(200, 200),
    new PointD(100, 200));
    gr.ClosePath();

    // Save the state to restore it later. That will NOT save the path
    gr.Save();
    Cairo.Gradient pat = new Cairo.LinearGradient(100, 200, 200, 100);
    pat.AddColorStop(0, new Cairo.Color(0, 0, 0, 1));
    pat.AddColorStop(1, new Cairo.Color(1, 0, 0, 1));
    gr.SetSource(pat);

    // Fill the path with pattern
    gr.FillPreserve();

    // We "undo" the pattern setting here
    gr.Restore();

    // Color for the stroke
    gr.SetSource(new Cairo.Color(0, 0, 0));

    gr.LineWidth = 3;
    gr.Stroke();

  8. Remove the instantiation for textureImage and replace all references to textureImage with i (i is the Cairo bitmap). Completely remove the follow two lines:

    textureImage[0] = LoadBMP("NeHe.Lesson06.NeHe.bmp"); // Load The Bitmap
    BitmapData bitmapData = textureImage[0].LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

  9. The most important step. Previously the program was copying an imported file into a texture. Now we want to copy out Cairo bitmap instead. We replace this line:
    Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB8, textureImage[0].Width, textureImage[0].Height, 0, Gl.GL_BGR, Gl.GL_UNSIGNED_BYTE, bitmapData.Scan0);
    with this one:
    Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB8, i.Width, i.Height, 0, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE, i.Data);
  10. At the end of the method, before we return, insert calls to dispose the Context and ImageSurface objects we created:

    ((IDisposable)gr.Target).Dispose();
    ((IDisposable)gr).Dispose();

  11. Voila! You have pretty vector graphics being mapped onto a cube.
    openglandcairo01.png

You can get the Microsoft Visual C# Express project as well.

3 Responses to “Using cairo, OpenGL, and C#”

  1. asko Says:

    Thanks!

    You seem to be rather on the cutting edge with this, the tips and the project were appreciated! :)

    I’m actually looking for a working C# + Cairo setup, and samples. Will be doing some vector animation of a network system. Thanks for giving a kick start!

  2. Martin Says:

    Hi

    I made a game in C# using Tao.OpenGL. I also built it from the Tao Nehe tutorial but he uses a lot of Windows specific API calls such as the ones found in Tao.Platform.Windows.Kernel, .Gdi, .User, .Wgl and so on.

    Does your example work on a Mac? I would like to know how you created the device and rendering contexts. Your help is appreciated.

  3. Jeff Says:

    can you re upload your Microsoft Visual C# Express project its down , it would be a great help ,
    Thanks


Leave a Reply