1 /* cairo - a vector graphics library with display and print output
3 * Copyright © 2009 Intel Corporation
5 * This library is free software; you can redistribute it and/or
6 * modify it either under the terms of the GNU Lesser General Public
7 * License version 2.1 as published by the Free Software Foundation
8 * (the "LGPL") or, at your option, under the terms of the Mozilla
9 * Public License Version 1.1 (the "MPL"). If you do not alter this
10 * notice, a recipient may use your version of this file under either
11 * the MPL or the LGPL.
13 * You should have received a copy of the LGPL along with this library
14 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16 * You should have received a copy of the MPL along with this library
17 * in the file COPYING-MPL-1.1
19 * The contents of this file are subject to the Mozilla Public License
20 * Version 1.1 (the "License"); you may not use this file except in
21 * compliance with the License. You may obtain a copy of the License at
22 * http://www.mozilla.org/MPL/
24 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26 * the specific language governing rights and limitations.
28 * The Original Code is the cairo graphics library.
30 * The Initial Developer of the Original Code is Red Hat, Inc.
33 * Chris Wilson <chris@chris-wilson.co.uk>
38 #include "cairo-clip-inline.h"
39 #include "cairo-error-private.h"
40 #include "cairo-composite-rectangles-private.h"
41 #include "cairo-pattern-private.h"
43 /* A collection of routines to facilitate writing compositors. */
45 void _cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents)
47 _cairo_clip_destroy (extents->clip);
50 static inline cairo_bool_t
51 _cairo_composite_rectangles_check_lazy_init (cairo_composite_rectangles_t *extents,
52 cairo_surface_t *surface,
53 const cairo_pattern_t *pattern)
55 cairo_pattern_type_t type;
57 if (! extents->is_bounded)
60 type = cairo_pattern_get_type ((cairo_pattern_t *)pattern);
62 if (type == CAIRO_PATTERN_TYPE_SURFACE) {
63 cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
64 cairo_surface_t *pattern_surface = surface_pattern->surface;
66 /* XXX: both source and target are GL surface */
67 if (cairo_surface_get_type (pattern_surface) == CAIRO_SURFACE_TYPE_GL &&
68 pattern_surface->backend->type == CAIRO_SURFACE_TYPE_GL &&
69 cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_GL &&
70 surface->device == pattern_surface->device)
72 } else if (type == CAIRO_PATTERN_TYPE_SOLID ||
73 type == CAIRO_PATTERN_TYPE_RADIAL ||
74 type == CAIRO_PATTERN_TYPE_LINEAR)
81 _cairo_composite_reduce_pattern (const cairo_pattern_t *src,
82 cairo_pattern_union_t *dst)
86 _cairo_pattern_init_static_copy (&dst->base, src);
87 if (dst->base.type == CAIRO_PATTERN_TYPE_SOLID)
90 dst->base.filter = _cairo_pattern_analyze_filter (&dst->base, NULL),
93 if (_cairo_matrix_is_pixman_translation (&dst->base.matrix,
97 dst->base.matrix.x0 = tx;
98 dst->base.matrix.y0 = ty;
102 static inline cairo_bool_t
103 _cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents,
104 cairo_surface_t *surface,
106 const cairo_pattern_t *source,
107 const cairo_clip_t *clip,
108 cairo_bool_t *should_be_lazy)
110 if (_cairo_clip_is_all_clipped (clip))
113 extents->surface = surface;
116 _cairo_surface_get_extents (surface, &extents->destination);
117 extents->clip = NULL;
119 extents->unbounded = extents->destination;
120 extents->is_bounded = _cairo_operator_bounded_by_either (op);
123 *should_be_lazy = _cairo_composite_rectangles_check_lazy_init (extents,
127 if ((! *should_be_lazy) &&
128 (clip && ! _cairo_rectangle_intersect (&extents->unbounded,
129 _cairo_clip_get_extents (clip))))
132 extents->bounded = extents->unbounded;
134 extents->original_source_pattern = source;
135 if (! *should_be_lazy) {
136 _cairo_composite_reduce_pattern (source, &extents->source_pattern);
138 _cairo_pattern_get_extents (&extents->source_pattern.base,
141 _cairo_pattern_get_extents (extents->original_source_pattern,
144 if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) {
145 if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source))
149 extents->original_mask_pattern = NULL;
150 extents->mask_pattern.base.type = CAIRO_PATTERN_TYPE_SOLID;
151 extents->mask_pattern.solid.color.alpha = 1.; /* XXX full initialisation? */
152 extents->mask_pattern.solid.color.alpha_short = 0xffff;
158 _cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
159 cairo_surface_t *surface,
161 const cairo_pattern_t *source,
162 const cairo_clip_t *clip)
164 cairo_bool_t should_be_lazy = FALSE;
165 if (! _cairo_composite_rectangles_init (extents,
166 surface, op, source, clip,
169 return CAIRO_INT_STATUS_NOTHING_TO_DO;
172 extents->mask = extents->destination;
174 extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
175 if (_cairo_clip_is_all_clipped (extents->clip))
176 return CAIRO_INT_STATUS_NOTHING_TO_DO;
178 if (! _cairo_rectangle_intersect (&extents->unbounded,
179 _cairo_clip_get_extents (extents->clip)))
180 return CAIRO_INT_STATUS_NOTHING_TO_DO;
182 if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
183 _cairo_pattern_sampled_area (&extents->source_pattern.base,
185 &extents->source_sample_area);
187 return CAIRO_STATUS_SUCCESS;
191 _cairo_composite_rectangles_lazy_init_for_paint (cairo_composite_rectangles_t *extents,
192 cairo_surface_t *surface,
194 const cairo_pattern_t *source,
195 const cairo_clip_t *clip)
197 cairo_bool_t should_be_lazy = TRUE;
198 if (! _cairo_composite_rectangles_init (extents,
199 surface, op, source, clip,
202 return CAIRO_INT_STATUS_NOTHING_TO_DO;
205 if (! should_be_lazy) {
206 extents->mask = extents->destination;
208 extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
209 if (_cairo_clip_is_all_clipped (extents->clip))
210 return CAIRO_INT_STATUS_NOTHING_TO_DO;
212 if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
213 _cairo_pattern_sampled_area (&extents->source_pattern.base,
215 &extents->source_sample_area);
217 extents->clip = _cairo_clip_copy (clip);
219 return CAIRO_STATUS_SUCCESS;
222 static cairo_int_status_t
223 _cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents,
224 const cairo_clip_t *clip)
228 ret = _cairo_rectangle_intersect (&extents->bounded, &extents->mask);
229 if (! ret && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
230 return CAIRO_INT_STATUS_NOTHING_TO_DO;
232 if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
233 extents->unbounded = extents->bounded;
234 } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
235 if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
236 return CAIRO_INT_STATUS_NOTHING_TO_DO;
239 extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
240 if (_cairo_clip_is_all_clipped (extents->clip))
241 return CAIRO_INT_STATUS_NOTHING_TO_DO;
243 if (! _cairo_rectangle_intersect (&extents->unbounded,
244 _cairo_clip_get_extents (extents->clip)))
245 return CAIRO_INT_STATUS_NOTHING_TO_DO;
247 if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
248 _cairo_pattern_sampled_area (&extents->source_pattern.base,
250 &extents->source_sample_area);
251 if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
252 _cairo_pattern_sampled_area (&extents->mask_pattern.base,
254 &extents->mask_sample_area);
255 if (extents->mask_sample_area.width == 0 ||
256 extents->mask_sample_area.height == 0) {
257 _cairo_composite_rectangles_fini (extents);
258 return CAIRO_INT_STATUS_NOTHING_TO_DO;
262 return CAIRO_STATUS_SUCCESS;
266 _cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents,
267 const cairo_box_t *box)
269 cairo_rectangle_int_t rect;
272 _cairo_box_round_to_rectangle (box, &rect);
273 if (rect.x == extents->source.x &&
274 rect.y == extents->source.y &&
275 rect.width == extents->source.width &&
276 rect.height == extents->source.height)
278 return CAIRO_INT_STATUS_SUCCESS;
281 _cairo_rectangle_intersect (&extents->source, &rect);
283 rect = extents->bounded;
284 if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source) &&
285 extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE)
286 return CAIRO_INT_STATUS_NOTHING_TO_DO;
288 if (rect.width == extents->bounded.width &&
289 rect.height == extents->bounded.height)
290 return CAIRO_INT_STATUS_SUCCESS;
292 if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
293 extents->unbounded = extents->bounded;
294 } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
295 if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
296 return CAIRO_INT_STATUS_NOTHING_TO_DO;
299 clip = extents->clip;
300 extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
301 if (clip != extents->clip)
302 _cairo_clip_destroy (clip);
304 if (_cairo_clip_is_all_clipped (extents->clip))
305 return CAIRO_INT_STATUS_NOTHING_TO_DO;
307 if (! _cairo_rectangle_intersect (&extents->unbounded,
308 _cairo_clip_get_extents (extents->clip)))
309 return CAIRO_INT_STATUS_NOTHING_TO_DO;
311 if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
312 _cairo_pattern_sampled_area (&extents->source_pattern.base,
314 &extents->source_sample_area);
315 if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
316 _cairo_pattern_sampled_area (&extents->mask_pattern.base,
318 &extents->mask_sample_area);
319 if (extents->mask_sample_area.width == 0 ||
320 extents->mask_sample_area.height == 0)
321 return CAIRO_INT_STATUS_NOTHING_TO_DO;
324 return CAIRO_INT_STATUS_SUCCESS;
328 _cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents,
329 const cairo_box_t *box)
331 cairo_rectangle_int_t mask;
334 _cairo_box_round_to_rectangle (box, &mask);
335 if (mask.x == extents->mask.x &&
336 mask.y == extents->mask.y &&
337 mask.width == extents->mask.width &&
338 mask.height == extents->mask.height)
340 return CAIRO_INT_STATUS_SUCCESS;
343 _cairo_rectangle_intersect (&extents->mask, &mask);
345 mask = extents->bounded;
346 if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask) &&
347 extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
348 return CAIRO_INT_STATUS_NOTHING_TO_DO;
350 if (mask.width == extents->bounded.width &&
351 mask.height == extents->bounded.height)
352 return CAIRO_INT_STATUS_SUCCESS;
354 if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
355 extents->unbounded = extents->bounded;
356 } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
357 if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
358 return CAIRO_INT_STATUS_NOTHING_TO_DO;
361 clip = extents->clip;
362 extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
363 if (clip != extents->clip)
364 _cairo_clip_destroy (clip);
366 if (_cairo_clip_is_all_clipped (extents->clip))
367 return CAIRO_INT_STATUS_NOTHING_TO_DO;
369 if (! _cairo_rectangle_intersect (&extents->unbounded,
370 _cairo_clip_get_extents (extents->clip)))
371 return CAIRO_INT_STATUS_NOTHING_TO_DO;
373 if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
374 _cairo_pattern_sampled_area (&extents->source_pattern.base,
376 &extents->source_sample_area);
377 if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
378 _cairo_pattern_sampled_area (&extents->mask_pattern.base,
380 &extents->mask_sample_area);
381 if (extents->mask_sample_area.width == 0 ||
382 extents->mask_sample_area.height == 0)
383 return CAIRO_INT_STATUS_NOTHING_TO_DO;
386 return CAIRO_INT_STATUS_SUCCESS;
390 _cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents,
391 cairo_surface_t*surface,
393 const cairo_pattern_t *source,
394 const cairo_pattern_t *mask,
395 const cairo_clip_t *clip)
397 cairo_bool_t should_be_lazy = FALSE;
399 if (! _cairo_composite_rectangles_init (extents,
400 surface, op, source, clip,
403 return CAIRO_INT_STATUS_NOTHING_TO_DO;
406 extents->original_mask_pattern = mask;
407 _cairo_composite_reduce_pattern (mask, &extents->mask_pattern);
408 _cairo_pattern_get_extents (&extents->mask_pattern.base, &extents->mask);
410 return _cairo_composite_rectangles_intersect (extents, clip);
414 _cairo_composite_rectangles_lazy_init_for_mask (cairo_composite_rectangles_t *extents,
415 cairo_surface_t *surface,
417 const cairo_pattern_t *source,
418 const cairo_pattern_t *mask,
419 const cairo_clip_t *clip)
422 cairo_bool_t should_be_lazy = (op == CAIRO_OPERATOR_SOURCE) ? FALSE : TRUE;
425 if (! _cairo_composite_rectangles_init (extents,
426 surface, op, source, clip,
429 return CAIRO_INT_STATUS_NOTHING_TO_DO;
432 extents->original_mask_pattern = mask;
434 if (! should_be_lazy) {
435 _cairo_composite_reduce_pattern (mask, &extents->mask_pattern);
436 _cairo_pattern_get_extents (&extents->mask_pattern.base,
438 return _cairo_composite_rectangles_intersect (extents, clip);
441 _cairo_pattern_get_extents (extents->original_mask_pattern,
444 ret = _cairo_rectangle_intersect (&extents->bounded, &extents->mask);
445 if (! ret && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
446 return CAIRO_INT_STATUS_NOTHING_TO_DO;
448 if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE))
449 extents->unbounded = extents->bounded;
450 else if ((extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) &&
451 (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask)))
452 return CAIRO_INT_STATUS_NOTHING_TO_DO;
454 extents->clip = _cairo_clip_copy (clip);
455 return CAIRO_INT_STATUS_SUCCESS;
459 _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents,
460 cairo_surface_t *surface,
462 const cairo_pattern_t *source,
463 const cairo_path_fixed_t *path,
464 const cairo_stroke_style_t *style,
465 const cairo_matrix_t *ctm,
466 const cairo_clip_t *clip)
468 cairo_bool_t should_be_lazy = FALSE;
470 if (! _cairo_composite_rectangles_init (extents,
471 surface, op, source, clip,
474 return CAIRO_INT_STATUS_NOTHING_TO_DO;
477 _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &extents->mask);
479 return _cairo_composite_rectangles_intersect (extents, clip);
483 _cairo_composite_rectangles_lazy_init_for_stroke (cairo_composite_rectangles_t *extents,
484 cairo_surface_t *surface,
486 const cairo_pattern_t *source,
487 const cairo_path_fixed_t *path,
488 const cairo_stroke_style_t *style,
489 const cairo_matrix_t *ctm,
490 const cairo_clip_t *clip)
492 cairo_bool_t should_be_lazy = TRUE;
494 if (! _cairo_composite_rectangles_init (extents,
495 surface, op, source, clip,
498 return CAIRO_INT_STATUS_NOTHING_TO_DO;
501 if (! should_be_lazy) {
502 _cairo_path_fixed_approximate_stroke_extents (path, style, ctm,
504 return _cairo_composite_rectangles_intersect (extents, clip);
507 extents->clip = _cairo_clip_copy (clip);
508 return CAIRO_INT_STATUS_SUCCESS;
512 _cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents,
513 cairo_surface_t *surface,
515 const cairo_pattern_t *source,
516 const cairo_path_fixed_t *path,
517 const cairo_clip_t *clip)
519 cairo_bool_t should_be_lazy = FALSE;
521 if (! _cairo_composite_rectangles_init (extents,
522 surface, op, source, clip,
525 return CAIRO_INT_STATUS_NOTHING_TO_DO;
528 _cairo_path_fixed_approximate_fill_extents (path, &extents->mask);
530 return _cairo_composite_rectangles_intersect (extents, clip);
534 _cairo_composite_rectangles_lazy_init_for_fill (cairo_composite_rectangles_t *extents,
535 cairo_surface_t *surface,
537 const cairo_pattern_t *source,
538 const cairo_path_fixed_t *path,
539 const cairo_clip_t *clip)
541 cairo_bool_t should_be_lazy = TRUE;
542 if (! _cairo_composite_rectangles_init (extents,
543 surface, op, source, clip,
546 return CAIRO_INT_STATUS_NOTHING_TO_DO;
549 if (! should_be_lazy) {
550 _cairo_path_fixed_approximate_fill_extents (path, &extents->mask);
552 return _cairo_composite_rectangles_intersect (extents, clip);
555 extents->clip = _cairo_clip_copy (clip);
556 return CAIRO_INT_STATUS_SUCCESS;
560 _cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents,
561 cairo_surface_t *surface,
563 const cairo_pattern_t *source,
564 const cairo_polygon_t *polygon,
565 const cairo_clip_t *clip)
567 cairo_bool_t should_be_lazy = FALSE;
568 if (! _cairo_composite_rectangles_init (extents,
569 surface, op, source, clip,
572 return CAIRO_INT_STATUS_NOTHING_TO_DO;
575 _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
576 return _cairo_composite_rectangles_intersect (extents, clip);
580 _cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents,
581 cairo_surface_t *surface,
583 const cairo_pattern_t *source,
584 const cairo_boxes_t *boxes,
585 const cairo_clip_t *clip)
588 cairo_bool_t should_be_lazy = FALSE;
590 if (! _cairo_composite_rectangles_init (extents,
591 surface, op, source, clip,
594 return CAIRO_INT_STATUS_NOTHING_TO_DO;
597 _cairo_boxes_extents (boxes, &box);
598 _cairo_box_round_to_rectangle (&box, &extents->mask);
599 return _cairo_composite_rectangles_intersect (extents, clip);
603 _cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents,
604 cairo_surface_t *surface,
606 const cairo_pattern_t *source,
607 cairo_scaled_font_t *scaled_font,
608 cairo_glyph_t *glyphs,
610 const cairo_clip_t *clip,
611 cairo_bool_t *overlap)
613 cairo_status_t status;
614 cairo_bool_t should_be_lazy = FALSE;
616 if (! _cairo_composite_rectangles_init (extents, surface, op, source,
617 clip, &should_be_lazy))
618 return CAIRO_INT_STATUS_NOTHING_TO_DO;
620 /* Computing the exact bbox and the overlap is expensive.
621 * First perform a cheap test to see if the glyphs are all clipped out.
623 if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK &&
624 _cairo_scaled_font_glyph_approximate_extents (scaled_font,
628 if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
629 return CAIRO_INT_STATUS_NOTHING_TO_DO;
632 status = _cairo_scaled_font_glyph_device_extents (scaled_font,
636 if (unlikely (status))
639 if (overlap && *overlap &&
640 scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE &&
641 _cairo_pattern_is_opaque_solid (&extents->source_pattern.base))
646 return _cairo_composite_rectangles_intersect (extents, clip);
650 _cairo_composite_rectangles_lazy_init_for_glyphs (cairo_composite_rectangles_t *extents,
651 cairo_surface_t *surface,
653 const cairo_pattern_t *source,
654 cairo_scaled_font_t *scaled_font,
655 cairo_glyph_t *glyphs,
657 const cairo_clip_t *clip,
658 cairo_bool_t *overlap)
660 cairo_status_t status;
661 cairo_bool_t should_be_lazy = TRUE;
663 if (! _cairo_composite_rectangles_init (extents, surface, op, source,
664 clip, &should_be_lazy))
665 return CAIRO_INT_STATUS_NOTHING_TO_DO;
667 status = _cairo_scaled_font_glyph_device_extents (scaled_font,
671 if (unlikely (status))
674 extents->clip = _cairo_clip_copy (clip);
676 return CAIRO_INT_STATUS_SUCCESS;
680 _cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite,
683 cairo_rectangle_int_t extents;
689 extents = composite->destination;
690 if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE)
691 _cairo_rectangle_intersect (&extents, &composite->source);
692 if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
693 _cairo_rectangle_intersect (&extents, &composite->mask);
695 _cairo_box_from_rectangle (&box, &extents);
696 return _cairo_clip_contains_box (clip, &box);
700 _cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite,
701 cairo_boxes_t *damage)
703 cairo_int_status_t status;
706 for (n = 0; n < composite->clip->num_boxes; n++) {
707 status = _cairo_boxes_add (damage,
708 CAIRO_ANTIALIAS_NONE,
709 &composite->clip->boxes[n]);
710 if (unlikely (status))
714 return CAIRO_INT_STATUS_SUCCESS;