Cairo图形指南(5)
2009-09-22 16:20
477 查看
形状与填充
这一部分,讲述一些基本的以及较为高级的形状绘制及其纯色 (solid color)、图案 (pattern) 与渐变 (gradient) 填充方法。基本形状
Cairo 提供了几个用于绘制基本形状的函数。#include <cairo.h>
#include <gtk/gtk.h>
#include <math.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
, 0
, 0
)
;
cairo_set_line_width (
cr, 1
)
;
cairo_rectangle (
cr, 20
, 20
, 120
, 80
)
;
cairo_rectangle (
cr, 180
, 20
, 80
, 80
)
;
cairo_stroke_preserve (
cr)
;
cairo_set_source_rgb (
cr, 1
, 1
, 1
)
;
cairo_fill (
cr)
;
cairo_set_source_rgb (
cr, 0
, 0
, 0
)
;
cairo_arc (
cr, 330
, 60
, 40
, 0
, 2
* M_PI)
;
cairo_stroke_preserve (
cr)
;
cairo_set_source_rgb (
cr, 1
, 1
, 1
)
;
cairo_fill (
cr)
;
cairo_set_source_rgb (
cr, 0
, 0
, 0
)
;
cairo_arc (
cr, 90
, 160
, 40
, M_PI / 4
, M_PI)
;
cairo_close_path (
cr)
;
cairo_stroke_preserve (
cr)
;
cairo_set_source_rgb (
cr, 1
, 1
, 1
)
;
cairo_fill (
cr)
;
cairo_set_source_rgb (
cr, 0
, 0
, 0
)
;
cairo_translate (
cr, 220
, 180
)
;
cairo_scale (
cr, 1
, 0.7
)
;
cairo_arc (
cr, 0
, 0
, 50
, 0
, 2
* M_PI)
;
cairo_stroke_preserve (
cr)
;
cairo_set_source_rgb (
cr, 1
, 1
, 1
)
;
cairo_fill (
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)
, 390
, 240
)
;
gtk_widget_show_all (
window)
;
gtk_main (
)
;
return
0
;
}
这个示例,绘制了矩形、正方形、圆、圆弧和椭圆。
下面对关键代码简单分析:
cairo_rectangle (
cr, 20
, 20
, 120
, 80
)
;
cairo_rectangle (
cr, 180
, 20
, 80
, 80
)
;
绘制矩形与正方形。正方形在 cairo 中是矩形的一种特例。
cairo_arc (
cr, 330
, 60
, 40
, 0
, 2
* M_PI)
;
画了一个圆,圆心为 (330, 60)px,半径为 40px。Cairo 所谓的圆,其实是起始角为 0 度,终止角为 360 度的弧线。
cairo_scale (
cr, 1
, 0.7
)
;
cairo_arc (
cr, 0
, 0
, 50
, 0
, 2
* M_PI)
;
画椭圆的方法也与画圆类似,只是需要先设定长轴与短轴的比例,在本例中为 1:0.7。
复杂的图形
复杂的图形是由简单的图形拼凑出来的,譬如下面这个绘制圆角矩形的程序。#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>
static
void
draw_round_rectangle (
cairo_t * cr,
double
x, double
y,
double
width, double
height, double
r)
{
cairo_move_to (
cr, x + r, y)
;
cairo_line_to (
cr, x + width - r, y)
;
cairo_move_to (
cr, x + width, y + r)
;
cairo_line_to (
cr, x + width, y + height - r)
;
cairo_move_to (
cr, x + width - r, y + height)
;
cairo_line_to (
cr, x + r, y + height)
;
cairo_move_to (
cr, x, y + height - r)
;
cairo_line_to (
cr, x, y + r)
;
cairo_arc (
cr, x + r, y + r, r, M_PI, 3
* M_PI / 2.0
)
;
cairo_arc (
cr, x + width - r, y + r, r, 3
* M_PI / 2
, 2
* M_PI)
;
cairo_arc (
cr, x + width - r, y + height - r, r, 0
, M_PI / 2
)
;
cairo_arc (
cr, x + r, y + height - r, r, M_PI / 2
, M_PI)
;
}
static
gboolean
on_expose_event (
GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;
int
width, height;
double
w, h, x, y, r;
gtk_window_get_size (
GTK_WINDOW (
widget)
, &width, &height)
;
x = width / 5.0
;
y = height / 5.0
;
w = 3
* width / 5.0
;
h = 3
* height / 5.0
;
r = h / 4.0
;
cr = gdk_cairo_create (
widget->window)
;
cairo_set_source_rgb (
cr, 0.8
, 0.4
, 0
)
;
cairo_set_line_width (
cr, 6
)
;
draw_round_rectangle (
cr, x, y, w, h, r)
;
cairo_stroke_preserve (
cr)
;
cairo_set_source_rgb (
cr, 0.8
, 0.8
, 0.2
)
;
cairo_fill (
cr)
;
cairo_destroy (
cr)
;
g_print (
"test/n
"
)
;
return
FALSE
;
}
static
gboolean
on_configure_event (
GtkWidget * widget,
GdkEventConfigure * event, gpointer data)
{
gdk_window_invalidate_rect (
widget->window,
&widget->allocation,
FALSE
)
;
return
FALSE
;
}
int
main (
int
argc, char
*argv[
]
)
{
GtkWidget *window;
GtkWidget *darea;
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
)
;
g_signal_connect(
G_OBJECT(
window)
, "configure-event"
,
G_CALLBACK(
on_configure_event)
, NULL
)
;
gtk_window_set_position (
GTK_WINDOW (
window)
,
GTK_WIN_POS_CENTER)
;
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
;
}
注:因为 "The cairo graphics tutorial" 在这一部分所提供的示例程序不具代表性,因此写了这个程序。
该示例程序绘制了一个可跟随窗口尺寸进行缩放变化的圆角矩形。
自定义的 draw_round_rectangle () 函数利用 Cairo
提供的基本图元函数,利用直线段与圆弧拼凑出圆角矩形。on_configure_event () 函数用于响应窗口尺寸变化事件,在其中调用
gdk_window_invalidate_rect () 函数让窗口绘图区域失效,并产生窗口重绘制事件(即 expose 事件)。
填充 (Fill)
虽然上一篇已经讲述了一些有关填充的知识,但这里所讲述的内容是与形状相关的。填充可分为三种类型:纯色、图案、渐变。纯色 (Solid color)
对象的颜色是采用红 (R)、绿 (G)、蓝 (B) 三原色描述的,Cairo 的 RGB 取值是从 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)
;
int
width, height;
gtk_window_get_size (
GTK_WINDOW (
widget)
, &width, &height)
;
cairo_set_source_rgb (
cr, 0.5
, 0.5
, 1
)
;
cairo_rectangle (
cr, 20
, 20
, 100
, 100
)
;
cairo_fill (
cr)
;
cairo_set_source_rgb (
cr, 0.6
, 0.6
, 0.6
)
;
cairo_rectangle (
cr, 150
, 20
, 100
, 100
)
;
cairo_fill (
cr)
;
cairo_set_source_rgb (
cr, 0
, 0.3
, 0
)
;
cairo_rectangle (
cr, 20
, 140
, 100
, 100
)
;
cairo_fill (
cr)
;
cairo_set_source_rgb (
cr, 1
, 0
, 0.5
)
;
cairo_rectangle (
cr, 150
, 140
, 100
, 100
)
;
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)
, 270
, 260
)
;
gtk_window_set_title (
GTK_WINDOW (
window)
, "colors"
)
;
gtk_widget_set_app_paintable (
window, TRUE
)
;
gtk_widget_show_all (
window)
;
gtk_main (
)
;
return
0
;
}
该示例绘制了 4 个正方形,分别采用四种不同颜色进行填充。这个例子,由于很简单,就不再像原作者那样自作多情的分析了。
图案 (Pattern)
所谓图案填充,就是将图片填充到形状内部。#include <math.h>
#include <cairo.h>
#include <gtk/gtk.h>
cairo_surface_t *surface1;
cairo_surface_t *surface2;
cairo_surface_t *surface3;
cairo_surface_t *surface4;
static
void
create_surfaces (
)
{
surface1 = cairo_image_surface_create_from_png (
"blueweb.png"
)
;
surface2 = cairo_image_surface_create_from_png (
"maple.png"
)
;
surface3 = cairo_image_surface_create_from_png (
"crack.png"
)
;
surface4 =
cairo_image_surface_create_from_png (
"chocolate.png"
)
;
}
static
void
destroy_surfaces (
)
{
g_print (
"destroying surfaces"
)
;
cairo_surface_destroy (
surface1)
;
cairo_surface_destroy (
surface2)
;
cairo_surface_destroy (
surface3)
;
cairo_surface_destroy (
surface4)
;
}
static
gboolean
on_expose_event (
GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;
cairo_pattern_t *pattern1;
cairo_pattern_t *pattern2;
cairo_pattern_t *pattern3;
cairo_pattern_t *pattern4;
cr = gdk_cairo_create (
widget->window)
;
int
width, height;
gtk_window_get_size (
GTK_WINDOW (
widget)
, &width, &height)
;
pattern1 = cairo_pattern_create_for_surface (
surface1)
;
pattern2 = cairo_pattern_create_for_surface (
surface2)
;
pattern3 = cairo_pattern_create_for_surface (
surface3)
;
pattern4 = cairo_pattern_create_for_surface (
surface4)
;
cairo_set_source (
cr, pattern1)
;
cairo_pattern_set_extend (
cairo_get_source (
cr)
,
CAIRO_EXTEND_REPEAT)
;
cairo_rectangle (
cr, 20
, 20
, 100
, 100
)
;
cairo_fill (
cr)
;
cairo_set_source (
cr, pattern2)
;
cairo_pattern_set_extend (
cairo_get_source (
cr)
,
CAIRO_EXTEND_REPEAT)
;
cairo_arc (
cr, 200
, 70
, 50
, 0
, 2
* M_PI)
;
cairo_fill (
cr)
;
cairo_set_source (
cr, pattern3)
;
cairo_pattern_set_extend (
cairo_get_source (
cr)
,
CAIRO_EXTEND_REPEAT)
;
cairo_rectangle (
cr, 20
, 140
, 100
, 100
)
;
cairo_fill (
cr)
;
cairo_set_source (
cr, pattern4)
;
cairo_pattern_set_extend (
cairo_get_source (
cr)
,
CAIRO_EXTEND_REPEAT)
;
cairo_rectangle (
cr, 150
, 140
, 100
, 100
)
;
cairo_fill (
cr)
;
cairo_pattern_destroy (
pattern1)
;
cairo_pattern_destroy (
pattern2)
;
cairo_pattern_destroy (
pattern3)
;
cairo_pattern_destroy (
pattern4)
;
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
)
;
create_surfaces (
)
;
gtk_window_set_position (
GTK_WINDOW (
window)
,
GTK_WIN_POS_CENTER)
;
gtk_window_set_default_size (
GTK_WINDOW (
window)
, 270
, 260
)
;
gtk_window_set_title (
GTK_WINDOW (
window)
, "patterns"
)
;
gtk_widget_set_app_paintable (
window, TRUE
)
;
gtk_widget_show_all (
window)
;
gtk_main (
)
;
destroy_surfaces (
)
;
return
0
;
}
该示例,载入 4 张图片,分别填充至三个矩形与一个圆形内部区域。所使用的 4 幅图,均采用 GIMP 制作。程序中,图片的外观
(surface) 实在 on_expose_event () 函数中创建的,这并不是很妥当,因为窗口每次被重绘时,都需要从硬盘中读取图片。
pattern1 = cairo_pattern_create_for_surface (
surface1)
;
由图片外观创建一个图案。
cairo_set_source (
cr, pattern1)
;
cairo_pattern_set_extend (
cairo_get_source (
cr)
,
CAIRO_EXTEND_REPEAT)
;
cairo_rectangle (
cr, 20
, 20
, 100
, 100
)
;
cairo_fill (
cr)
;
这里,绘制第一个矩形。cairo_set_source () 函数通知 Cairo 环境,让它使用一份图案作为源
(source)。图片所形成的图案或许并不适合于形状,当使用 cairo_pattern_set_extend () 函数讲图案填充模式设为
CAIRO_EXTEND_REPEAT 时,可以让图案像瓦片那样填充于形状内部。cairo_rectangle ()
函数创建一个矩形路径,cairo_fill () 函数将已经准备好的图案填充到矩形路径所构成的封闭区域中。
渐变 (Gradient)
在计算机图形学中,渐变是形状由明到暗或者从一种颜色向另一种颜色的平滑过度。在 2D 绘图与渲染程序中,渐变通常被用于创造多彩的背景与一些特效,比如光影的仿真。#include <cairo.h>
#include <gtk/gtk.h>
static
gboolean
on_expose_event (
GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;
cairo_pattern_t *pat1;
cairo_pattern_t *pat2;
cairo_pattern_t *pat3;
cr = gdk_cairo_create (
widget->window)
;
pat1 = cairo_pattern_create_linear (
0.0
, 0.0
, 350.0
, 350.0
)
;
gdouble j;
gint count = 1
;
for
(
j = 0.1
; j < 1
; j += 0.1
)
{
if
(
(
count % 2
)
)
{
cairo_pattern_add_color_stop_rgb (
pat1, j, 0
, 0
,
0
)
;
}
else
{
cairo_pattern_add_color_stop_rgb (
pat1, j, 1
, 0
,
0
)
;
}
count++;
}
cairo_rectangle (
cr, 20
, 20
, 300
, 100
)
;
cairo_set_source (
cr, pat1)
;
cairo_fill (
cr)
;
pat2 = cairo_pattern_create_linear (
0.0
, 0.0
, 350.0
, 0.0
)
;
gdouble i;
count = 1
;
for
(
i = 0.05
; i < 0.95
; i += 0.025
)
{
if
(
(
count % 2
)
)
{
cairo_pattern_add_color_stop_rgb (
pat2, i, 0
, 0
,
0
)
;
}
else
{
cairo_pattern_add_color_stop_rgb (
pat2, i, 0
, 0
,
1
)
;
}
count++;
}
cairo_rectangle (
cr, 20
, 140
, 300
, 100
)
;
cairo_set_source (
cr, pat2)
;
cairo_fill (
cr)
;
pat3 = cairo_pattern_create_linear (
20.0
, 260.0
, 20.0
, 360.0
)
;
cairo_pattern_add_color_stop_rgb (
pat3, 0.1
, 0
, 0
, 0
)
;
cairo_pattern_add_color_stop_rgb (
pat3, 0.5
, 1
, 1
, 0
)
;
cairo_pattern_add_color_stop_rgb (
pat3, 0.9
, 0
, 0
, 0
)
;
cairo_rectangle (
cr, 20
, 260
, 300
, 100
)
;
cairo_set_source (
cr, pat3)
;
cairo_fill (
cr)
;
cairo_pattern_destroy (
pat1)
;
cairo_pattern_destroy (
pat2)
;
cairo_pattern_destroy (
pat3)
;
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)
, 340
, 390
)
;
gtk_window_set_title (
GTK_WINDOW (
window)
, "gradients"
)
;
gtk_widget_set_app_paintable (
window, TRUE
)
;
gtk_widget_show_all (
window)
;
gtk_main (
)
;
return
0
;
}
在这一示例程序中,我们绘制了三个具有不同渐变风格的矩形。
pat3 = cairo_pattern_create_linear (
20.0
, 260.0
, 20.0
, 360.0
)
;
这里,创建了一个线性渐变图案。参数设定了绘制渐变方向的直线,在示例中,它是一条竖线。
cairo_pattern_add_color_stop_rgb (
pat3, 0.1
, 0
, 0
, 0
)
;
cairo_pattern_add_color_stop_rgb (
pat3, 0.5
, 1
, 1
, 0
)
;
cairo_pattern_add_color_stop_rgb (
pat3, 0.9
, 0
, 0
, 0
)
;
定义了渐变图案的断点。在示例中,渐变图案表现为黑色与黄色的过渡。通过添加两个黑色断点和一个黄色断点,就可以构成一个水平方向的渐变图案,颜色
的变化方向则是沿竖直方向。渐变图案从矩形的上端至下端,开始是黑色,到 1/10
宽度时,黑色便停止了,然后就是由黑色向黄色的渐变渲染;到达矩形中部时,黄色达到饱和状态。黄色断点会在 9/10 宽度处终止,最后的 1/10
又是黑色。
相关文章推荐
- Cairo 图形指南 (1) ―― 简介
- Cairo 图形指南 (6) —— 透明
- Cairo 图形指南 (6) —— 透明
- Cairo图形指南(2)
- Cairo 图形指南 (1) —— 简介
- Cairo 图形指南(1) —— 基本绘图
- Cairo 图形指南 (3) —— 变换
- Cairo 图形指南 (2) —— Cairo 概念
- Cairo图形指南(7)--合成
- Cairo 图形指南 (7) —— 合成
- Cairo图形指南(3)
- Cairo 图形指南 (2) —— Cairo 概念
- Cairo 图形指南 (4) —— 裁剪与遮蔽
- Cairo 图形指南 (3) —— Cairo 后端
- Cairo 图形指南 (8) —— 裁剪与遮蔽
- Cairo图形指南(4)
- Cairo 图形指南 (3) —— Cairo 后端
- Cairo 图形指南 (2) —— 文本
- Cairo 图形指南 (5) —— 图像
- Cairo 图形指南 (9) —— 变换