这一部分讲述如何绘制一些简单的图元,包括直线、填充与笔画操作、虚线、线端(Cap)与线的交合等图形的绘制方法。 直线段 直线段是非常基础的矢量图形对象。画一条直线段,需要调用两个函数:cairo_move_to()
函数,用于设置线段起点;cairo_line_to() 用于设定线段终点。 #include #include 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 1; i++ ) {
for ( j = 0; j -1; j++ ) {
cairo_move_to(cr, coordx, coordy);
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 1; i++ ) { for ( j = 0; j -1; j++ ) {
cairo_move_to(cr, coordx, coordy);
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 #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_line_width (cr, 9); cairo_set_source_rgb (cr, 0.69,
0.19, 0); cairo_arc (cr, width / 2, height / 2,
(width ) / 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
之所以引入这个头文件,是因为程序中使用了圆周率常量 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 ) / 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
#include 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 #include 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 #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.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 的矩形。
本文来自ChinaUnix博客,如果查看原文请点: |