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