GTK3   グラフィックス

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


カイロ

ライン

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

実行結果

コード説明

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

実行結果

コード説明
  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    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);
}
	

実行結果

コード説明

  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>       // 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!");
}
	

実行結果


コード説明

  1. #include <math.h>。M_PI定数を使うために読み込んでいます。M_PIは 3.141593に設定されているみたいです。
  2. cairo_save (cr); カイロの現在の設定を保存しています。あとで、保存状態に戻すために使います。
  3. 以下、図形ごとに説明します。
  4. 楕円形(Ellipse)
    • 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引数に円弧の終了地点をラディアン角度で指定します。
  5. 角丸四角形(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関数はパスの開始地点と終了地点を結ぶ線を描いて 開いているパスを閉じます。
  6. 円弧(Arc)
      GTKには円弧と中心地点を結んだ図形を描く関数もないみたいです。 ここでもcairo_line_to関数とcairo_arc関数を使って描きました。
  7. 正円(Circle)
      GTKには円を描く関数もありません。360度の円弧を描けば正円になります。
  8. 正方形(Square)
      cairo_rectangle関数で長方形を描けます。
      1. 第1引数にカイロ
      2. 第2引数に長方形の左上の横位置
      3. 第3引数に長方形の左上の縦位置
      4. 第4引数に長方形の横サイズ
      5. 第5引数に長方形の縦サイズ
    • 横と縦のサイズが同じ長方形を描けば正方形になります。
  9. 文字列(String)
    • cairo_set_font_size関数で第2引数の大きさにフォントのサイズを設定します。
    • cairo_show_text関数で第2引数の文字列を描きます。
    • cairo_select_font_face関数でフォントの種類を設定できるみたいですが、 私の場合は効果はありませんでした。
  10. cairo_stroke_preserve関数は今までに設定した図形の線を描画しますが、 図形のデータは残します。
    cairo_stroke関数では描画した後に図形のデータは破棄されます。
  11. cairo_fill関数で図形を塗ります。cairo_stroke_preserve関数で図形のデータ が残っている状態なのでこの関数で図形を塗ることができます。
    図形のデータを残す、cairo_fill_preserve関数もあります。



960 visits
Posted: Jan. 09, 2020
Update: Jan. 09, 2020

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