Tizen 2.0 Release
[framework/graphics/cairo.git] / src / cairo-composite-rectangles.c
1 /* cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2009 Intel Corporation
4  *
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.
12  *
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
18  *
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/
23  *
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.
27  *
28  * The Original Code is the cairo graphics library.
29  *
30  * The Initial Developer of the Original Code is Red Hat, Inc.
31  *
32  * Contributor(s):
33  *      Chris Wilson <chris@chris-wilson.co.uk>
34  */
35
36 #include "cairoint.h"
37
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"
42
43 /* A collection of routines to facilitate writing compositors. */
44
45 void _cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents)
46 {
47     _cairo_clip_destroy (extents->clip);
48 }
49
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)
54 {
55     cairo_pattern_type_t type;
56
57     if (! extents->is_bounded)
58         return FALSE;
59
60     type = cairo_pattern_get_type ((cairo_pattern_t *)pattern);
61
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;
65
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)
71         return TRUE;
72         } else  if (type == CAIRO_PATTERN_TYPE_SOLID ||
73                 type == CAIRO_PATTERN_TYPE_RADIAL ||
74                 type == CAIRO_PATTERN_TYPE_LINEAR)
75         return TRUE;
76
77     return FALSE;
78 }
79
80 static void
81 _cairo_composite_reduce_pattern (const cairo_pattern_t *src,
82                                  cairo_pattern_union_t *dst)
83 {
84     int tx, ty;
85
86     _cairo_pattern_init_static_copy (&dst->base, src);
87     if (dst->base.type == CAIRO_PATTERN_TYPE_SOLID)
88         return;
89
90     dst->base.filter = _cairo_pattern_analyze_filter (&dst->base, NULL),
91
92     tx = ty = 0;
93     if (_cairo_matrix_is_pixman_translation (&dst->base.matrix,
94                                              dst->base.filter,
95                                              &tx, &ty))
96     {
97         dst->base.matrix.x0 = tx;
98         dst->base.matrix.y0 = ty;
99     }
100 }
101
102 static inline cairo_bool_t
103 _cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents,
104                                   cairo_surface_t *surface,
105                                   cairo_operator_t op,
106                                   const cairo_pattern_t *source,
107                                   const cairo_clip_t *clip,
108                                   cairo_bool_t *should_be_lazy)
109 {
110     if (_cairo_clip_is_all_clipped (clip))
111         return FALSE;
112
113     extents->surface = surface;
114     extents->op = op;
115
116     _cairo_surface_get_extents (surface, &extents->destination);
117     extents->clip = NULL;
118
119     extents->unbounded = extents->destination;
120     extents->is_bounded = _cairo_operator_bounded_by_either (op);
121
122     if (*should_be_lazy)
123         *should_be_lazy = _cairo_composite_rectangles_check_lazy_init (extents,
124                                                                        surface,
125                                                                        source);
126
127     if ((! *should_be_lazy) &&
128         (clip && ! _cairo_rectangle_intersect (&extents->unbounded,
129                                                _cairo_clip_get_extents (clip))))
130         return FALSE;
131
132     extents->bounded = extents->unbounded;
133
134         extents->original_source_pattern = source;
135         if (! *should_be_lazy) {
136                 _cairo_composite_reduce_pattern (source, &extents->source_pattern);
137
138                 _cairo_pattern_get_extents (&extents->source_pattern.base,
139                                     &extents->source);
140         } else
141         _cairo_pattern_get_extents (extents->original_source_pattern,
142                                     &extents->source);
143
144     if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) {
145         if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source))
146             return FALSE;
147     }
148
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;
153
154     return TRUE;
155 }
156
157 cairo_int_status_t
158 _cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
159                                             cairo_surface_t *surface,
160                                             cairo_operator_t             op,
161                                             const cairo_pattern_t       *source,
162                                             const cairo_clip_t          *clip)
163 {
164     cairo_bool_t should_be_lazy = FALSE;
165     if (! _cairo_composite_rectangles_init (extents,
166                                             surface, op, source, clip,
167                                             &should_be_lazy))
168     {
169         return CAIRO_INT_STATUS_NOTHING_TO_DO;
170     }
171
172     extents->mask = extents->destination;
173
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;
177
178     if (! _cairo_rectangle_intersect (&extents->unbounded,
179                                       _cairo_clip_get_extents (extents->clip)))
180         return CAIRO_INT_STATUS_NOTHING_TO_DO;
181
182     if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
183         _cairo_pattern_sampled_area (&extents->source_pattern.base,
184                                      &extents->bounded,
185                                      &extents->source_sample_area);
186
187     return CAIRO_STATUS_SUCCESS;
188 }
189
190 cairo_int_status_t
191 _cairo_composite_rectangles_lazy_init_for_paint (cairo_composite_rectangles_t *extents,
192                                                  cairo_surface_t *surface,
193                                                  cairo_operator_t op,
194                                                  const cairo_pattern_t *source,
195                                                  const cairo_clip_t *clip)
196 {
197     cairo_bool_t should_be_lazy = TRUE;
198     if (! _cairo_composite_rectangles_init (extents,
199                                             surface, op, source, clip,
200                                             &should_be_lazy))
201     {
202         return CAIRO_INT_STATUS_NOTHING_TO_DO;
203     }
204
205     if (! should_be_lazy) {
206         extents->mask = extents->destination;
207
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;
211
212         if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
213             _cairo_pattern_sampled_area (&extents->source_pattern.base,
214                                          &extents->bounded,
215                                          &extents->source_sample_area);
216     } else
217         extents->clip = _cairo_clip_copy (clip);
218
219     return CAIRO_STATUS_SUCCESS;
220 }
221
222 static cairo_int_status_t
223 _cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents,
224                                        const cairo_clip_t *clip)
225 {
226     cairo_bool_t ret;
227
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;
231
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;
237     }
238
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;
242
243     if (! _cairo_rectangle_intersect (&extents->unbounded,
244                                       _cairo_clip_get_extents (extents->clip)))
245         return CAIRO_INT_STATUS_NOTHING_TO_DO;
246
247     if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
248         _cairo_pattern_sampled_area (&extents->source_pattern.base,
249                                      &extents->bounded,
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,
253                                      &extents->bounded,
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;
259         }
260     }
261
262     return CAIRO_STATUS_SUCCESS;
263 }
264
265 cairo_int_status_t
266 _cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents,
267                                                       const cairo_box_t *box)
268 {
269     cairo_rectangle_int_t rect;
270     cairo_clip_t *clip;
271
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)
277     {
278         return CAIRO_INT_STATUS_SUCCESS;
279     }
280
281     _cairo_rectangle_intersect (&extents->source, &rect);
282
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;
287
288     if (rect.width  == extents->bounded.width &&
289         rect.height == extents->bounded.height)
290         return CAIRO_INT_STATUS_SUCCESS;
291
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;
297     }
298
299     clip = extents->clip;
300     extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
301     if (clip != extents->clip)
302         _cairo_clip_destroy (clip);
303
304     if (_cairo_clip_is_all_clipped (extents->clip))
305         return CAIRO_INT_STATUS_NOTHING_TO_DO;
306
307     if (! _cairo_rectangle_intersect (&extents->unbounded,
308                                       _cairo_clip_get_extents (extents->clip)))
309         return CAIRO_INT_STATUS_NOTHING_TO_DO;
310
311     if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
312         _cairo_pattern_sampled_area (&extents->source_pattern.base,
313                                      &extents->bounded,
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,
317                                      &extents->bounded,
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;
322     }
323
324     return CAIRO_INT_STATUS_SUCCESS;
325 }
326
327 cairo_int_status_t
328 _cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents,
329                                                     const cairo_box_t *box)
330 {
331     cairo_rectangle_int_t mask;
332     cairo_clip_t *clip;
333
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)
339     {
340         return CAIRO_INT_STATUS_SUCCESS;
341     }
342
343     _cairo_rectangle_intersect (&extents->mask, &mask);
344
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;
349
350     if (mask.width  == extents->bounded.width &&
351         mask.height == extents->bounded.height)
352         return CAIRO_INT_STATUS_SUCCESS;
353
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;
359     }
360
361     clip = extents->clip;
362     extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
363     if (clip != extents->clip)
364         _cairo_clip_destroy (clip);
365
366     if (_cairo_clip_is_all_clipped (extents->clip))
367         return CAIRO_INT_STATUS_NOTHING_TO_DO;
368
369     if (! _cairo_rectangle_intersect (&extents->unbounded,
370                                       _cairo_clip_get_extents (extents->clip)))
371         return CAIRO_INT_STATUS_NOTHING_TO_DO;
372
373     if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
374         _cairo_pattern_sampled_area (&extents->source_pattern.base,
375                                      &extents->bounded,
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,
379                                      &extents->bounded,
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;
384     }
385
386     return CAIRO_INT_STATUS_SUCCESS;
387 }
388
389 cairo_int_status_t
390 _cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents,
391                                            cairo_surface_t*surface,
392                                            cairo_operator_t              op,
393                                            const cairo_pattern_t        *source,
394                                            const cairo_pattern_t        *mask,
395                                            const cairo_clip_t           *clip)
396 {
397     cairo_bool_t should_be_lazy = FALSE;
398
399     if (! _cairo_composite_rectangles_init (extents,
400                                             surface, op, source, clip,
401                                             &should_be_lazy))
402     {
403         return CAIRO_INT_STATUS_NOTHING_TO_DO;
404     }
405
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);
409
410     return _cairo_composite_rectangles_intersect (extents, clip);
411 }
412
413 cairo_int_status_t
414 _cairo_composite_rectangles_lazy_init_for_mask (cairo_composite_rectangles_t *extents,
415                                                 cairo_surface_t *surface,
416                                                 cairo_operator_t op,
417                                                 const cairo_pattern_t *source,
418                                                 const cairo_pattern_t *mask,
419                                                 const cairo_clip_t *clip)
420 {
421         cairo_bool_t ret;
422         cairo_bool_t should_be_lazy = (op == CAIRO_OPERATOR_SOURCE) ? FALSE : TRUE;
423
424
425         if (! _cairo_composite_rectangles_init (extents,
426                                                 surface, op, source, clip,
427                                                 &should_be_lazy))
428     {
429         return CAIRO_INT_STATUS_NOTHING_TO_DO;
430     }
431
432         extents->original_mask_pattern = mask;
433
434         if (! should_be_lazy) {
435                 _cairo_composite_reduce_pattern (mask, &extents->mask_pattern);
436                 _cairo_pattern_get_extents (&extents->mask_pattern.base,
437                                         &extents->mask);
438                 return _cairo_composite_rectangles_intersect (extents, clip);
439         }
440
441         _cairo_pattern_get_extents (extents->original_mask_pattern,
442                                 &extents->mask);
443
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;
447
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;
453
454         extents->clip = _cairo_clip_copy (clip);
455         return CAIRO_INT_STATUS_SUCCESS;
456 }
457
458 cairo_int_status_t
459 _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents,
460                                              cairo_surface_t *surface,
461                                              cairo_operator_t            op,
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)
467 {
468     cairo_bool_t should_be_lazy = FALSE;
469
470     if (! _cairo_composite_rectangles_init (extents,
471                                             surface, op, source, clip,
472                                             &should_be_lazy))
473     {
474         return CAIRO_INT_STATUS_NOTHING_TO_DO;
475     }
476
477     _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &extents->mask);
478
479     return _cairo_composite_rectangles_intersect (extents, clip);
480 }
481
482 cairo_int_status_t
483 _cairo_composite_rectangles_lazy_init_for_stroke (cairo_composite_rectangles_t *extents,
484                                                   cairo_surface_t *surface,
485                                                   cairo_operator_t op,
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)
491 {
492     cairo_bool_t should_be_lazy = TRUE;
493
494     if (! _cairo_composite_rectangles_init (extents,
495                                             surface, op, source, clip,
496                                             &should_be_lazy))
497     {
498         return CAIRO_INT_STATUS_NOTHING_TO_DO;
499     }
500
501     if (! should_be_lazy) {
502         _cairo_path_fixed_approximate_stroke_extents (path, style, ctm,
503                                                       &extents->mask);
504         return _cairo_composite_rectangles_intersect (extents, clip);
505     }
506
507     extents->clip = _cairo_clip_copy (clip);
508     return CAIRO_INT_STATUS_SUCCESS;
509 }
510
511 cairo_int_status_t
512 _cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents,
513                                            cairo_surface_t *surface,
514                                            cairo_operator_t              op,
515                                            const cairo_pattern_t        *source,
516                                            const cairo_path_fixed_t             *path,
517                                            const cairo_clip_t           *clip)
518 {
519     cairo_bool_t should_be_lazy = FALSE;
520
521     if (! _cairo_composite_rectangles_init (extents,
522                                             surface, op, source, clip,
523                                             &should_be_lazy))
524     {
525         return CAIRO_INT_STATUS_NOTHING_TO_DO;
526     }
527
528     _cairo_path_fixed_approximate_fill_extents (path, &extents->mask);
529
530     return _cairo_composite_rectangles_intersect (extents, clip);
531 }
532
533 cairo_int_status_t
534 _cairo_composite_rectangles_lazy_init_for_fill (cairo_composite_rectangles_t *extents,
535                                                 cairo_surface_t *surface,
536                                                 cairo_operator_t op,
537                                                 const cairo_pattern_t *source,
538                                                 const cairo_path_fixed_t *path,
539                                                 const cairo_clip_t *clip)
540 {
541     cairo_bool_t should_be_lazy = TRUE;
542     if (! _cairo_composite_rectangles_init (extents,
543                                             surface, op, source, clip,
544                                             &should_be_lazy))
545     {
546         return CAIRO_INT_STATUS_NOTHING_TO_DO;
547     }
548
549     if (! should_be_lazy) {
550         _cairo_path_fixed_approximate_fill_extents (path, &extents->mask);
551
552         return _cairo_composite_rectangles_intersect (extents, clip);
553     }
554
555     extents->clip = _cairo_clip_copy (clip);
556     return CAIRO_INT_STATUS_SUCCESS;
557 }
558
559 cairo_int_status_t
560 _cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents,
561                                               cairo_surface_t           *surface,
562                                               cairo_operator_t           op,
563                                               const cairo_pattern_t     *source,
564                                               const cairo_polygon_t     *polygon,
565                                               const cairo_clip_t                *clip)
566 {
567     cairo_bool_t should_be_lazy = FALSE;
568     if (! _cairo_composite_rectangles_init (extents,
569                                             surface, op, source, clip,
570                                             &should_be_lazy))
571     {
572         return CAIRO_INT_STATUS_NOTHING_TO_DO;
573     }
574
575     _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
576     return _cairo_composite_rectangles_intersect (extents, clip);
577 }
578
579 cairo_int_status_t
580 _cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents,
581                                               cairo_surface_t           *surface,
582                                               cairo_operator_t           op,
583                                               const cairo_pattern_t     *source,
584                                               const cairo_boxes_t       *boxes,
585                                               const cairo_clip_t                *clip)
586 {
587     cairo_box_t box;
588     cairo_bool_t should_be_lazy = FALSE;
589
590     if (! _cairo_composite_rectangles_init (extents,
591                                             surface, op, source, clip,
592                                             &should_be_lazy))
593     {
594         return CAIRO_INT_STATUS_NOTHING_TO_DO;
595     }
596
597     _cairo_boxes_extents (boxes, &box);
598     _cairo_box_round_to_rectangle (&box, &extents->mask);
599     return _cairo_composite_rectangles_intersect (extents, clip);
600 }
601
602 cairo_int_status_t
603 _cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents,
604                                              cairo_surface_t *surface,
605                                              cairo_operator_t            op,
606                                              const cairo_pattern_t      *source,
607                                              cairo_scaled_font_t        *scaled_font,
608                                              cairo_glyph_t              *glyphs,
609                                              int                         num_glyphs,
610                                              const cairo_clip_t         *clip,
611                                              cairo_bool_t               *overlap)
612 {
613     cairo_status_t status;
614     cairo_bool_t should_be_lazy = FALSE;
615
616     if (! _cairo_composite_rectangles_init (extents, surface, op, source,
617                                             clip, &should_be_lazy))
618         return CAIRO_INT_STATUS_NOTHING_TO_DO;
619
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.
622      */
623     if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK &&
624         _cairo_scaled_font_glyph_approximate_extents (scaled_font,
625                                                       glyphs, num_glyphs,
626                                                       &extents->mask))
627     {
628         if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
629             return CAIRO_INT_STATUS_NOTHING_TO_DO;
630     }
631
632     status = _cairo_scaled_font_glyph_device_extents (scaled_font,
633                                                       glyphs, num_glyphs,
634                                                       &extents->mask,
635                                                       overlap);
636     if (unlikely (status))
637         return status;
638
639     if (overlap && *overlap &&
640         scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE &&
641         _cairo_pattern_is_opaque_solid (&extents->source_pattern.base))
642     {
643         *overlap = FALSE;
644     }
645
646     return _cairo_composite_rectangles_intersect (extents, clip);
647 }
648
649 cairo_int_status_t
650 _cairo_composite_rectangles_lazy_init_for_glyphs (cairo_composite_rectangles_t *extents,
651                                                   cairo_surface_t *surface,
652                                                   cairo_operator_t op,
653                                                   const cairo_pattern_t *source,
654                                                   cairo_scaled_font_t *scaled_font,
655                                                   cairo_glyph_t *glyphs,
656                                                   int num_glyphs,
657                                                   const cairo_clip_t *clip,
658                                                   cairo_bool_t *overlap)
659 {
660     cairo_status_t status;
661     cairo_bool_t should_be_lazy = TRUE;
662
663     if (! _cairo_composite_rectangles_init (extents, surface, op, source,
664                                             clip, &should_be_lazy))
665         return CAIRO_INT_STATUS_NOTHING_TO_DO;
666
667     status = _cairo_scaled_font_glyph_device_extents (scaled_font,
668                                                       glyphs, num_glyphs,
669                                                       &extents->source,
670                                                       overlap);
671     if (unlikely (status))
672         return status;
673
674     extents->clip = _cairo_clip_copy (clip);
675
676     return CAIRO_INT_STATUS_SUCCESS;
677 }
678
679 cairo_bool_t
680 _cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite,
681                                              cairo_clip_t *clip)
682 {
683     cairo_rectangle_int_t extents;
684     cairo_box_t box;
685
686     if (clip == NULL)
687         return TRUE;
688
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);
694
695     _cairo_box_from_rectangle (&box, &extents);
696     return _cairo_clip_contains_box (clip, &box);
697 }
698
699 cairo_int_status_t
700 _cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite,
701                                            cairo_boxes_t *damage)
702 {
703     cairo_int_status_t status;
704     int n;
705
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))
711             return status;
712     }
713
714     return CAIRO_INT_STATUS_SUCCESS;
715 }