天天看点

cairo裁剪使用分析一步一步分析:

工作中遇到一个调用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了。

  1. 当传入的path是通过cairo_rectangle创建的,这个范围就是一个简单的box,会执行_cairo_clip_intersect_box函数,并返回。
  2. 当传入的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对象,导致后面的绘制都不会执行。

继续阅读