mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-05 18:08:03 +02:00
Clip trapezoids that are partially (or wholly) outside the clip region.
It's quite simple to add a new _cairo_traps_limit call which installs a box into the cairo_traps_t structure. Then at the time of _cairo_traps_add we can discard any trapezoid that is wholly outside the box and also clip any trapezoid that is partially outside the box. We take advantage of this for both cairo_stroke and cairo_fill, (when cairo is computing the trapezoids in cairo-surface-fallback.c). Note that we explicitly do not do any clipping for cairo_stroke_extents, cairo_fill_extents, cairo_in_stroke, or cairo_in_fill which are defined to ignore clipping. As seen by the long-lines perf case, this fix successfully works around the bug in the X server where it creates overly large masks for partially-outside-the-destination-surface trapezoids: xlib-rgba long-lines-uncropped-100 545.84 -> 5.83: 93.09x speedup ██████████████████████████████████████████████ xlib-rgb long-lines-uncropped-100 554.74 -> 8.10: 69.04x speedup ██████████████████████████████████
This commit is contained in:
parent
2dcfb944b0
commit
8286b87416
3 changed files with 126 additions and 1 deletions
|
|
@ -804,9 +804,35 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface,
|
|||
{
|
||||
cairo_status_t status;
|
||||
cairo_traps_t traps;
|
||||
cairo_box_t box;
|
||||
cairo_rectangle_int16_t extents;
|
||||
|
||||
status = _cairo_surface_get_extents (surface, &extents);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (_cairo_operator_bounded_by_source (op)) {
|
||||
cairo_rectangle_int16_t source_extents;
|
||||
status = _cairo_pattern_get_extents (source, &source_extents);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
_cairo_rectangle_intersect (&extents, &source_extents);
|
||||
}
|
||||
|
||||
status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
box.p1.x = _cairo_fixed_from_int (extents.x);
|
||||
box.p1.y = _cairo_fixed_from_int (extents.y);
|
||||
box.p2.x = _cairo_fixed_from_int (extents.x + extents.width);
|
||||
box.p2.y = _cairo_fixed_from_int (extents.y + extents.height);
|
||||
|
||||
_cairo_traps_init (&traps);
|
||||
|
||||
_cairo_traps_limit (&traps, &box);
|
||||
|
||||
status = _cairo_path_fixed_stroke_to_traps (path,
|
||||
stroke_style,
|
||||
ctm, ctm_inverse,
|
||||
|
|
@ -840,9 +866,35 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface,
|
|||
{
|
||||
cairo_status_t status;
|
||||
cairo_traps_t traps;
|
||||
cairo_box_t box;
|
||||
cairo_rectangle_int16_t extents;
|
||||
|
||||
status = _cairo_surface_get_extents (surface, &extents);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (_cairo_operator_bounded_by_source (op)) {
|
||||
cairo_rectangle_int16_t source_extents;
|
||||
status = _cairo_pattern_get_extents (source, &source_extents);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
_cairo_rectangle_intersect (&extents, &source_extents);
|
||||
}
|
||||
|
||||
status = _cairo_clip_intersect_to_rectangle (surface->clip, &extents);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
box.p1.x = _cairo_fixed_from_int (extents.x);
|
||||
box.p1.y = _cairo_fixed_from_int (extents.y);
|
||||
box.p2.x = _cairo_fixed_from_int (extents.x + extents.width);
|
||||
box.p2.y = _cairo_fixed_from_int (extents.y + extents.height);
|
||||
|
||||
_cairo_traps_init (&traps);
|
||||
|
||||
_cairo_traps_limit (&traps, &box);
|
||||
|
||||
status = _cairo_path_fixed_fill_to_traps (path,
|
||||
fill_rule,
|
||||
tolerance,
|
||||
|
|
|
|||
|
|
@ -61,6 +61,17 @@ _cairo_traps_init (cairo_traps_t *traps)
|
|||
traps->traps = NULL;
|
||||
traps->extents.p1.x = traps->extents.p1.y = INT32_MAX;
|
||||
traps->extents.p2.x = traps->extents.p2.y = INT32_MIN;
|
||||
|
||||
traps->has_limits = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
_cairo_traps_limit (cairo_traps_t *traps,
|
||||
cairo_box_t *limits)
|
||||
{
|
||||
traps->has_limits = TRUE;
|
||||
|
||||
traps->limits = *limits;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -124,7 +135,62 @@ _cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bo
|
|||
if (traps->status)
|
||||
return;
|
||||
|
||||
if (top == bottom) {
|
||||
/* Note: With the goofy trapezoid specification, (where an
|
||||
* arbitrary two points on the lines can specified for the left
|
||||
* and right edges), these limit checks would not work in
|
||||
* general. For example, one can imagine a trapezoid entirely
|
||||
* within the limits, but with two points used to specify the left
|
||||
* edge entirely to the right of the limits. Fortunately, for our
|
||||
* purposes, cairo will never generate such a crazy
|
||||
* trapezoid. Instead, cairo always uses for its points the
|
||||
* extreme positions of the edge that are visible on at least some
|
||||
* trapezoid. With this constraint, it's impossible for both
|
||||
* points to be outside the limits while the relevant edge is
|
||||
* entirely inside the limits.
|
||||
*/
|
||||
if (traps->has_limits) {
|
||||
/* Trivially reject if trapezoid is entirely to the right or
|
||||
* to the left of the limits. */
|
||||
if (left->p1.x >= traps->limits.p2.x &&
|
||||
left->p2.x >= traps->limits.p2.x)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (right->p1.x <= traps->limits.p1.x &&
|
||||
right->p2.x <= traps->limits.p1.x)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise, clip the trapezoid to the limits. We only clip
|
||||
* where an edge is entirely outside the limits. If we wanted
|
||||
* to be more clever, we could handle cases where a trapezoid
|
||||
* edge intersects the edge of the limits, but that would
|
||||
* require slicing this trapezoid into multiple trapezoids,
|
||||
* and I'm not sure the effort would be worth it. */
|
||||
if (top < traps->limits.p1.y)
|
||||
top = traps->limits.p1.y;
|
||||
|
||||
if (bottom > traps->limits.p2.y)
|
||||
bottom = traps->limits.p2.y;
|
||||
|
||||
if (left->p1.x < traps->limits.p1.x &&
|
||||
left->p2.x < traps->limits.p1.x)
|
||||
{
|
||||
left->p1.x = traps->limits.p1.x;
|
||||
left->p2.x = traps->limits.p1.x;
|
||||
}
|
||||
|
||||
if (right->p1.x > traps->limits.p2.x &&
|
||||
right->p2.x > traps->limits.p2.x)
|
||||
{
|
||||
right->p1.x = traps->limits.p2.x;
|
||||
right->p2.x = traps->limits.p2.x;
|
||||
}
|
||||
}
|
||||
|
||||
if (top >= bottom) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1036,6 +1036,9 @@ typedef struct _cairo_traps {
|
|||
int traps_size;
|
||||
cairo_trapezoid_t *traps;
|
||||
cairo_trapezoid_t traps_embedded[1];
|
||||
|
||||
cairo_bool_t has_limits;
|
||||
cairo_box_t limits;
|
||||
} cairo_traps_t;
|
||||
|
||||
#define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL
|
||||
|
|
@ -2171,6 +2174,10 @@ _cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
|
|||
cairo_private void
|
||||
_cairo_traps_init (cairo_traps_t *traps);
|
||||
|
||||
cairo_private void
|
||||
_cairo_traps_limit (cairo_traps_t *traps,
|
||||
cairo_box_t *limits);
|
||||
|
||||
cairo_private cairo_status_t
|
||||
_cairo_traps_init_box (cairo_traps_t *traps,
|
||||
cairo_box_t *box);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue