Cairo binding for use with Gdk and Gtk widgets. It is extension for compile-time generated bindings to "libcairo-gobject2" library. This bindings is generated "gobject" shard (https://awesomeopensource.com/project/jhass/crystal-gobject). I used mainly code from "cairo-cr" shard , so it is a fork "cairo-cr" shard (https://awesomeopensource.com/project/TamasSzekeres/cairo-cr)
First install cairo:
sudo apt-get install libgirepository1.0-dev libgtk-3-dev libcairo-gobject2 gir1.2-freedesktop
Note: "crystal-gobject" uses cairo-1.0.typelib from gir1.2-freedesktop package for generating cairo bindings. Different versions of Ubuntu use different versions of gir1.2-freedesktop package. If you use Ubuntu16.04 based distributives uncomment line 3 in src/cairo.cr file
If you use Ubuntu18.04 based distributives uncomment Rectangle structure in src/lib_cairo.cr file (lines 7...12).
If you use Ubuntu 20.04 LTS (Focal Fossa) based distributives need install libevent-2.1-6 pkg :
sudo add-apt-repository "deb http://mirrors.kernel.org/ubuntu/ eoan main" sudo apt-get update sudo apt-get install libevent-2.1-6
Add the dependency to your
dependencies: cairo-gobject: github: viachpaliy/cairo-gobject
require "gobject/gtk" require "cairo-gobject/cairo"
For more details see the sample in /samples folder.
Run sample :
cd cairo-gobject shards install crystal run samples/sample_name.cr
See also samples in :
TODO: Write development instructions here
git checkout -b my-new-feature)
git commit -am 'Add some feature')
git push origin my-new-feature)
Here's a well known list of them:
Cairo-gobject is a Crystal shard for working with the Cairo library. It is a set of Crystal bindings to the Cairo C library. It closely matches the C API with the exception of cases, where more Crystal way is desirable.
Cairo is a library for creating 2D vector graphics. It is written in the C programming language.
Bindings for other computer languages exist, including Python, Perl, C++, C#, or Java.
Cairo is a multiplatform library; it works on Linux, BSDs, Windows, and OSX.
Cairo supports various backends. Backends are output devices for displaying the created graphics.
Here we provide some useful definitions. To do some drawing in Cairo, we must first create a drawing context.
The drawing context holds all of the graphics state parameters that describe how drawing is to be done.
This includes information such as line width, color, the surface to draw to, and many other things.
It allows the actual drawing functions to take fewer arguments to simplify the interface.
A path is a collection of points used to create primitive shapes such as lines, arcs, and curves.
There are two kinds of paths: open and closed paths. In a closed path, starting and ending points meet.
In an open path, starting and ending point do not meet. In Cairo, we start with an empty path.
First, we define a path and then we make them visible by stroking and/or filling them.
fill method call, the path is emptied. We have to define a new path.
If we want to keep the existing path for later drawing, we can use the
A path is made of subpaths.
A source is the paint we use in drawing. We can compare the source to a pen or ink that we use to draw the outlines
and fill the shapes. There are four kinds of basic sources: colors, gradients, patterns, and images.
A surface is a destination that we are drawing to. We can render documents using the PDF or PostScript surfaces,
directly draw to a platform via the Xlib and Win32 surfaces.
Before the source is applied to the surface, it is filtered first. The mask is used as a filter.
It determines where the source is applied and where not. Opaque parts of the mask allow to copy the source. Transparent parts do not let to copy the source to the surface.
A pattern represents a source when drawing onto a surface. In Cairo, a pattern is something
that you can read from and that is used as the source or mask of a drawing operation.
Patterns can be solid, surface-based, or gradients.
In the first example, we draw on a GTK window.
This backend will be used throughout the rest of the tutorial.
require "gobject/gtk/autorun" require "../src/cairo" class CairoApp @window : Gtk::Window delegate show_all, to: @window def initialize @window = Gtk::Window.new @window.title = "Simple drawing" @window.resize 600,150 @window.connect "destroy", &->Gtk.main_quit darea = Gtk::DrawingArea.new darea.connect "draw",&->drawfun @window.add darea end def drawfun context = Gdk.cairo_create(@window.window.not_nil!) context.set_source_rgb(0, 0, 100) context.select_font_face("Sans", Cairo::FontSlant::NORMAL , Cairo::FontWeight::NORMAL) context.font_size=40 context.move_to(10,50) context.show_text("Cairo draw on a GTK window!") end end app=CairoApp.new app.show_all
The example pops up a GTK window on which we draw the "Cairo draw on a GTK window!" text.
Gtk specifically has a convenience wrapper that starts the mainloop automatically:
We import the Cairo module:
In the next line we create an empty window:
@window = Gtk::Window.new
We tell it to set the value of the property title to "Simple drawing":
@window.title = "Simple drawing"
We set a size of window :
Followed by connecting to the window\92s delete event to ensure that
the application is terminated if we click on the x to close the window:
@window.connect "destroy", &->Gtk.main_quit
We will be drawing on a Gtk.DrawingArea widget:
darea = Gtk::DrawingArea.new
When the window is redrawn, a
draw signal is emitted.
We connect that signal to the
The drawing is done inside the
We create a Cairo context from window :
context = Gdk.cairo_create(@window.window.not_nil!)
We draw our text in blue ink. The ink is specified with the
context.set_source_rgb(0, 0, 100)
We choose a font type with the
and set its size with the
context.select_font_face("Sans", Cairo::FontSlant::NORMAL , Cairo::FontWeight::NORMAL) context.font_size=40
We move to a position at x=10.0, y=50.0 and draw the text:
context.move_to(10,50) context.show_text("Cairo draw on a GTK window!")
stroke operation draws the outlines of shapes and the
fill operation fills the insides of shapes.
In the example, we draw a circle and fill it with a solid color.
require "gobject/gtk/autorun" require "../src/cairo" require "math" class CairoApp @window : Gtk::Window delegate show_all, to: @window def initialize @window = Gtk::Window.new @window.title = "Fill and stroke" @window.resize 400,300 @window.connect "destroy", &->Gtk.main_quit darea = Gtk::DrawingArea.new darea.connect "draw",&->drawfun @window.add darea end def drawfun context = Gdk.cairo_create(@window.window.not_nil!) context.line_width=9 context.set_source_rgb( 0.69, 0.19, 0) context.translate(200,150) context.arc(0,0,50,0,2*Math::PI) context.stroke_preserve context.set_source_rgb( 0.30, 0.40, 0.60) context.fill end end app=CairoApp.new app.show_all
This module is needed for the pi constant which is used to draw a circle.
We set a line width with the
We set the source to some dark red color using the
context.line_width=9 context.set_source_rgb( 0.69, 0.19, 0)
translate() method, we move the drawing origin to the center of the window.
We want our circle to be centered.
arc() method adds a new circular path to the Cairo drawing context.
stroke_preserve() method draws the outline of the circle.
stroke() method, it also preserves the shape for later drawing.
We change the color for drawing and fill the circle with a new color using the
context.set_source_rgb( 0.30, 0.40, 0.60) context.fill
Each line can be drawn with a different pen dash. A pen dash defines the style of the line.
The dash pattern is specified by the
The pattern is set by the dash list which is a list of floating values.
They set the on and off parts of the dash pattern.
The dash is used by the
stroke() method to create a line.
If the number of dashes is 0, dashing is disabled.
If the number of dashes is 1, a symmetric pattern is assumed with alternating on
and off portions of the size specified by the single value in dashes.
def drawfun context = Gdk.cairo_create(@window.window.not_nil!) context.set_source_rgb( 0.69, 0.19, 0) context.line_width=1.5 context.set_dash([4.0, 21.0, 2.0], 0) context.move_to(40, 30) context.line_to(200, 30) context.stroke context.set_dash([14.0, 6.0], 0) context.move_to(40,50) context.line_to(200,50) context.stroke context.set_dash([1.0], 0) context.move_to(40,70) context.line_to(200,70) context.stroke end
We draw three lines in three different pen dashes.
context.set_dash([4.0, 21.0, 2.0], 0)
We have a pattern of three numbers. We have 4 points drawn, 21 not drawn, and 2 drawn, then 4 points not drawn, 21 points drawn. and 2 not drawn. This pattern takes turns until the end of the line.
context.set_dash([14.0, 6.0], 0)
In this pattern, we have always 14 points drawn and 6 not drawn.
Here we create a pen dash of a symmetric pattern of alternating single on and off points.
The line caps are end points of lines.
There are three different line cap styles in Cairo :
Cairo::LINE_CAP_SQUAREcap has a different size than a line with a
Cairo::LINE_CAP_BUTTcap. If a line is x units wide, the line with a
Cairo::LINE_CAP_SQUAREcap will be exactly x units greater in size; x/2 units at the beginning and x/2 units at the end.
def drawfun context = Gdk.cairo_create(@window.window.not_nil!) context.set_source_rgb( 0.3, 0.19, 0.4) context.line_width=10 context.line_cap=Cairo::LineCap::BUTT context.move_to(30, 50) context.line_to(150, 50) context.stroke context.line_cap=Cairo::LineCap::ROUND context.move_to(30, 90) context.line_to(150, 90) context.stroke context.line_cap=Cairo::LineCap::SQUARE context.move_to(30, 130) context.line_to(150, 130) context.stroke context.line_width=1.5 context.move_to(30, 40) context.line_to(30, 140) context.stroke context.move_to(150, 40) context.line_to(150, 140) context.stroke context.move_to(155, 40) context.line_to(155, 140) context.stroke end
The example draws three lines with three different line caps. It will also graphically demonstrate the differences in size of the lines by drawing three additional thin vertical lines.
Our lines will be 10 px wide.
context.line_cap=Cairo::LineCap::ROUND context.move_to(30, 90) context.line_to(150, 90) context.stroke
Here we draw a horizontal line with a
context.line_width=1.5 context.move_to(30, 40) context.line_to(30, 140) context.stroke
This is one of the three vertical lines used to demostrate the differences in size.
The lines can be joined using three different join styles :
def drawfun context = Gdk.cairo_create(@window.window.not_nil!) context.set_source_rgb( 0.3, 0.19, 0.4) context.line_width=14 context.rectangle(30, 30, 100, 100) context.line_join=Cairo::LineJoin::MITER context.stroke context.rectangle(160, 30, 100, 100) context.line_join=Cairo::LineJoin::BEVEL context.stroke context.rectangle(100, 160, 100, 100) context.line_join=Cairo::LineJoin::ROUND context.stroke end
In this example, we draw three thick rectangles with various line joins.
The lines are 14 px wide.
context.rectangle(30, 30, 100, 100) context.line_join=Cairo::LineJoin::MITER context.stroke
Here we draw a rectangle with cairo.LINE_JOIN_MITER join style.