GTK+中文社区(gtk.awaysoft.com)

 找回密码
 马上加入

QQ登录

只需一步,快速开始

查看: 5077|回复: 3

[转]Cairo 图形指南 (4) —— 基本绘图

[复制链接]
  • TA的每日心情
    奋斗
    2021-11-19 13:15
  • 签到天数: 20 天

    连续签到: 1 天

    [LV.4]偶尔看看III

    发表于 2011-2-3 02:14:08 | 显示全部楼层 |阅读模式
       

    这一部分讲述如何绘制一些简单的图元,包括直线、填充与笔画操作、虚线、线端(Cap)与线的交合等图形的绘制方法。
    直线段
    直线段是非常基础的矢量图形对象。画一条直线段,需要调用两个函数:cairo_move_to() 函数,用于设置线段起点;cairo_line_to() 用于设定线段终点。
    #include <cairo.h>
    #include <gtk/gtk.h>

    double coordx[100];
    double coordy[100];

    int count = 0;

    static gboolean
    on_expose_event(GtkWidget *widget,
                    GdkEventExpose *event,
                    gpointer data)
    {
            cairo_t *cr;
            
            cr = gdk_cairo_create(widget->window);
            
            cairo_set_source_rgb(cr, 0, 0, 0);
            cairo_set_line_width (cr, 0.5);
            
            int i, j;
            for ( i = 0; i <= count - 1; i++ ) {
                    for ( j  = 0; j <= count -1; j++ ) {
                            cairo_move_to(cr, coordx[i], coordy[i]);
                            cairo_line_to(cr, coordx[j], coordy[j]);
                    }
            }
            
            count = 0;
            cairo_stroke(cr);
            cairo_destroy(cr);
            
            return FALSE;
    }

    gboolean clicked(GtkWidget *widget, GdkEventButton *event,
                     gpointer user_data)
    {
            if (event->button == 1) {
                    coordx[count] = event->x;
                    coordy[count++] = event->y;
            }
            
            if (event->button == 3) {
                    gtk_widget_queue_draw(widget);
            }
            
            return TRUE;
    }


    int
    main (int argc, char *argv[])
    {
            
            GtkWidget *window;
            
            gtk_init(&argc, &argv);
            
            window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
            
            gtk_widget_add_events (window, GDK_BUTTON_PRESS_MASK);
            
            g_signal_connect(window, "expose-event",
                             G_CALLBACK(on_expose_event), NULL);
            g_signal_connect(window, "destroy",
                             G_CALLBACK(gtk_main_quit), NULL);
            g_signal_connect(window, "button-press-event",
                             G_CALLBACK(clicked), NULL);
            
            gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
            gtk_window_set_title(GTK_WINDOW(window), "lines");
            gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
            gtk_widget_set_app_paintable(window, TRUE);
            
            gtk_widget_show_all(window);
            
            gtk_main();
            
            return 0;
    }


    该示例会创建一个支持鼠标交互绘制直线段的 GTK+ 窗口。在窗口中使用鼠标左键随便点几下,每一次点击时,光标位置的坐标都会被记入长度为 100 的数组;然后点击鼠标右键,所有由鼠标左键点击所得到的点会被彼此连接形成直线段;在窗口中再次点击鼠标右键时,会对窗口绘图区域进行清除。
    下面对该示例程序代码进行分析:

            cairo_set_source_rgb(cr, 0, 0, 0);
            cairo_set_line_width (cr, 0.5);


    设置颜色为黑色,线宽为 0.5pt 为参数,绘制直线段。

            int i, j;
            for ( i = 0; i <= count - 1; i++ ) {
                    for ( j  = 0; j <= count -1; j++ ) {
                            cairo_move_to(cr, coordx[i], coordy[i]);
                            cairo_line_to(cr, coordx[j], coordy[j]);
                    }
            }


    用 cairo_move_to() 和 cairo_line_to() 函数在 cr 中定义绘图路径 (path),连接 coordx[] 和 coordy[] 所记录的每个点。

            cairo_stroke(cr);

    cairo_stroke() 函数会将 cr 中的路径绘制出来。

            g_signal_connect(window, "button-press-event",
                             G_CALLBACK(clicked), NULL);


    设定 button-press-event 事件的回调函数为 clicked ()。

            if (event->button == 1) {
                    coordx[count] = event->x;
                    coordy[count++] = event->y;
            }


    在 clicked () 函数中,当鼠标左键点击事件发生时,讲光标所在位置的 x 和 y 坐标分别记入数组 coordx 和 coordy。

            if (event->button == 3) {
                    gtk_widget_queue_draw(widget);
            }


    在 clicked () 函数中,当鼠标右键单击时,调用 gtk_widget_queue_draw () 函数重绘窗口区域。



    描绘 (Stroke) 与填充 (Fill)
    描绘 (Stroke) 可以绘制形状的轮廓,填充 (Fill) 则用于向形状内部灌注颜色。
    #include <math.h>
    #include <cairo.h>
    #include <gtk/gtk.h>

    static gboolean
    on_expose_event (GtkWidget * widget,
                     GdkEventExpose * event, gpointer data)
    {
            cairo_t *cr;

            cr = gdk_cairo_create (widget->window);

            int width, height;
            gtk_window_get_size (GTK_WINDOW (widget), &width, &height);
            cairo_set_line_width (cr, 9);

            cairo_set_source_rgb (cr, 0.69, 0.19, 0);
            cairo_arc (cr, width / 2, height / 2,
                       (width < height ? width : height) / 2 - 10, 0,
                       2 * M_PI);
            cairo_stroke_preserve (cr);

            cairo_set_source_rgb (cr, 0.3, 0.4, 0.6);
            cairo_fill (cr);

            cairo_destroy (cr);

            return FALSE;
    }

    int
    main (int argc, char *argv[])
    {
            GtkWidget *window;

            gtk_init (&argc, &argv);

            window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

            g_signal_connect (G_OBJECT (window), "expose-event",
                              G_CALLBACK (on_expose_event), NULL);
            g_signal_connect (G_OBJECT (window), "destroy",
                              G_CALLBACK (gtk_main_quit), NULL);

            gtk_window_set_position (GTK_WINDOW (window),
                                     GTK_WIN_POS_CENTER);
            gtk_window_set_default_size (GTK_WINDOW (window), 200, 150);

            gtk_widget_set_app_paintable (window, TRUE);
            gtk_widget_show_all (window);

            gtk_main ();

            return 0;
    }


    这个示例绘制一个内部填充灰色的圆。
    下面对代码进行解析:

    #include <math.h>

    之所以引入这个头文件,是因为程序中使用了圆周率常量 M_PI。

            int width, height;
            gtk_window_get_size (GTK_WINDOW (widget), &width, &height);


    获取窗口的宽度与高度尺寸。程序中将使用这些值作为绘制圆形的参考尺寸,以实现窗口尺寸变化时,所绘制的圆的尺寸也会相应变化。

            cairo_set_source_rgb (cr, 0.69, 0.19, 0);
            cairo_arc (cr, width / 2, height / 2,
                       (width < height ? width : height) / 2 - 10, 0,
                       2 * M_PI);
            cairo_stroke_preserve (cr);


    描绘圆的轮廓。这里要注意一下 cairo_stroke_preserve () 函数与 cairo_stroke () 函数的区别(最好的办法是用后者替换一下前者,看看程序执行效果)。cairo_stroke_preserve () 函数会将它绘制的路径依然保存在 cairo 环境中,而 cairo_stroke () 所绘制的路径,在绘制完成后,就从 cairo的环境中清除了。

            cairo_set_source_rgb (cr, 0.3, 0.4, 0.6);
            cairo_fill (cr);


    对使用 cairo_stroke_preserve () 函数绘制的路径进行蓝色填充。



    虚线 (Dash)
    每条线都可以用不同的虚线笔 (dash pen) 来画。虚线模式是通过 cairo_set_dash () 函数来设定。模式类型通过一个数组来定义,数组中的值均为正数,它们用于设置虚线的虚部分与实部分。数组的长度与偏移量可以在程序中设定。如果数组的长度 为 0,虚线模式就是被禁止了,那所绘制的线是实线。如果数组长度为 1,则对应着虚实均匀分布的虚线模式。偏移量是用来设置在虚线的始端在一个虚线周期(包含一个实部单元和一个虚部单元)内的起始位置。
    #include <cairo.h>
    #include <gtk/gtk.h>

    static gboolean
    on_expose_event (GtkWidget * widget,
                     GdkEventExpose * event, gpointer data)
    {
            cairo_t *cr;

            cr = gdk_cairo_create (widget->window);

            cairo_set_source_rgba (cr, 0, 0, 0, 1);

            static const double dashed1[] = { 4.0, 1.0 };
            static int len1 = sizeof (dashed1) / sizeof (dashed1[0]);

            static const double dashed2[] = { 4.0, 10.0, 4.0 };
            static int len2 = sizeof (dashed2) / sizeof (dashed2[0]);

            static const double dashed3[] = { 1.0 };

            cairo_set_line_width (cr, 1.5);

            cairo_set_dash (cr, dashed1, len1, 0);

            cairo_move_to (cr, 40, 60);
            cairo_line_to (cr, 360, 60);
            cairo_stroke (cr);

            cairo_set_dash (cr, dashed2, len2, 10);

            cairo_move_to (cr, 40, 120);
            cairo_line_to (cr, 360, 120);
            cairo_stroke (cr);

            cairo_set_dash (cr, dashed3, 1, 0);

            cairo_move_to (cr, 40, 180);
            cairo_line_to (cr, 360, 180);
            cairo_stroke (cr);

            cairo_destroy (cr);

            return FALSE;
    }


    int
    main (int argc, char *argv[])
    {

            GtkWidget *window;
            GtkWidget *darea;

            gtk_init (&argc, &argv);

            window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

            darea = gtk_drawing_area_new ();
            gtk_container_add (GTK_CONTAINER (window), darea);

            g_signal_connect (darea, "expose-event",
                              G_CALLBACK (on_expose_event), NULL);
            g_signal_connect (window, "destroy",
                              G_CALLBACK (gtk_main_quit), NULL);

            gtk_window_set_position (GTK_WINDOW (window),
                                     GTK_WIN_POS_CENTER);
            gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);

            gtk_widget_show_all (window);

            gtk_main ();

            return 0;
    }


    该示例演示了三种虚线模式的设置及绘制。
    下面分析一下关键代码。

            static const double dashed1[] = { 4.0, 1.0 };

    设定第一条虚线的模式,它的实部是 4 个像素,虚部是 1 个像素。

            static int len1 = sizeof (dashed1) / sizeof (dashed1[0]);

    计算数组 dashed1 的长度。

            cairo_set_dash (cr, dashed1, len1, 0);

    设置虚线模式。

            darea = gtk_drawing_area_new ();
            gtk_container_add (GTK_CONTAINER (window), darea);


    这次,我们是在 drawing_area 部件上绘图,不再是窗口区域了。



    线帽 (Line caps)
    线帽是针对直线段的端点形状而言的,分为三种:
      [li]CAIRO_LINE_CAP_SQUARE[/li][li]CAIRO_LINE_CAP_ROUND[/li][li]CAIRO_LINE_CAP_BUTT[/li]
    对应形状如下图所示:

    同一条直线段,CAIRO_LINE_CAP_SQUARE 线帽与 CAIRO_LINE_CAP_BUTT 线帽会导致直线段长度有所差别,前者会比后者长一个线宽尺寸。
    #include <cairo.h>
    #include <gtk/gtk.h>

    static gboolean
    on_expose_event (GtkWidget * widget,
                     GdkEventExpose * event, gpointer data)
    {
            cairo_t *cr;

            cr = gdk_cairo_create (widget->window);

            cairo_set_source_rgba (cr, 0, 0, 0, 1);
            cairo_set_line_width (cr, 10);

            cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
            cairo_move_to (cr, 40, 60);
            cairo_line_to (cr, 360, 60);
            cairo_stroke (cr);

            cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
            cairo_move_to (cr, 40, 150);
            cairo_line_to (cr, 360, 150);
            cairo_stroke (cr);

            cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
            cairo_move_to (cr, 40, 240);
            cairo_line_to (cr, 360, 240);
            cairo_stroke (cr);

            cairo_set_line_width (cr, 1.5);

            cairo_move_to (cr, 40, 40);
            cairo_line_to (cr, 40, 260);
            cairo_stroke (cr);

            cairo_move_to (cr, 360, 40);
            cairo_line_to (cr, 360, 260);
            cairo_stroke (cr);

            cairo_move_to (cr, 365, 40);
            cairo_line_to (cr, 365, 260);
            cairo_stroke (cr);

            cairo_destroy (cr);

            return FALSE;
    }


    该示例绘制三条具有不同线帽的直线段,同时也展示了不同线帽对线的长度的影响。
    下面对关键代码进行简单分析:

            cairo_set_line_width (cr, 10);

    设置线的宽度为 10px。

            cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
            cairo_move_to (cr, 40, 150);
            cairo_line_to (cr, 360, 150);
            cairo_stroke (cr);


    画了一条线帽为 CAIRO_LINE_CAP_ROUND 的直线段。

            cairo_move_to (cr, 40, 40);
            cairo_line_to (cr, 40, 260);
            cairo_stroke (cr);


    这是三条竖线之一,用于表现线帽对线的长度的影响。



    线的交合 (Line joins)
    线的交合存在以下三种风格:
      [li]CAIRO_LINE_JOIN_MITER[/li][li]CAIRO_LINE_JOIN_BEVEL[/li][li]CAIRO_LINE_JOIN_ROUND[/li]
    对应形状如下图所示。

    #include <cairo.h>
    #include <gtk/gtk.h>

    static gboolean
    on_expose_event (GtkWidget * widget,
                     GdkEventExpose * event, gpointer data)
    {
            cairo_t *cr;

            cr = gdk_cairo_create (widget->window);

            cairo_set_source_rgb (cr, 0.1, 0, 0);

            cairo_rectangle (cr, 30, 30, 100, 100);
            cairo_set_line_width (cr, 14);
            cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
            cairo_stroke (cr);

            cairo_rectangle (cr, 160, 30, 100, 100);
            cairo_set_line_width (cr, 14);
            cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
            cairo_stroke (cr);

            cairo_rectangle (cr, 100, 160, 100, 100);
            cairo_set_line_width (cr, 14);
            cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
            cairo_stroke (cr);

            cairo_destroy (cr);

            return FALSE;
    }

    int
    main (int argc, char *argv[])
    {
            GtkWidget *window;
            GtkWidget *darea;

            gtk_init (&argc, &argv);

            window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

            darea = gtk_drawing_area_new ();
            gtk_container_add (GTK_CONTAINER (window), darea);

            g_signal_connect (darea, "expose-event",
                              G_CALLBACK (on_expose_event), NULL);
            g_signal_connect (window, "destroy",
                              G_CALLBACK (gtk_main_quit), NULL);

            gtk_window_set_position (GTK_WINDOW (window),
                                     GTK_WIN_POS_CENTER);
            gtk_window_set_default_size (GTK_WINDOW (window), 300, 280);

            gtk_widget_show_all (window);

            gtk_main ();

            return 0;
    }


    该示例采用不同的交合类型绘制了三个矩形。
    下面对关键代码进行简单分析:

            cairo_rectangle (cr, 30, 30, 100, 100);
            cairo_set_line_width (cr, 14);
            cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
            cairo_stroke (cr);


    绘制了一个线宽为 14px,交合类型为 CAIRO_LINE_JOIN_MITER 的矩形。




      
              原文地址:http://liyanrui.is-programmer.com/2009/3/18/cairo-tutorial-04.7729.html
    cd_dmeimoshen 该用户已被删除
    发表于 2013-4-23 09:17:44 | 显示全部楼层
    提示: 作者被禁止或删除 内容自动屏蔽
  • TA的每日心情
    开心
    2017-11-25 14:22
  • 签到天数: 77 天

    连续签到: 1 天

    [LV.6]常住居民II

    发表于 2016-2-19 09:10:35 | 显示全部楼层
    gtk3下好像不能运行。
  • TA的每日心情
    开心
    2016-4-26 10:20
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    发表于 2016-4-26 10:22:01 | 显示全部楼层

    RE: [转]Cairo 图形指南 (4) —— 基本绘图

    很好很强大的样子,学习了
    *滑块验证:
    您需要登录后才可以回帖 登录 | 马上加入

    本版积分规则

    申请友链|Archiver|小黑屋|手机版|GTK+中文社区 ( 粤ICP备13080851号 )

    我要啦免费统计

    GMT+8, 2024-4-20 10:11 , Processed in 0.063146 second(s), 7 queries , Redis On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表