ホーム
C/C++チュートリアル
カイロ
- GTK では、カイロ(cairo)と呼ばれる描画システムでグラフィックスを描きます。
カイロを使って描画するには、その描画される場所が必要です。
- GTK では、この描画する場所として
ドローイングエリア(drawing area)と呼ばれるものを使います。
- なお、この章はGTK3のみに対応しています。
ライン
line.c
#include <gtk/gtk.h>
void activate();
void draw_callback();
int main(int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("jp.vivacocoa.line", 0 );
g_signal_connect (app, "activate",
G_CALLBACK( activate), NULL);
status = g_application_run (G_APPLICATION(app), argc, argv);
g_object_unref (app);
return status;
}
void activate(GtkApplication *app, gpointer data)
{
GtkWidget *window;
GtkWidget *darea;
/* Window */
window =
gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW(window), "Line" );
gtk_window_set_default_size (GTK_WINDOW(window), 300, 200);
gtk_window_set_position (GTK_WINDOW(window), 1 );
/* Drawing area */
darea = gtk_drawing_area_new();
gtk_container_add (GTK_CONTAINER(window), darea );
/* Signal */
g_signal_connect (darea, "draw",
G_CALLBACK(draw_callback), NULL);
/* Show window */
gtk_widget_show_all (window);
}
void draw_callback(GtkWidget *widget, 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, 20, 20 );
cairo_line_to (cr, 280, 20 );
cairo_stroke (cr);
cairo_move_to (cr, 20, 50 );
cairo_line_to (cr, 280, 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, 20, 80 );
cairo_line_to (cr, 280, 80 );
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, 30, 110 );
cairo_line_to (cr, 270, 110 );
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, 30, 140 );
cairo_line_to (cr, 270, 140 );
gdk_rgba_parse (&color, "#0ff" );
gdk_cairo_set_source_rgba
(cr, &color );
cairo_set_line_cap
(cr, CAIRO_LINE_CAP_ROUND );
cairo_stroke (cr);
cairo_move_to (cr, 30, 170 );
cairo_line_to (cr, 270, 170 );
gdk_rgba_parse (&color, "#f0f" );
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> // for time
void activate();
void draw_callback();
int main(int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("jp.vivacocoa.point", 0 );
g_signal_connect (app, "activate",
G_CALLBACK( activate), NULL);
status = g_application_run (G_APPLICATION(app), argc, argv);
g_object_unref (app);
return status;
}
void activate(GtkApplication *app, gpointer data)
{
GtkWidget *window;
GtkWidget *darea;
/* Window */
window =
gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW(window), "Point" );
gtk_window_set_default_size (GTK_WINDOW(window), 300, 200);
gtk_window_set_position (GTK_WINDOW(window), 1 );
/* Drawing area */
darea = gtk_drawing_area_new();
gtk_container_add (GTK_CONTAINER(window), darea );
/* Signal */
g_signal_connect (darea, "draw",
G_CALLBACK(draw_callback), NULL);
/* Show window */
gtk_widget_show_all (window);
}
void draw_callback(GtkWidget *darea, cairo_t *cr)
{
/* Set random */
srand(time(NULL));
GdkRGBA color;
guint x = 0, y = 0, width = 0, height = 0;
width = gtk_widget_get_allocated_width (darea);
height = gtk_widget_get_allocated_height (darea);
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 );
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 activate();
void draw_callback();
int main(int argc, char **argv)
{
GtkApplication *app;
guchar status;
app = gtk_application_new ("jp.vivacocoa.color", 0 );
g_signal_connect (app, "activate",
G_CALLBACK( activate), NULL);
status = g_application_run (G_APPLICATION(app), argc, argv);
g_object_unref (app);
return status;
}
void activate(GtkApplication *app, gpointer data)
{
GtkWidget *window;
GtkWidget *darea;
/* Window */
window =
gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW(window), "Color" );
gtk_window_set_default_size (GTK_WINDOW(window), 300, 200);
gtk_window_set_position (GTK_WINDOW(window), 1 );
/* Drawing area */
darea = gtk_drawing_area_new();
gtk_container_add (GTK_CONTAINER(window), darea );
/* Signal */
g_signal_connect (darea, "draw",
G_CALLBACK(draw_callback), NULL);
/* Show window */
gtk_widget_show_all (window);
}
void draw_callback(GtkWidget *darea, cairo_t *cr)
{
GdkRGBA color;
cairo_rectangle (cr, 15, 15, 80, 75 );
gdk_rgba_parse (&color, "#0ff" ); // cyan
gdk_cairo_set_source_rgba(cr, &color );
cairo_fill (cr);
cairo_rectangle (cr, 110, 15, 80, 75 );
gdk_rgba_parse (&color, "#f0f" ); // magenta
gdk_cairo_set_source_rgba(cr, &color );
cairo_fill (cr);
cairo_rectangle (cr, 205, 15, 80, 75 );
gdk_rgba_parse (&color, "#ff0" ); // yellow
gdk_cairo_set_source_rgba(cr, &color );
cairo_fill (cr);
cairo_rectangle (cr, 15, 110, 80, 75);
gdk_rgba_parse (&color, "#f00" ); // red
gdk_cairo_set_source_rgba(cr, &color );
cairo_fill (cr);
cairo_rectangle (cr, 110, 110, 80, 75 );
gdk_rgba_parse (&color, "#0f0" ); // green
gdk_cairo_set_source_rgba(cr, &color );
cairo_fill (cr);
cairo_rectangle (cr, 205, 110, 80, 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> // for M_PI
void activate();
void draw_callback();
int main(int argc, char **argv)
{
GtkApplication *app;
guchar status;
app = gtk_application_new ("jp.vivacocoa.shape", 0 );
g_signal_connect (app, "activate",
G_CALLBACK( activate), NULL);
status = g_application_run (G_APPLICATION(app), argc, argv);
g_object_unref (app);
return status;
}
void activate(GtkApplication *app, gpointer data)
{
GtkWidget *window;
GtkWidget *darea;
/* Window */
window =
gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW(window), "Shape" );
gtk_window_set_default_size (GTK_WINDOW(window), 300, 200);
gtk_window_set_position (GTK_WINDOW(window), 1 );
/* Drawing area */
darea = gtk_drawing_area_new();
gtk_container_add (GTK_CONTAINER(window), darea );
/* Signal */
g_signal_connect (darea, "draw",
G_CALLBACK(draw_callback), NULL);
/* Show window */
gtk_widget_show_all (window);
}
void draw_callback(GtkWidget *darea, cairo_t *cr)
{
GdkRGBA color;
cairo_set_line_width(cr, 1.0);
cairo_save (cr);
/* Ellipse */
cairo_scale (cr, 1, 0.6);
cairo_arc (cr, 60, 60, 40, (0.0 * (M_PI/180.0)),
(360.0 * (M_PI/180.0)));
/* Rounded rectanble*/
cairo_restore (cr);
cairo_move_to (cr, 120, 25);
cairo_arc (cr, 130, 25, 10, (180 * (M_PI/180.0)),
(270.0 * (M_PI/180.0)));
cairo_line_to (cr, 190, 15);
cairo_arc (cr, 190, 25, 10, (270 * (M_PI/180.0)),
(360.0 * (M_PI/180.0)));
cairo_line_to (cr, 200, 60);
cairo_arc (cr, 190, 60, 10, (0 * (M_PI/180.0)),
(90.0 * (M_PI/180.0)));
cairo_line_to (cr, 130, 70);
cairo_arc (cr, 130, 60, 10, (90 * (M_PI/180.0)),
(180.0 * (M_PI/180.0)));
cairo_close_path (cr);
/* Arc */
cairo_move_to (cr, 220, 15);
cairo_line_to (cr, 280, 15);
cairo_arc (cr, 220, 15, 60, (0 * (M_PI/180.0)),
(90.0 * (M_PI/180.0)));
cairo_close_path (cr);
/* Circle */
cairo_move_to (cr, 60, 130);
cairo_arc (cr, 60, 130, 40, (0 * (M_PI/180.0)),
(360.0 * (M_PI/180.0)));
/* Square */
cairo_rectangle (cr, 120, 90, 80, 80);
/* Draw */
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);
/* String */
gdk_rgba_parse (&color, "#777" );
gdk_cairo_set_source_rgba (cr, &color );
/*
cairo_select_font_face (cr, "Sans",
CAIRO_FONT_SLANT_OBLIQUE,
CAIRO_FONT_WEIGHT_BOLD);
*/
cairo_set_font_size (cr, 30.0);
cairo_move_to (cr, 210, 170);
cairo_show_text (cr, "Hello!");
}
実行結果
コード説明
-
#include <math.h>。M_PI定数を使うために読み込んでいます。M_PIは
3.141593に設定されているみたいです。
-
cairo_save (cr);
カイロの現在の設定を保存しています。あとで、保存状態に戻すために使います。
- 以下、図形ごとに説明します。
- 楕円形(Ellipse)
-
cairo_scale関数で、描画した図形を、第1引数の横サイズ 1 に対して、
第2引数の縦サイズ 0.6 に変形します。
GTKには、楕円を作る関数がありません。この変形によって正円から楕円形を作ります。
-
-
cairo_arc関数で円弧を作ります。360度の円弧を作れば、正円になります。
- 第1引数にカイロ
- 第2引数に円弧が正円だった場合の中心の横位置
- 第3引数に円弧が正円だった場合の中心の縦位置
- 第4引数に円弧の半径
- 第5引数に円弧の開始地点をラディアン(radian)角度で指定します。
ラディアン角度は、角度 * (円周率 / 180.0)で取得できます。
右横(通常の90度)が、0度になりますので注意してください。
- 第6引数に円弧の終了地点をラディアン角度で指定します。
- 角丸四角形(Rounded rectangle)
-
cairo_restore関数でcairo_save関数で保存した状態に戻します。
carito_scale関数で設定した変形は、cairo_scale(cr, 1, 1.6666)などとと
逆の設定をしないと元に戻りません。
cairo_restore関数で元に戻すことが推奨されています。
-
GTKには、角丸四角形を描く関数がありません。代わりにcairo_arc関数と
cairo_line_to関数を使って描きます。
-
cairo_close_path関数はパスの開始地点と終了地点を結ぶ線を描いて
開いているパスを閉じます。
- 円弧(Arc)
GTKには円弧と中心地点を結んだ図形を描く関数もないみたいです。
ここでもcairo_line_to関数とcairo_arc関数を使って描きました。
- 正円(Circle)
GTKには円を描く関数もありません。360度の円弧を描けば正円になります。
- 正方形(Square)
cairo_rectangle関数で長方形を描けます。
- 第1引数にカイロ
- 第2引数に長方形の左上の横位置
- 第3引数に長方形の左上の縦位置
- 第4引数に長方形の横サイズ
- 第5引数に長方形の縦サイズ
-
横と縦のサイズが同じ長方形を描けば正方形になります。
- 文字列(String)
-
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: Jan. 09, 2020
Update: Jan. 09, 2020