Chinaunix首页 | 论坛 | 博客
  • 博客访问: 714086
  • 博文数量: 94
  • 博客积分: 2812
  • 博客等级: 少校
  • 技术积分: 1555
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-08 21:28
文章分类

全部博文(94)

文章存档

2012年(23)

2011年(39)

2010年(14)

2009年(18)

分类: C/C++

2010-05-06 08:35:01

这一部分,讲述一些基本的以及较为高级的形状绘制及其纯色 (solid color)、图案 (pattern) 与渐变 (gradient) 填充方法。

基本形状

Cairo 提供了几个用于绘制基本形状的函数。

#include
#include
#include

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,半径为 40pxCairo 所谓的圆,其实是起始角为 0 度,终止角为 360 度的弧线。


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

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



复杂的图形

复杂的图形是由简单的图形拼凑出来的,譬如下面这个绘制圆角矩形的程序。

#include
#include
#include

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
#include

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
#include
#include

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
#include

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 又是黑色。


 

阅读(2147) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~