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

 找回密码
 马上加入

QQ登录

只需一步,快速开始

查看: 3601|回复: 0

[转]Cairo 图形指南 (5) —— 形状与填充

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

    连续签到: 1 天

    [LV.4]偶尔看看III

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

    这一部分,讲述一些基本的以及较为高级的形状绘制及其纯色 (solid color)、图案 (pattern) 与渐变 (gradient) 填充方法。
    基本形状
    Cairo 提供了几个用于绘制基本形状的函数。
    #include <cairo.h>
    #include <gtk/gtk.h>
    #include <math.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, 0, 0);
            cairo_set_line_width (cr, 1);

            cairo_rectangle (cr, 20, 20, 120, 80);
            cairo_rectangle (cr, 180, 20, 80, 80);
            cairo_stroke_preserve (cr);
            cairo_set_source_rgb (cr, 1, 1, 1);
            cairo_fill (cr);

            cairo_set_source_rgb (cr, 0, 0, 0);
            cairo_arc (cr, 330, 60, 40, 0, 2 * M_PI);
            cairo_stroke_preserve (cr);
            cairo_set_source_rgb (cr, 1, 1, 1);
            cairo_fill (cr);

            cairo_set_source_rgb (cr, 0, 0, 0);
            cairo_arc (cr, 90, 160, 40, M_PI / 4, M_PI);
            cairo_close_path (cr);
            cairo_stroke_preserve (cr);
            cairo_set_source_rgb (cr, 1, 1, 1);
            cairo_fill (cr);

            cairo_set_source_rgb (cr, 0, 0, 0);
            cairo_translate (cr, 220, 180);
            cairo_scale (cr, 1, 0.7);
            cairo_arc (cr, 0, 0, 50, 0, 2 * M_PI);
            cairo_stroke_preserve (cr);
            cairo_set_source_rgb (cr, 1, 1, 1);
            cairo_fill (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), 390, 240);

            gtk_widget_show_all (window);

            gtk_main ();

            return 0;
    }


    这个示例,绘制了矩形、正方形、圆、圆弧和椭圆。
    下面对关键代码简单分析:

            cairo_rectangle (cr, 20, 20, 120, 80);
            cairo_rectangle (cr, 180, 20, 80, 80);


    绘制矩形与正方形。正方形在 cairo 中是矩形的一种特例。

            cairo_arc (cr, 330, 60, 40, 0, 2 * M_PI);

    画了一个圆,圆心为 (330, 60)px,半径为 40px。Cairo 所谓的圆,其实是起始角为 0 度,终止角为 360 度的弧线。

            cairo_scale (cr, 1, 0.7);
            cairo_arc (cr, 0, 0, 50, 0, 2 * M_PI);


    画椭圆的方法也与画圆类似,只是需要先设定长轴与短轴的比例,在本例中为 1:0.7。



    复杂的图形
    复杂的图形是由简单的图形拼凑出来的,譬如下面这个绘制圆角矩形的程序。
    #include <cairo.h>
    #include <gtk/gtk.h>
    #include <math.h>

    static void
    draw_round_rectangle (cairo_t * cr,
                          double x, double y,
                          double width, double height, double r)
    {
            cairo_move_to (cr, x + r, y);
            cairo_line_to (cr, x + width - r, y);

            cairo_move_to (cr, x + width, y + r);
            cairo_line_to (cr, x + width, y + height - r);

            cairo_move_to (cr, x + width - r, y + height);
            cairo_line_to (cr, x + r, y + height);

            cairo_move_to (cr, x, y + height - r);
            cairo_line_to (cr, x, y + r);

            cairo_arc (cr, x + r, y + r, r, M_PI, 3 * M_PI / 2.0);
            cairo_arc (cr, x + width - r, y + r, r, 3 * M_PI / 2, 2 * M_PI);
            cairo_arc (cr, x + width - r, y + height - r, r, 0, M_PI / 2);
            cairo_arc (cr, x + r, y + height - r, r, M_PI / 2, M_PI);
    }

    static gboolean
    on_expose_event (GtkWidget * widget,
                     GdkEventExpose * event, gpointer data)
    {
            cairo_t *cr;
            int width, height;
            
            double w, h, x, y, r;
            
            gtk_window_get_size (GTK_WINDOW (widget), &width, &height);
            
            x = width / 5.0;
            y = height / 5.0;
            w = 3 * width / 5.0;
            h = 3 * height / 5.0;
            r = h / 4.0;
            
            cr = gdk_cairo_create (widget->window);

            cairo_set_source_rgb (cr, 0.8, 0.4, 0);
            cairo_set_line_width (cr, 6);

            draw_round_rectangle (cr, x, y, w, h, r);
            cairo_stroke_preserve (cr);
            cairo_set_source_rgb (cr, 0.8, 0.8, 0.2);
            cairo_fill (cr);

            cairo_destroy (cr);

            g_print ("test\n");
            
            return FALSE;
    }

    static gboolean
    on_configure_event (GtkWidget * widget,
                  GdkEventConfigure * event, gpointer data)
    {
            gdk_window_invalidate_rect (widget->window,
                                        &widget->allocation,
                                        FALSE);
            return FALSE;
    }

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

            gtk_init (&argc, &argv);

            window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

            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(G_OBJECT(window), "configure-event",
                             G_CALLBACK(on_configure_event), NULL);

            
            gtk_window_set_position (GTK_WINDOW (window),
                                     GTK_WIN_POS_CENTER);
            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;
    }


    注:因为 "The cairo graphics tutorial" 在这一部分所提供的示例程序不具代表性,因此写了这个程序。
    该示例程序绘制了一个可跟随窗口尺寸进行缩放变化的圆角矩形。
    自定义的 draw_round_rectangle () 函数利用 Cairo 提供的基本图元函数,利用直线段与圆弧拼凑出圆角矩形。on_configure_event () 函数用于响应窗口尺寸变化事件,在其中调用 gdk_window_invalidate_rect () 函数让窗口绘图区域失效,并产生窗口重绘制事件(即 expose 事件)。



    填充 (Fill)
    虽然上一篇已经讲述了一些有关填充的知识,但这里所讲述的内容是与形状相关的。填充可分为三种类型:纯色、图案、渐变。
    纯色 (Solid color)
    对象的颜色是采用红 (R)、绿 (G)、蓝 (B) 三原色描述的,Cairo 的 RGB 取值是从 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);

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


            cairo_set_source_rgb (cr, 0.5, 0.5, 1);
            cairo_rectangle (cr, 20, 20, 100, 100);
            cairo_fill (cr);

            cairo_set_source_rgb (cr, 0.6, 0.6, 0.6);
            cairo_rectangle (cr, 150, 20, 100, 100);
            cairo_fill (cr);

            cairo_set_source_rgb (cr, 0, 0.3, 0);
            cairo_rectangle (cr, 20, 140, 100, 100);
            cairo_fill (cr);

            cairo_set_source_rgb (cr, 1, 0, 0.5);
            cairo_rectangle (cr, 150, 140, 100, 100);
            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), 270, 260);
            gtk_window_set_title (GTK_WINDOW (window), "colors");

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

            gtk_main ();

            return 0;
    }


    该示例绘制了 4 个正方形,分别采用四种不同颜色进行填充。这个例子,由于很简单,就不再像原作者那样自作多情的分析了。



    图案 (Pattern)
    所谓图案填充,就是将图片填充到形状内部。
    #include <math.h>
    #include <cairo.h>
    #include <gtk/gtk.h>

    cairo_surface_t *surface1;
    cairo_surface_t *surface2;
    cairo_surface_t *surface3;
    cairo_surface_t *surface4;

    static void
    create_surfaces ()
    {
            surface1 = cairo_image_surface_create_from_png ("blueweb.png");
            surface2 = cairo_image_surface_create_from_png ("maple.png");
            surface3 = cairo_image_surface_create_from_png ("crack.png");
            surface4 =
                cairo_image_surface_create_from_png ("chocolate.png");
    }

    static void
    destroy_surfaces ()
    {
            g_print ("destroying surfaces");
            cairo_surface_destroy (surface1);
            cairo_surface_destroy (surface2);
            cairo_surface_destroy (surface3);
            cairo_surface_destroy (surface4);
    }

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

            cairo_pattern_t *pattern1;
            cairo_pattern_t *pattern2;
            cairo_pattern_t *pattern3;
            cairo_pattern_t *pattern4;

            cr = gdk_cairo_create (widget->window);

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


            pattern1 = cairo_pattern_create_for_surface (surface1);
            pattern2 = cairo_pattern_create_for_surface (surface2);
            pattern3 = cairo_pattern_create_for_surface (surface3);
            pattern4 = cairo_pattern_create_for_surface (surface4);


            cairo_set_source (cr, pattern1);
            cairo_pattern_set_extend (cairo_get_source (cr),
                                      CAIRO_EXTEND_REPEAT);
            cairo_rectangle (cr, 20, 20, 100, 100);
            cairo_fill (cr);

            cairo_set_source (cr, pattern2);
            cairo_pattern_set_extend (cairo_get_source (cr),
                                      CAIRO_EXTEND_REPEAT);
            cairo_arc (cr, 200, 70, 50, 0, 2 * M_PI);
            cairo_fill (cr);

            cairo_set_source (cr, pattern3);
            cairo_pattern_set_extend (cairo_get_source (cr),
                                      CAIRO_EXTEND_REPEAT);
            cairo_rectangle (cr, 20, 140, 100, 100);
            cairo_fill (cr);

            cairo_set_source (cr, pattern4);
            cairo_pattern_set_extend (cairo_get_source (cr),
                                      CAIRO_EXTEND_REPEAT);
            cairo_rectangle (cr, 150, 140, 100, 100);
            cairo_fill (cr);

            cairo_pattern_destroy (pattern1);
            cairo_pattern_destroy (pattern2);
            cairo_pattern_destroy (pattern3);
            cairo_pattern_destroy (pattern4);

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

            create_surfaces ();

            gtk_window_set_position (GTK_WINDOW (window),
                                     GTK_WIN_POS_CENTER);
            gtk_window_set_default_size (GTK_WINDOW (window), 270, 260);
            gtk_window_set_title (GTK_WINDOW (window), "patterns");

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

            gtk_main ();

            destroy_surfaces ();

            return 0;
    }


    该示例,载入 4 张图片,分别填充至三个矩形与一个圆形内部区域。所使用的 4 幅图,均采用 GIMP 制作。程序中,图片的外观 (surface) 实在 on_expose_event () 函数中创建的,这并不是很妥当,因为窗口每次被重绘时,都需要从硬盘中读取图片。

            pattern1 = cairo_pattern_create_for_surface (surface1);

    由图片外观创建一个图案。

            cairo_set_source (cr, pattern1);
            cairo_pattern_set_extend (cairo_get_source (cr),
                                      CAIRO_EXTEND_REPEAT);
            cairo_rectangle (cr, 20, 20, 100, 100);
            cairo_fill (cr);


    这里,绘制第一个矩形。cairo_set_source () 函数通知 Cairo 环境,让它使用一份图案作为源 (source)。图片所形成的图案或许并不适合于形状,当使用 cairo_pattern_set_extend () 函数讲图案填充模式设为 CAIRO_EXTEND_REPEAT 时,可以让图案像瓦片那样填充于形状内部。cairo_rectangle () 函数创建一个矩形路径,cairo_fill () 函数将已经准备好的图案填充到矩形路径所构成的封闭区域中。



    渐变 (Gradient)
    在计算机图形学中,渐变是形状由明到暗或者从一种颜色向另一种颜色的平滑过度。在 2D 绘图与渲染程序中,渐变通常被用于创造多彩的背景与一些特效,比如光影的仿真。
    #include <cairo.h>
    #include <gtk/gtk.h>

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

            cr = gdk_cairo_create (widget->window);

            pat1 = cairo_pattern_create_linear (0.0, 0.0, 350.0, 350.0);

            gdouble j;
            gint count = 1;
            for (j = 0.1; j < 1; j += 0.1) {
                    if ((count % 2)) {
                            cairo_pattern_add_color_stop_rgb (pat1, j, 0, 0,
                                                              0);
                    } else {
                            cairo_pattern_add_color_stop_rgb (pat1, j, 1, 0,
                                                              0);
                    }
                    count++;
            }

            cairo_rectangle (cr, 20, 20, 300, 100);
            cairo_set_source (cr, pat1);
            cairo_fill (cr);


            pat2 = cairo_pattern_create_linear (0.0, 0.0, 350.0, 0.0);

            gdouble i;
            count = 1;
            for (i = 0.05; i < 0.95; i += 0.025) {
                    if ((count % 2)) {
                            cairo_pattern_add_color_stop_rgb (pat2, i, 0, 0,
                                                              0);
                    } else {
                            cairo_pattern_add_color_stop_rgb (pat2, i, 0, 0,
                                                              1);
                    }
                    count++;
            }

            cairo_rectangle (cr, 20, 140, 300, 100);
            cairo_set_source (cr, pat2);
            cairo_fill (cr);


            pat3 = cairo_pattern_create_linear (20.0, 260.0, 20.0, 360.0);

            cairo_pattern_add_color_stop_rgb (pat3, 0.1, 0, 0, 0);
            cairo_pattern_add_color_stop_rgb (pat3, 0.5, 1, 1, 0);
            cairo_pattern_add_color_stop_rgb (pat3, 0.9, 0, 0, 0);

            cairo_rectangle (cr, 20, 260, 300, 100);
            cairo_set_source (cr, pat3);
            cairo_fill (cr);

            cairo_pattern_destroy (pat1);
            cairo_pattern_destroy (pat2);
            cairo_pattern_destroy (pat3);

            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), 340, 390);
            gtk_window_set_title (GTK_WINDOW (window), "gradients");

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

            gtk_main ();

            return 0;
    }


    在这一示例程序中,我们绘制了三个具有不同渐变风格的矩形。

            pat3 = cairo_pattern_create_linear (20.0, 260.0, 20.0, 360.0);

    这里,创建了一个线性渐变图案。参数设定了绘制渐变方向的直线,在示例中,它是一条竖线。

            cairo_pattern_add_color_stop_rgb (pat3, 0.1, 0, 0, 0);
            cairo_pattern_add_color_stop_rgb (pat3, 0.5, 1, 1, 0);
            cairo_pattern_add_color_stop_rgb (pat3, 0.9, 0, 0, 0);


    定义了渐变图案的断点。在示例中,渐变图案表现为黑色与黄色的过渡。通过添加两个黑色断点和一个黄色断点,就可以构成一个水平方向的渐变图案,颜色 的变化方向则是沿竖直方向。渐变图案从矩形的上端至下端,开始是黑色,到 1/10 宽度时,黑色便停止了,然后就是由黑色向黄色的渐变渲染;到达矩形中部时,黄色达到饱和状态。黄色断点会在 9/10 宽度处终止,最后的 1/10 又是黑色。



    原文地址:http://liyanrui.is-programmer.com/2009/3/18/cairo-tutorial-05.7742.html

      
    *滑块验证:
    您需要登录后才可以回帖 登录 | 马上加入

    本版积分规则

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

    我要啦免费统计

    GMT+8, 2024-3-28 17:01 , Processed in 0.037539 second(s), 8 queries , Redis On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

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