现在的位置: 首页 > 综合 > 正文

Cairo图形指南(4)

2013年05月03日 ⁄ 综合 ⁄ 共 9705字 ⁄ 字号 评论关闭

基本绘图

这一部分讲述如何绘制一些简单的图元,包括直线、填充与笔画操作、虚线、线端(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)

线帽是针对直线段的端点形状而言的,分为三种:

  • CAIRO_LINE_CAP_SQUARE
  • CAIRO_LINE_CAP_ROUND
  • CAIRO_LINE_CAP_BUTT

对应形状如下图所示:

同一条直线段,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)

线的交合存在以下三种风格:

  • CAIRO_LINE_JOIN_MITER
  • CAIRO_LINE_JOIN_BEVEL
  • CAIRO_LINE_JOIN_ROUND

对应形状如下图所示。

#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 的矩形。


抱歉!评论已关闭.