ホーム
C/C++チュートリアル
カイロ
- GTK では、カイロ(cairo)と呼ばれる描画システムでグラフィックスを描きます。
カイロを使って描画するには、その描画される場所が必要です。
- GTK では、この描画する場所として
ドローイングエリア(drawing area)と呼ばれるものを使います。
- なお、この章はGTK3のみに対応しています。
ライン
line.c
#include <gtk/gtk.h>
void draw_callback();
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *da;
gtk_init(&argc, &argv);
window = gtk_window_new (0);
gtk_window_set_title (GTK_WINDOW(window), "Line");
gtk_window_set_default_size (GTK_WINDOW(window), 320, 240);
gtk_container_set_border_width (GTK_CONTAINER(window), 15);
da = gtk_drawing_area_new ();
gtk_container_add (GTK_CONTAINER(window), da);
g_signal_connect (da, "draw",
G_CALLBACK(draw_callback), NULL);
g_signal_connect (window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
void draw_callback(GtkWidget *da, cairo_t *cr)
{
GdkRGBA color;
gdouble dashes[]= {50.0, 10.0, 10.0, 10.0};
guchar num = sizeof(dashes) / sizeof(dashes[0]);
cairo_move_to(cr, 0, 10);
cairo_line_to(cr, 300, 10);
cairo_stroke(cr);
cairo_move_to(cr, 0, 50);
cairo_line_to(cr, 300, 50);
gdk_rgba_parse(&color, "#f00");
gdk_cairo_set_source_rgba(cr, &color);
cairo_set_dash(cr, dashes, num, 0.0);
cairo_set_line_width(cr, 1.0);
cairo_stroke(cr);
cairo_move_to(cr, 0, 90);
cairo_line_to(cr, 300, 90);
cairo_set_dash(cr, dashes, num, 25.0);
gdk_rgba_parse(&color, "#0f0");
gdk_cairo_set_source_rgba(cr, &color);
cairo_stroke(cr);
cairo_move_to(cr, 20, 130);
cairo_line_to(cr, 250, 130);
cairo_set_dash(cr, dashes, 0, 25.0);
gdk_rgba_parse(&color, "#00f");
gdk_cairo_set_source_rgba(cr, &color);
cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
cairo_set_line_width(cr, 20.0);
cairo_stroke(cr);
cairo_move_to(cr, 20, 170);
cairo_line_to(cr, 250, 170);
gdk_rgba_parse(&color, "#f0f");
gdk_cairo_set_source_rgba(cr, &color);
cairo_set_line_cap(cr,CAIRO_LINE_CAP_ROUND);
cairo_stroke(cr);
cairo_move_to(cr, 20, 200);
cairo_line_to(cr, 250, 200);
gdk_rgba_parse(&color, "#ff0");
gdk_cairo_set_source_rgba(cr, &color);
cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
cairo_stroke(cr);
}
実行結果
コード説明
-
gtk_drawing_area_new関数でドローイングエリアを作ります。引数はありません。
-
g_signal_connect(da, "draw",、
ドローイングエリアが描画される時に発生するイベントは "draw" です。
-
void draw_callback(GtkWidget *da, cairo_t *cr)、
"draw"イベントで呼び出されるイベントハンドラーには、
呼び出したウィジェットだけではなく、カイロも自動的に渡されるみたいです。
-
GdkRGBA color;、GTK3で色を格納するデータ型はGdkRGBAです。
-
gdouble dashes[]= {50.0, 10.0, 10.0, 10.0};、
dashes配列は、破線を作るときに必要です。
それぞれの要素の値は、実線、間隔、実線、間隔の値になります。
-
guchar num = sizeof(dashes) / sizeof(dashes[0]);は、
C言語で配列の要素数を取得する常套の式です。
sizeof関数で引数に指定した変数や要素が使用するメモリー量を取得できます。
配列全体のメモリー量 割る 配列の一つの要素のメモリー量で、
配列の要素数が分かります。配列の要素数も破線を作るときに必要です。
-
cairo_move_to関数で描画するスタート地点を移動します。
第1引数にカイロを、第2引数にドローイングエリア内の横位置、
第3引数にドローイングエリア内の縦位置を指定します。
-
cairo_line_to関数で線(line、ライン)を定義します。第1引数にカイロを、
第2引数に線のの終端の横位置を、第3引数に線の終端の縦位置を指定します。
-
cairo_stroke関数で今までに設定してきた線を描きます。
引数にカイロを指定します。
カイロのデフォルトで色は黒、線の幅は2ピクセルに設定されています。
-
gdk_rgba_parse関数で、第1引数のGdkRGBAのポインターに、
第2引数の文字列で表さられた色を設定します。
-
gdk_cairo_set_source_rgba関数で、第1引数のカイロに、
第2引数のGdkRGBAポインターの色を設定します。
-
cairo_set_dash関数で破線の設定をします。第1引数にカイロを、
第2引数には線の間隔を指定する配列を、第3引数に配列の要素数を、
第4引数にオフセット(offset、ズレ)を指定します。
要素数を 0 にすると破線ではなく実線が描かれることになります。
オフセットを指定すると、
破線のパターンの指定された位置から始まる破線が描かれます。
-
cairo_set_line_width関数で、線の幅を設定します。第1引数にカイロを、
第2引数に線の幅を指定します。
-
以上の、色、破線、線の幅などの設定は、次に変更されるまで維持されます。
-
cairo_set_dash(cr, dashes, num, 25.0)で、
破線のパターンの25.0ピクセル目から始まるように指定しています。
オフセットを変更した場合は、
前のオフセットからのオフセットになることに注意してください。
-
cairo_set_dash(cr, dashes, 0, 25.0)関数の要素数を
0 に指定することによって破線の描画が中止され、実戦の描画に変更されます。
-
cairo_set_line_cap関数で、線の始点と終点の形を指定できます。
第1引数にカイロを、第2引数に形のパターンの定数を指定します。
パターンの種類は次のとおりです。
-
CAIRO_LINE_CAP_BUTT、デフォルトのパターンです。
何も指定しない時にはこのパターンになります。
-
CAIRO_LINE_CAP_ROUND、線の端に半円が追加されます。
折れ線の場合に角が丸く埋まります。
-
CAIRO_LINE_CAP_SQUARE、線の端に四角形が追加されます。
折れ線の場合に角が鋭角で埋まります。
ポイント
point.c
#include <gtk/gtk.h>
#include <time.h>
void draw_callback();
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *da;
gtk_init(&argc, &argv);
srand(time(NULL));
window = gtk_window_new (0);
gtk_window_set_title (GTK_WINDOW(window), "Point");
gtk_window_set_default_size (GTK_WINDOW(window), 320, 240);
gtk_container_set_border_width (GTK_CONTAINER(window), 15);
da = gtk_drawing_area_new ();
gtk_container_add (GTK_CONTAINER(window), da);
g_signal_connect (da, "draw",
G_CALLBACK(draw_callback), NULL);
g_signal_connect (window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
void draw_callback(GtkWidget *da, cairo_t *cr)
{
GdkRGBA color;
guint x = 0, y = 0, width = 0, height = 0;
width = gtk_widget_get_allocated_width (da);
height = gtk_widget_get_allocated_height (da);
gdk_rgba_parse (&color, "#00f");
gdk_cairo_set_source_rgba (cr, &color);
cairo_paint (cr);
gdk_rgba_parse (&color, "#fff");
gdk_cairo_set_source_rgba (cr, &color);
cairo_set_line_width (cr, 1.0);
for (int i = 0; i < 1000; i++)
{
x = rand() % width + 1;
y = rand() % height + 1;
cairo_move_to(cr, x, y);
cairo_line_to(cr, x+1, y);
}
cairo_stroke(cr);
}
実行結果
コード説明
-
#include <time.h>
乱数を初期化するために現在時刻を使います。そのためにこのヘッダーが必要です。
-
srand(time(NULL));
乱数を初期化しています。手順は次のとおりです。
- srand関数で乱数を初期化します。
- 乱数は初期化しないと毎回同じ乱数を発生します。
- 乱数を初期化する値として現在の時刻を使います。
現在の時刻を使うことで、毎回違う値で初期化されることになります。
- time関数は 1970年1月1日から現在までの経過秒数を取得します。
引数には通常 NULL を指定します。
-
gtk_widget_get_allocated_width関数で
引数のウィジェットの横サイズを取得します。
-
gtk_widget_get_allocated_height関数で
引数のウィジェットの縦サイズを取得します。
-
cairo_paint関数は引数のカイロを現在のカラーで塗りつぶします。
-
rand() % width + 1;の式は次の意味になります。
-
rand関数は、0 から RAND_MAX
定数で定義されている値までの整数の乱数を発生します。
-
RAND_MAXは、macOSとLinuxでは2147483647(約21億)、
Windowsでは32767に設定されています。
-
% は整数の割り算の余りを取得する算術演算子です。
-
widthが500の場合、乱数を500で割った余りは
0 から 499 です。その値に 1 を足せば 1 から 500 になります。
-
カイロにポイントを描画する関数が見つからなかったので、
線幅を1ピクセルにして、cairo_move_to関数とcairo_line_to関数を使って
縦横1ピクセルの点を描画しています。
カラー
color.cp
#include <gtk/gtk.h>
void draw_callback();
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *da;
gtk_init(&argc, &argv);
window = gtk_window_new (0);
gtk_window_set_title (GTK_WINDOW(window), "Color");
gtk_window_set_default_size (GTK_WINDOW(window), 410, 240);
gtk_container_set_border_width (GTK_CONTAINER(window), 15);
da = gtk_drawing_area_new ();
gtk_container_add (GTK_CONTAINER(window), da);
g_signal_connect (da, "draw",
G_CALLBACK(draw_callback), NULL);
g_signal_connect (window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
void draw_callback(GtkWidget *da, cairo_t *cr)
{
GdkRGBA color;
cairo_rectangle(cr, 20, 20, 100, 75);
gdk_rgba_parse (&color, "#0ff"); // シアン(cyan)
gdk_cairo_set_source_rgba(cr, &color);
cairo_fill(cr);
cairo_rectangle(cr, 140, 20, 100, 75);
gdk_rgba_parse (&color, "#f0f"); // マジェンタ(magenta)
gdk_cairo_set_source_rgba(cr, &color);
cairo_fill(cr);
cairo_rectangle(cr, 260, 20, 100, 75);
gdk_rgba_parse (&color, "#ff0"); // イエロー(yellow)
gdk_cairo_set_source_rgba(cr, &color);
cairo_fill(cr);
cairo_rectangle(cr, 20, 115, 100, 75);
gdk_rgba_parse (&color, "#f00"); // レッド(red)
gdk_cairo_set_source_rgba(cr, &color);
cairo_fill(cr);
cairo_rectangle(cr, 140, 115, 100, 75);
gdk_rgba_parse (&color, "#0f0"); // グリーン(green)
gdk_cairo_set_source_rgba(cr, &color);
cairo_fill(cr);
cairo_rectangle(cr, 260, 115, 100, 75);
gdk_rgba_parse (&color, "#00f"); // ブルー(blue)
gdk_cairo_set_source_rgba(cr,&color);
cairo_stroke(cr);
}
実行結果
コード説明
-
cairo_rectangle関数で四角形を作ります。引数は次のとおりです。
- 第1引数にカイロ
- 第2引数に四角形の左上の横位置
- 第3引数に四角形の左上の縦位置
- 第4引数に四角形の横のサイズ
- 第5引数に四角形の縦のサイズ
-
GTKで色を名前で指定する方法を私はまだ見つけていません。
-
今のところ、gdk_rgba_parse関数を使って
色を表す文字列で指定する方法を見つけています。第1引数にカイロを、
第2引数に "#fff" か "rgb(255,255,255)" のどちらかの文字列を
指定します。
- 代表的な、インクの三原色と光の三原色は、次の文字列になります。
| シアン | Cyan |
"#0ff" | "rgb(0, 255, 255)" |
| マジェンタ | Magenta |
"#f0f" | "rgb(255, 0, 255)" |
| イエロー | Yellow | "#ff0" |
"rgb(255, 255, 0)" |
| レッド | Red |
"#f00" | "rgb(255, 0, 0)" |
| グリーン | Green |
"#0f0" | "rgb(0, 255, 0)" |
| ブルー | Blue |
"#00f" | "rgb(0, 0, 255)" |
| ホワイト | White |
"#fff" | "rgb(255, 255, 255)" |
| ブラック | Black |
"#000" | "rgb(0, 0, 0)" |
| グレイ | Gray |
"#666" | "rgb(127, 127, 127)" |
-
cairo_fill関数で図形に色を塗ります。引数にカイロを指定します。
-
cairo_stroke関数を使うと枠線が描かれます。引数にカイロを指定します。
シェープ
shape.c
#include <gtk/gtk.h>
#include <math.h>
void draw_callback();
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *da;
gtk_init(&argc, &argv);
window = gtk_window_new (0);
gtk_window_set_title (GTK_WINDOW(window), "Shape");
gtk_window_set_default_size (GTK_WINDOW(window), 430, 260);
gtk_container_set_border_width (GTK_CONTAINER(window), 15);
da = gtk_drawing_area_new ();
gtk_container_add (GTK_CONTAINER(window), da);
g_signal_connect (da, "draw",
G_CALLBACK(draw_callback), NULL);
g_signal_connect (window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
void draw_callback(GtkWidget *da, cairo_t *cr)
{
GdkRGBA color;
cairo_set_line_width(cr, 1.0);
cairo_save (cr);
/*楕円形*/
cairo_scale (cr, 1, 0.6);
cairo_arc (cr, 70, 70, 50, (0.0 * (M_PI/180.0)),
(360.0 * (M_PI/180.0)));
/*角丸四角形*/
cairo_restore (cr);
cairo_move_to (cr, 150, 20);
cairo_arc (cr, 160, 20, 10, (180 * (M_PI/180.0)),
(270.0 * (M_PI/180.0)));
cairo_line_to (cr, 240, 10);
cairo_arc (cr, 240, 20, 10, (270 * (M_PI/180.0)),
(360.0 * (M_PI/180.0)));
cairo_line_to (cr, 250, 60);
cairo_arc (cr, 240, 60, 10, (0 * (M_PI/180.0)),
(90.0 * (M_PI/180.0)));
cairo_line_to (cr, 160, 70);
cairo_arc (cr, 160, 60, 10, (90 * (M_PI/180.)),
(180.0 * (M_PI/180.0)));
cairo_close_path(cr);
/*円弧*/
cairo_move_to (cr, 280, 10);
cairo_line_to (cr, 340, 10);
cairo_arc (cr, 280, 10, 100, (0 * (M_PI/180.0)),
(90.0 * (M_PI/180.0)));
cairo_close_path(cr);
/*正円*/
cairo_move_to (cr, 70, 160);
cairo_arc (cr, 70, 160, 50, (0 * (M_PI/180.0)),
(360.0 * (M_PI/180.0)));
/*正方形*/
cairo_rectangle (cr, 150, 110, 100, 100);
gdk_rgba_parse (&color, "rgb(255, 17, 17)");
gdk_cairo_set_source_rgba(cr, &color);
cairo_stroke_preserve(cr);
gdk_rgba_parse (&color, "rgb(17, 255, 17)");
gdk_cairo_set_source_rgba(cr, &color);
cairo_fill (cr);
/*文字列*/
gdk_rgba_parse (&color, "#777");
gdk_cairo_set_source_rgba(cr, &color);
/*
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_BOLD);
*/
cairo_set_font_size(cr, 45.0);
cairo_move_to (cr, 280, 210);
cairo_show_text (cr, "Hello!");
}
実行結果
コード説明
-
#include <math.h>。M_PI定数を使うために読み込んでいます。M_PIは
3.141593に設定されているみたいです。
-
gtk_window_set_default_size (GTK_WINDOW(window), 430, 260);
今回はウィンドウのサイズを大きめにしています。
-
cairo_save (cr);
カイロの現在の設定を保存しています。あとで、保存状態に戻すために使います。
- 以下、図形ごとに説明します。
- 楕円形
-
cairo_scale関数で、描画した図形を、第1引数の横サイズ 1 に対して、
第2引数の縦サイズ 0.6 に変形します。
GTKには、楕円を作る関数がありません。この変形によって正円から楕円形を作ります。
-
-
cairo_arc関数で円弧を作ります。360度の円弧を作れば、正円になります。
- 第1引数にカイロ
- 第2引数に円弧が正円だった場合の中心の横位置
- 第3引数に円弧が正円だった場合の中心の縦位置
- 第4引数に円弧の半径
- 第5引数に円弧の開始地点をラディアン(radian)角度で指定します。
ラディアン角度は、角度 * (円周率 / 180.0)で取得できます。
右横(通常の90度)が、0度になりますので注意してください。
- 第6引数に円弧の終了地点をラディアン角度で指定します。
- 角丸四角形
-
cairo_restore関数でcairo_save関数で保存した状態に戻します。
carito_scale関数で設定した変形は、cairo_scale(cr, 1, 1.6666)などとと
逆の設定をしないと元に戻りません。
cairo_restore関数で元に戻すことが推奨されています。
-
GTKには、角丸四角形を描く関数がありません。代わりにcairo_arc関数と
cairo_line_to関数を使って描きます。
-
cairo_close_path関数はパスの開始地点と終了地点を結ぶ線を描いて
開いているパスを閉じます。
- 円弧
GTKには円弧と中心地点を結んだ図形を描く関数もないみたいです。
ここでもcairo_line_to関数とcairo_arc関数を使って描きました。
- 正円
GTKには円を描く関数もありません。360度の円弧を描けば正円になります。
- 正方形
cairo_rectangle関数で長方形を描けます。
- 第1引数にカイロ
- 第2引数に長方形の左上の横位置
- 第3引数に長方形の左上の縦位置
- 第4引数に長方形の横サイズ
- 第5引数に長方形の縦サイズ
-
横と縦のサイズが同じ長方形を描けば正方形になります。
- 文字列
-
cairo_set_font_size関数で第2引数の大きさにフォントのサイズを設定します。
-
cairo_show_text関数で第2引数の文字列を描きます。
-
cairo_select_font_face関数でフォントの種類を設定できるみたいですが、
私の場合は効果はありませんでした。
-
cairo_stroke_preserve関数は今までに設定した図形の線を描画しますが、
図形のデータは残します。
cairo_stroke関数では描画した後に図形のデータは破棄されます。
-
cairo_fill関数で図形を塗ります。cairo_stroke_preserve関数で図形のデータ
が残っている状態なのでこの関数で図形を塗ることができます。
図形のデータを残す、cairo_fill_preserve関数もあります。
Posted: Dec. 18, 2019
Update: Dec. 19, 2019