GTK   グラフィックス

ホーム   C/C++チュートリアル


カイロ

ライン

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);
}
    

実行結果

コード説明

  1. gtk_drawing_area_new関数でドローイングエリアを作ります。引数はありません。
  2. g_signal_connect(da, "draw",、 ドローイングエリアが描画される時に発生するイベントは "draw" です。
  3. void draw_callback(GtkWidget *da, cairo_t *cr)、 "draw"イベントで呼び出されるイベントハンドラーには、
    呼び出したウィジェットだけではなく、カイロも自動的に渡されるみたいです。
  4. GdkRGBA color;、GTK3で色を格納するデータ型はGdkRGBAです。
  5. gdouble dashes[]= {50.0, 10.0, 10.0, 10.0};、 dashes配列は、破線を作るときに必要です。 それぞれの要素の値は、実線、間隔、実線、間隔の値になります。
  6. guchar num = sizeof(dashes) / sizeof(dashes[0]);は、 C言語で配列の要素数を取得する常套の式です。 sizeof関数で引数に指定した変数や要素が使用するメモリー量を取得できます。 配列全体のメモリー量 割る 配列の一つの要素のメモリー量で、 配列の要素数が分かります。配列の要素数も破線を作るときに必要です。
  7. cairo_move_to関数で描画するスタート地点を移動します。 第1引数にカイロを、第2引数にドローイングエリア内の横位置、 第3引数にドローイングエリア内の縦位置を指定します。
  8. cairo_line_to関数で線(line、ライン)を定義します。第1引数にカイロを、 第2引数に線のの終端の横位置を、第3引数に線の終端の縦位置を指定します。
  9. cairo_stroke関数で今までに設定してきた線を描きます。 引数にカイロを指定します。 カイロのデフォルトで色は黒、線の幅は2ピクセルに設定されています。
  10. gdk_rgba_parse関数で、第1引数のGdkRGBAのポインターに、 第2引数の文字列で表さられた色を設定します。
  11. gdk_cairo_set_source_rgba関数で、第1引数のカイロに、 第2引数のGdkRGBAポインターの色を設定します。
  12. cairo_set_dash関数で破線の設定をします。第1引数にカイロを、 第2引数には線の間隔を指定する配列を、第3引数に配列の要素数を、 第4引数にオフセット(offset、ズレ)を指定します。
    要素数を 0 にすると破線ではなく実線が描かれることになります。 オフセットを指定すると、 破線のパターンの指定された位置から始まる破線が描かれます。
  13. cairo_set_line_width関数で、線の幅を設定します。第1引数にカイロを、 第2引数に線の幅を指定します。
  14. 以上の、色、破線、線の幅などの設定は、次に変更されるまで維持されます。
  15. cairo_set_dash(cr, dashes, num, 25.0)で、 破線のパターンの25.0ピクセル目から始まるように指定しています。 オフセットを変更した場合は、
    前のオフセットからのオフセットになることに注意してください。
  16. cairo_set_dash(cr, dashes, 0, 25.0)関数の要素数を 0 に指定することによって破線の描画が中止され、実戦の描画に変更されます。
  17. cairo_set_line_cap関数で、線の始点と終点の形を指定できます。 第1引数にカイロを、第2引数に形のパターンの定数を指定します。 パターンの種類は次のとおりです。
    1. CAIRO_LINE_CAP_BUTT、デフォルトのパターンです。 何も指定しない時にはこのパターンになります。
    2. CAIRO_LINE_CAP_ROUND、線の端に半円が追加されます。 折れ線の場合に角が丸く埋まります。
    3. 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);
}
    

実行結果

コード説明
  1. #include <time.h>
    乱数を初期化するために現在時刻を使います。そのためにこのヘッダーが必要です。
  2. srand(time(NULL));
    乱数を初期化しています。手順は次のとおりです。
    • srand関数で乱数を初期化します。
    • 乱数は初期化しないと毎回同じ乱数を発生します。
    • 乱数を初期化する値として現在の時刻を使います。 現在の時刻を使うことで、毎回違う値で初期化されることになります。
    • time関数は 1970年1月1日から現在までの経過秒数を取得します。 引数には通常 NULL を指定します。
  3. gtk_widget_get_allocated_width関数で 引数のウィジェットの横サイズを取得します。
  4. gtk_widget_get_allocated_height関数で 引数のウィジェットの縦サイズを取得します。
  5. cairo_paint関数は引数のカイロを現在のカラーで塗りつぶします。
  6. rand() % width + 1;の式は次の意味になります。
    • rand関数は、0 から RAND_MAX 定数で定義されている値までの整数の乱数を発生します。
    • RAND_MAXは、macOSとLinuxでは2147483647(約21億)、 Windowsでは32767に設定されています。
    • % は整数の割り算の余りを取得する算術演算子です。
    • widthが500の場合、乱数を500で割った余りは 0 から 499 です。その値に 1 を足せば 1 から 500 になります。
  7. カイロにポイントを描画する関数が見つからなかったので、 線幅を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);
}
	

実行結果

コード説明

  1. cairo_rectangle関数で四角形を作ります。引数は次のとおりです。
    • 第1引数にカイロ
    • 第2引数に四角形の左上の横位置
    • 第3引数に四角形の左上の縦位置
    • 第4引数に四角形の横のサイズ
    • 第5引数に四角形の縦のサイズ
  2. GTKで色を名前で指定する方法を私はまだ見つけていません。
  3. 今のところ、gdk_rgba_parse関数を使って 色を表す文字列で指定する方法を見つけています。第1引数にカイロを、 第2引数に "#fff" か "rgb(255,255,255)" のどちらかの文字列を
    指定します。
  4. 代表的な、インクの三原色と光の三原色は、次の文字列になります。
    シアン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)"
  5. cairo_fill関数で図形に色を塗ります。引数にカイロを指定します。
  6. 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!"); 
    
}
	

実行結果


コード説明

  1. #include <math.h>。M_PI定数を使うために読み込んでいます。M_PIは 3.141593に設定されているみたいです。
  2. gtk_window_set_default_size (GTK_WINDOW(window), 430, 260); 今回はウィンドウのサイズを大きめにしています。
  3. cairo_save (cr); カイロの現在の設定を保存しています。あとで、保存状態に戻すために使います。
  4. 以下、図形ごとに説明します。
  5. 楕円形
    • cairo_scale関数で、描画した図形を、第1引数の横サイズ 1 に対して、 第2引数の縦サイズ 0.6 に変形します。
      GTKには、楕円を作る関数がありません。この変形によって正円から楕円形を作ります。
    • cairo_arc関数で円弧を作ります。360度の円弧を作れば、正円になります。
      1. 第1引数にカイロ
      2. 第2引数に円弧が正円だった場合の中心の横位置
      3. 第3引数に円弧が正円だった場合の中心の縦位置
      4. 第4引数に円弧の半径
      5. 第5引数に円弧の開始地点をラディアン(radian)角度で指定します。
        ラディアン角度は、角度 * (円周率 / 180.0)で取得できます。 右横(通常の90度)が、0度になりますので注意してください。
      6. 第6引数に円弧の終了地点をラディアン角度で指定します。
  6. 角丸四角形
    • cairo_restore関数でcairo_save関数で保存した状態に戻します。
      carito_scale関数で設定した変形は、cairo_scale(cr, 1, 1.6666)などとと 逆の設定をしないと元に戻りません。
      cairo_restore関数で元に戻すことが推奨されています。
    • GTKには、角丸四角形を描く関数がありません。代わりにcairo_arc関数と cairo_line_to関数を使って描きます。
    • cairo_close_path関数はパスの開始地点と終了地点を結ぶ線を描いて 開いているパスを閉じます。
  7. 円弧
      GTKには円弧と中心地点を結んだ図形を描く関数もないみたいです。 ここでもcairo_line_to関数とcairo_arc関数を使って描きました。
  8. 正円
      GTKには円を描く関数もありません。360度の円弧を描けば正円になります。
  9. 正方形
      cairo_rectangle関数で長方形を描けます。
      1. 第1引数にカイロ
      2. 第2引数に長方形の左上の横位置
      3. 第3引数に長方形の左上の縦位置
      4. 第4引数に長方形の横サイズ
      5. 第5引数に長方形の縦サイズ
    • 横と縦のサイズが同じ長方形を描けば正方形になります。
  10. 文字列
    • cairo_set_font_size関数で第2引数の大きさにフォントのサイズを設定します。
    • cairo_show_text関数で第2引数の文字列を描きます。
    • cairo_select_font_face関数でフォントの種類を設定できるみたいですが、 私の場合は効果はありませんでした。
  11. cairo_stroke_preserve関数は今までに設定した図形の線を描画しますが、 図形のデータは残します。
    cairo_stroke関数では描画した後に図形のデータは破棄されます。
  12. cairo_fill関数で図形を塗ります。cairo_stroke_preserve関数で図形のデータ が残っている状態なのでこの関数で図形を塗ることができます。
    図形のデータを残す、cairo_fill_preserve関数もあります。


7004 visits
Posted: Dec. 18, 2019
Update: Dec. 19, 2019

ホーム   C/C++チュートリアル   目次