Drawing with Cairo in Java Gnome

In this part of the Java programming tutorial, we will do some drawing with the Cairo library.

Cairo is a library for creating 2D vector graphics. We can use it to draw our own widgets, charts or various effects or animations.

Simple drawing

The stroke operation draws the outlines of shapes and the fill operation fills the insides of shapes. Next we will demonstrate these two operations.

simpledrawing.java
package com.zetcode;

import org.freedesktop.cairo.Context;

import org.gnome.gdk.Event;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.Allocation;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;

/**
 * Java Gnome tutorial
 *
 * This program draws a simple
 * drawing with the Cairo library.
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class GSimpleDrawing extends Window implements Widget.ExposeEvent {

    public GSimpleDrawing() {
    
        setTitle("Simple drawing");
        
        initUI();
        
        connect(new Window.DeleteEvent() {
            public boolean onDeleteEvent(Widget source, Event event) {
                Gtk.mainQuit();
                return false;
            }
        });
        
        setDefaultSize(250, 200);
        setPosition(WindowPosition.CENTER);
        showAll();
    }
    
    public void initUI() {
        DrawingArea darea = new DrawingArea();
        darea.connect(this);
        add(darea);
    }
    
    public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
        final Context cr;
        
        cr = new Context(widget.getWindow());    
        drawShape(cr);
        
        return false;
    }
    
    public void drawShape(Context cr) {
    
        cr.setLineWidth(9);
        cr.setSource(0.7, 0.2, 0.0);
                
        int width, height;
        width = getAllocation().getWidth();
        height = getAllocation().getHeight();

        cr.translate(width/2, height/2);
        cr.arc(0, 0, (width < height ? width : height) / 2 - 10, 0, 2*Math.PI);
        cr.strokePreserve();
        
        cr.setSource(0.3, 0.4, 0.6);
        cr.fill();
    }    
    
    public static void main(String[] args) {
        Gtk.init(args);
        new GSimpleDrawing();
        Gtk.main();
    }
}

In our example, we will draw a circle and fill it with a solid colour.

DrawingArea darea = new DrawingArea();

We will be doing our drawing operations on the DrawingArea widget.

public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
   final Context cr;
   
   cr = new Context(widget.getWindow());    
   drawShape(cr);
   
   return false;
}

When the window needs to be redrawn, the ExposeEvent is created. The actual drawing is delegated to the drawShape() method.

cr = new Context(widget.getWindow());

We create the Context object from the gdk window of the drawing area. The context is an object onto which we do all our drawings.

cr.setLineWidth(9);

We set the width of the line to 9 pixels.

cr.setSource(0.7, 0.2, 0.0);

We set the colour to dark red.

int width, height;
width = getAllocation().getWidth();
height = getAllocation().getHeight();

cr.translate(width/2, height/2);

We get the width and height of the drawing area. We move the origin into the middle of the window.

cr.arc(0, 0, (width < height ? width : height) / 2 - 10, 0, 2*Math.PI);
cr.strokePreserve();

We draw the outside shape of a circle. The strokePreserve() strokes the current path according to the current line width, line join, line cap, and dash settings. Unlike the stroke(), it preserves the path within the cairo context.

cr.setSource(0.3, 0.4, 0.6);
cr.fill();

This fills the interior of the circle with some blue colour.

Simple drawing
Figure: Simple drawing

Basic shapes

The next example draws some basic shapes onto the window.

basicshapes.java
package com.zetcode;

import org.freedesktop.cairo.Context;
import org.freedesktop.cairo.Matrix;

import org.gnome.gdk.Event;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;


/**
 * ZetCode Java Gnome tutorial
 *
 * This program draws basic shapes
 * with the cairo library.
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class GBasicShapes extends Window implements Window.ExposeEvent {

    public GBasicShapes() {
    
        setTitle("Basic Shapes");
        
        initUI();
        
        connect(new Window.DeleteEvent() {
            public boolean onDeleteEvent(Widget source, Event event) {
                Gtk.mainQuit();
                return false;
            }
        });
    
        setDefaultSize(390, 240);
        setPosition(WindowPosition.CENTER);
        showAll();
    }
    
    public void initUI() {
        DrawingArea darea = new DrawingArea();
        darea.connect(this);
        add(darea);
    }
    
    public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
        final Context cr;
        
        cr = new Context(widget.getWindow());    
        drawShapes(cr);
        
        return false;
    }
    
    public void drawShapes(Context cr) {
    
        cr.setSource(0.6, 0.6, 0.6);

        cr.rectangle(20, 20, 120, 80);
        cr.rectangle(180, 20, 80, 80);
        cr.fill();

        cr.arc(330, 60, 40, 0, 2*Math.PI);
        cr.fill();

        cr.arc(90, 160, 40, Math.PI/4, Math.PI);
        cr.fill();
        
        Matrix mat = new Matrix();
        mat.translate(220, 180);        
        mat.scale(1, 0.7);
        cr.transform(mat);
      
        cr.arc(0, 0, 50, 0, 2*Math.PI);
        cr.fill();  
    }
    
    public static void main(String[] args) {
        Gtk.init(args);
        new GBasicShapes();
        Gtk.main();
    }
}

In this example, we will create a rectangle, a square, a circle, an arc and an ellipse.

cr.rectangle(20, 20, 120, 80);
cr.rectangle(180, 20, 80, 80);
cr.fill();

These lines draw a rectangle and a square.

cr.arc(330, 60, 40, 0, 2*Math.PI);
cr.fill();

Here the arc() method draws a full circle.

Matrix mat = new Matrix();
mat.translate(220, 180);        
mat.scale(1, 0.7);
cr.transform(mat);
     
cr.arc(0, 0, 50, 0, 2*Math.PI);
cr.fill();  

If we want to draw an oval, we do some scaling first. The scale() method shrinks the y axis.

Basic shapes
Figure: Basic shapes

Colours

A colour is an object representing a combination of Red, Green, and Blue (RGB) intensity values. Cairo valid RGB values are in the range 0 to 1.

colors.java
package com.zetcode;

import org.freedesktop.cairo.Context;

import org.gnome.gdk.Event;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;

/**
 * ZetCode Java Gnome tutorial 
 *
 * This program draws nine rectangles
 * on the drawing area. Each of them
 * has different color. 
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class GColors extends Window
            implements Widget.ExposeEvent {
    
    public GColors() {
    
        setTitle("Colors");
    
        connect(new Window.DeleteEvent() {
            public boolean onDeleteEvent(Widget source, Event event) {
                Gtk.mainQuit();
                return false;
            }
        });
        
        initUI();
        
        setDefaultSize(350, 280);
        setPosition(WindowPosition.CENTER);
        showAll();
    }
    
    public void initUI() {
        DrawingArea darea = new DrawingArea();
        darea.connect(this);
        add(darea);
    }

    public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
        final Context cr;
    
        cr = new Context(widget.getWindow());
        drawRectangles(cr);       
 
        return false;
    }
    
    
    public void drawRectangles(Context cr) {
        cr.setSource(0.5, 0.65, 0.45);
        cr.rectangle(10, 15, 90, 60);
        cr.fill();

        cr.setSource(0.16, 0.7, 0.9);
        cr.rectangle(130, 15, 90, 60);
        cr.fill();

        cr.setSource(0.274, 0.262, 0.48);
        cr.rectangle(250, 15, 90, 60);
        cr.fill();

        cr.setSource(0.5, 0.39, 0.33);
        cr.rectangle(10, 105, 90, 60);
        cr.fill();
        
        cr.setSource(0.99, 0.83, 0.24);
        cr.rectangle(130, 105, 90, 60);
        cr.fill();
        
        cr.setSource(0.95, 0.38, 0.27);
        cr.rectangle(250, 105, 90, 60);
        cr.fill();
        
        cr.setSource(0.85, 0.57, 0.21);
        cr.rectangle(10, 195, 90, 60);
        cr.fill();
        
        cr.setSource(0.25, 0.04, 0.73);
        cr.rectangle(130, 195, 90, 60);
        cr.fill();
        
        cr.setSource(0.12, 0.08, 0.03);
        cr.rectangle(250, 195, 90, 60);
        cr.fill();
    }
    
    public static void main(String[] args)  {
        Gtk.init(args);
        new GColors();
        Gtk.main();
    }
}

We draw nine rectangles in nine different colors.

cr.setSource(0.5, 0.65, 0.45);

The setSource() method sets a color for the cairo context. The three parameters of the method are the color intensity values.

cr.rectangle(10, 15, 90, 60);
cr.fill();

We create a rectangle shape and fill it with the previously specified color.

Colors
Figure: Colors

Transparent rectangles

Transparency is the quality of being able to see through a material. The easiest way to understand transparency is to imagine a piece of glass or water. Technically, the rays of light can go through the glass and this way we can see objects behind the glass.

In computer graphics, we can achieve transparency effects using alpha compositing. Alpha compositing is the process of combining an image with a background to create the appearance of partial transparency. The composition process uses an alpha channel. (wikipedia.org, answers.com)

transparentrectangles.java
package com.zetcode;

import org.freedesktop.cairo.Context;

import org.gnome.gdk.Event;
import org.gnome.gdk.EventExpose;
import org.gnome.gtk.DrawingArea;
import org.gnome.gtk.Gtk;
import org.gnome.gtk.Widget;
import org.gnome.gtk.Window;
import org.gnome.gtk.WindowPosition;

/**
 * ZetCode Java Gnome tutorial
 *
 * This program draws ten rectangles
 * with different levels of transparency.
 *
 * @author jan bodnar
 * website zetcode.com
 * last modified March 2009
 */

public class GTransparentRectangles extends Window
    implements Widget.ExposeEvent {

    public GTransparentRectangles() {
        setTitle("Transparent Rectangles");
        
        connect(new Window.DeleteEvent() {
            public boolean onDeleteEvent(Widget source, Event event) {
                Gtk.mainQuit();
                return false;
            }
        });
        
        initUI();
        
        setSizeRequest(590, 90);
        setPosition(WindowPosition.CENTER);
        showAll();
    }
    
    
    public void initUI() {
        DrawingArea darea = new DrawingArea();
        add(darea);
        darea.connect(this);
    }
 

    public void doDrawing(Context cr) {
    
        for (int i = 1; i<=10; i++) {
            cr.setSource(0, 0, 1, 0.1*i);
            cr.rectangle(50*i, 20, 40, 40);
            cr.fill();
        }
    }

    public boolean onExposeEvent(Widget widget, EventExpose eventExpose) {
        
            Context cr = new Context(widget.getWindow());
            doDrawing(cr);
            
            return false;
    }
    
    public static void main(String[] args) {
        Gtk.init(args);
        new GTransparentRectangles();
        Gtk.main();
    }
}

In the example we will draw ten rectangles with different levels of transparency.

cr.setSource(0, 0, 1, 0.1*i);

The last parameter of the setSource() method is the alpha transparency.

Transparent rectangles
Figure: Transparent rectangles

In this chapter of the Java Gnome programming library, we were drawing with Cairo library.