本节中,我们会绘制一些基本的图元。我们会绘制简单的线,填充图形,描边,还会涉及到点画线,线的端点形状,线的连接等。
线:
线是一种最基本的几何体。我们可以通过调用两个函数实现的线的绘制。通过调用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 < count; i ++ ) { for ( j = 0; j < count; j ++ ) { cairo_move_to ( cr, coordx[i], coordy[i] ); cairo_line_to ( cr, coordx[j], coordy[j] ); } } printf ( "draw line/n" ); 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; count ++; printf ( "x = %f, y = %f/n", event->x, event->y ); } if ( event->button == 3 ) { gtk_widget_queue_draw ( widget ); printf ( "middle button/n" ); } 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; }
在这个例子里,我们随机在窗口上单击左键。每个点击的点会记录到一个数组里面。当我们点击右键时,每个点就会连接到其它的所有点上。这样,我们就创建了几个相交的物体。再次点击右键,会清空窗体,然后可以开始另外一个物体的绘制。
cairo_set_source_rgb( cr, 0, 0, 0 ); cairo_set_line_width ( cr, 0.5 );
这些线段会用黑色,0.5个像素进行绘制。
int i, j; for ( i = 0; i < count; i ++ ) { for ( j = 0; j < count; j ++ ) { cairo_move_to ( cr, coordx[i], coordy[i] ); cairo_line_to ( cr, coordx[j], coordy[j] ); } }
连接一个点到所有的其它点。
cairo_stroke( cr );
调用cairo_stroke()绘制线条。
g_signal_connect ( window, "button-press-event", G_CALLBACK ( clicked ), NULL );
设置鼠标点击的回调函数
if ( event->button == 1 ) { coordx[count] = event->x; coordy[count] = event->y; count ++; }
在回调函数里面,我们要进行判断现在点的是左键还是右键。如果是左键,就保存现在点击的位置的x,y值到数组里面。
if ( event->button == 3 ) { gtk_widget_queuq_draw ( widget ); }
如果是右键,就重画整个窗口。
填充和描边
stroke操作可以画一个形状的外边。填充操作可以填充形状的里面。
#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 < 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
这个头文件里定义了我们需要的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 < heigh ? width : height ) / 2 - 10, 0, 2 * M_PI ); cairo_stroke_preserve ( cr );
这里画的是外面的线。
cairo_sete_source_rgb ( cr, 0.3, 0.4, 0.6 ); cairo_fill(cr);
这里是画一个蓝色的实心圆。
点划线
每条线都可以用不同画线方式。它们定义了线的类型。点划模式主要是在调用cairo_stroke()函数时起作用。点划模式可以用cairo_sete_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, 2.0}; static int len1 = sizeof ( dashed1 ) / sizeof ( dashed1[0] ); static const double dashed2[] = {4.0, 1.0, 2.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, 30 ); cairo_line_to ( cr, 200, 30 ); cairo_stroke ( cr ); cairo_set_dash ( cr, dashed2, len2, 1 ); cairo_move_to ( cr, 40, 50 ); cairo_line_to ( cr, 200, 50 ); cairo_stroke ( cr ); cairo_set_dash ( cr, dashed3, 1, 0 ); cairo_move_to ( cr, 40, 70 ); cairo_line_to ( cr, 200, 70 ); 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 ), 250, 100 ); gtk_widget_show_all(window); gtk_main(); return 0; }
本例子里,我们用不同的点划模式,画了三条线段。
static const double dashed1[] = {4.0, 1.0};
这个点划方式是:画四个像素的线,然后是一个空点。
static int len1 = sizeof ( dashed1 ) / sizeof ( dashed1[0] );
得到这个数组的长度
cairo_set_dash(cr, dashed1, len1, 0 );
设置这个点划模式。
darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(window), darea);
这个例子里,我们创建了一个画图区域,而不是直接画在了窗口上面。
线的端点类型
线的端点是一个线段的终点。
有三种类型:
CAIRO_LINE_CAP_SQUARE
CAIRO_LINE_CAP_ROUND
CAIRO_LINE_CAP_BUTT
用CAIRO_LINE_CAP_SQUARE类型的线和CAIRO_LINE_CAP_BUTT类型的线,长度上会有区别。CAIRO_LINE_CAP_SQUARE类型的线会比CAIRO_LINE_CAP_BUTT类型的线在开始和结束处长width/2个像素。
#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, 30, 50 ); cairo_line_to ( cr, 150, 50 ); cairo_stroke( cr ); cairo_set_line_cap ( cr, CAIRO_LINE_CAP_ROUND ); cairo_move_to ( cr, 30, 90 ); cairo_line_to ( cr, 150, 90 ); cairo_stroke ( cr ); cairo_set_line_cap ( cr, CAIRO_LINE_CAP_SQUARE ); cairo_move_to ( cr, 30, 130 ); cairo_line_to ( cr, 150, 130 ); cairo_stroke ( cr ); cairo_set_line_width ( cr, 1.5 ); cairo_move_to ( cr, 30, 40 ); cairo_line_to ( cr, 30, 140 ); cairo_stroke ( cr ); cairo_move_to ( cr, 150, 40 ); cairo_line_to ( cr, 150, 140 ); cairo_stroke ( cr ); cairo_move_to ( cr, 155, 40 ); cairo_line_to ( cr, 155, 140 ); 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, 200 ); gtk_widget_show_all(window); gtk_main(); return 0; }
本例子中,我们画了三个不同端点类型的线条。它们很生动的显示了三种线条的区别。
cairo_set_line_width(cr, 10);
设置线宽为10个像素。
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND) cairo_move_to( cr, 30, 90 ); cairo_line_to ( cr, 150, 90 ); cairo_stroke(cr);
画了一个CAIRO_LINE_CAP_ROUND类型的线条。
cairo_set_line_width(cr, 1.5 ); cairo_move_to ( cr, 30, 40 ); cairo_line_to ( cr, 30, 140 ); cairo_stroke( cr );
这是用来比较三种类型端点线条不同的三条竖线中的一条。
线条的连接方式也有三种类型:
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);
这段代码,我们画了一个连接方式为CAIRO_LINE_JOIN_MITER的矩形。线宽为14px。
原始地址:http://zetcode.com/tutorials/cairographicstutorial/basicdrawing/