[转载]Cairo 图形指南[转载]

Cairo 图形指南 (1) —— 简介

欢迎阅读 Cairo 绘图指南,这份指南会向你讲述 Cairo 二维矢量绘图库的基本知识以及一些高级问题。指南中的示例采用 C 语言实现,其中大部分使用了 GTK+ 库。

二维矢量图形

计算机图形可分为两类,矢量图形与光栅图形。光栅图形是将图像表示为像素点集。矢量图形则是使用一些几何图元(点、直线、曲线、多边形等)表示图像,这些图元是使用数学公式生成的。

这两类计算机图形表达方式各有所长短。相较于光栅图形,矢量图形存在以下优越性:

  • 图形文件更小
  • 可任意缩放
  • 平移、缩放、填充或旋转等图形变换操作对图形质量无影响

Cairo

Cairo 是用于绘制二维矢量图形的库,采用 C 语言实现,又被许多其它计算机语言所绑定,譬如 Python、PERL、C++、C#、Java。Cairo 是跨平台库,可运行于 Linux、BSD、OSX 等操作系统。

Cairo 支持多种后端 (backend):

  • X Window 系统
  • Win32 GDI
  • Mac OS X Quartz
  • PNG
  • PDF
  • PostScript
  • SVG

这些后端意味着可使用 Cairo 库在 Windows、Linux/BSD、OSX 等平台的窗口中绘图,也可以用于生成 PNG 图片、PDF/PostScript/SVG 文件。

与 Windows 操作系统的 GDI+ 以及 Mac OS 的 Quartz 2D 库相比,Cairo 是自由软件库。自 GTK+ 2.8 版本开始,Cairo 成为 GTK+ 库的一部分。

 

Cairo 图形指南 (2) —— Cairo 概念

本文讲述 Cairo 图形库中一些有用的定义/概念,理解它们可以帮助你更好的理解 Cairo 绘图模型。

环境 (Context)

使用 Cairo 绘图,必须要首先创建 Cairo 环境 (Context)。Cairo 环境保存着所有的图形状态参数,这些参数描述了图形的构成,譬如线条宽度、颜色、要绘制的外观 (Surface) 以及其它一些信息。Cairo 环境允许真正的绘图函数使用很少的一部分参数,以此提高接口的易用性。调用 gdk_cairo_create () 函数可为所绘制的图形创建一个 Cairo 环境。

cairo_t * cr;
cr = gdk_cairo_create (widget->window);

这两行代码创建了一个 Cairo 环境,并且这个 Cairo 环境是关联到 GdkDrawable 对象上的。cairo_t 结构体包含了当前渲染设备的状态,也包含了所绘制图形的坐标。从技术上来讲,cairo_t 就是所谓的 Cairo 环境。

Cairo 所有的绘图函数都要去操作 cairo_t 对象。一个 Cairo 环境可以被关联到一种特定的外观,譬如 pdf、svg、png、GdkDrawable 等。

GDK 没有对 Cairo API 进行封装,它只允许创建一个可基于 GdkDrawable 对象绘制图形的 Cairo 环境。有一些 GDK 函数可以将 GDK 的矩形或填充区域转换为 Cairo Path (路径),然后使用 Cairo 绘图与渲染。

路径 (Path)

一条 Path(路径)通常是由一条或多条首位相接的直线段构成的,也可以由直线段与曲线段构成。路径可分为 Open(开放)类型与 Closed(闭合)类型,前者的首尾端点不重合,后者的首尾端点重合。

在 Cairo 中,绘图要从一条空路径开始,首先定义一条路径,然后通过绘制/填充操作使之可见。要注意的是,每次调用 cairo_stroke () 或 cairo_fill () 函数之后,路径会被清空,不得不再定义新的路径。

一条路径可由一些子路径构成。

源 (Source)

源好比绘图中所使用的画笔/颜料,使用它来绘制/填充图形轮廓。有 4 种基本的源:color、gradient、pattern 与 image。

外观 (Surface)

Surface 就是要绘制图形的最终体现形式,譬如可使用 PDF 或 PostScript 外观实现文本内容的渲染,或者使用 Xlib、Win32 外观实现屏幕绘图。

Cairo 具体有那些外观类型,可参考其定义:

typedef enum _cairo_surface_type {
  CAIRO_SURFACE_TYPE_IMAGE,
  CAIRO_SURFACE_TYPE_PDF,
  CAIRO_SURFACE_TYPE_PS,
  CAIRO_SURFACE_TYPE_XLIB,
  CAIRO_SURFACE_TYPE_XCB,
  CAIRO_SURFACE_TYPE_GLITZ,
  CAIRO_SURFACE_TYPE_QUARTZ,
  CAIRO_SURFACE_TYPE_WIN32,
  CAIRO_SURFACE_TYPE_BEOS,
  CAIRO_SURFACE_TYPE_DIRECTFB,
  CAIRO_SURFACE_TYPE_SVG,
  CAIRO_SURFACE_TYPE_OS2
} cairo_surface_type_t;

蒙板 (Mask)

在源作用于外观之前,可对其实现过滤,蒙板 (mask) 即是过滤器。蒙板决定哪些源可被显示。蒙板不透明的部分允许复制源至外观,蒙板透明的部分则禁止复制源至外观。

图案 (Pattern)

图案表示被绘制到外观的源。在 Cairo 中,图案是一种可以读取的内容,可用作绘图操作的源或蒙板。图案可以是纯色模式、基于外观的模式以及渐变模式。

 

Cairo 图形指南 (3) —— Cairo 后端

Cairo 支持多种后端,本文基于几个示例讲述如何使用 Cairo 各种后端创建 PNG 图像、PDF 文件与 SVG 文件以及如何使用 Cairo 在 GTK 窗口中绘图。

1. PNG 图像

第一个示例 (example-1.c) 用于生成 PNG 图像。

#include <cairo.h>

int
main (int argc, char *argv[])
{
        cairo_surface_t *surface;
        cairo_t *cr;

        surface =
            cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 320, 48);
        cr = cairo_create (surface);

        cairo_set_source_rgb (cr, 0.627, 0, 0);
        cairo_select_font_face (cr, "Adobe Heiti Std",
                                CAIRO_FONT_SLANT_NORMAL,
                                CAIRO_FONT_WEIGHT_NORMAL);
        cairo_set_font_size (cr, 24.0);

        cairo_move_to (cr, 10.0, 34.0);
        cairo_show_text (cr, "我是中国人,我爱我的祖国。");

        cairo_surface_write_to_png (surface, "image.png");

        cairo_destroy (cr);
        cairo_surface_destroy (surface);

        return 0;
}

这个示例是一个很小的控制台程序,运行后可生成一份 PNG 图像文件。

 #include <cairo.h>

上述头文件声明了上面示例中调用的函数以及一些常量的定义。

 cairo_surface_t *surface;
 cairo_t *cr;

 

这两行代码声明了一个 Cairo 外观与一个 Cairo 环境。

 

 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 320, 48);
 cr = cairo_create(surface);

现在我们生成了 Cairo 外观与 Cairo 环境,所生成的外观是一份 320x48 px 的图像。

 

 cairo_set_source_rgb (cr, 0.627, 0, 0);

设置源的颜色为 darkred,就好比是选择了暗红色的颜料。

 

 cairo_select_font_face(cr, "Adobe Heiti Std", CAIRO_FONT_SLANT_NORMAL,
      CAIRO_FONT_WEIGHT_NORMAL);
 cairo_set_font_size(cr, 24.0);

选择字体类型并设置其尺寸。(注:可使用 "fc-list" 命令查看系统所安装字体)

 

 cairo_move_to(cr, 10.0, 34.0);
 cairo_show_text(cr, "我是中国人,我爱我的祖国。");

将“画笔”移动到图像区域的 (10.0, 34.0) 位置开始绘制文本。

 

 cairo_surface_write_to_png(surface, "image.png");

这个函数创建 PNG 图像。

 

 cairo_destroy(cr);
 cairo_surface_destroy(surface);

最后,回收所有 Cairo 环境与外观所占用的内存资源。

编译这个示例:

$ gcc -o example-1 `pkg-config --cflags --libs gtk+-2.0` example-1.c

生成的 PNG 图像如下图所示:

2. PDF 文件

在第二个示例 (example-2.c) 中,将使用 Cairo 生成一份 PDF 文件,其内容与第一个示例所生成的图像是相同的。

#include <cairo.h>
#include <cairo-pdf.h>

int
main (int argc, char *argv[])
{
        cairo_surface_t *surface;
        cairo_t *cr;

        surface = cairo_pdf_surface_create ("pdffile.pdf", 320, 48);
        cr = cairo_create (surface);

        cairo_set_source_rgb (cr, 0.627, 0, 0);
        cairo_select_font_face (cr, "Adobe Heiti Std",
                                CAIRO_FONT_SLANT_NORMAL,
                                CAIRO_FONT_WEIGHT_NORMAL);
        cairo_set_font_size (cr, 24.0);

        cairo_move_to (cr, 10.0, 34.0);
        cairo_show_text (cr, "我是中国人,我爱我的祖国。");

        cairo_show_page (cr);

        cairo_destroy (cr);
        cairo_surface_destroy (surface);

        return 0;
}

编译这个示例:

$ gcc -o example-2 `pkg-config --cflags --libs gtk+-2.0` example-2.c

生成的 PDF 文件,请使用 PDF 阅读器查看,Linux 用户可使用 Evince 或 KPDF。

 surface = cairo_pdf_surface_create ("pdffile.pdf", 320, 48);

要生成 pdf 文件,必须使用 cairo_pdf_surface () 函数创建一个 pdf 外观。pdf 文件的页面大小是以排版标准中的像素点尺寸为单位控制的。

 cairo_show_page(cr);

 

生成的 PDF 文档在 Evince 中显示效果如下图所示:

3. SVG 文件

第三个示例演示如何使用 Cairo SVG 后端生成一份简单的 SVG (Scalble Vector Graphics) 文件。SVG 技术近几年很热门。

#include <cairo.h>
#include <cairo-svg.h>

int
main (int argc, char *argv[])
{
        cairo_surface_t *surface;
        cairo_t *cr;

        surface = cairo_svg_surface_create ("svgfile.svg", 320, 48);
        cr = cairo_create (surface);

        cairo_set_source_rgb (cr, 0.627, 0, 0);
        cairo_select_font_face (cr, "Adobe Heiti Std",
                                CAIRO_FONT_SLANT_NORMAL,
                                CAIRO_FONT_WEIGHT_NORMAL);
        cairo_set_font_size (cr, 24.0);

        cairo_move_to (cr, 10.0, 34.0);
        cairo_show_text (cr, "我是中国人,我爱我的祖国。");

        cairo_destroy (cr);
        cairo_surface_destroy (surface);

        return 0;
}

编译这个示例:

$ gcc -o example-3 `pkg-config --cflags --libs gtk+-2.0` example-3.c

生成的 SVG 文件可以使用 Firefox、Opera、Inkscape 程序查看。

surface = cairo_svg_surface_create("svgfile.svg", 320, 48);

要生成一份 SVG 文件,必须使用 cairo_svg_surface_create () 函数创建一个 svg 外观。除此之外,其余代码的功用与上述示例类似。

本例生成的 SVG 文件,使用 Firefox 查看结果如下图所示:

4. GTK 窗口

在最后这个示例中,演示如何在 GTK 窗口中使用 Cairo 绘制图形。基于 GTK 后端的 Cairo 绘图模型将贯穿于本指南。

#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.627, 0, 0);
        cairo_select_font_face (cr, "Adobe Heiti Std", CAIRO_FONT_SLANT_NORMAL,
                                CAIRO_FONT_WEIGHT_NORMAL);
        cairo_set_font_size (cr, 24.0);

        cairo_move_to (cr, 10.0, 34.0);
        cairo_show_text (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 (window, "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), 320, 48);
        gtk_widget_set_app_paintable (window, TRUE);

        gtk_widget_show_all (window);

        gtk_main ();

        return 0;
}

这个示例程序运行后,会在屏幕中央跳出一个 GTK+ 窗口,上面绘制了一串文本,如下图所示:

 #include <cairo.h>
 #include <gtk/gtk.h>

首先要包含 cairo 与 gtk+ 库的头文件。

 g_signal_connect (window, "expose-event",
                          G_CALLBACK (on_expose_event), NULL);

当 GTK+窗口被重绘时,会发出 expose-event 信号,我们可将这一信号连接到 on_expose_event () 回调函数上。

gtk_widget_set_app_paintable (window, TRUE);

要在 GTK+ 窗口中绘制 Cairo 图形,可以使用 GtkDrawingArea widget 或者更为简单的 GtkWindow widget,本例选择 GtkWindow 。由 GtkWindow widget 对 expose-event 信号处理后,默认要重新绘制窗口背景,这会将我们在 on_expose_event () 函数中定义的 Cairo 图形覆盖掉,因此需要调用 gtk_widget_set_app_paintable () 函数通知 GTK+ 不要这么干。如果是在 GtkDrawingArea widget 中绘制 Cairo 图形,则可省区这一步。

cairo_t *cr;

 cr = gdk_cairo_create (widget->window);

Cairo 图形绘制工作是在 on_expose_event () 函数中进行的,在该函数中,我们为 GTK+ 系统创建了一个 Cairo 环境,并在该环境中绘制了一行文本。

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


 

http://liyanrui.is-programmer.com/2009/3/18/cairo-tutorial-04.7729.html

http://liyanrui.is-programmer.com/2009/3/19/cairo-tutorial-05.7742.html

http://liyanrui.is-programmer.com/2009/3/20/cairo-tutorial-06.7754.html

http://liyanrui.is-programmer.com/2009/3/20/cairo-tutorial-07.7760.html

http://liyanrui.is-programmer.com/2010/4/5/cairo-masking-and-clipping.16579.html

http://liyanrui.is-programmer.com/2010/4/6/cairo-transformation.16627.html

http://liyanrui.is-programmer.com/2010/4/7/cairo-text.16643.html

http://liyanrui.is-programmer.com/2010/4/9/cairo-image.16682.html

 

在cairo中使用OpenGL

这是我写一篇关于在OpenGL中使用cairo的教程的开端。本文档的一般观点是linux开发人员的观点。请随意添加您在其他平台上获得的其他用例(最好是源代码)的示例和经验。本文假设您熟悉cairo和OpenGL的api,这是Linux下的典型开发工具链和一些一般的图形编程。此外,我想指出,我不是一个母语为英语的人,不能保证这篇文章是没有错误的。

什么是OpenGL,它与cairo有什么关系?

OpenGL是一个低层抽象,用于向GPU提供渲染基元。基本元素是三角形和着色器。

Cairo是用于布局页面和其他视图的高级画布绘制模型。基本元素是独立于分辨率的路径和模式。Cairo可以使用OpenGL将画布模型转换为GPU原语,但画布模型通常无法有效地转换为GPU。

Cairo是一个更高层次的抽象,它既有优点也有缺点:

对于应用程序程序员来说,canvas api(例如cairo)使用起来要简单得多。你不需要担心GPU的限制,并且是独立于设备和显示的,使用一种更接近图形设计师的布局语言。

相反地,画布api的抽象使应用程序程序员不必考虑GPU的局限性以及他们在渲染画布模型时的糟糕程度。为了获得最大的性能,你需要在GPU和程序的能力范围内构建完全认识的UI。这是高度特定于设备和驱动程序的。

Cairo -易于使用,可移植到任何设备,可扩展到任何设备

Opengl -很难使用(必须编写自己的工具包),依赖于设备。可能会更快。

要在不使用Cairo的情况下使用OpenGL,您基本上必须创建自己的UI工具包,如果您希望获得Cairo提供的一般特性,那么该工具包最终看起来非常像Cairo,具有与原始OpenGL相似的优点和缺点。在最容易使用OpenGL的地方(例如混合多层),Cairo也很容易有效地使用OpenGL。

https://www.cairographics.org/OpenGL/

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值