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
_cairo_composite_rectangles_fini(cairo_composite_rectangles_t * extents)45 void _cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents)
46 {
47 _cairo_clip_destroy (extents->clip);
48 }
49
50 static void
_cairo_composite_reduce_pattern(const cairo_pattern_t * src,cairo_pattern_union_t * dst)51 _cairo_composite_reduce_pattern (const cairo_pattern_t *src,
52 cairo_pattern_union_t *dst)
53 {
54 int tx, ty;
55
56 _cairo_pattern_init_static_copy (&dst->base, src);
57 if (dst->base.type == CAIRO_PATTERN_TYPE_SOLID)
58 return;
59
60 dst->base.filter = _cairo_pattern_analyze_filter (&dst->base);
61
62 tx = ty = 0;
63 if (_cairo_matrix_is_pixman_translation (&dst->base.matrix,
64 dst->base.filter,
65 &tx, &ty))
66 {
67 dst->base.matrix.x0 = tx;
68 dst->base.matrix.y0 = ty;
69 }
70 }
71
72 static inline cairo_bool_t
_cairo_composite_rectangles_init(cairo_composite_rectangles_t * extents,cairo_surface_t * surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_clip_t * clip)73 _cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents,
74 cairo_surface_t *surface,
75 cairo_operator_t op,
76 const cairo_pattern_t *source,
77 const cairo_clip_t *clip)
78 {
79 if (_cairo_clip_is_all_clipped (clip))
80 return FALSE;
81
82 extents->surface = surface;
83 extents->op = op;
84
85 _cairo_surface_get_extents (surface, &extents->destination);
86 extents->clip = NULL;
87
88 extents->unbounded = extents->destination;
89 if (clip && ! _cairo_rectangle_intersect (&extents->unbounded,
90 _cairo_clip_get_extents (clip)))
91 return FALSE;
92
93 extents->bounded = extents->unbounded;
94 extents->is_bounded = _cairo_operator_bounded_by_either (op);
95
96 extents->original_source_pattern = source;
97 _cairo_composite_reduce_pattern (source, &extents->source_pattern);
98
99 _cairo_pattern_get_extents (&extents->source_pattern.base,
100 &extents->source,
101 surface->is_vector);
102 if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) {
103 if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source))
104 return FALSE;
105 }
106
107 extents->original_mask_pattern = NULL;
108 extents->mask_pattern.base.type = CAIRO_PATTERN_TYPE_SOLID;
109 extents->mask_pattern.solid.color.alpha = 1.; /* XXX full initialisation? */
110 extents->mask_pattern.solid.color.alpha_short = 0xffff;
111
112 return TRUE;
113 }
114
115 cairo_int_status_t
_cairo_composite_rectangles_init_for_paint(cairo_composite_rectangles_t * extents,cairo_surface_t * surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_clip_t * clip)116 _cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
117 cairo_surface_t *surface,
118 cairo_operator_t op,
119 const cairo_pattern_t *source,
120 const cairo_clip_t *clip)
121 {
122 if (! _cairo_composite_rectangles_init (extents,
123 surface, op, source, clip))
124 {
125 return CAIRO_INT_STATUS_NOTHING_TO_DO;
126 }
127
128 extents->mask = extents->destination;
129
130 extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
131 if (_cairo_clip_is_all_clipped (extents->clip))
132 return CAIRO_INT_STATUS_NOTHING_TO_DO;
133
134 if (! _cairo_rectangle_intersect (&extents->unbounded,
135 _cairo_clip_get_extents (extents->clip)))
136 return CAIRO_INT_STATUS_NOTHING_TO_DO;
137
138 if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
139 _cairo_pattern_sampled_area (&extents->source_pattern.base,
140 &extents->bounded,
141 &extents->source_sample_area);
142
143 return CAIRO_STATUS_SUCCESS;
144 }
145
146 static cairo_int_status_t
_cairo_composite_rectangles_intersect(cairo_composite_rectangles_t * extents,const cairo_clip_t * clip)147 _cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents,
148 const cairo_clip_t *clip)
149 {
150 if ((!_cairo_rectangle_intersect (&extents->bounded, &extents->mask)) &&
151 (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK))
152 return CAIRO_INT_STATUS_NOTHING_TO_DO;
153
154 if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
155 extents->unbounded = extents->bounded;
156 } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
157 if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
158 return CAIRO_INT_STATUS_NOTHING_TO_DO;
159 }
160
161 extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
162 if (_cairo_clip_is_all_clipped (extents->clip))
163 return CAIRO_INT_STATUS_NOTHING_TO_DO;
164
165 if (! _cairo_rectangle_intersect (&extents->unbounded,
166 _cairo_clip_get_extents (extents->clip)))
167 return CAIRO_INT_STATUS_NOTHING_TO_DO;
168
169 if (! _cairo_rectangle_intersect (&extents->bounded,
170 _cairo_clip_get_extents (extents->clip)) &&
171 extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
172 {
173 return CAIRO_INT_STATUS_NOTHING_TO_DO;
174 }
175
176 if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
177 _cairo_pattern_sampled_area (&extents->source_pattern.base,
178 &extents->bounded,
179 &extents->source_sample_area);
180 if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
181 _cairo_pattern_sampled_area (&extents->mask_pattern.base,
182 &extents->bounded,
183 &extents->mask_sample_area);
184 if (extents->mask_sample_area.width == 0 ||
185 extents->mask_sample_area.height == 0) {
186 _cairo_composite_rectangles_fini (extents);
187 return CAIRO_INT_STATUS_NOTHING_TO_DO;
188 }
189 }
190
191 return CAIRO_STATUS_SUCCESS;
192 }
193
194 cairo_int_status_t
_cairo_composite_rectangles_intersect_source_extents(cairo_composite_rectangles_t * extents,const cairo_box_t * box)195 _cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents,
196 const cairo_box_t *box)
197 {
198 cairo_rectangle_int_t rect;
199 cairo_clip_t *clip;
200
201 _cairo_box_round_to_rectangle (box, &rect);
202 if (rect.x == extents->source.x &&
203 rect.y == extents->source.y &&
204 rect.width == extents->source.width &&
205 rect.height == extents->source.height)
206 {
207 return CAIRO_INT_STATUS_SUCCESS;
208 }
209
210 _cairo_rectangle_intersect (&extents->source, &rect);
211
212 rect = extents->bounded;
213 if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source) &&
214 extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE)
215 return CAIRO_INT_STATUS_NOTHING_TO_DO;
216
217 if (rect.width == extents->bounded.width &&
218 rect.height == extents->bounded.height)
219 return CAIRO_INT_STATUS_SUCCESS;
220
221 if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
222 extents->unbounded = extents->bounded;
223 } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
224 if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
225 return CAIRO_INT_STATUS_NOTHING_TO_DO;
226 }
227
228 clip = extents->clip;
229 extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
230 if (clip != extents->clip)
231 _cairo_clip_destroy (clip);
232
233 if (_cairo_clip_is_all_clipped (extents->clip))
234 return CAIRO_INT_STATUS_NOTHING_TO_DO;
235
236 if (! _cairo_rectangle_intersect (&extents->unbounded,
237 _cairo_clip_get_extents (extents->clip)))
238 return CAIRO_INT_STATUS_NOTHING_TO_DO;
239
240 if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
241 _cairo_pattern_sampled_area (&extents->source_pattern.base,
242 &extents->bounded,
243 &extents->source_sample_area);
244 if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
245 _cairo_pattern_sampled_area (&extents->mask_pattern.base,
246 &extents->bounded,
247 &extents->mask_sample_area);
248 if (extents->mask_sample_area.width == 0 ||
249 extents->mask_sample_area.height == 0)
250 return CAIRO_INT_STATUS_NOTHING_TO_DO;
251 }
252
253 return CAIRO_INT_STATUS_SUCCESS;
254 }
255
256 cairo_int_status_t
_cairo_composite_rectangles_intersect_mask_extents(cairo_composite_rectangles_t * extents,const cairo_box_t * box)257 _cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents,
258 const cairo_box_t *box)
259 {
260 cairo_rectangle_int_t mask;
261 cairo_clip_t *clip;
262
263 _cairo_box_round_to_rectangle (box, &mask);
264 if (mask.x == extents->mask.x &&
265 mask.y == extents->mask.y &&
266 mask.width == extents->mask.width &&
267 mask.height == extents->mask.height)
268 {
269 return CAIRO_INT_STATUS_SUCCESS;
270 }
271
272 _cairo_rectangle_intersect (&extents->mask, &mask);
273
274 mask = extents->bounded;
275 if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask) &&
276 extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
277 return CAIRO_INT_STATUS_NOTHING_TO_DO;
278
279 if (mask.width == extents->bounded.width &&
280 mask.height == extents->bounded.height)
281 return CAIRO_INT_STATUS_SUCCESS;
282
283 if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
284 extents->unbounded = extents->bounded;
285 } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
286 if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
287 return CAIRO_INT_STATUS_NOTHING_TO_DO;
288 }
289
290 clip = extents->clip;
291 extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
292 if (clip != extents->clip)
293 _cairo_clip_destroy (clip);
294
295 if (_cairo_clip_is_all_clipped (extents->clip))
296 return CAIRO_INT_STATUS_NOTHING_TO_DO;
297
298 if (! _cairo_rectangle_intersect (&extents->unbounded,
299 _cairo_clip_get_extents (extents->clip)))
300 return CAIRO_INT_STATUS_NOTHING_TO_DO;
301
302 if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
303 _cairo_pattern_sampled_area (&extents->source_pattern.base,
304 &extents->bounded,
305 &extents->source_sample_area);
306 if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
307 _cairo_pattern_sampled_area (&extents->mask_pattern.base,
308 &extents->bounded,
309 &extents->mask_sample_area);
310 if (extents->mask_sample_area.width == 0 ||
311 extents->mask_sample_area.height == 0)
312 return CAIRO_INT_STATUS_NOTHING_TO_DO;
313 }
314
315 return CAIRO_INT_STATUS_SUCCESS;
316 }
317
318 cairo_int_status_t
_cairo_composite_rectangles_init_for_mask(cairo_composite_rectangles_t * extents,cairo_surface_t * surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_pattern_t * mask,const cairo_clip_t * clip)319 _cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents,
320 cairo_surface_t *surface,
321 cairo_operator_t op,
322 const cairo_pattern_t *source,
323 const cairo_pattern_t *mask,
324 const cairo_clip_t *clip)
325 {
326 if (! _cairo_composite_rectangles_init (extents,
327 surface, op, source, clip))
328 {
329 return CAIRO_INT_STATUS_NOTHING_TO_DO;
330 }
331
332 extents->original_mask_pattern = mask;
333 _cairo_composite_reduce_pattern (mask, &extents->mask_pattern);
334 _cairo_pattern_get_extents (&extents->mask_pattern.base, &extents->mask, surface->is_vector);
335
336 return _cairo_composite_rectangles_intersect (extents, clip);
337 }
338
339 cairo_int_status_t
_cairo_composite_rectangles_init_for_stroke(cairo_composite_rectangles_t * extents,cairo_surface_t * surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_path_fixed_t * path,const cairo_stroke_style_t * style,const cairo_matrix_t * ctm,const cairo_clip_t * clip)340 _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents,
341 cairo_surface_t *surface,
342 cairo_operator_t op,
343 const cairo_pattern_t *source,
344 const cairo_path_fixed_t *path,
345 const cairo_stroke_style_t *style,
346 const cairo_matrix_t *ctm,
347 const cairo_clip_t *clip)
348 {
349 if (! _cairo_composite_rectangles_init (extents,
350 surface, op, source, clip))
351 {
352 return CAIRO_INT_STATUS_NOTHING_TO_DO;
353 }
354
355 _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, surface->is_vector, &extents->mask);
356
357 return _cairo_composite_rectangles_intersect (extents, clip);
358 }
359
360 cairo_int_status_t
_cairo_composite_rectangles_init_for_fill(cairo_composite_rectangles_t * extents,cairo_surface_t * surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_path_fixed_t * path,const cairo_clip_t * clip)361 _cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents,
362 cairo_surface_t *surface,
363 cairo_operator_t op,
364 const cairo_pattern_t *source,
365 const cairo_path_fixed_t *path,
366 const cairo_clip_t *clip)
367 {
368 if (! _cairo_composite_rectangles_init (extents,
369 surface, op, source, clip))
370 {
371 return CAIRO_INT_STATUS_NOTHING_TO_DO;
372 }
373
374 _cairo_path_fixed_approximate_fill_extents (path, &extents->mask);
375
376 return _cairo_composite_rectangles_intersect (extents, clip);
377 }
378
379 cairo_int_status_t
_cairo_composite_rectangles_init_for_polygon(cairo_composite_rectangles_t * extents,cairo_surface_t * surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_polygon_t * polygon,const cairo_clip_t * clip)380 _cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents,
381 cairo_surface_t *surface,
382 cairo_operator_t op,
383 const cairo_pattern_t *source,
384 const cairo_polygon_t *polygon,
385 const cairo_clip_t *clip)
386 {
387 if (! _cairo_composite_rectangles_init (extents,
388 surface, op, source, clip))
389 {
390 return CAIRO_INT_STATUS_NOTHING_TO_DO;
391 }
392
393 _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
394 return _cairo_composite_rectangles_intersect (extents, clip);
395 }
396
397 cairo_int_status_t
_cairo_composite_rectangles_init_for_boxes(cairo_composite_rectangles_t * extents,cairo_surface_t * surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_boxes_t * boxes,const cairo_clip_t * clip)398 _cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents,
399 cairo_surface_t *surface,
400 cairo_operator_t op,
401 const cairo_pattern_t *source,
402 const cairo_boxes_t *boxes,
403 const cairo_clip_t *clip)
404 {
405 cairo_box_t box;
406
407 if (! _cairo_composite_rectangles_init (extents,
408 surface, op, source, clip))
409 {
410 return CAIRO_INT_STATUS_NOTHING_TO_DO;
411 }
412
413 _cairo_boxes_extents (boxes, &box);
414 _cairo_box_round_to_rectangle (&box, &extents->mask);
415 return _cairo_composite_rectangles_intersect (extents, clip);
416 }
417
418 cairo_int_status_t
_cairo_composite_rectangles_init_for_glyphs(cairo_composite_rectangles_t * extents,cairo_surface_t * surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_scaled_font_t * scaled_font,cairo_glyph_t * glyphs,int num_glyphs,const cairo_clip_t * clip,cairo_bool_t * overlap)419 _cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents,
420 cairo_surface_t *surface,
421 cairo_operator_t op,
422 const cairo_pattern_t *source,
423 cairo_scaled_font_t *scaled_font,
424 cairo_glyph_t *glyphs,
425 int num_glyphs,
426 const cairo_clip_t *clip,
427 cairo_bool_t *overlap)
428 {
429 cairo_status_t status;
430
431 if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip))
432 return CAIRO_INT_STATUS_NOTHING_TO_DO;
433
434 /* Computing the exact bbox and the overlap is expensive.
435 * First perform a cheap test to see if the glyphs are all clipped out.
436 */
437 if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK &&
438 _cairo_scaled_font_glyph_approximate_extents (scaled_font,
439 glyphs, num_glyphs,
440 &extents->mask))
441 {
442 if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
443 return CAIRO_INT_STATUS_NOTHING_TO_DO;
444 }
445
446 status = _cairo_scaled_font_glyph_device_extents (scaled_font,
447 glyphs, num_glyphs,
448 &extents->mask,
449 overlap);
450 if (unlikely (status))
451 return status;
452
453 if (overlap && *overlap &&
454 scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE &&
455 _cairo_pattern_is_opaque_solid (&extents->source_pattern.base))
456 {
457 *overlap = FALSE;
458 }
459
460 return _cairo_composite_rectangles_intersect (extents, clip);
461 }
462
463 cairo_bool_t
_cairo_composite_rectangles_can_reduce_clip(cairo_composite_rectangles_t * composite,cairo_clip_t * clip)464 _cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite,
465 cairo_clip_t *clip)
466 {
467 cairo_rectangle_int_t extents;
468 cairo_box_t box;
469
470 if (clip == NULL)
471 return TRUE;
472
473 extents = composite->destination;
474 if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE)
475 _cairo_rectangle_intersect (&extents, &composite->source);
476 if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
477 _cairo_rectangle_intersect (&extents, &composite->mask);
478
479 _cairo_box_from_rectangle (&box, &extents);
480 return _cairo_clip_contains_box (clip, &box);
481 }
482
483 cairo_int_status_t
_cairo_composite_rectangles_add_to_damage(cairo_composite_rectangles_t * composite,cairo_boxes_t * damage)484 _cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite,
485 cairo_boxes_t *damage)
486 {
487 cairo_int_status_t status;
488 int n;
489
490 for (n = 0; n < composite->clip->num_boxes; n++) {
491 status = _cairo_boxes_add (damage,
492 CAIRO_ANTIALIAS_NONE,
493 &composite->clip->boxes[n]);
494 if (unlikely (status))
495 return status;
496 }
497
498 return CAIRO_INT_STATUS_SUCCESS;
499 }
500