Home  Contents

Painting with Cairo

In this part of the Ruby GTK tutorial, we will do some painting 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.

Colors

In the first example, we will work with colors. A color 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.

#!/usr/bin/ruby

# ZetCode Ruby GTK tutorial
#
# This program shows how to work
# with colors in Cairo
#
# author: jan bodnar
# website: www.zetcode.com
# last modified: June 2009

require 'gtk2'


class RubyApp < Gtk::Window

    def initialize
        super
    
        set_title "Colors"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end
        
        init_ui

        set_default_size 360, 100
        set_window_position Gtk::Window::POS_CENTER
        
        show_all
    end
    
    def init_ui
    
        @darea = Gtk::DrawingArea.new  
        
        @darea.signal_connect "expose-event" do  
            on_expose
        end
    
        add(@darea)

    end
    
    def on_expose
    
        cr = @darea.window.create_cairo_context  
        draw_colors cr

    end 
    
    def draw_colors cr
        
        cr.set_source_rgb 0.2, 0.23, 0.9
        cr.rectangle 10, 15, 90, 60
        cr.fill
         
        cr.set_source_rgb 0.9, 0.1, 0.1
        cr.rectangle 130, 15, 90, 60
        cr.fill

        cr.set_source_rgb 0.4, 0.9, 0.4
        cr.rectangle 250, 15, 90, 60
        cr.fill
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

In our example, we will draw three rectangles and fill them with three different colors.

@darea = Gtk::DrawingArea.new 

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

@darea.signal_connect "expose-event" do  
    on_expose
end

When the window needs to be redrawn, the the expose-event is triggered. In response to this event, we call the on_expose method.

 cr = @darea.window.create_cairo_context  

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

draw_colors cr

The actual drawing is delegated to the draw_colors method.

cr.set_source_rgb 0.2, 0.23, 0.9

The set_source_rgb 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

We draw a rectangle. The first two parameters are the x, y coordinates of the top left corner of the rectangle. The last two parameters are the width and height of the rectangle.

 cr.fill

We fill the inside of the rectangle with the current color.

Colors
Figure: Colors

Basic shapes

The next example draws some basic shapes onto the window.

#!/usr/bin/ruby

# ZetCode Ruby GTK tutorial
#
# This code example draws basic shapes
# with the Cairo library
#
# author: jan bodnar
# website: www.zetcode.com
# last modified: June 2009

require 'gtk2'


class RubyApp < Gtk::Window

    def initialize
        super
    
        set_title "Basic shapes"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end
        
        init_ui

        set_default_size 390, 240
        set_window_position Gtk::Window::POS_CENTER
        
        show_all
    end
    
    def init_ui
    
        @darea = Gtk::DrawingArea.new  
        
        @darea.signal_connect "expose-event" do  
            on_expose
        end
    
        add(@darea)
    end
    
    def on_expose
    
        cr = @darea.window.create_cairo_context  
        draw_shapes cr
    end
    
    def draw_shapes cr
    
        cr.set_source_rgb 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

        cr.translate 220, 180
        cr.scale 1, 0.7
        cr.arc 0, 0, 50, 0, 2*Math::PI
        cr.fill
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

In this example, we will create a rectangle, a square, a circle, an arc and an ellipse. We draw outlines in blue color, insides in white.

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.

cr.translate 220, 180
cr.scale 1, 0.7
cr.arc 0, 0, 50, 0, 2*Math::PI
cr.fill

The translate method moves the object to a specific point. If we want to draw an oval, we do some scaling first. Here the scale method shrinks the y axis.

Basic shapes
Figure: Basic shapes

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)

#!/usr/bin/ruby

# ZetCode Ruby GTK tutorial
#
# This program shows transparent
# rectangles using Cairo
#
# author: jan bodnar
# website: www.zetcode.com
# last modified: June 2009

require 'gtk2'


class RubyApp < Gtk::Window

    def initialize
        super
    
        set_title "Transparent rectangles"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end
        
        init_ui

        set_default_size 590, 90
        set_window_position Gtk::Window::POS_CENTER
        
        show_all
    end
    
    def init_ui
    
        @darea = Gtk::DrawingArea.new  
        
        @darea.signal_connect "expose-event" do  
            on_expose
        end
    
        add(@darea)

    end
    
    def on_expose
    
        cr = @darea.window.create_cairo_context  
        
        for i in (1..10)
            cr.set_source_rgba 0, 0, 1, i*0.1
            cr.rectangle 50*i, 20, 40, 40
            cr.fill
        end
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

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

cr.set_source_rgba 0, 0, 1, i*0.1

The last parameter of the set_source_rgba method is the alpha transparency.

Transparent rectangles
Figure: Transparent rectangles

Donut

In the following example we create a complex shape by rotating a bunch of ellipses.

#!/usr/bin/ruby

# ZetCode Ruby GTK tutorial
#
# This program creates a donut
# with Cairo library
#
# author: jan bodnar
# website: www.zetcode.com
# last modified: June 2009

require 'gtk2'


class RubyApp < Gtk::Window

    def initialize
        super
    
        set_title "Donut"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end
        
        init_ui

        set_default_size 350, 250
        set_window_position Gtk::Window::POS_CENTER
        
        show_all
    end
    
    def init_ui
    
        @darea = Gtk::DrawingArea.new  
        
        @darea.signal_connect "expose-event" do  
            on_expose
        end
    
        add(@darea)

    end
    
    def on_expose
    
        cr = @darea.window.create_cairo_context  
        cr.set_line_width 0.5

        w = allocation.width
        h = allocation.height
       
        cr.translate w/2, h/2
        cr.arc 0, 0, 120, 0, 2*Math::PI
        cr.stroke
         
        for i in (1..36)
            cr.save
            cr.rotate i*Math::PI/36
            cr.scale 0.3, 1
            cr.arc 0, 0, 120, 0, 2*Math::PI
            cr.restore
            cr.stroke
        end
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

In this example, we create a donut. The shape resembles a cookie, hence the name donut.

cr.translate w/2, h/2
cr.arc 0, 0, 120, 0, 2*Math::PI
cr.stroke

In the beginning there is an ellipse.

for i in (1..36)
    cr.save
    cr.rotate i*Math::PI/36
    cr.scale 0.3, 1
    cr.arc 0, 0, 120, 0, 2*Math::PI
    cr.restore
    cr.stroke
end

After several rotations, there is a donut.

Donut
Figure: Donut

Drawing text

In the next example, we draw some text on the window.

#!/usr/bin/ruby

# ZetCode Ruby GTK tutorial
#
# This program draws text
# using Cairo
#
# author: jan bodnar
# website: www.zetcode.com
# last modified: June 2009

require 'gtk2'


class RubyApp < Gtk::Window

    def initialize
        super
    
        set_title "Soulmate"
        signal_connect "destroy" do 
            Gtk.main_quit 
        end
        
        init_ui

        set_default_size 370, 240
        set_window_position Gtk::Window::POS_CENTER
        
        show_all
    end
    
    def init_ui
    
        @darea = Gtk::DrawingArea.new  
        
        @darea.signal_connect "expose-event" do  
            on_expose
        end
    
        add(@darea)

    end
    
    def on_expose
    
        cr = @darea.window.create_cairo_context  
        
        cr.set_source_rgb 0.1, 0.1, 0.1
         
        cr.select_font_face "Purisa", Cairo::FONT_SLANT_NORMAL, 
            Cairo::FONT_WEIGHT_NORMAL
        cr.set_font_size 13 
       
        cr.move_to 20, 30
        cr.show_text "Most relationships seem so transitory"
        cr.move_to 20, 60
        cr.show_text "They're all good but not the permanent one"
        cr.move_to 20, 120
        cr.show_text "Who doesn't long for someone to hold"
        cr.move_to 20, 150
        cr.show_text "Who knows how to love without being told"
        cr.move_to 20, 180
        cr.show_text "Somebody tell me why I'm on my own"
        cr.move_to 20, 210
        cr.show_text "If there's a soulmate for everyone"
    end
end

Gtk.init
    window = RubyApp.new
Gtk.main

We display part of the lyrics from the Natasha Bedingfields Soulmate song.

cr.select_font_face "Purisa", Cairo::FONT_SLANT_NORMAL, 
    Cairo::FONT_WEIGHT_NORMAL

Here we specify the font, that we use. Purisa normal.

cr.set_font_size 13 

We specify the size of the font.

cr.move_to 20, 30

We move to the point, where we will draw the text.

cr.show_text "Most relationships seem so transitory"

The show_text method draws text onto the window.

Soulmate
Figure: Soulmate

In this chapter of the Ruby GTK tutorial, we were painting with Cairo library.