您的位置:首页 > 大数据 > 人工智能

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
又是黑色。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: