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
1254732
void _cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents)
46
{
47
    /* If adding further free() code here, make sure those fields are inited by
48
     * _cairo_composite_rectangles_init IN ALL CASES
49
     */
50
1254732
    _cairo_clip_destroy (extents->clip);
51
1254732
    extents->clip = NULL;
52
1254732
}
53

            
54
static void
55
1183327
_cairo_composite_reduce_pattern (const cairo_pattern_t *src,
56
				 cairo_pattern_union_t *dst)
57
{
58
    int tx, ty;
59

            
60
1183327
    _cairo_pattern_init_static_copy (&dst->base, src);
61
1183327
    if (dst->base.type == CAIRO_PATTERN_TYPE_SOLID)
62
865413
	return;
63

            
64
317914
    dst->base.filter = _cairo_pattern_analyze_filter (&dst->base);
65

            
66
317914
    tx = ty = 0;
67
317914
    if (_cairo_matrix_is_pixman_translation (&dst->base.matrix,
68
					     dst->base.filter,
69
					     &tx, &ty))
70
    {
71
146387
	dst->base.matrix.x0 = tx;
72
146387
	dst->base.matrix.y0 = ty;
73
    }
74
}
75

            
76
static inline cairo_bool_t
77
1254732
_cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents,
78
				  cairo_surface_t *surface,
79
				  cairo_operator_t op,
80
				  const cairo_pattern_t *source,
81
				  const cairo_clip_t *clip)
82
{
83
    /* Always set the clip so that a _cairo_composite_rectangles_init can ALWAYS be
84
     * balanced by a _cairo_composite_rectangles_fini */
85
1254732
    extents->clip = NULL;
86

            
87
1254732
    if (_cairo_clip_is_all_clipped (clip))
88
	return FALSE;
89
1254732
    extents->surface = surface;
90
1254732
    extents->op = op;
91

            
92
1254732
    _cairo_surface_get_extents (surface, &extents->destination);
93

            
94
1254732
    extents->unbounded = extents->destination;
95
1254732
    if (clip && ! _cairo_rectangle_intersect (&extents->unbounded,
96
					      _cairo_clip_get_extents (clip)))
97
76094
	return FALSE;
98

            
99
1178638
    extents->bounded = extents->unbounded;
100
1178638
    extents->is_bounded = _cairo_operator_bounded_by_either (op);
101

            
102
1178638
    extents->original_source_pattern = source;
103
1178638
    _cairo_composite_reduce_pattern (source, &extents->source_pattern);
104

            
105
1178638
    _cairo_pattern_get_extents (&extents->source_pattern.base,
106
				&extents->source,
107
1178638
				surface->is_vector);
108
1178638
    if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) {
109
949401
	if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source))
110
20
	    return FALSE;
111
    }
112

            
113
1178618
    extents->original_mask_pattern = NULL;
114
1178618
    extents->mask_pattern.base.type = CAIRO_PATTERN_TYPE_SOLID;
115
1178618
    extents->mask_pattern.solid.color.alpha = 1.; /* XXX full initialisation? */
116
1178618
    extents->mask_pattern.solid.color.alpha_short = 0xffff;
117

            
118
1178618
    return TRUE;
119
}
120

            
121
cairo_int_status_t
122
639162
_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
123
					    cairo_surface_t *surface,
124
					    cairo_operator_t		 op,
125
					    const cairo_pattern_t	*source,
126
					    const cairo_clip_t		*clip)
127
{
128
639162
    if (! _cairo_composite_rectangles_init (extents,
129
					    surface, op, source, clip))
130
    {
131
76111
	goto NOTHING_TO_DO;
132
    }
133

            
134
563051
    extents->mask = extents->destination;
135

            
136
563051
    extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
137
563051
    if (_cairo_clip_is_all_clipped (extents->clip))
138
	goto NOTHING_TO_DO;
139

            
140
563051
    if (! _cairo_rectangle_intersect (&extents->unbounded,
141
563051
				      _cairo_clip_get_extents (extents->clip)))
142
	goto NOTHING_TO_DO;
143

            
144
563051
    if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
145
286033
	_cairo_pattern_sampled_area (&extents->source_pattern.base,
146
286033
				     &extents->bounded,
147
				     &extents->source_sample_area);
148

            
149
563051
    return CAIRO_INT_STATUS_SUCCESS;
150
76111
NOTHING_TO_DO:
151
76111
    _cairo_composite_rectangles_fini(extents);
152
76111
    return CAIRO_INT_STATUS_NOTHING_TO_DO;
153
}
154

            
155
static cairo_int_status_t
156
615567
_cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents,
157
				       const cairo_clip_t *clip)
158
{
159
615567
    if ((!_cairo_rectangle_intersect (&extents->bounded, &extents->mask)) &&
160
1959
        (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK))
161
1959
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
162

            
163
613608
    if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
164
609159
	extents->unbounded = extents->bounded;
165
4449
    } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
166
3888
	if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
167
	    return CAIRO_INT_STATUS_NOTHING_TO_DO;
168
    }
169

            
170
613608
    extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
171
613608
    if (_cairo_clip_is_all_clipped (extents->clip))
172
30
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
173

            
174
613578
    if (! _cairo_rectangle_intersect (&extents->unbounded,
175
613578
				      _cairo_clip_get_extents (extents->clip)))
176
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
177

            
178
613578
    if (! _cairo_rectangle_intersect (&extents->bounded,
179
613578
				      _cairo_clip_get_extents (extents->clip)) &&
180
	extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
181
    {
182
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
183
    }
184

            
185
613578
    if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
186
30015
	_cairo_pattern_sampled_area (&extents->source_pattern.base,
187
30015
				     &extents->bounded,
188
				     &extents->source_sample_area);
189
613578
    if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
190
1455
	_cairo_pattern_sampled_area (&extents->mask_pattern.base,
191
1455
				     &extents->bounded,
192
				     &extents->mask_sample_area);
193
1455
	if (extents->mask_sample_area.width == 0 ||
194
1455
	    extents->mask_sample_area.height == 0) {
195
	    _cairo_composite_rectangles_fini (extents);
196
	    return CAIRO_INT_STATUS_NOTHING_TO_DO;
197
	}
198
    }
199

            
200
613578
    return CAIRO_INT_STATUS_SUCCESS;
201
}
202

            
203
cairo_int_status_t
204
_cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents,
205
						      const cairo_box_t *box)
206
{
207
    cairo_rectangle_int_t rect;
208
    cairo_clip_t *clip;
209

            
210
    _cairo_box_round_to_rectangle (box, &rect);
211
    if (rect.x == extents->source.x &&
212
	rect.y == extents->source.y &&
213
	rect.width  == extents->source.width &&
214
	rect.height == extents->source.height)
215
    {
216
	return CAIRO_INT_STATUS_SUCCESS;
217
    }
218

            
219
    _cairo_rectangle_intersect (&extents->source, &rect);
220

            
221
    rect = extents->bounded;
222
    if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source) &&
223
	extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE)
224
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
225

            
226
    if (rect.width  == extents->bounded.width &&
227
	rect.height == extents->bounded.height)
228
	return CAIRO_INT_STATUS_SUCCESS;
229

            
230
    if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
231
	extents->unbounded = extents->bounded;
232
    } else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
233
	if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
234
	    return CAIRO_INT_STATUS_NOTHING_TO_DO;
235
    }
236

            
237
    clip = extents->clip;
238
    extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
239
    if (clip != extents->clip)
240
	_cairo_clip_destroy (clip);
241

            
242
    if (_cairo_clip_is_all_clipped (extents->clip))
243
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
244

            
245
    if (! _cairo_rectangle_intersect (&extents->unbounded,
246
				      _cairo_clip_get_extents (extents->clip)))
247
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
248

            
249
    if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
250
	_cairo_pattern_sampled_area (&extents->source_pattern.base,
251
				     &extents->bounded,
252
				     &extents->source_sample_area);
253
    if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
254
	_cairo_pattern_sampled_area (&extents->mask_pattern.base,
255
				     &extents->bounded,
256
				     &extents->mask_sample_area);
257
	if (extents->mask_sample_area.width == 0 ||
258
	    extents->mask_sample_area.height == 0)
259
	    return CAIRO_INT_STATUS_NOTHING_TO_DO;
260
    }
261

            
262
    return CAIRO_INT_STATUS_SUCCESS;
263
}
264

            
265
cairo_int_status_t
266
1107882
_cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents,
267
						    const cairo_box_t *box)
268
{
269
    cairo_rectangle_int_t mask;
270
    cairo_clip_t *clip;
271

            
272
1107882
    _cairo_box_round_to_rectangle (box, &mask);
273
1107882
    if (mask.x == extents->mask.x &&
274
322727
	mask.y == extents->mask.y &&
275
310281
	mask.width  == extents->mask.width &&
276
306532
	mask.height == extents->mask.height)
277
    {
278
306004
	return CAIRO_INT_STATUS_SUCCESS;
279
    }
280

            
281
801878
    _cairo_rectangle_intersect (&extents->mask, &mask);
282

            
283
801878
    mask = extents->bounded;
284
801878
    if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask) &&
285
216463
	extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
286
216331
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
287

            
288
585547
    if (mask.width  == extents->bounded.width &&
289
578842
	mask.height == extents->bounded.height)
290
578563
	return CAIRO_INT_STATUS_SUCCESS;
291

            
292
6984
    if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
293
6747
	extents->unbounded = extents->bounded;
294
237
    } 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
6984
    clip = extents->clip;
300
6984
    extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
301
6984
    if (clip != extents->clip)
302
6984
	_cairo_clip_destroy (clip);
303

            
304
6984
    if (_cairo_clip_is_all_clipped (extents->clip))
305
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
306

            
307
6984
    if (! _cairo_rectangle_intersect (&extents->unbounded,
308
6984
				      _cairo_clip_get_extents (extents->clip)))
309
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
310

            
311
6984
    if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
312
126
	_cairo_pattern_sampled_area (&extents->source_pattern.base,
313
126
				     &extents->bounded,
314
				     &extents->source_sample_area);
315
6984
    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
6984
    return CAIRO_INT_STATUS_SUCCESS;
325
}
326

            
327
cairo_int_status_t
328
4689
_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents,
329
					   cairo_surface_t              *surface,
330
					   cairo_operator_t		 op,
331
					   const cairo_pattern_t	*source,
332
					   const cairo_pattern_t	*mask,
333
					   const cairo_clip_t		*clip)
334
{
335
    cairo_int_status_t status;
336
4689
    if (! _cairo_composite_rectangles_init (extents,
337
					    surface, op, source, clip))
338
    {
339
	_cairo_composite_rectangles_fini(extents);
340
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
341
    }
342

            
343
4689
    extents->original_mask_pattern = mask;
344
4689
    _cairo_composite_reduce_pattern (mask, &extents->mask_pattern);
345
4689
    _cairo_pattern_get_extents (&extents->mask_pattern.base, &extents->mask, surface->is_vector);
346

            
347
4689
    status = _cairo_composite_rectangles_intersect (extents, clip);
348
4689
    if(status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
349
48
	_cairo_composite_rectangles_fini(extents);
350
    }
351
4689
    return status;
352
}
353

            
354
cairo_int_status_t
355
40750
_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents,
356
					     cairo_surface_t *surface,
357
					     cairo_operator_t		 op,
358
					     const cairo_pattern_t	*source,
359
					     const cairo_path_fixed_t		*path,
360
					     const cairo_stroke_style_t	*style,
361
					     const cairo_matrix_t	*ctm,
362
					     const cairo_clip_t		*clip)
363
{
364
    cairo_int_status_t status;
365
40750
    if (! _cairo_composite_rectangles_init (extents,
366
					    surface, op, source, clip))
367
    {
368
3
	_cairo_composite_rectangles_fini(extents);
369
3
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
370
    }
371

            
372
40747
    _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, surface->is_vector, &extents->mask);
373

            
374
40747
    status = _cairo_composite_rectangles_intersect (extents, clip);
375
40747
    if(status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
376
84
	_cairo_composite_rectangles_fini(extents);
377
    }
378
40747
    return status;
379
}
380

            
381
cairo_int_status_t
382
499913
_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents,
383
					   cairo_surface_t *surface,
384
					   cairo_operator_t		 op,
385
					   const cairo_pattern_t	*source,
386
					   const cairo_path_fixed_t		*path,
387
					   const cairo_clip_t		*clip)
388
{
389
    cairo_int_status_t status;
390
499913
    if (! _cairo_composite_rectangles_init (extents,
391
					    surface, op, source, clip))
392
    {
393
	_cairo_composite_rectangles_fini(extents);
394
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
395
    }
396

            
397
499913
    _cairo_path_fixed_approximate_fill_extents (path, &extents->mask);
398

            
399
499913
    status = _cairo_composite_rectangles_intersect (extents, clip);
400
499913
        if(status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
401
666
    _cairo_composite_rectangles_fini(extents);
402
    }
403
499913
    return status;
404
}
405

            
406
cairo_int_status_t
407
96
_cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents,
408
					      cairo_surface_t		*surface,
409
					      cairo_operator_t		 op,
410
					      const cairo_pattern_t	*source,
411
					      const cairo_polygon_t	*polygon,
412
					      const cairo_clip_t		*clip)
413
{
414
    cairo_int_status_t status;
415
96
    if (! _cairo_composite_rectangles_init (extents,
416
					    surface, op, source, clip))
417
    {
418
	_cairo_composite_rectangles_fini(extents);
419
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
420
    }
421

            
422
96
    _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
423
96
    status = _cairo_composite_rectangles_intersect (extents, clip);
424
96
    if(status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
425
	_cairo_composite_rectangles_fini(extents);
426
    }
427
96
    return status;
428
}
429

            
430
cairo_int_status_t
431
27
_cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents,
432
					      cairo_surface_t		*surface,
433
					      cairo_operator_t		 op,
434
					      const cairo_pattern_t	*source,
435
					      const cairo_boxes_t	*boxes,
436
					      const cairo_clip_t		*clip)
437
{
438
    cairo_box_t box;
439
    cairo_int_status_t status;
440

            
441
27
    if (! _cairo_composite_rectangles_init (extents,
442
					    surface, op, source, clip))
443
    {
444
	_cairo_composite_rectangles_fini(extents);
445
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
446
    }
447

            
448
27
    _cairo_boxes_extents (boxes, &box);
449
27
    _cairo_box_round_to_rectangle (&box, &extents->mask);
450
27
    status = _cairo_composite_rectangles_intersect (extents, clip);
451
27
    if(status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
452
	_cairo_composite_rectangles_fini(extents);
453
    }
454
27
    return status;
455
}
456

            
457
cairo_int_status_t
458
70095
_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents,
459
					     cairo_surface_t *surface,
460
					     cairo_operator_t		 op,
461
					     const cairo_pattern_t	*source,
462
					     cairo_scaled_font_t	*scaled_font,
463
					     cairo_glyph_t		*glyphs,
464
					     int			 num_glyphs,
465
					     const cairo_clip_t		*clip,
466
					     cairo_bool_t		*overlap)
467
{
468
    cairo_status_t status;
469
    cairo_int_status_t int_status;
470

            
471
70095
    if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip)) {
472
	_cairo_composite_rectangles_fini(extents);
473
	return CAIRO_INT_STATUS_NOTHING_TO_DO;
474
    }
475

            
476
70095
    status = _cairo_scaled_font_glyph_device_extents (scaled_font,
477
						      glyphs, num_glyphs,
478
						      &extents->mask,
479
						      overlap);
480
70095
    if (unlikely (status)) {
481
	_cairo_composite_rectangles_fini(extents);
482
	return status;
483
    }
484
70095
    if (overlap && *overlap &&
485
34959
	scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE &&
486
	_cairo_pattern_is_opaque_solid (&extents->source_pattern.base))
487
    {
488
	*overlap = FALSE;
489
    }
490

            
491
70095
    int_status = _cairo_composite_rectangles_intersect (extents, clip);
492
70095
    if (int_status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
493
1191
	_cairo_composite_rectangles_fini(extents);
494
    }
495
70095
    return int_status;
496
}
497

            
498
cairo_bool_t
499
2741
_cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite,
500
					     cairo_clip_t *clip)
501
{
502
    cairo_rectangle_int_t extents;
503
    cairo_box_t box;
504

            
505
2741
    if (clip == NULL)
506
26
	return TRUE;
507

            
508
2715
    extents = composite->destination;
509
2715
    if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE)
510
2590
	_cairo_rectangle_intersect (&extents, &composite->source);
511
2715
    if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
512
2715
	_cairo_rectangle_intersect (&extents, &composite->mask);
513

            
514
2715
    _cairo_box_from_rectangle (&box, &extents);
515
2715
    return _cairo_clip_contains_box (clip, &box);
516
}
517

            
518
cairo_int_status_t
519
_cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite,
520
					   cairo_boxes_t *damage)
521
{
522
    cairo_int_status_t status;
523
    int n;
524

            
525
    for (n = 0; n < composite->clip->num_boxes; n++) {
526
	status = _cairo_boxes_add (damage,
527
			  CAIRO_ANTIALIAS_NONE,
528
			  &composite->clip->boxes[n]);
529
	if (unlikely (status))
530
	    return status;
531
    }
532

            
533
    return CAIRO_INT_STATUS_SUCCESS;
534
}