@c -----------------------------------------------------------------------------
@c File     : gtk-cairo.texi
@c License  : GNU General Public License (GPL)
@c Language : English
@c Author   : Dieter Kaiser
@c Date     : 01.02.2014
@c Revision : 01.02.2014
@c
@c Copyright (C) 2014 by Dieter Kaiser
@c -----------------------------------------------------------------------------

This tutorial is taken from the offical Cairo website at
@url{http://cairographics.org/tutorial/} which has been derived from Michael
Urman's Cairo tutorial for python programmers. The code snippets have
been translated to Lisp and the text has only been changed as much as necessary.

Cairo is a powerful 2d graphics library. This document introduces you to how
Cairo works and many of the functions you will use to create the graphic
experience you desire.

@menu
* Cairo Drawing Model::
* Drawing with Cairo::
* Understanding Text::
* Working with Transforms::
* Where to Go Next::
* Tips and Tricks::
* Writing a Widget Using Cairo and GTK::
@end menu

@c -----------------------------------------------------------------------------
@node Cairo Drawing Model, Drawing with Cairo, Top, Top
@section Cairo's Drawing Model

In order to explain the operations used by cairo, we first delve into a model
of how Cairo models drawing.  There are only a few concepts involved, which are
then applied over and over by the different methods.  First I will describe the
nouns: destination, source, mask, path, and context.  After that I will describe
the verbs which offer ways to manipulate the nouns and draw the graphics you
wish to create.

@menu
* Nouns::
* Verbs::
@end menu

@c -----------------------------------------------------------------------------
@node Nouns, Verbs, Cairo Drawing Model
@subsection Nouns

Cairo's nouns are somewhat abstract.  To make them concrete I am including
diagrams that depict how they interact.  The first three nouns are the three
layers in the diagrams you see in this section.  The fourth noun, the path, is
drawn on the middle layer when it is relevant.  The final noun, the context,
is not shown.

@subheading Destination

@ifnotinfo
@image{figures/destination}
@end ifnotinfo

The destination is the @b{surface} on which you are drawing.  It may be tied to
an array of pixels like in this tutorial, or it might be tied to a SVG or PDF
file, or something else.  This surface collects the elements of your graphic as
you apply them, allowing you to build up a complex work as though painting on a
canvas.

@subheading Source

@ifnotinfo
@image{figures/source}
@end ifnotinfo

The source is the "paint" you are about to work with.  I show this as it
is - plain black for several examples - but translucent to show lower layers.
Unlike real paint, it does not have to be a single color; it can be a
@b{pattern} or even a previously created destination @b{surface} (see
@url{http://cairographics.org/FAQ/#paint_from_a_surface, How do I paint from
one surface to another?}).  Also unlike real paint it can contain  transparency
information - the Alpha channel.

@subheading Mask

@ifnotinfo
@image{figures/the-mask}
@end ifnotinfo

The mask is the most important piece: it controls where you apply the source to
the destination.  I will show it as a yellow layer with holes where it lets the
source through.  When you apply a drawing verb, it is like you stamp the source
to the destination.  Anywhere the mask allows, the source is copied.  Anywhere
the mask disallows, nothing happens.

@subheading Path

The path is somewhere between part of the mask and part of the context.  I will
show it as thin green lines on the mask layer.  It is manipulated by path verbs,
then used by drawing verbs.

@subheading Context

The context keeps track of everything that verbs affect.  It tracks one source,
one destination, and one mask.  It also tracks several helper variables like
your line width and style, your font face and size, and more.  Most importantly
it tracks the path, which is turned into a mask by drawing verbs.

Before you can start to draw something with Cairo, you need to create the
context.  The context is stored in Cairo's central data type, called
@xurl{cairo-t}.  When you create a Cairo context, it must be tied to a specific
surface - for example, an image surface if you want to create a PNG file.
There is also a data type for the surface, called @xurl{cairo-surface-t}.  You
can initialize your Cairo context with the functions
@xurl{cairo-image-surface-create} and @xurl{cairo-create} like this:
@example
(let* ((surface (cairo-image-surface-create :argb32 120 120))
       (cr (cairo-create surface)))
  ... )
@end example
The Cairo context in this example is tied to an image surface of dimension
120 x 120 and 32 bits per pixel to store RGB and Alpha information.  Surfaces
can be created specific to most Cairo backends, see the
@url{http://www.crategus.com/books/cl-cffi-gtk/pages/cairo.html,
Cairo API documentation} for details.

@c -----------------------------------------------------------------------------
@node Verbs, , Nouns, Cairo Drawing Model
@subsection Verbs

The reason you are using Cairo in a program is to draw.  Cairo internally draws
with one fundamental drawing operation: the source and mask are freely placed
somewhere over the destination.  Then the layers are all pressed together and
the paint from the source is transferred to the destination wherever the mask
allows it.  To that extent the following five drawing verbs, or operations, are
all similar.  They differ by how they construct the mask.

@heading Stroke

@ifnotinfo
@image{figures/stroke}
@end ifnotinfo

The @xurl{cairo-stroke} operation takes a virtual pen along the path.  It allows
the source to transfer through the mask in a thin (or thick) line around the
path, according to the pen's
@url{http://www.crategus.com/books/cl-cffi-gtk/pages/cairo_fun_cairo-set-line-width.html, line width},
@url{http://www.crategus.com/books/cl-cffi-gtk/pages/cairo_fun_cairo-set-dash.html, dash style},
and
@url{http://www.crategus.com/books/cl-cffi-gtk/pages/cairo_fun_cairo-set-line-cap.html,line caps}.

@example
(cairo-set-line-width cr 0.1)
(cairo-set-source-rgb cr 1.0 0.0 0.0)
(cairo-rectangle cr 0.25 0.25 0.5 0.5)
(cairo-stroke cr)
@end example

The following example shows the above code snippet in action and the code to
produce the output:

@ifnotinfo
@image{figures/cairo-stroke}
@end ifnotinfo

@float Example, demo-cairo-stroke
@caption{Demo Cairo Stroke}
@end float
@verbatim
(defun demo-cairo-stroke ()
  (within-main-loop
    (let ((window (make-instance 'gtk-window
                                 :type :toplevel
                                 :title "Demo Cairo Stroke"
                                 :border-width 12
                                 :default-width 400
                                 :default-height 400)))
      (g-signal-connect window "destroy"
                        (lambda (widget)
                          (declare (ignore widget))
                          (leave-gtk-main)))
      ;; Signals used to handle the backing surface
      (g-signal-connect window "draw"
         (lambda (widget cr)
           (let ((cr (pointer cr))
                 ;; Get the GdkWindow for the widget
                 (window (gtk-widget-window widget)))
           ;; Clear surface
           (cairo-set-source-rgb cr 1.0 1.0 1.0)
           (cairo-paint cr)
           ;; Example is in 1.0 x 1.0 coordinate space
           (cairo-scale cr
                        (gdk-window-get-width window)
                        (gdk-window-get-height window))
           ;; Drawing code goes here
           (cairo-set-line-width cr 0.1)
           (cairo-set-source-rgb cr 1.0 0.0 0.0)
           (cairo-rectangle cr 0.25 0.25 0.5 0.5)
           (cairo-stroke cr)
           t)))
      (gtk-widget-show-all window))))
@end verbatim

@heading Fill

@ifnotinfo
@image{figures/fill}
@end ifnotinfo

The @xurl{cairo-fill} operation instead uses the path like the lines of a
coloring book, and allows the source through the mask within the hole whose
boundaries are the path.  For complex paths (paths with multiple closed
sub-paths - like a donut - or paths that self-intersect) this is influenced by
the
@url{http://www.crategus.com/books/cl-cffi-gtk/pages/cairo_fun_cairo-set-fill-rule.html, file rule}.
Note that while stroking the path transfers the source for half
of the line width on each side of the path, filling a path fills directly up to
the edge of the path and no further.

@example
(cairo-set-source-rgb cr 1.0 0.0 0.0)
(cairo-rectangle cr 0.25 0.25 0.5 0.5)
(cairo-fill cr)
@end example

@heading Show Text / Glyphs

@ifnotinfo
@image{figures/showtext}
@end ifnotinfo

The @xurl{cairo-show-text} operation forms the mask from text.  It may be easier
to think of @xurl{cairo-show-text} as a shortcut for creating a path with
@xurl{cairo-text-path} and then using @xurl{cairo-fill} to transfer it.  Be
aware @xurl{cairo-show-text} caches glyphs so is much more efficient if you work
with a lot of text.

@example
(cairo-set-source-rgb cr 0.0 0.0 0.0)
(cairo-select-font-face cr "Georgia" :normal :bold)
(cairo-set-font-size cr 1.2)
(let ((text-extents (cairo-text-extents cr "a")))
  (cairo-move-to cr
                 (- 0.5
                    (/ (cairo-text-extents-t-width text-extents) 2)
                    (cairo-text-extents-t-x-bearing text-extents))
                 (- 0.5
                    (/ (cairo-text-extents-t-height text-extents) 2)
                    (cairo-text-extents-t-y-bearing text-extents)))
  (cairo-show-text cr "a"))
@end example

@heading Paint

@ifnotinfo
@image{figures/paint}
@end ifnotinfo

The @xurl{cairo-paint} operation uses a mask that transfers the entire source
to the destination.  Some people consider this an infinitely large mask, and
others consider it no mask; the result is the same.  The related operation
@xurl{cairo-paint-with-alpha} similarly allows transfer of the full source to
destination, but it transfers only the provided percentage of the color.

@example
(cairo-set-source-rgb cr 0.0 0.0 0.0)
(cairo-paint-with-alpha cr 0.5)
@end example

@heading Mask

@ifnotinfo
@image{figures/mask}
@end ifnotinfo

The @xurl{cairo-mask} and @xurl{cairo-mask-surface} operations allow transfer
according to the transparency/opacity of a second source pattern or surface.
Where the pattern or surface is opaque, the current source is transferred to
the destination.  Where the pattern or surface is transparent, nothing is
transferred.

@example
(let ((linpat (cairo-pattern-create-linear 0 0 1 1))
      (radpat (cairo-pattern-create-radial 0.5 0.5 0.25 0.5 0.5 0.75)))

  (cairo-pattern-add-color-stop-rgb linpat 0 0 0.3 0.8)
  (cairo-pattern-add-color-stop-rgb linpat 1 0 0.8 0.3)

  (cairo-pattern-add-color-stop-rgba radpat 0 0 0 0 1)
  (cairo-pattern-add-color-stop-rgba radpat 0.5 0 0 0 0)

  (cairo-set-source cr linpat)
  (cairo-mask cr radpat))
@end example

@c -----------------------------------------------------------------------------
@node Drawing with Cairo, Understanding Text, Cairo Drawing Model, Top
@section Drawing with Cario

In order to create an image you desire, you have to prepare the context for each
of the drawing verbs.  To use @xurl{cairo-stroke} or @xurl{cairo-fill} you
first need a path.  To use @xurl{cairo-show-text} you must position your text
by its insertion point.  To use @xurl{cairo-mask} you need a second source
pattern or surface.  And to use any of the operations, including
@xurl{cairo-paint}, you need a primary source.

@menu
* Preparing and Selecting a Source::
* Creating a Path::
@end menu

@c -----------------------------------------------------------------------------
@node Preparing and Selecting a Source, Creating a Path, Drawing with Cairo
@subsection Preparing and Selecting a Source

@ifnotinfo
@image{figures/setsourcergba}
@end ifnotinfo

There are three main kinds of sources in cairo: colors, gradients, and images.
Colors are the simplest; they use a uniform hue and opacity for the entire
source.  You can select these without any preparation with
@xurl{cairo-set-source-rgb} and @xurl{cairo-set-source-rgba}.  Using
@code{(cairo-set-source-rgb cr r g b} is equivalent to using
@code{(cairo-set-source-rgba cr r g b 1.0}, and it sets your source color to
use full opacity.

@example
(cairo-set-source-rgb cr 0 0 0)
(cairo-move-to cr 0 0)
(cairo-line-to cr 1 1)
(cairo-move-to cr 1 0)
(cairo-line-to cr 0 1)
(cairo-set-line-width cr 0.2)
(cairo-stroke cr)

(cairo-rectangle cr 0 0 0.5 0.5)
(cairo-set-source-rgba cr 1 0 0 0.80)
(cairo-fill cr)

(cairo-rectangle cr 0 0.5 0.5 0.5)
(cairo-set-source-rgba cr 0 1 0 0.60)
(cairo-fill cr)

(cairo-rectangle cr 0.5 0 0.5 0.5)
(cairo-set-source-rgba cr 0 0 1 0.40)
(cairo-fill cr)
@end example

Gradients describe a progression of colors by setting a start and stop reference
location and a series of "stops" along the way.  Linear gradients are built from
two points which pass through parallel lines to define the start and stop
locations.  Radial gradients are also built from two points, but each has an
associated radius of the circle on which to define the start and stop locations.
Stops are added to the gradient with @xurl{cairo-pattern-add-color-stop-rgb} and
@xurl{cairo-pattern-add-color-stop-rgba} which take a color like
@code{cairo-set-source-rgb*}, as well as an offset to indicate where it lies
between the reference locations.  The colors between adjacent stops are averaged
over space to form a smooth blend.  Finally, the behavior beyond the reference
locations can be controlled with @xurl{cairo-pattern-set-extend}.

@ifnotinfo
@image{figures/setsourcegradient}
@end ifnotinfo

@example
(let ((radpat (cairo-pattern-create-radial 0.25
                                           0.25 0.10 0.50 0.50 0.50))
      (linpat (cairo-pattern-create-linear 0.25 0.35 0.75 0.65)))
  (cairo-pattern-add-color-stop-rgb radpat 0.00 1.00 0.80 0.80)
  (cairo-pattern-add-color-stop-rgb radpat 1.00 0.90 0.00 0.00)
  (iter (for i from 1 below 10)
        (iter (for j from 1 below 10)
              (cairo-rectangle cr
                               (- (/ i 10.0) 0.04)
                               (- (/ j 10.0) 0.04)
                               0.08
                               0.08)))
  (cairo-set-source cr radpat)
  (cairo-fill cr)

  (cairo-pattern-add-color-stop-rgba linpat 0.00 1.0 1.0 1.0 0.0)
  (cairo-pattern-add-color-stop-rgba linpat 0.25 0.0 1.0 0.0 0.5)
  (cairo-pattern-add-color-stop-rgba linpat 0.50 1.0 1.0 1.0 0.0)
  (cairo-pattern-add-color-stop-rgba linpat 0.75 0.0 0.0 1.0 0.5)
  (cairo-pattern-add-color-stop-rgba linpat 1.00 1.0 1.0 1.0 0.0)

  (cairo-rectangle cr 0.0 0.0 1.0 1.0)
  (cairo-set-source cr linpat)
  (cairo-fill cr))
@end example

Images include both surfaces loaded from existing files with
@xurl{cairo-image-surface-create-from-png} and surfaces created from within
Cairo as an earlier destination.  As of Cairo 1.2, the easiest way to make and
use an earlier destination as a source is with @xurl{cairo-push-group} and
either @xurl{cairo-pop-group} or @xurl{cairo-pop-group-to-source}.  Use
@xurl{cairo-pop-group-to-source} to use it just until you select a new source,
and @xurl{cairo-pop-group} when you want to save it so you can select it over
and over again with @xurl{cairo-set-source}.

@c -----------------------------------------------------------------------------
@node Creating a Path, , Preparing and Selecting a Source
@subsection Creating a Path

Cairo always has an active path.  If you call @xurl{cairo-stroke} it will draw
the path with your line settings.  If you call @xurl{cairo-fill} it will fill
the inside of the path.  But as often as not, the path is empty, and both calls
will result in no change to your destination.  Why is it empty so often? For
one, it starts that way; but more importantly after each @xurl{cairo-stroke} or
@xurl{cairo-fill} it is emptied again to let you start building your next path.

What if you want to do multiple things with the same path? For instance to draw
a red rectangle with a black border, you would want to fill the rectangle path
with a red source, then stroke the same path with a black source.  A rectangle
path is easy to create multiple times, but a lot of paths are more complex.

Cairo supports easily reusing paths by having alternate versions of its
operations.  Both draw the same thing, but the alternate does not reset the
path.  For stroking, alongside @xurl{cairo-stroke} there is
@xurl{cairo-stroke-preserve}; for filling, @xurl{cairo-fill-preserve} joins
@xurl{cairo-fill}.  Even setting the clip has a preserve variant.  Apart from
choosing when to preserve your path, there are only a couple common operations.

@heading Moving

@ifnotinfo
@image{figures/path-moveto}
@end ifnotinfo

Cairo uses a connect-the-dots style system when creating paths.  Start at 1,
draw a line to 2, then 3, and so forth.  When you start a path, or when you
need to start a new sub-path, you want it to be like point 1: it has nothing
connecting to it.  For this, use @xurl{cairo-move-to}.  This sets the current
reference point without making the path connect the previous point to it.  There
is also a relative coordinate variant, @xurl{cairo-rel-move-to}, which sets the
new reference a specified distance away from the current reference instead.
After setting your first reference point, use the other path operations which
both update the reference point and connect to it in some way.

@example
(cairo-move-to cr 0.25 0.25)
@end example

@heading Straight Lines

@ifnotinfo
@image{figures/path-lineto}
@end ifnotinfo

Whether with absolute coordinates @xurl{cairo-line-to} (extend the path from
the reference to this point), or relative coordinates @xurl{cairo-rel-line-to}
(extend the path from the reference this far in this direction), the path
connection will be a straight line.  The new reference point will be at the
other end of the line.

@example
(cairo-line-to cr 0.5 0.375)
(cairo-rel-line-to cr 0.25 -0.125)
@end example

@heading Arcs

@ifnotinfo
@image{figures/path-arcto}
@end ifnotinfo

Arcs are parts of the outside of a circle.  Unlike straight lines, the point you
directly specify is not on the path.  Instead it is the center of the circle
that makes up the addition to the path.  Both a starting and ending point on the
circle must be specified, and these points are connected either clockwise by
@xurl{cairo-arc} or counter-clockwise by @xurl{cairo-arc-negative}.  If the
previous reference point is not on this new curve, a straight line is added from
it to where the arc begins.  The reference point is then updated to where the
arc ends.  There are only absolute versions.

@example
(cairo-arc cr 0.5 0.5 (* 0.25 (sqrt 2)) (* -0.25 pi) (* 0.25 pi))
@end example

@heading Curves

@ifnotinfo
@image{figures/path-curveto}
@end ifnotinfo

Curves in Cairo are cubic Bezier splines.  They start at the current reference
point and smoothly follow the direction of two other points (without going
through them) to get to a third specified point.  Like lines, there are both
absolute @xurl{cairo-curve-to} and relative @xurl{cairo-rel-curve-to} versions.
Note that the relative variant specifies all points relative to the previous
reference point, rather than each relative to the preceding control point of
the curve.

@example
(cairo-rel-curve-to cr -0.25 -0.125 -0.25 0.125 -0.5, 0)
@end example

@heading Close the path

@ifnotinfo
@image{figures/path-close}
@end ifnotinfo

Cairo can also close the path by drawing a straight line to the beginning of the
current sub-path.  This straight line can be useful for the last edge of a
polygon, but is not directly useful for curve-based shapes.  A closed path is
fundamentally different from an open path: it is one continuous path and has no
start or end.  A closed path has no line caps for there is no place to put them.

@example
(cairo-close-path cr)
@end example

@heading Text

Finally text can be turned into a path with @xurl{cairo-text-path}.  Paths
created from text are like any other path, supporting stroke or fill operations.
This path is placed anchored to the current reference point, so
@xurl{cairo-move-to} your desired location before turning text into a path.
However there are performance concerns to doing this if you are working with a
lot of text; when possible you should prefer using the verb
@xurl{cairo-show-text} over @xurl{cairo-text-path} and @xurl{cairo-fill}.

@c -----------------------------------------------------------------------------
@node Understanding Text, Working with Transforms, Drawing with Cairo, Top
@section Understanding Text

@ifnotinfo
@image{figures/textextents}
@end ifnotinfo

To use text effectively you need to know where it will go.  The methods
@xurl{cairo-font-extents} and @xurl{cairo-text-extents} get you this
information.  Since this diagram is hard to see so small, I suggest getting its
source and bump the size up to 600.  It shows the relation between the
reference point (red dot); suggested next reference point (blue dot); bounding
box (dashed blue lines); bearing displacement (solid blue line); and height,
ascent, baseline, and descent lines (dashed green).

The reference point is always on the baseline.  The descent line is below that,
and reflects a rough bounding box for all characters in the font.  However it is
an artistic choice intended to indicate alignment rather than a true bounding
box.  The same is true for the ascent line above.  Next above that is the height
line, the artist-recommended spacing between subsequent baselines.  All three of
these are reported as distances from the baseline, and expected to be positive
despite their differing directions.

The bearing is the displacement from the reference point to the upper-left
corner of the bounding box.  It is often zero or a small positive value for x
displacement, but can be negative x for characters like j as shown; it is almost
always a negative value for y displacement.  The width and height then describe
the size of the bounding box.  The advance takes you to the suggested reference
point for the next letter.  Note that bounding boxes for subsequent blocks of
text can overlap if the bearing is negative, or the advance is smaller than the
width would suggest.

In addition to placement, you also need to specify a face, style, and size.  Set
the face and style together with @xurl{cairo-select-font-face}, and the size
with @xurl{cairo-set-font-size}.  If you need even finer control, try getting a
@xurl{cairo-font-options-t} with @xurl{cairo-get-font-options}, tweaking it, and
setting it with @xurl{cairo-set-font-options}.

@c -----------------------------------------------------------------------------
@node Working with Transforms, Where to Go Next, Understanding Text, Top
@section Working with Transforms

Transforms have three major uses.  First they allow you to set up a coordinate
system that is easy to think in and work in, yet have the output be of any size.
Second they allow you to make helper functions that work at or around a (0, 0)
but can be applied anywhere in the output image.  Thirdly they let you deform
the image, turning a circular arc into an elliptical arc, etc.  Transforms are a
way of setting up a relation between two coordinate systems.  The device-space
coordinate system is tied to the surface, and cannot change.  The user-space
coordinate system matches that space by default, but can be changed for the
above reasons.  The helper functions @xurl{cairo-user-to-device} and
@xurl{cairo-user-to-device-distance} tell you what the device-coordinates are
for a user-coordinates position or distance.  Correspondingly
@xurl{cairo-device-to-user} and @xurl{cairo-device-to-user-distance} tell you
user-coordinates for a device-coordinates position or distance.  Remember to
send positions through the non-distance variant, and relative moves or other
distances through the distance variant.

I leverage all of these reasons to draw the diagrams in this document.  Whether
I am drawing 120 x 120 or 600 x 600, I use @xurl{cairo-scale} to give me a
1.0 x 1.0 workspace.  To place the results along the right column, such as in
the discussion of cairo's drawing model, I use @xurl{cairo-translate}.  And to
add the perspective view for the overlapping layers, I set up an arbitrary
deformation with @xurl{cairo-transform} on a @xurl{cairo-matrix-t}.

To understand your transforms, read them bottom to top, applying them to the
point you are drawing.  To figure out which transforms to create, think through
this process in reverse.  For example if I want my 1.0 x 1.0 workspace to be
100 x 100 pixels in the middle of a 120 x 120 pixel surface, I can set it up
one of three ways:

@verbatim
1. (cairo-translate cr 10 10)
   (cairo-scale cr 100 100)

2. (cairo-scale cr 100 100)
   (cairo-translate cr 0.1 0.1)

3. (let ((mat (cairo-matrix-init 100 0 0 100 10 10)))
     (cairo-transform cr mat)
     ... )
@end verbatim

Use the first when relevant because it is often the most readable; use the third
when necessary to access additional control not available with the primary
functions.

Be careful when trying to draw lines while under transform.  Even if you set
your line width while the scale factor was 1, the line width setting is always
in user-coordinates and is not modified by setting the scale.  While you are
operating under a scale, the width of your line is multiplied by that scale.
To specify a width of a line in pixels, use @xurl{cairo-device-to-user-distance}
to turn a (1, 1) device-space distance into, for example, a (0.01, 0.01)
user-space distance.  Note that if your transform deforms the image there is not
necessarily a way to specify a line with a uniform width.

@c -----------------------------------------------------------------------------
@node Where to Go Next, Tips and Tricks, Working with Transforms, Top
@section Where to Go Next

This wraps up the tutorial.  It does not cover all functions in Cairo, so for
some "advanced" lesser-used features, you will need to look elsewhere.  The code
behind the examples (layer diagrams, drawing illustrations) uses a handful of
techniques that are not described within, so analyzing them may be a good first
step.  Other examples on cairographics.org lead in different directions.  As
with everything, there is a large gap between knowing the rules of the tool, and
being able to use it well.  The final section of this document provides some
ideas to help you traverse parts of the gap.

@c -----------------------------------------------------------------------------
@node Tips and Tricks, Writing a Widget Using Cairo and GTK, Where to Go Next, Top
@section Tips and Tricks

In the previous sections you should have built up a firm grasp of the operations
Cairo uses to create images.  In this section I've put together a small handful
of snippets I have found particularly useful or non-obvious.  I am still new to
Cairo myself, so there may be other better ways to do these things.  If you find
a better way, or find a cool way to do something else, let me know and perhaps I
can incorporate it into these tips.

@menu
* Line Width::
* Text Alignment::
@end menu

@c -----------------------------------------------------------------------------
@node Line Width, Text Alignment, Tips and Tricks
@subsection Line Width

When you are working under a uniform scaling transform, you can not just use
pixels for the width of your line.  However it is easy to translate it with the
help of @xurl{cairo-device-to-user-distance} (assuming that the pixel width is
 1):

@example
(muliple-value-bind (ux uy)
    (cairo-device-to-user-distance cr)
  (cairo-set-line-width cr (min ux uy)))
@end example

When you are working under a deforming scale, you may wish to still have line
widths that are uniform in device space.  For this you should return to a
uniform scale before you stroke the path.  In the image, the arc on the left is
stroked under a deformation, while the arc on the right is stroked under a
uniform scale.

@example
(cairo-set-line-width cr 0.1)

(cairo-save cr)
(cairo-scale cr 0.5 1)
(cairo-arc cr 0.5 0.5 0.40 0 (* 2 pi))
(cairo-stroke cr)

(cairo-translate cr 1 0)
(cairo-arc cr 0.5 0.5 0.40 0 (* 2pi))
(cairo-restore cr)
(cairo-stroke cr)
@end example

@c -----------------------------------------------------------------------------
@node Text Alignment, , Line Width
@subsection Text Alignment

When you try to center text letter by letter at various locations, you have to
decide how you want to center it.  For example the following code will actually
center letters individually, leading to poor results when your letters are of
different sizes.  (Unlike most examples, here I assume a 26 x 1 workspace.)

@example
cairo_text_extents_t te;
char alphabet[] = "AbCdEfGhIjKlMnOpQrStUvWxYz";
char letter[2];

for (i=0; i < strlen(alphabet); i++) @{
    *letter = '\0';
    strncat (letter, alphabet + i, 1);

    cairo_text_extents (cr, letter, &te);
    cairo_move_to (cr, i + 0.5 - te.x_bearing - te.width / 2,
            0.5 - te.y_bearing - te.height / 2);
    cairo_show_text (cr, letter);
@}
@end example

Instead the vertical centering must be based on the general size of the font,
thus keeping your baseline steady.  Note that the exact positioning now depends
on the metrics provided by the font itself, so the results are not necessarily
the same from font to font.

@example
cairo_font_extents_t fe;
cairo_text_extents_t te;
char alphabet[] = "AbCdEfGhIjKlMnOpQrStUvWxYz";
char letter[2];

cairo_font_extents (cr, &fe);
for (i=0; i < strlen(alphabet); i++) @{
    *letter = '\0';
    strncat (letter, alphabet + i, 1);

    cairo_text_extents (cr, letter, &te);
    cairo_move_to (cr, i + 0.5 - te.x_bearing - te.width / 2,
            0.5 - fe.descent + fe.height / 2);
    cairo_show_text (cr, letter);
@}
@end example

@c -----------------------------------------------------------------------------
@node Writing a Widget Using Cairo and GTK, , Tips and Tricks, Top
@section Writing a Widget Using Cairo and GTK+

@ifnotinfo
@float Figure, figure-cairo-clock
@caption{Cairo Clock}
@center @image{figures/cairo-clock}
@end float
@end ifnotinfo

This demo shows a custom widget named @code{egg-clock-face} which draws a
clock using Cairo.  This Cairo Clock example is inspired by the C code from
Davyd Madeley.  @code{egg-clock-face} is defined as a subclass of the
@xurl{gtk-drawing-area} class.  Only the property @code{time} is added to hold
the actual time of the clock.  The @code{initialize-instance} method installs a
timeout source with the function @xurl{g-timeout-add}, which updates every
second the @code{time} property of @code{egg-clock-face} and requests the
redrawing of the widget.  The "draw" signal handler draws the clock into the
@xurl{gtk-drawing-area} of the @code{egg-clock-face} widget.

@float Example, demo-cairo-clock
@caption{Demo Cairo Clock}
@end float
@verbatim
(asdf:load-system :cl-cffi-gtk)

(defpackage :cairo-clock
  (:use :gtk :gdk :gobject :glib :pango :cairo :cffi :iterate :common-lisp)
  (:export #:demo-cairo-clock))

(in-package :cairo-clock)

;; Class egg-clock-face is a subclass of a GtkDrawingArea

(defclass egg-clock-face (gtk-drawing-area)
  ((time :initarg :time
         :initform (multiple-value-list (get-decoded-time))
         :accessor egg-clock-face-time))
  (:metaclass gobject-class))

(defmethod initialize-instance :after
    ((clock egg-clock-face) &key &allow-other-keys)
  ;; A timeout source for the time
  (g-timeout-add 1000
                 (lambda ()
                   (setf (egg-clock-face-time clock)
                         (multiple-value-list (get-decoded-time)))
                   (gtk-widget-queue-draw clock)
                   +g-source-continue+))
  ;; Signal handler which draws the clock
  (g-signal-connect clock "draw"
     (lambda (widget cr)
       (let ((cr (pointer cr))
             ;; Get the GdkWindow for the widget
             (window (gtk-widget-window widget)))
       ;; Clear surface
       (cairo-set-source-rgb cr 1.0 1.0 1.0)
       (cairo-paint cr)
       (let* ((x (/ (gdk-window-get-width window) 2))
              (y (/ (gdk-window-get-height window) 2))
              (radius (- (min x y) 12)))
         ;; Clock back
         (cairo-arc cr x y radius 0 (* 2 pi))
         (cairo-set-source-rgb cr 1 1 1)
         (cairo-fill-preserve cr)
         (cairo-set-source-rgb cr 0 0 0)
         (cairo-stroke cr)
         ;; Clock ticks
         (let ((inset 0.0)
               (angle 0.0))
           (dotimes (i 12)
             (cairo-save cr)
             (setf angle (/ (* i pi) 6))
             (if (eql 0 (mod i 3))
                 (setf inset (* 0.2 radius))
                 (progn
                   (setf inset (* 0.1 radius))
                   (cairo-set-line-width cr
                                         (* 0.5 (cairo-get-line-width cr)))))
             (cairo-move-to cr
                            (+ x (* (- radius inset) (cos angle)))
                            (+ y (* (- radius inset) (sin angle))))
             (cairo-line-to cr
                            (+ x (* radius (cos angle)))
                            (+ y (* radius (sin angle))))
             (cairo-stroke cr)
             (cairo-restore cr)))
         (let ((seconds (first (egg-clock-face-time clock)))
               (minutes (second (egg-clock-face-time clock)))
               (hours (third (egg-clock-face-time clock))))
           ;; The hour hand is rotated 30 degrees (pi/6 r) per hour
           ;; + 1/2 a degree (pi/360 r) per minute
           (let ((hours-angle (* (/ pi 6) hours))
                 (minutes-angle (* (/ pi 360) minutes)))
             (cairo-save cr)
             (cairo-set-line-width cr (* 2.5 (cairo-get-line-width cr)))
             (cairo-move-to cr x y)
             (cairo-line-to cr
                            (+ x
                               (* (/ radius 2)
                                  (sin (+ hours-angle minutes-angle))))
                            (+ y
                               (* (/ radius 2)
                                  (- (cos (+ hours-angle minutes-angle))))))
             (cairo-stroke cr)
             (cairo-restore cr))
           ;; The minute hand is rotated 6 degrees (pi/30 r)
           ;; per minute
           (let ((angle (* (/ pi 30) minutes)))
             (cairo-move-to cr x y)
             (cairo-line-to cr
                            (+ x (* radius 0.75 (sin angle)))
                            (+ y (* radius 0.75 (- (cos angle)))))
             (cairo-stroke cr))
           ;; Seconds hand: Operates identically to the minute hand
           (let ((angle (* (/ pi 30) seconds)))
             (cairo-save cr)
             (cairo-set-source-rgb cr 1 0 0)
             (cairo-move-to cr x y)
             (cairo-line-to cr (+ x (* radius 0.7 (sin angle)))
                               (+ y (* radius 0.7 (- (cos angle)))))
             (cairo-stroke cr)
             (cairo-restore cr))))
       t))))

(defun demo-cairo-clock ()
  (within-main-loop
    (let ((window (make-instance 'gtk-window
                                 :title "Demo Cairo Clock"
                                 :default-width 250
                                 :default-height 250))
          (clock (make-instance 'egg-clock-face)))
      (g-signal-connect window "destroy"
                        (lambda (widget)
                          (declare (ignore widget))
                          (leave-gtk-main)))
      (gtk-container-add window clock)
      (gtk-widget-show-all window))))
@end verbatim

