Chinaunix首页 | 论坛 | 博客
  • 博客访问: 42283
  • 博文数量: 3
  • 博客积分: 321
  • 博客等级: 一等列兵
  • 技术积分: 40
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-25 14:20
文章分类
文章存档

2011年(2)

2010年(1)

我的朋友

分类:

2010-05-01 16:31:57

这是翻译的cairo官网上的一篇教程,第一次认真的翻译文章,很多术语都拿捏不准,很多词语也是斟酌再三,即使这样翻译的我还是不满意。但翻译完了激情也没了,只能如此。另外:原文的代码这儿没链接,大家可以到去查看。
欢迎转载,但请注明出处(nketc.cublog.cn,nketc@163.com
Cairo教程

这篇教程来自于Michael Urman的 . 原来的代码片段已经被翻译成C代码,文字仅做了必要的修改.

Cairo是一个强大的2D图形库。本文档会向你介绍cairo如何工作和cairo的诸多函数,你会使用它们创建想象中的图像体验.

以便能在自己的计算机上继续下面的步骤,你需要这些东西:

  1. Cairo自身。你需要库和开发用的(头)文件。如果还没有准备好这些,请参考 

  2. 一个C编译器. 这篇  中有一个关于如何把代码编译为希望的程序的最小示例。

    如何你想在阅读过程中想查看这篇教程中包含的代码片段,可以点击本教程中的相关图片, 就会获得一个包含期望的绘制代码的小c程序.

或者如果你愿意面对挑战,可以把例子翻译为自己喜欢的编程语言,这样就只需上面列出的cairo.

注意:这篇文档中提到的cairo_push_group() 和 cairo_pop_group(). 需要最小1.2.0版的cairo.

目录

Cairo绘制模型

为了解释cairo使用的操作,我们先深入cairo绘制模型。这涉及到仅几个概念, 他们会一次又一次的应用于不同的函数。首先我会介绍这些: , , , , 和 . 之后我会介绍一些动词,他们提供了操作名词的方式和创建你所希望的图形的方法.

名词

Cairo的名词有些抽象。我提供了一些描述这些名词之间关系的图表,来使这些概念具体化。前三个名词对应的就是这一节图中的三个层。第四个名词路径当谈到它时把其画在了中间层上表示。最后一个名词上下文在图上没有表示。

目标(Destination)

目标就是用来在上面绘制的  . 它可能是一个像素阵列,就和这篇教程中使用的一样,或者可能是一个SVG或PDF文件,或者其他别的东西。当你使用图形元素的时候它们就被包含在Surface上,使用它可以建立复杂的工作就像在画布上绘制一样

源(Source)

就是你工作是用的“涂料”。作为例子我把它显示为黑色,但是有有些半透明以显示它的下一层。(看右图)和真正的涂料不一样的是, 它不仅仅只是单一的颜色,可以是一个  或者之前创建的目标 . 还和真正的涂料不一样的是源(Source)可以包含有透明度的信息----Alpha通道.

蒙板(Mask)

蒙板最重要的一点是:它控制着把源的哪些地方应用到目标上。我把它显示为图中中间有一个洞的黄色的那个层,那个洞运行源通过。当你使用一个绘制动词的时候,就像使用源在目标上盖了一个章。凡是蒙板允许的地方源就被复制到目标。蒙板不运行的地方对目标没有任何影响.

路径(Path)

路径在某种程度上是介于蒙板和上下文之间的一个东西。我把它显示为蒙板层上的绿色的线。它由路径相关的动词控制,可以被绘制动词使用.

上下文(Context)

上下文记录着可以影响动词的一切事物。它记录着一个源,一个目标,一个蒙板。同时它还记录着像线宽和及其样式、字体及其大小等一些辅助变量。最重要的是上下文会记录路径,路径最终会被绘制动词转换成蒙板.

在使用cairo绘制前需要创建上下文。上下文信息被保存在cairo的中心数据类型 cairo_t. 创建一个上下文时必须和一个特定的Surface关联,比如,图像surface,如果你想创建一个png图片文件的话。Surface也有一个数据类型: cairo_surface_t. 可以像下面这样初始化cairo上下文:

cairo_surface_t *surface;
cairo_t *cr;

surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 120, 120);
cr = cairo_create (surface);

在这个例子中cairo上下文和一个维度是120 x 120 图像surface相关联,其颜色深度是32比特可以 保存RGB颜色值和Alpha信息. 可以创建特定于许多cairo后端的surface, 请参考  获取详细信息.

动词

在一个程序中使用cairo的原因使要使用它进行图形绘制. 在cairo内部使用的一个基本的绘制操作是: 首先源和蒙板随意放在目标上. 然后这些层叠加在一起,最后凡是蒙板允许的地方源被绘制到目标上. 在这个程度上,下面的五个绘制动词,或操作,是相似的. 其区别在于构造蒙板的方式.

描边(Stroke)

 操作就是一个虚拟画笔沿着特定的路径(path)描边. 画笔所过之处使得源(Source)通过一个空心或实心的路径线蒙板(mask)传输到目标, 路径线取决于画笔的 , , 和 .

cairo_set_line_width (cr, 0.1);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_rectangle (cr, 0.25, 0.25, 0.5, 0.5);
cairo_stroke (cr);

填充(Fill)

 不会像着色书本的线条一样使用路径, 而是使源通过边界为该路径的中空的蒙板. 对于复杂的多个路径 (有多个闭合子路径的路径—就像炸圈饼— 或自身相交的路径) 填充操作受 的影响. 要注意的是当描边(stroking)一个路径的时候使得源通过路径两边各半个线宽被传送(到目标), 而填充(filling)一个路径仅填充到边缘,不会超出.

cairo_set_source_rgb (cr, 0, 0, 0);
cairo_rectangle (cr, 0.25, 0.25, 0.5, 0.5);
cairo_fill (cr);

显示文本 / 字形

 操作使用文本来形成蒙板. 把 cairo_show_text()当做是使用 创建路径 然后使用  来传送(源到目标)应该比较容易理解一下. 如果你有大量文本要处理要知道 cairo_show_text() 缓存字形更为有效.

cairo_text_extents_t te;
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
cairo_select_font_face (cr, "Georgia",
    CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size (cr, 1.2);
cairo_text_extents (cr, "a", &te);
cairo_move_to (cr, 0.5 - te.width / 2 - te.x_bearing,
    0.5 - te.height / 2 - te.y_bearing);
cairo_show_text (cr, "a");

喷刷(Paint)

 操作使用的是能传送整个源到目标的蒙板. 一些人认为这是一个无穷大的蒙板,然而也有人认为根本就没有蒙板; 但是结果是一样的. 和此相关的操作  同样传送整个源到目标, 但是只把颜色按照提供的百分比传送.

cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
cairo_paint_with_alpha (cr, 0.5);

蒙板(Mask)

 和  操作使得传送按照另外的一个源图案或surface的透明度/不透明度进行。该图案或surface不透明的位置对应的当前源的地方被传送到目标。透明位置处对于的地方什么也不会传送。

cairo_pattern_t *linpat, *radpat;
linpat = cairo_pattern_create_linear (0, 0, 1, 1);
cairo_pattern_add_color_stop_rgb (linpat, 0, 0, 0.3, 0.8);
cairo_pattern_add_color_stop_rgb (linpat, 1, 0, 0.8, 0.3);

radpat = cairo_pattern_create_radial (0.5, 0.5, 0.25, 0.5, 0.5, 0.75);
cairo_pattern_add_color_stop_rgba (radpat, 0, 0, 0, 0, 1);
cairo_pattern_add_color_stop_rgba (radpat, 0.5, 0, 0, 0, 0);

cairo_set_source (cr, linpat);
cairo_mask (cr, radpat);

使用cairo绘制

为了创建你所期望的图像,要先为每一个绘制动作准备好  for .如要使用  或  首先要有一个路径. 要使用 必须在其插入点安放文本. 使用  需要另一个源  或 . 而使用任何操作,包括 , 需要一个主源(primary source)(指的应该是目标吧).

准备和选择一个源

在cairo中共有三中主要类型的源: 颜色(colors), 渐变(gradients), 和 图像(images).其中颜色是最简单的; 它们对真个源使用统一的色调和透明度. 可以用  和  把其选入(上下文),而不需要任何准备操作. 使用 cairo_set_source_rgb (cr, r, g, b) 和使用cairo_set_source_rgba (cr, r, g, b, 1.0)是等效的, 它们设置源使用完全不透明的颜色.

cairo_set_source_rgb (cr, 0, 0, 0);
cairo_move_to (cr, 0, 0);
cairo_line_to (cr, 1, 1);
cairo_move_to (cr, 1, 0);
cairo_line_to (cr, 0, 1);
cairo_set_line_width (cr, 0.2);
cairo_stroke (cr);

cairo_rectangle (cr, 0, 0, 0.5, 0.5);
cairo_set_source_rgba (cr, 1, 0, 0, 0.80);
cairo_fill (cr);

cairo_rectangle (cr, 0, 0.5, 0.5, 0.5);
cairo_set_source_rgba (cr, 0, 1, 0, 0.60);
cairo_fill (cr);

cairo_rectangle (cr, 0.5, 0, 0.5, 0.5);
cairo_set_source_rgba (cr, 0, 0, 1, 0.40);
cairo_fill (cr);

渐变是通过设置一对起始和结束参考点以及它们之间的一些列“栓点(stops)”来描述逐渐过渡的颜色.  由两个点来构造,通过它们的平行线定义了开始和结束位置.  也是由两个点来构造, 只是每个点都有关联的圆周半径,开始和结束位置就是由分别以它们为圆心的圆周定义. 栓点(Stops)使用  和 被加入到渐变上。 这两个函数使用的颜色值和 cairo_set_source_rgb*()一样, 同时一个偏移量指定了栓点在参考点之间的位置. 相邻栓点(stops)间颜色被平均分布在这个空间,形成平滑的混合。最后参考点之外的地方的行为由 来控制.

int i, j;
cairo_pattern_t *radpat, *linpat;

radpat = cairo_pattern_create_radial (0.25, 0.25, 0.1,  0.5, 0.5, 0.5);
cairo_pattern_add_color_stop_rgb (radpat, 0,  1.0, 0.8, 0.8);
cairo_pattern_add_color_stop_rgb (radpat, 1,  0.9, 0.0, 0.0);

for (i=1; i<10; i++)
    for (j=1; j<10; j++)
        cairo_rectangle (cr, i/10.0 - 0.04, j/10.0 - 0.04, 0.08, 0.08);
cairo_set_source (cr, radpat);
cairo_fill (cr);

linpat = cairo_pattern_create_linear (0.25, 0.35, 0.75, 0.65);
cairo_pattern_add_color_stop_rgba (linpat, 0.00,  1, 1, 1, 0);
cairo_pattern_add_color_stop_rgba (linpat, 0.25,  0, 1, 0, 0.5);
cairo_pattern_add_color_stop_rgba (linpat, 0.50,  1, 1, 1, 0);
cairo_pattern_add_color_stop_rgba (linpat, 0.75,  0, 0, 1, 0.5);
cairo_pattern_add_color_stop_rgba (linpat, 1.00,  1, 1, 1, 0);

cairo_rectangle (cr, 0.0, 0.0, 1, 1);
cairo_set_source (cr, linpat);
cairo_fill (cr);

Images include both surfaces loaded from existing files with cairo_image_surface_create_from_png() and surfaces created from within cairo as an earlier destination. As of cairo 1.2, the easiest way to make and use an earlier destination as a source is with and either  or . Use cairo_pop_group_to_source() to use it just until you select a new source, andcairo_pop_group() when you want to save it so you can select it over and over again with .

创建路径

cairo总是有一个活动的路径. 如果调用 就会使用设定的线型设置来画这个路径. 如果调用  就会填充路径的内部区域. 但是常常不是这样, 路径是空的, 这两个函数调用都不会改变目标. 为什么路径常常是空的? 原因之一是, 一开始就是空的; 但是更重要的是每次  或之后路径就被清空,以便接下来创建新的路径.

如果想使用相同的路径去做多种事情要怎么办?比如要画一个黑边的红色矩形, 你可能需要使用红色的源 去填充这个矩形路径, 然后给这个路径描一个黑边. 一个矩形路径比较容易的创建多次, 但如果是一大堆路径就比较复杂了.

cairo有其动词操作的备用版本,很容易的支持路径重用. 和一般的操作绘制的东西没有什么不同, 但是备用版不会重置路径. 对于描边, 除了 还有 ; 对于填,  和 一道. 甚至设置剪裁也有保留路径的变体. 除了选择保留路径之外,成对的操作是一样的.

移动(Moving)

创建路径时cairo使用connect-the-dots的方式。开始在点1,画一条线到点3,接着画到3,等等。 当开启一个路径或需要一个新的子路径时,你希望开始时像点1一样:没有别的东西连接到它。使用 可以达到这一目的. 这个函数设置当前参考点并且使之前的路径不会连接到它。这里还有一个使用相对坐标的变体, , 此函数从当前参考点一个特定的距离出设置一个新的参考点.设定好第一个参考点后,使用其他的路径操作,这些操作更新参考点同时以某种方式连接到参考点。

cairo_move_to (cr, 0.25, 0.25);

直线(Straight Lines)

无论使用绝对坐标  (从参考点扩展路径到该点), 还是相对坐标  (从参考点以指定的距离和方向扩展路径), 路径连接都是一条直线. 新的参考点就是直线的另一端点.

cairo_line_to (cr, 0.5, 0.375);
cairo_rel_line_to (cr, 0.25, -0.125);

弧形(Arcs)

弧形是圆周的一部分. 和直线不一样传递给弧形函数的点不出现在路径上. 是圆心包含了弧形路径的信息. 圆周上的开始和结束位置必须指定, 这些点或者顺时针连接  或者逆时针方向连接 . 如果前一个参考点不足这个弧线上, 一个从其开始的直线就被连接到弧线的开始处. 参考点被更新为弧线的终点. 弧形函数只有绝对坐标的版本.

cairo_arc (cr, 0.5, 0.5, 0.25 * sqrt(2), -0.25 * M_PI, 0.25 * M_PI);

曲线(Curves)

cairo中的曲线是三次Bézier样条曲线. 它由当前参考点开始按照另外两个点(不通过这两个点)的方向平滑的延伸到第三个指定的点. 和直线一样 有绝对坐标版 (cairo_curve_to()) 和相对坐标版 (cairo_rel_curve_to()) . 要注意的是相对坐标版是所有的点都是相对于前一个参考点,而不是每个点相对于曲线的前一个控制点.

cairo_rel_curve_to (cr, -0.25, -0.125, -0.25, 0.125, -0.5, 0);

闭合路径(Close the path)

Cairo能够画一条到当前子路经开始处的直线来闭合一个路径. 这条直线可以用于多边形的最后一条边, 但是对于基于曲线的形状不是非常有用. 闭合路径和开放路径有着 根本的不同: 它是一个连续的没有开始和结束位置的路径. 闭合路径没有线头(line caps),因为没有地方来放它.

cairo_close_path (cr);

文本(Text)

使用 文本最终能够被转化为 路径. 从文本创建的路径和其他路径一样,支持描边(stroke)或填充(fill)操作. 文本路径被锚定放置在当前参考点, 所以在文本转换为路径前使用 设置你期望的位置. 不过,如果你要处理大量文本这种方法有性能问题; 如有可能应当使用这个动词,而不是  和 .

认识文本(Understanding Text)

为了有效的使用文本,你需要知道它的来龙去脉。函数  和  可以让你得到这些信息. 由于这个图表太小而不好分辨,我建议你下载生成这个图表的 把其尺寸增加到600. 这幅图展示了参考点(图中的红点),暗示的下一个参考点(图中的蓝点), 最小包围框(bounding box)(蓝色虚线); 相对位移(bearing displacement) (蓝色实心线); 以及字体高度(height),上间距(ascent), 基线(baseline), 和下间距线(descent lines) (绿色虚线)这些概念之间的关系.

参考点总是在基线(baseline)上. 下间距线(descent line)在它下边,反映了字体中所有字符的大致边界. 然而,它是为了指示对齐的技术上的选择,而不是真实的边界。上面的上间距线(ascent line)也是这样. 再上面是 height line,它是上下两个基线(baseline)之间的推荐距离。所有这三个距离是从基线(baseline)开始计算的 ,不管其方向都应该是正值.

方位是参考点到最小包围框(bounding box)左上角的位移.x的位移通常是0或者小的正数,但对于象上面显示的字符j ,x可以是负数。y的位移总是负数.宽度和高度描述的是最小包围框(bounding box)的大小. 步进宽度(advance)指示了下一个字符的参考点. 要注意的是,如果方位位移是负值或者宽度步进(adance)小于建议的宽度,一些字符序列的最小包围框(bounding box)可能会重叠.

除了位置坐标信息,还需要指定字体的风格(face)(译注:通常是字体名),样式(style)(译注:是否斜体等), 和大小(size). 风格和样式可以同时使用 来设定, 大小使用 指定. 如果需要更精细的控制, 可以使用获取一个, 并调整之,接下来用 来设定.

使用变换(Working with Transforms)

变换有三者主要用法. 首先它们允许你设置容易思考和容易工作的坐标系统,还有任意尺寸的输出. 第二,它们允许你定制工作在点(0,0)或其附近的辅助函数,并且能应用到输出图形的任意位置. 第三,它们能让图形变形,比如把一个圆弧变成椭圆弧等. 变换是在两个坐标系统之间建立联系的方法. 设备空间坐标和具体的surface联系在一起,并且不能改变。用户空间坐标默认是和设备坐标相符的,但那是基于 上面的几点原因,用户空间坐标可以被改变. 这些辅助函数 cairo_user_to_device() 和 cairo_user_to_device_distance() 告诉你对于用户坐标的位置或者距离,对应的设备坐标是什么. 同样地cairo_device_to_user() 和 cairo_device_to_user_distance() 告诉你对于设备坐标的位置和距离,对应的用户坐标是什么. 请留意,通过非距离变量设置位置和通过距离变量设置相对移动和距离的情况.

我就是利用上面的方法来绘制的这篇文档中的图表. 无论是画120 x 120 还是 600 x 600(大小的图形) , 我都使用  得到一个 1.0 x 1.0 的工作区. 沿着正确的位置放置结果, 就像在中讨论的一样, 我使用的是 . 我在之上使用来构造一个任意的变形,就得到了你看到的层叠层的透视效果图.

要理解变换,要从细节到整体来学习(read them bottom to top), 要在实际中多使用它们. 搞明白创建了那些变换,来回思考变换的过程及其逆过程. 比如,如果我要把一个 1.0 x 1.0 的工作区映射为一个在 120 x 120 像素的surface上中间位置处 100 x 100 大小的区域,可以有三中方法做到:

  1. cairo_translate (cr, 10, 10); cairo_scale (cr, 100, 100);
  2. cairo_scale (cr, 100, 100); cairo_translate (cr, 0.1, 0.1);
  3. cairo_matrix_t mat; cairo_matrix_init (&mat, 100, 0, 0, 100, 10, 10); cairo_transform (cr, &mat);

应该使用第一种方法,相关原因是它的可读性最好; 当需要使用主要函数没有提供的控制是可以使用第三种方法.

在有变换的情况下要留意线形的绘制. 即使在缩放因子(scale factor)为1的情况下设置了线宽,该线宽总是在用户坐标空间中的值,这不因缩放 (scale)而改变. 当在缩放方式下操作时,线宽被乘以缩放因子. 要指定一条线的像素宽度,可以使用 cairo_device_to_user_distance() 把 (1, 1) 设备空间距离, 转为,举个例子,(0.01, 0.01)的用户空间距离. 请注意,如果处理图形变形变换,不一定有一个统一方式指定线宽.

下一步做什么(Where to Go Next)

这儿总结一下这篇教程:它没有覆盖cairo的所有函数,所以对于某些高级的不常用的特性,你应当到别的地方找找. 示例图表的源代码(, ) 使用了几个没有说明的技巧,分析它们是学习cairo的一个不错的开始. 在cairographics.org上的其他指向其他不同的方向. 对于 任何事物来说, 在理解一个工具的规则和能够使用它之间都有一个不小的困难. 本文档的最后一节提供了一些箴言帮你克服这个困难.

提示和技巧(Tips and Tricks)

通过前几节的学习,你应该对cairo的操作有了一个比较牢固的理解. 在这一节,我会提供几个我发现的非常有用或不是那么显而易见的代码片段. 我也还只是一个cairo新手,所有可能有更好的方法来完成这些事。如果你发现了更好的方法,或发现了比较酷的做其他别的事情的方法,可以告诉我,很可能我会把它们加入到这些提示中.

关于线宽(Line Width)

当在一个统一的比例变换下工作,不能够正好使用像素表示线的宽度. 然而,在cairo_device_to_user_distance()的帮助下很容易进行转换(假设像素宽度是1):

double ux=1, uy=1;
cairo_device_to_user_distance (cr, &ux, &uy);
if (ux < uy)
    ux = uy;
cairo_set_line_width (cr, ux);

当在变形比例方式下工作时,你可能希望线在设备空间下仍然有统一的宽度. 这样你就需要在描边(stroke)路径前,恢复为一个统一的比例. 在这幅图形中左边的弧形是在变形方式下被描绘的,而右边的是在统一比例下描绘的.

cairo_set_line_width (cr, 0.1);

cairo_save (cr);
cairo_scale (cr, 0.5, 1);
cairo_arc (cr, 0.5, 0.5, 0.40, 0, 2 * M_PI);
cairo_stroke (cr);

cairo_translate (cr, 1, 0);
cairo_arc (cr, 0.5, 0.5, 0.40, 0, 2 * M_PI);
cairo_restore (cr);
cairo_stroke (cr);

关于文本对齐(Text Alignment)

当你尝试让文本中的字符在不同的地方一个一个的居中对齐时,要确定使用哪种方式使其居中。例如下面的代码,它实际上是使单个字符 独立地居中,导致当不同大小的字符在一起时出现难看的效果. (和大多数例子不一样的是, 我在这儿使用的是 26 x 1 的工作区.)

cairo_text_extents_t te;
char alphabet[] = "AbCdEfGhIjKlMnOpQrStUvWxYz";
char letter[2];

for (i=0; i < strlen(alphabet); i++) {
    *letter = '\0';
    strncat (letter, alphabet + i, 1);

    cairo_text_extents (cr, letter, &te);
    cairo_move_to (cr, i + 0.5 - te.x_bearing - te.width / 2,
            0.5 - te.y_bearing - te.height / 2);
    cairo_show_text (cr, letter);
}

相反,垂直居中必须立足于一般的字体大小(而不是单个字符大小),从而是基线(baseline)稳定。注意现在位置的控制依赖于 字体本身提供的量度(metrics),这样的量度从字体到字体不一定是相同的.

cairo_font_extents_t fe;
cairo_text_extents_t te;
char alphabet[] = "AbCdEfGhIjKlMnOpQrStUvWxYz";
char letter[2];

cairo_font_extents (cr, &fe);
for (i=0; i < strlen(alphabet); i++) {
    *letter = '\0';
    strncat (letter, alphabet + i, 1);

    cairo_text_extents (cr, letter, &te);
    cairo_move_to (cr, i + 0.5 - te.x_bearing - te.width / 2,
            0.5 - fe.descent + fe.height / 2);
    cairo_show_text (cr, letter);
}
阅读(9580) | 评论(2) | 转发(0) |
0

上一篇:没有了

下一篇:使用xmpp库loudmouth连接google talk服务器

给主人留下些什么吧!~~

chinaunix网友2010-06-04 23:59:01

狂顶

chinaunix网友2010-05-27 12:59:35

非常感谢,希望有更多的文章出来,顶