cairo裁剪使用分析
程序员文章站
2022-04-19 17:16:18
...
工作中遇到一个调用cairo_clip之后,无法执行任何绘制的问题,在官方文档里面仅仅是对这一堆函数的简单介绍。因此自己啃代码研究无法绘制的具体原因。
首先贴出官方给出的一个简单例子:
cairo_arc (cr, 128.0, 128.0, 76.8, 0, 2 * M_PI);
cairo_clip (cr);
cairo_new_path (cr); /* current path is not
consumed by cairo_clip() */
cairo_rectangle (cr, 0, 0, 256, 256);
cairo_fill (cr);
一步一步分析:
1、cairo_arc
其实只是在cairo_t里面创建了一个path对象。
2、cairo_clip
在cairo_t里面根据前面的path创建了一个clip对象,此函数的核心代码如下:
cairo_clip_t *
_cairo_clip_intersect_path (cairo_clip_t *clip,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_clip_path_t *clip_path;
cairo_status_t status;
cairo_rectangle_int_t extents;
cairo_box_t box;
if (_cairo_clip_is_all_clipped (clip))
return clip;
/* catch the empty clip path */
if (_cairo_path_fixed_fill_is_empty (path))
return _cairo_clip_set_all_clipped (clip);
if (_cairo_path_fixed_is_box (path, &box)) {
if (antialias == CAIRO_ANTIALIAS_NONE) {
box.p1.x = _cairo_fixed_round_down (box.p1.x);
box.p1.y = _cairo_fixed_round_down (box.p1.y);
box.p2.x = _cairo_fixed_round_down (box.p2.x);
box.p2.y = _cairo_fixed_round_down (box.p2.y);
}
return _cairo_clip_intersect_box (clip, &box);
}
if (_cairo_path_fixed_fill_is_rectilinear (path))
return _cairo_clip_intersect_rectilinear_path (clip, path,
fill_rule, antialias);
_cairo_path_fixed_approximate_clip_extents (path, &extents);
if (extents.width == 0 || extents.height == 0)
return _cairo_clip_set_all_clipped (clip);
clip = _cairo_clip_intersect_rectangle (clip, &extents);
if (_cairo_clip_is_all_clipped (clip))
return clip;
clip_path = _cairo_clip_path_create (clip);
if (unlikely (clip_path == NULL))
return _cairo_clip_set_all_clipped (clip);
status = _cairo_path_fixed_init_copy (&clip_path->path, path);
if (unlikely (status))
return _cairo_clip_set_all_clipped (clip);
clip_path->fill_rule = fill_rule;
clip_path->tolerance = tolerance;
clip_path->antialias = antialias;
if (clip->region) {
cairo_region_destroy (clip->region);
clip->region = NULL;
}
clip->is_region = FALSE;
return clip;
}
_cairo_path_fixed_fill_is_empty函数主要是判断path是否为空,如果为空,则执行_cairo_clip_set_all_clipped 。
再来看看_cairo_clip_set_all_clipped 怎么写的:
static inline cairo_clip_t *
_cairo_clip_set_all_clipped (cairo_clip_t *clip)
{
_cairo_clip_destroy (clip);
return (cairo_clip_t *) &__cairo_clip_all;
}
所以,如果path为空,cairo会直接直接返回全局cairo_clip_t对象。因此,调用cairo_clip之前,必须给cairo一个非空的path对象。
当path存在后,接下来就是创建一个clip了。
- 当传入的path是通过cairo_rectangle创建的,这个范围就是一个简单的box,会执行_cairo_clip_intersect_box函数,并返回。
- 当传入的path是通过cairo_arc创建的,则会走后面的流程,_cairo_clip_intersect_rectangle里面所做的事情,其实本质上和_cairo_clip_intersect_box是一样的。然后接下来,调用_cairo_clip_path_create,为clip创建一个裁剪路径。最后调用_cairo_path_fixed_init_copy,使用cairo_arc创建的路径初始化裁剪路径。
3、cairo_rectangle
函数也是在cairo_t里面新增一个path对象。
4、cairo_fill
函数是cairo的实际绘制的函数。
cairo_status_t
_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
{
cairo_status_t status;
status = _cairo_gstate_get_pattern_status (gstate->source);
if (unlikely (status))
return status;
if (gstate->op == CAIRO_OPERATOR_DEST)
return CAIRO_STATUS_SUCCESS;
if (_cairo_clip_is_all_clipped (gstate->clip))
return CAIRO_STATUS_SUCCESS;
。。。。
}
在绘制时,同样会判断上下文中的clip对象是否是全局默认的clip对象,如果是,则直接返回,之后的绘制流程不会再执行。后面的绘制过程,因笔者水平有限,就不再分析。
总结:
调用cairo_clip之前,必须给一个非空的path,否则cairo_clip会直接使用全局clip对象,导致后面的绘制都不会执行。