Cairo 图形指南 (4) —— 基本绘图

这一部分讲述如何绘制一些简单的图元,包括直线、填充与笔画操作、虚线、线端(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,  000 );
        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 )400300 )
        gtk_widget_set_app_paintable (window,  TRUE );
        
        gtk_widget_show_all (window );
        
        gtk_main ( );
        
         return  0;
}

该示例会创建一个支持鼠标交互绘制直线段的 GTK+ 窗口。在窗口中使用鼠标左键随便点几下,每一次点击时,光标位置的坐标都会被记入长度为 100 的数组;然后点击鼠标右键,所有由鼠标左键点击所得到的点会被彼此连接形成直线段;在窗口中再次点击鼠标右键时,会对窗口绘图区域进行清除。

下面对该示例程序代码进行分析:


        cairo_set_source_rgb (cr,  000 );
        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.690.190 );
        cairo_arc  (cr, width /  2, height /  2,
                    (width < height ? width : height ) /  2 -  100,
                    2 * M_PI );
        cairo_stroke_preserve  (cr );

        cairo_set_source_rgb  (cr,  0.30.40.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 )200150 );

        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.690.190 );
        cairo_arc  (cr, width /  2, height /  2,
                    (width < height ? width : height ) /  2 -  100,
                    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.30.40.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,  0001 );

         static  const  double dashed1 [ ] =  {  4.01.0  };
         static  int len1 =  sizeof  (dashed1 ) /  sizeof  (dashed1 [ 0 ] );

         static  const  double dashed2 [ ] =  {  4.010.04.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,  4060 );
        cairo_line_to  (cr,  36060 );
        cairo_stroke  (cr );

        cairo_set_dash  (cr, dashed2, len2,  10 );

        cairo_move_to  (cr,  40120 );
        cairo_line_to  (cr,  360120 );
        cairo_stroke  (cr );

        cairo_set_dash  (cr, dashed3,  10 );

        cairo_move_to  (cr,  40180 );
        cairo_line_to  (cr,  360180 );
        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 )400300 );

        gtk_widget_show_all  (window );

        gtk_main  ( );

         return  0;
}

该示例演示了三种虚线模式的设置及绘制。

下面分析一下关键代码。


         static  const  double dashed1 [ ] =  {  4.01.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,  0001 );
        cairo_set_line_width  (cr,  10 );

        cairo_set_line_cap  (cr, CAIRO_LINE_CAP_BUTT );
        cairo_move_to  (cr,  4060 );
        cairo_line_to  (cr,  36060 );
        cairo_stroke  (cr );

        cairo_set_line_cap  (cr, CAIRO_LINE_CAP_ROUND );
        cairo_move_to  (cr,  40150 );
        cairo_line_to  (cr,  360150 );
        cairo_stroke  (cr );

        cairo_set_line_cap  (cr, CAIRO_LINE_CAP_SQUARE );
        cairo_move_to  (cr,  40240 );
        cairo_line_to  (cr,  360240 );
        cairo_stroke  (cr );

        cairo_set_line_width  (cr,  1.5 );

        cairo_move_to  (cr,  4040 );
        cairo_line_to  (cr,  40260 );
        cairo_stroke  (cr );

        cairo_move_to  (cr,  36040 );
        cairo_line_to  (cr,  360260 );
        cairo_stroke  (cr );

        cairo_move_to  (cr,  36540 );
        cairo_line_to  (cr,  365260 );
        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,  40150 );
        cairo_line_to  (cr,  360150 );
        cairo_stroke  (cr );

 画了一条线帽为 CAIRO_LINE_CAP_ROUND 的直线段。


        cairo_move_to  (cr,  4040 );
        cairo_line_to  (cr,  40260 );
        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.100 );

        cairo_rectangle  (cr,  3030100100 );
        cairo_set_line_width  (cr,  14 );
        cairo_set_line_join  (cr, CAIRO_LINE_JOIN_MITER );
        cairo_stroke  (cr );

        cairo_rectangle  (cr,  16030100100 );
        cairo_set_line_width  (cr,  14 );
        cairo_set_line_join  (cr, CAIRO_LINE_JOIN_BEVEL );
        cairo_stroke  (cr );

        cairo_rectangle  (cr,  100160100100 );
        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 )300280 );

        gtk_widget_show_all  (window );

        gtk_main  ( );

         return  0;
}
 

该示例采用不同的交合类型绘制了三个矩形。

下面对关键代码进行简单分析:


        cairo_rectangle  (cr,  3030100100 );
        cairo_set_line_width  (cr,  14 );
        cairo_set_line_join  (cr, CAIRO_LINE_JOIN_MITER );
        cairo_stroke  (cr );

绘制了一个线宽为 14px,交合类型为 CAIRO_LINE_JOIN_MITER 的矩形。



  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要下载Cairo图形指南,首先需要找到可靠的来源。Cairo是一个跨平台的开源图形库,用于绘制2D向量图形。在进行下载前,可以先到Cairo官方网站(https://cairographics.org/)浏览相关信息。 在官方网站上找到"Downloads"或者"获取"的选项,点击进入下载页面。通常,下载页面会列出各种版本的Cairo图形指南可供选择。选择适合自己操作系统的版本,例如Windows、Linux或者macOS。 根据自己的操作系统,点击相应的下载链接,等待下载完成。一般来说,Cairo图形指南的下载文件以压缩包的形式提供,可以是zip文件、tar.gz文件等。如果下载的是压缩文件,需要先解压缩,可以使用系统自带的解压软件或者第三方解压工具。 解压缩完成后,就可以开始阅读Cairo图形指南了。Cairo图形指南通常以PDF、HTML或者文本格式提供,可以根据个人的喜好选择合适的格式进行阅读。可以使用电脑自带的PDF阅读器、文本编辑器等工具进行查看。 在阅读Cairo图形指南时,可以了解Cairo基本概念、使用方法和相关的示例代码。图形指南会提供详细的说明和实例,以帮助读者更好地理解和应用Cairo图形库。 总之,要下载Cairo图形指南,需要先访问Cairo官方网站,找到适合自己操作系统的版本,下载并解压缩文件,最后选择合适的格式进行阅读。通过学习Cairo图形指南,可以更好地利用Cairo开发出高质量的2D图形应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值