====== Vektorgrafiken mit GTK+ und Cairo ====== In vielen Anwendungen wird nicht nur die Benutzeroberfläche in das Fenster gezeichnet, oft werden Grafiken erstellt, die sich nicht über ein GUI-Toolkit wie GTK+ darstellen lassen. Hier kommen andere Libraries ins Spiel, eine davon ist Cairo. Diese Bibliothek für Vektorgrafiken werden wir uns nun genauer anschauen. ===== Was ist Cairo? ===== {{ http://cairographics.org/cairo-banner.png}}[[http://cairographics.org/|Cairo]] ist eine freie Bibliothek zur Erstellung von vektorbasierten 2D-Zeichnungen. Sie unterstützt die Ausgabe auf dem X Window System (Linux/Unix), unter Win32 (Windows), unter Quartz (Mac OS X) und in die Dateiformate PNG, PostScipt, PDF und SVG. Andere Back-Ends befinden sich in Entwicklung. Da Cairo frei verfügbar ist, findet es besonders im Open Source Bereich große Verwendung. Unter anderem basiert die Gecko-Browserengine, benutzt von Mozilla Firefox, Cairo zur Darstellung von SVG-Dateien. Auch die Programme Inkscape, OpenOffice.org, Evince und Okular verwenden Cairo als Grafikbibliothek. Nicht zu vergessen ist die Verwendung in GTK+ ab der Version 2.8.0. Ab dieser Version zeichnet GTK+ die meisten seiner Widgets mit Cairo, wodurch Cairo bei der Installation von GTK+ gleich mitgeliefert wird. ===== Der Cairokontext ===== Wie schon angesprochen benutzt GTK+ intern Cairo zum Zeichnen, allerdings stellt es auch Funktionen bereit, mit denen wir mit Cairo auf GTK-Widgets zeichnen können. Das Zauberwort heißt hier //Cairokontext//. Wir erstellen eine //"leere"// GTK Anwendung, binden aber einen neuen Header mit ein: #include /* Der neue Header, der die Cairo-Funktionen enthält*/ #include int main (int argc, char *argv[]) { GtkWidget *window; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(window, "destroy", G_CALLBACK (gtk_main_quit), NULL); gtk_widget_show_all(window); gtk_main(); return 0; } Als nächstes erstellen wir eine Callback Funktion für das Expose-Event, also wenn das Fenster gezeigt wird: static gboolean on_expose(GtkWidget *widget, GdkEventExpose *event, gpointer data) { cairo_t *cr; cr = gdk_cairo_create(widget->window); cairo_destroy(cr); return FALSE; } Hierbei ist ''cairo_t'' unser Cairokontext, den wir über die Funktion ''gdk_cairo_create'' auf unser Fenster legen (genauer gesagt: auf das GdkDrawable). Wichtig: mit ''cairo_destroy'' den Cairokontext wieder auflösen! Nun müssen wir noch einen Signalhandler für unser Event schreiben: g_signal_connect(window, "expose-event", G_CALLBACK (on_expose), NULL); und GTK+ sagen, dass wir auf das Fenster zeichnen wollen: gtk_widget_set_app_paintable(window, TRUE); Zum Kompilieren braucht ihr nichts an den Flags ändern. ===== Linien ===== {{ :gui:gtk:advanced:cairo_line.jpg|Horiontale Linie, gezeichnet mit Cairo}} Wie ihr seht, hätte man sich bis jetzt die ganze Cairo Geschichte auch sparen können, denn man sieht nichts. Nun wäre aber Cairo keine Grafikbibliothek, wenn man damit nicht zeichnen könnte: Also ran an die Stifte! =) Gezeichnet wird zwischen dem Aufruf von ''gdk_cairo_create'' und ''cairo_destroy''. Um nun eine einfache Linie zu zeichnen, bewegen wir den "Stift" von Cairo erst zu einer beliebigen Stelle: cairo_move_to(cr, 0, 100); und ziehen mit ihm dann eine Gerade zu einem anderen Punkt: cairo_line_to(cr, 200, 200); Achtung! Wer sich jetzt wundert, dass er nichts sieht, hat vielleicht eine kleine Zeile vergessen: cairo_stroke(cr); ''cairo_stroke'' übermittelt alle Zeichenoperationen an das //Backend//, in unserem Fall an den //Cairokontext//. Sprich: Um etwas zu sehen, erst ''cairo_stroke'' aufrufen! Noch etwas zu den Koordinaten: Diese gehen von der linken oberen Ecke aus. Wie gewohnt bezeichnet ''x'' die horizontale Achse und ''y'' die vertikale. ===== Bögen, Kreise ===== {{ :gui:gtk:advanced:cairo_arc.png|Kreis und Halbkreis}} Bekanntlich ist die Banane nicht gerade, sondern krumm: Sie bildet einen Bogen. Auch solche lassen sich mit Cairo zeichnen. Hierzu gibt es ''cairo_arc''. Um nun die nebenstehende Figur mit Cairo zu zeichnen packen wir folgendes zwischen ''gdk_cairo_create'' und ''cairo_destroy'': cairo_arc(cr, 100.0, 100.0, 50.0, 0 * (M_PI / 180.0), 360.0 * (M_PI / 180.0)); cairo_stroke(cr); cairo_arc(cr, 100.0, 100.0, 60.0, 0 * (M_PI / 180.0), 180.0 * (M_PI / 180.0)); cairo_stroke(cr); ''M_PI'' ist nichts anderes als die Kreiszahl π und ist definiert in [[c:lib:math:start|]], den Header also einbinden! Benötigt wird π zur Umrechnung von Grad in Bogenmaß ((Natürlich könnte man sich die Umrechnung sparen und alles gleich in Bogenmaß schreiben, ist so meiner Meinung nach aber anschaulicher.)). Um nun etwas Licht ins Dunkel der Argumente zu bringen, schaut euch diese Tabelle an: |void cairo_arc(cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2)|| |''cr''| Der //Cairokontext//.| |''xc''| Die x-Koordinate des Mittelpunktes.| |''yc''| Die y-Koordinate des Mittelpunktes.| |''radius''| //Selbsterklärend//| |''angle1''| Winkel am Beginn des Bogens. Angegeben in [[theory:math:trigonometry:angular-dimensions|Bogenmaß]].| |''angle2''| Winkel am Ende des Bogens. Angegeben in [[theory:math:trigonometry:angular-dimensions|Bogenmaß]].| Etwas "ungewöhnliches" müsste euch an diesem Beispiel dennoch aufgefallen sein: Der Aufruf von ''cairo_stroke'' zwischen den beiden ''cairo_arc'' Befehlen. Um der Sache einfach etwas nähre zu kommen, verzichtet einfach mal auf das erste ''cairo_stroke''!
...
Verwundert? Diese "Erscheinung" zu verstehen ist ziemlich einfach: Wie bei den Linien bereits erwähnt arbeitet Cairo mit einem Cursor (einem //Stift//). Mit Funktionen wie ''cairo_line_to'', ''cairo_move_to'' oder auch ''cairo_arc'' beschreibt man Positionsänderungen des Cursors. Diese werden von Cairo gespeichert, und mit ''cairo_stroke'' ausgegeben. Dies ist der //CairoPath//((//Cairo-Pfad//)). Wenn nicht anders beschrieben (zum Beispiel mit ''cairo_move_to''), werden alle Punkte des Pfades mit einer Linie verbunden. So kommt es, dass zwischen dem angeblichen //Endpunkt// des Kreises und dem //Anfangspunkt// des Halbbogens ein Linie gezeichnet wird, der nächste Punkt im Pfad ist ja schließlich dort. Somit besitzt ''cairo_stroke'' neben der Ausgabe des Pfades auch noch eine andere Bedeutung: Es setzt den //CairoPath// zurück! ===== Text ===== {{ :gui:gtk:advanced:cairo_text.png|}} Auch Text lässt sich mit Cairo einfach zeichnen. Hierfür muss zunächst die Art der Darstellung festgelegt werden, bevor der Text gezeichnet wird. Setzt einfach folgenden Code wieder zwischen ''gdk_cairo_create'' und ''cairo_destroy'': cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(cr, 20.0); Mit ''cairo_select_font_face'' setzen wir die Schriftart fest, mit ''cairo_set_font_size'' die Schriftgröße. Ein Hinweis: Wenn Cairo die Schriftart, die ''cairo_select_font_face'' übergeben wurde, nicht findet, wird automatisch die Standardschriftart des Systems gewählt! Um //kursiv// oder **fett** zu schreiben, müssen einfach die beiden letzten Argumente für ''cairo_select_font_face'' geändert werden: ^CAIRO_FONT_SLANT_*^Die "Schräge" des Textes^ |CAIRO_FONT_SLANT_NORMAL|Normaler Text.| |CAIRO_FONT_SLANT_ITALIC|Kursiver Text.| |CAIRO_FONT_SLANT_OBLIQUE|Schräger Text. ((Nicht in allen Schriftarten vorhanden.))| ^CAIRO_FONT_WEIGHT_*^Die "Gewichtung" des Textes^ |CAIRO_FONT_WEIGHT_NORMAL|Normaler Text.| |CAIRO_FONT_WEIGHT_BOLD|Fetter Text.| Wer den Code jetzt schon kompiliert, ausführt und sich wundert, dass noch nichts zu sehen ist, der war mal wieder zu voreilig, denn gezeichnet wird erst mit: cairo_move_to(cr, 10.0, 20.0); cairo_show_text(cr, "Proggen.org."); Auf ''cairo_stroke'' können wir hier verzichten, da wir nichts am CairoPath geändert haben. **Achtung:** Der mit ''cairo_move_to'' angegebene Punkt auf dem Fenster bezeichnet die linke untere Ecke des später gezeichneten Textes! ===== Farben ===== {{ :gui:gtk:advanced:cairo_colors.png|}} Natürlich können wir auch bunte Stifte aus dem Cairo-Malkasten nehmen: cairo_set_source_rgb (cr, 1, 0, 0); cairo_move_to (cr, 0, 0); cairo_line_to (cr, 10, 10); cairo_stroke (cr); cairo_set_source_rgb (cr, 0, 1, 0); cairo_move_to (cr, 10, 10); cairo_line_to (cr, 20, 20); cairo_stroke (cr); \\ \\ | void cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue); || |''cr''| Der //Cairokontext//.| |''red''| Der Rot-Anteil der Farbe (zwischen 0.0 und 1.0).| |''green''| Der Grün-Anteil der Farbe (zwischen 0.0 und 1.0).| |''blue''| Der Blau-Anteil der Farbe (zwischen 0.0 und 1.0).| **Wichtig:** Möchte man mehrere Farben verwenden (wie in dem Beispiel), so muss __vor dem Wechsel der Farbe__ erst ''cairo_stroke'' aufgerufen werden! Neben ''cairo_set_source_rgb'' kann auch die Funktion ''cairo_set_source_rgba'' verwendet werden. Diese bietet auch noch die Möglichkeit den Alpha-Kanal zu benutzen, womit sich wiederum Transparenz erzeugen lässt. ===== und Flächen ===== FIXME