1
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
2
/* cairo - a vector graphics library with display and print output
3
 *
4
 * Copyright © 2002 University of Southern California
5
 * Copyright © 2005 Red Hat, Inc.
6
 * Copyright © 2011 Intel Corporation
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it either under the terms of the GNU Lesser General Public
10
 * License version 2.1 as published by the Free Software Foundation
11
 * (the "LGPL") or, at your option, under the terms of the Mozilla
12
 * Public License Version 1.1 (the "MPL"). If you do not alter this
13
 * notice, a recipient may use your version of this file under either
14
 * the MPL or the LGPL.
15
 *
16
 * You should have received a copy of the LGPL along with this library
17
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
19
 * You should have received a copy of the MPL along with this library
20
 * in the file COPYING-MPL-1.1
21
 *
22
 * The contents of this file are subject to the Mozilla Public License
23
 * Version 1.1 (the "License"); you may not use this file except in
24
 * compliance with the License. You may obtain a copy of the License at
25
 * http://www.mozilla.org/MPL/
26
 *
27
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
28
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
29
 * the specific language governing rights and limitations.
30
 *
31
 * The Original Code is the cairo graphics library.
32
 *
33
 * The Initial Developer of the Original Code is University of Southern
34
 * California.
35
 *
36
 * Contributor(s):
37
 *	Carl D. Worth <cworth@cworth.org>
38
 *      Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
39
 *	Chris Wilson <chris@chris-wilson.co.uk>
40
 */
41

            
42
/* This compositor renders the shape to a mask using an image surface
43
 * then calls composite.
44
 */
45

            
46
#include "cairoint.h"
47

            
48
#include "cairo-clip-inline.h"
49
#include "cairo-compositor-private.h"
50
#include "cairo-image-surface-private.h"
51
#include "cairo-pattern-inline.h"
52
#include "cairo-region-private.h"
53
#include "cairo-surface-observer-private.h"
54
#include "cairo-surface-offset-private.h"
55
#include "cairo-surface-snapshot-private.h"
56
#include "cairo-surface-subsurface-private.h"
57

            
58
typedef cairo_int_status_t
59
(*draw_func_t) (const cairo_mask_compositor_t *compositor,
60
		cairo_surface_t			*dst,
61
		void				*closure,
62
		cairo_operator_t		 op,
63
		const cairo_pattern_t		*src,
64
		const cairo_rectangle_int_t	*src_sample,
65
		int				 dst_x,
66
		int				 dst_y,
67
		const cairo_rectangle_int_t	*extents,
68
		cairo_clip_t			*clip);
69

            
70
static void do_unaligned_row(void (*blt)(void *closure,
71
					 int16_t x, int16_t y,
72
					 int16_t w, int16_t h,
73
					 uint16_t coverage),
74
			     void *closure,
75
			     const cairo_box_t *b,
76
			     int tx, int y, int h,
77
			     uint16_t coverage)
78
{
79
    int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
80
    int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
81
    if (x2 > x1) {
82
	if (! _cairo_fixed_is_integer (b->p1.x)) {
83
	    blt(closure, x1, y, 1, h,
84
		coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
85
	    x1++;
86
	}
87

            
88
	if (x2 > x1)
89
	    blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
90

            
91
	if (! _cairo_fixed_is_integer (b->p2.x))
92
	    blt(closure, x2, y, 1, h,
93
		coverage * _cairo_fixed_fractional_part (b->p2.x));
94
    } else
95
	blt(closure, x1, y, 1, h,
96
	    coverage * (b->p2.x - b->p1.x));
97
}
98

            
99
static void do_unaligned_box(void (*blt)(void *closure,
100
					 int16_t x, int16_t y,
101
					 int16_t w, int16_t h,
102
					 uint16_t coverage),
103
			     void *closure,
104
			     const cairo_box_t *b, int tx, int ty)
105
{
106
    int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
107
    int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
108
    if (y2 > y1) {
109
	if (! _cairo_fixed_is_integer (b->p1.y)) {
110
	    do_unaligned_row(blt, closure, b, tx, y1, 1,
111
			     256 - _cairo_fixed_fractional_part (b->p1.y));
112
	    y1++;
113
	}
114

            
115
	if (y2 > y1)
116
	    do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
117

            
118
	if (! _cairo_fixed_is_integer (b->p2.y))
119
	    do_unaligned_row(blt, closure, b, tx, y2, 1,
120
			     _cairo_fixed_fractional_part (b->p2.y));
121
    } else
122
	do_unaligned_row(blt, closure, b, tx, y1, 1,
123
			 b->p2.y - b->p1.y);
124
}
125

            
126
struct blt_in {
127
    const cairo_mask_compositor_t *compositor;
128
    cairo_surface_t *dst;
129
};
130

            
131
static void blt_in(void *closure,
132
		   int16_t x, int16_t y,
133
		   int16_t w, int16_t h,
134
		   uint16_t coverage)
135
{
136
    struct blt_in *info = closure;
137
    cairo_color_t color;
138
    cairo_rectangle_int_t rect;
139

            
140
    if (coverage == 0xffff)
141
	return;
142

            
143
    rect.x = x;
144
    rect.y = y;
145
    rect.width  = w;
146
    rect.height = h;
147

            
148
    _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff);
149
    info->compositor->fill_rectangles (info->dst, CAIRO_OPERATOR_IN,
150
				       &color, &rect, 1);
151
}
152

            
153
static cairo_surface_t *
154
create_composite_mask (const cairo_mask_compositor_t *compositor,
155
		       cairo_surface_t		*dst,
156
		       void			*draw_closure,
157
		       draw_func_t		 draw_func,
158
		       draw_func_t		 mask_func,
159
		       const cairo_composite_rectangles_t *extents)
160
{
161
    cairo_surface_t *surface;
162
    cairo_int_status_t status;
163
    struct blt_in info;
164
    int i;
165

            
166
    surface = _cairo_surface_create_scratch (dst, CAIRO_CONTENT_ALPHA,
167
					     extents->bounded.width,
168
					     extents->bounded.height,
169
					     NULL);
170
    if (unlikely (surface->status))
171
	return surface;
172

            
173
    status = compositor->acquire (surface);
174
    if (unlikely (status)) {
175
	cairo_surface_destroy (surface);
176
	return _cairo_int_surface_create_in_error (status);
177
    }
178

            
179
    if (!surface->is_clear) {
180
	cairo_rectangle_int_t rect;
181

            
182
	rect.x = rect.y = 0;
183
	rect.width = extents->bounded.width;
184
	rect.height = extents->bounded.height;
185

            
186
	status = compositor->fill_rectangles (surface, CAIRO_OPERATOR_CLEAR,
187
					      CAIRO_COLOR_TRANSPARENT,
188
					      &rect, 1);
189
	if (unlikely (status))
190
	    goto error;
191
    }
192

            
193
    if (mask_func) {
194
	status = mask_func (compositor, surface, draw_closure,
195
			    CAIRO_OPERATOR_SOURCE, NULL, NULL,
196
			    extents->bounded.x, extents->bounded.y,
197
			    &extents->bounded, extents->clip);
198
	if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
199
	    goto out;
200
    }
201

            
202
    /* Is it worth setting the clip region here? */
203
    status = draw_func (compositor, surface, draw_closure,
204
			CAIRO_OPERATOR_ADD, NULL, NULL,
205
			extents->bounded.x, extents->bounded.y,
206
			&extents->bounded, NULL);
207
    if (unlikely (status))
208
	goto error;
209

            
210
    info.compositor = compositor;
211
    info.dst = surface;
212
    for (i = 0; i < extents->clip->num_boxes; i++) {
213
	cairo_box_t *b = &extents->clip->boxes[i];
214

            
215
	if (! _cairo_fixed_is_integer (b->p1.x) ||
216
	    ! _cairo_fixed_is_integer (b->p1.y) ||
217
	    ! _cairo_fixed_is_integer (b->p2.x) ||
218
	    ! _cairo_fixed_is_integer (b->p2.y))
219
	{
220
	    do_unaligned_box(blt_in, &info, b,
221
			     extents->bounded.x,
222
			     extents->bounded.y);
223
	}
224
    }
225

            
226
    if (extents->clip->path != NULL) {
227
	status = _cairo_clip_combine_with_surface (extents->clip, surface,
228
						   extents->bounded.x,
229
						   extents->bounded.y);
230
	if (unlikely (status))
231
	    goto error;
232
    }
233

            
234
out:
235
    compositor->release (surface);
236
    surface->is_clear = FALSE;
237
    return surface;
238

            
239
error:
240
    compositor->release (surface);
241
    if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
242
	cairo_surface_destroy (surface);
243
	surface = _cairo_int_surface_create_in_error (status);
244
    }
245
    return surface;
246
}
247

            
248
/* Handles compositing with a clip surface when the operator allows
249
 * us to combine the clip with the mask
250
 */
251
static cairo_status_t
252
clip_and_composite_with_mask (const cairo_mask_compositor_t *compositor,
253
			      void			*draw_closure,
254
			      draw_func_t		 draw_func,
255
			      draw_func_t		 mask_func,
256
			      cairo_operator_t		 op,
257
			      cairo_pattern_t		*pattern,
258
			      const cairo_composite_rectangles_t*extents)
259
{
260
    cairo_surface_t *dst = extents->surface;
261
    cairo_surface_t *mask, *src;
262
    int src_x, src_y;
263

            
264
    mask = create_composite_mask (compositor, dst, draw_closure,
265
				  draw_func, mask_func,
266
				  extents);
267
    if (unlikely (mask->status))
268
	return mask->status;
269

            
270
    if (pattern != NULL || dst->content != CAIRO_CONTENT_ALPHA) {
271
	src = compositor->pattern_to_surface (dst,
272
					      &extents->source_pattern.base,
273
					      FALSE,
274
					      &extents->bounded,
275
					      &extents->source_sample_area,
276
					      &src_x, &src_y);
277
	if (unlikely (src->status)) {
278
	    cairo_surface_destroy (mask);
279
	    return src->status;
280
	}
281

            
282
	compositor->composite (dst, op, src, mask,
283
			       extents->bounded.x + src_x,
284
			       extents->bounded.y + src_y,
285
			       0, 0,
286
			       extents->bounded.x,      extents->bounded.y,
287
			       extents->bounded.width,  extents->bounded.height);
288

            
289
	cairo_surface_destroy (src);
290
    } else {
291
	compositor->composite (dst, op, mask, NULL,
292
			       0, 0,
293
			       0, 0,
294
			       extents->bounded.x,      extents->bounded.y,
295
			       extents->bounded.width,  extents->bounded.height);
296
    }
297
    cairo_surface_destroy (mask);
298

            
299
    return CAIRO_STATUS_SUCCESS;
300
}
301

            
302
static cairo_surface_t *
303
get_clip_source (const cairo_mask_compositor_t *compositor,
304
		 cairo_clip_t *clip,
305
		 cairo_surface_t *dst,
306
		 const cairo_rectangle_int_t *bounds,
307
		 int *out_x, int *out_y)
308
{
309
    cairo_surface_pattern_t pattern;
310
    cairo_rectangle_int_t r;
311
    cairo_surface_t *surface;
312

            
313
    surface = _cairo_clip_get_image (clip, dst, bounds);
314
    if (unlikely (surface->status))
315
	return surface;
316

            
317
    _cairo_pattern_init_for_surface (&pattern, surface);
318
    pattern.base.filter = CAIRO_FILTER_NEAREST;
319
    cairo_surface_destroy (surface);
320

            
321
    r.x = r.y = 0;
322
    r.width  = bounds->width;
323
    r.height = bounds->height;
324

            
325
    surface = compositor->pattern_to_surface (dst, &pattern.base, TRUE,
326
					      &r, &r, out_x, out_y);
327
    _cairo_pattern_fini (&pattern.base);
328

            
329
    *out_x += -bounds->x;
330
    *out_y += -bounds->y;
331
    return surface;
332
}
333

            
334
/* Handles compositing with a clip surface when we have to do the operation
335
 * in two pieces and combine them together.
336
 */
337
static cairo_status_t
338
clip_and_composite_combine (const cairo_mask_compositor_t *compositor,
339
			    void			*draw_closure,
340
			    draw_func_t		 draw_func,
341
			    cairo_operator_t		 op,
342
			    const cairo_pattern_t	*pattern,
343
			    const cairo_composite_rectangles_t*extents)
344
{
345
    cairo_surface_t *dst = extents->surface;
346
    cairo_surface_t *tmp, *clip;
347
    cairo_status_t status;
348
    int clip_x, clip_y;
349

            
350
    tmp = _cairo_surface_create_scratch (dst, dst->content,
351
					 extents->bounded.width,
352
					 extents->bounded.height,
353
					 NULL);
354
    if (unlikely (tmp->status))
355
	return tmp->status;
356

            
357
    compositor->composite (tmp, CAIRO_OPERATOR_SOURCE, dst, NULL,
358
			   extents->bounded.x,      extents->bounded.y,
359
			   0, 0,
360
			   0, 0,
361
			   extents->bounded.width,  extents->bounded.height);
362

            
363
    status = draw_func (compositor, tmp, draw_closure, op,
364
			pattern, &extents->source_sample_area,
365
			extents->bounded.x, extents->bounded.y,
366
			&extents->bounded, NULL);
367
    if (unlikely (status))
368
	goto cleanup;
369

            
370
    clip = get_clip_source (compositor,
371
			    extents->clip, dst, &extents->bounded,
372
			    &clip_x, &clip_y);
373
    if (unlikely ((status = clip->status)))
374
	goto cleanup;
375

            
376
    if (dst->is_clear) {
377
	compositor->composite (dst, CAIRO_OPERATOR_SOURCE, tmp, clip,
378
			       0, 0,
379
			       clip_x, clip_y,
380
			       extents->bounded.x,      extents->bounded.y,
381
			       extents->bounded.width,  extents->bounded.height);
382
    } else {
383
	/* Punch the clip out of the destination */
384
	compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, clip, NULL,
385
			       clip_x, clip_y,
386
			       0, 0,
387
			       extents->bounded.x,     extents->bounded.y,
388
			       extents->bounded.width, extents->bounded.height);
389

            
390
	/* Now add the two results together */
391
	compositor->composite (dst, CAIRO_OPERATOR_ADD, tmp, clip,
392
			       0, 0,
393
			       clip_x, clip_y,
394
			       extents->bounded.x,     extents->bounded.y,
395
			       extents->bounded.width, extents->bounded.height);
396
    }
397
    cairo_surface_destroy (clip);
398

            
399
cleanup:
400
    cairo_surface_destroy (tmp);
401
    return status;
402
}
403

            
404
/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
405
 * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
406
 */
407
static cairo_status_t
408
clip_and_composite_source (const cairo_mask_compositor_t	*compositor,
409
			   void				*draw_closure,
410
			   draw_func_t			 draw_func,
411
			   draw_func_t			 mask_func,
412
			   cairo_pattern_t		*pattern,
413
			   const cairo_composite_rectangles_t	*extents)
414
{
415
    cairo_surface_t *dst = extents->surface;
416
    cairo_surface_t *mask, *src;
417
    int src_x, src_y;
418

            
419
    /* Create a surface that is mask IN clip */
420
    mask = create_composite_mask (compositor, dst, draw_closure,
421
				  draw_func, mask_func,
422
				  extents);
423
    if (unlikely (mask->status))
424
	return mask->status;
425

            
426
    src = compositor->pattern_to_surface (dst,
427
					  pattern,
428
					  FALSE,
429
					  &extents->bounded,
430
					  &extents->source_sample_area,
431
					  &src_x, &src_y);
432
    if (unlikely (src->status)) {
433
	cairo_surface_destroy (mask);
434
	return src->status;
435
    }
436

            
437
    if (dst->is_clear) {
438
	compositor->composite (dst, CAIRO_OPERATOR_SOURCE, src, mask,
439
			       extents->bounded.x + src_x, extents->bounded.y + src_y,
440
			       0, 0,
441
			       extents->bounded.x,      extents->bounded.y,
442
			       extents->bounded.width,  extents->bounded.height);
443
    } else {
444
	/* Compute dest' = dest OUT (mask IN clip) */
445
	compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
446
			       0, 0, 0, 0,
447
			       extents->bounded.x,     extents->bounded.y,
448
			       extents->bounded.width, extents->bounded.height);
449

            
450
	/* Now compute (src IN (mask IN clip)) ADD dest' */
451
	compositor->composite (dst, CAIRO_OPERATOR_ADD, src, mask,
452
			       extents->bounded.x + src_x, extents->bounded.y + src_y,
453
			       0, 0,
454
			       extents->bounded.x,     extents->bounded.y,
455
			       extents->bounded.width, extents->bounded.height);
456
    }
457

            
458
    cairo_surface_destroy (src);
459
    cairo_surface_destroy (mask);
460

            
461
    return CAIRO_STATUS_SUCCESS;
462
}
463

            
464
static cairo_bool_t
465
can_reduce_alpha_op (cairo_operator_t op)
466
{
467
    int iop = op;
468
    switch (iop) {
469
    case CAIRO_OPERATOR_OVER:
470
    case CAIRO_OPERATOR_SOURCE:
471
    case CAIRO_OPERATOR_ADD:
472
	return TRUE;
473
    default:
474
	return FALSE;
475
    }
476
}
477

            
478
static cairo_bool_t
479
reduce_alpha_op (cairo_surface_t *dst,
480
		 cairo_operator_t op,
481
		 const cairo_pattern_t *pattern)
482
{
483
    return dst->is_clear &&
484
	   dst->content == CAIRO_CONTENT_ALPHA &&
485
	   _cairo_pattern_is_opaque_solid (pattern) &&
486
	   can_reduce_alpha_op (op);
487
}
488

            
489
static cairo_status_t
490
fixup_unbounded (const cairo_mask_compositor_t *compositor,
491
		 cairo_surface_t *dst,
492
		 const cairo_composite_rectangles_t *extents)
493
{
494
    cairo_rectangle_int_t rects[4];
495
    int n;
496

            
497
    if (extents->bounded.width  == extents->unbounded.width &&
498
	extents->bounded.height == extents->unbounded.height)
499
    {
500
	return CAIRO_STATUS_SUCCESS;
501
    }
502

            
503
    n = 0;
504
    if (extents->bounded.width == 0 || extents->bounded.height == 0) {
505
	rects[n].x = extents->unbounded.x;
506
	rects[n].width = extents->unbounded.width;
507
	rects[n].y = extents->unbounded.y;
508
	rects[n].height = extents->unbounded.height;
509
	n++;
510
    } else {
511
	/* top */
512
	if (extents->bounded.y != extents->unbounded.y) {
513
	    rects[n].x = extents->unbounded.x;
514
	    rects[n].width = extents->unbounded.width;
515
	    rects[n].y = extents->unbounded.y;
516
	    rects[n].height = extents->bounded.y - extents->unbounded.y;
517
	    n++;
518
	}
519
	/* left */
520
	if (extents->bounded.x != extents->unbounded.x) {
521
	    rects[n].x = extents->unbounded.x;
522
	    rects[n].width = extents->bounded.x - extents->unbounded.x;
523
	    rects[n].y = extents->bounded.y;
524
	    rects[n].height = extents->bounded.height;
525
	    n++;
526
	}
527
	/* right */
528
	if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
529
	    rects[n].x = extents->bounded.x + extents->bounded.width;
530
	    rects[n].width = extents->unbounded.x + extents->unbounded.width - rects[n].x;
531
	    rects[n].y = extents->bounded.y;
532
	    rects[n].height = extents->bounded.height;
533
	    n++;
534
	}
535
	/* bottom */
536
	if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
537
	    rects[n].x = extents->unbounded.x;
538
	    rects[n].width = extents->unbounded.width;
539
	    rects[n].y = extents->bounded.y + extents->bounded.height;
540
	    rects[n].height = extents->unbounded.y + extents->unbounded.height - rects[n].y;
541
	    n++;
542
	}
543
    }
544

            
545
    return compositor->fill_rectangles (dst, CAIRO_OPERATOR_CLEAR,
546
					CAIRO_COLOR_TRANSPARENT,
547
					rects, n);
548
}
549

            
550
static cairo_status_t
551
fixup_unbounded_with_mask (const cairo_mask_compositor_t *compositor,
552
			   cairo_surface_t *dst,
553
			   const cairo_composite_rectangles_t *extents)
554
{
555
    cairo_surface_t *mask;
556
    int mask_x, mask_y;
557

            
558
    mask = get_clip_source (compositor,
559
			    extents->clip, dst, &extents->unbounded,
560
			    &mask_x, &mask_y);
561
    if (unlikely (mask->status))
562
	return mask->status;
563

            
564
    /* top */
565
    if (extents->bounded.y != extents->unbounded.y) {
566
	int x = extents->unbounded.x;
567
	int y = extents->unbounded.y;
568
	int width = extents->unbounded.width;
569
	int height = extents->bounded.y - y;
570

            
571
	compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
572
			       x + mask_x, y + mask_y,
573
			       0, 0,
574
			       x, y,
575
			       width, height);
576
    }
577

            
578
    /* left */
579
    if (extents->bounded.x != extents->unbounded.x) {
580
	int x = extents->unbounded.x;
581
	int y = extents->bounded.y;
582
	int width = extents->bounded.x - x;
583
	int height = extents->bounded.height;
584

            
585
	compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
586
			       x + mask_x, y + mask_y,
587
			       0, 0,
588
			       x, y,
589
			       width, height);
590
    }
591

            
592
    /* right */
593
    if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
594
	int x = extents->bounded.x + extents->bounded.width;
595
	int y = extents->bounded.y;
596
	int width = extents->unbounded.x + extents->unbounded.width - x;
597
	int height = extents->bounded.height;
598

            
599
	compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
600
			       x + mask_x, y + mask_y,
601
			       0, 0,
602
			       x, y,
603
			       width, height);
604
    }
605

            
606
    /* bottom */
607
    if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
608
	int x = extents->unbounded.x;
609
	int y = extents->bounded.y + extents->bounded.height;
610
	int width = extents->unbounded.width;
611
	int height = extents->unbounded.y + extents->unbounded.height - y;
612

            
613
	compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
614
			       x + mask_x, y + mask_y,
615
			       0, 0,
616
			       x, y,
617
			       width, height);
618
    }
619

            
620
    cairo_surface_destroy (mask);
621

            
622
    return CAIRO_STATUS_SUCCESS;
623
}
624

            
625
static cairo_status_t
626
fixup_unbounded_boxes (const cairo_mask_compositor_t *compositor,
627
		       const cairo_composite_rectangles_t *extents,
628
		       cairo_boxes_t *boxes)
629
{
630
    cairo_surface_t *dst = extents->surface;
631
    cairo_boxes_t clear;
632
    cairo_region_t *clip_region;
633
    cairo_box_t box;
634
    cairo_status_t status;
635
    struct _cairo_boxes_chunk *chunk;
636
    int i;
637

            
638
    assert (boxes->is_pixel_aligned);
639

            
640
    clip_region = NULL;
641
    if (_cairo_clip_is_region (extents->clip) &&
642
	(clip_region = _cairo_clip_get_region (extents->clip)) &&
643
	cairo_region_contains_rectangle (clip_region,
644
					 &extents->bounded) == CAIRO_REGION_OVERLAP_IN)
645
	clip_region = NULL;
646

            
647

            
648
    if (boxes->num_boxes <= 1 && clip_region == NULL)
649
	return fixup_unbounded (compositor, dst, extents);
650

            
651
    _cairo_boxes_init (&clear);
652

            
653
    box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
654
    box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
655
    box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
656
    box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
657

            
658
    if (clip_region == NULL) {
659
	cairo_boxes_t tmp;
660

            
661
	_cairo_boxes_init (&tmp);
662

            
663
	status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
664
	assert (status == CAIRO_STATUS_SUCCESS);
665

            
666
	tmp.chunks.next = &boxes->chunks;
667
	tmp.num_boxes += boxes->num_boxes;
668

            
669
	status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
670
							  CAIRO_FILL_RULE_WINDING,
671
							  &clear);
672

            
673
	tmp.chunks.next = NULL;
674
    } else {
675
	pixman_box32_t *pbox;
676

            
677
	pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
678
	_cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
679

            
680
	status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
681
	assert (status == CAIRO_STATUS_SUCCESS);
682

            
683
	for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
684
	    for (i = 0; i < chunk->count; i++) {
685
		status = _cairo_boxes_add (&clear,
686
					   CAIRO_ANTIALIAS_DEFAULT,
687
					   &chunk->base[i]);
688
		if (unlikely (status)) {
689
		    _cairo_boxes_fini (&clear);
690
		    return status;
691
		}
692
	    }
693
	}
694

            
695
	status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
696
							  CAIRO_FILL_RULE_WINDING,
697
							  &clear);
698
    }
699

            
700
    if (likely (status == CAIRO_STATUS_SUCCESS)) {
701
	status = compositor->fill_boxes (dst,
702
					 CAIRO_OPERATOR_CLEAR,
703
					 CAIRO_COLOR_TRANSPARENT,
704
					 &clear);
705
    }
706

            
707
    _cairo_boxes_fini (&clear);
708

            
709
    return status;
710
}
711

            
712
enum {
713
    NEED_CLIP_REGION = 0x1,
714
    NEED_CLIP_SURFACE = 0x2,
715
    FORCE_CLIP_REGION = 0x4,
716
};
717

            
718
static cairo_bool_t
719
need_bounded_clip (cairo_composite_rectangles_t *extents)
720
{
721
    unsigned int flags = NEED_CLIP_REGION;
722
    if (! _cairo_clip_is_region (extents->clip))
723
	flags |= NEED_CLIP_SURFACE;
724
    return flags;
725
}
726

            
727
static cairo_bool_t
728
need_unbounded_clip (cairo_composite_rectangles_t *extents)
729
{
730
    unsigned int flags = 0;
731
    if (! extents->is_bounded) {
732
	flags |= NEED_CLIP_REGION;
733
	if (! _cairo_clip_is_region (extents->clip))
734
	    flags |= NEED_CLIP_SURFACE;
735
    }
736
    if (extents->clip->path != NULL)
737
	flags |= NEED_CLIP_SURFACE;
738
    return flags;
739
}
740

            
741
static cairo_status_t
742
clip_and_composite (const cairo_mask_compositor_t *compositor,
743
		    draw_func_t			 draw_func,
744
		    draw_func_t			 mask_func,
745
		    void			*draw_closure,
746
		    cairo_composite_rectangles_t*extents,
747
		    unsigned int need_clip)
748
{
749
    cairo_surface_t *dst = extents->surface;
750
    cairo_operator_t op = extents->op;
751
    cairo_pattern_t *src = &extents->source_pattern.base;
752
    cairo_region_t *clip_region = NULL;
753
    cairo_status_t status;
754

            
755
    compositor->acquire (dst);
756

            
757
    if (need_clip & NEED_CLIP_REGION) {
758
	clip_region = _cairo_clip_get_region (extents->clip);
759
	if ((need_clip & FORCE_CLIP_REGION) == 0 &&
760
	    _cairo_composite_rectangles_can_reduce_clip (extents,
761
							 extents->clip))
762
	    clip_region = NULL;
763
	if (clip_region != NULL) {
764
	    status = compositor->set_clip_region (dst, clip_region);
765
	    if (unlikely (status)) {
766
		compositor->release (dst);
767
		return status;
768
	    }
769
	}
770
    }
771

            
772
    if (reduce_alpha_op (dst, op, &extents->source_pattern.base)) {
773
	op = CAIRO_OPERATOR_ADD;
774
	src = NULL;
775
    }
776

            
777
    if (op == CAIRO_OPERATOR_SOURCE) {
778
	status = clip_and_composite_source (compositor,
779
					    draw_closure, draw_func, mask_func,
780
					    src, extents);
781
    } else {
782
	if (op == CAIRO_OPERATOR_CLEAR) {
783
	    op = CAIRO_OPERATOR_DEST_OUT;
784
	    src = NULL;
785
	}
786

            
787
	if (need_clip & NEED_CLIP_SURFACE) {
788
	    if (extents->is_bounded) {
789
		status = clip_and_composite_with_mask (compositor,
790
						       draw_closure,
791
						       draw_func,
792
						       mask_func,
793
						       op, src, extents);
794
	    } else {
795
		status = clip_and_composite_combine (compositor,
796
						     draw_closure,
797
						     draw_func,
798
						     op, src, extents);
799
	    }
800
	} else {
801
	    status = draw_func (compositor,
802
				dst, draw_closure,
803
				op, src, &extents->source_sample_area,
804
				0, 0,
805
				&extents->bounded,
806
				extents->clip);
807
	}
808
    }
809

            
810
    if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
811
	if (need_clip & NEED_CLIP_SURFACE)
812
	    status = fixup_unbounded_with_mask (compositor, dst, extents);
813
	else
814
	    status = fixup_unbounded (compositor, dst, extents);
815
    }
816

            
817
    if (clip_region)
818
	compositor->set_clip_region (dst, NULL);
819

            
820
    compositor->release (dst);
821

            
822
    return status;
823
}
824

            
825
static cairo_int_status_t
826
trim_extents_to_boxes (cairo_composite_rectangles_t *extents,
827
		       cairo_boxes_t *boxes)
828
{
829
    cairo_box_t box;
830

            
831
    _cairo_boxes_extents (boxes, &box);
832
    return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
833
}
834

            
835
static cairo_status_t
836
upload_boxes (const cairo_mask_compositor_t *compositor,
837
	      cairo_composite_rectangles_t *extents,
838
	      cairo_boxes_t *boxes)
839
{
840
    cairo_surface_t *dst = extents->surface;
841
    const cairo_pattern_t *source = &extents->source_pattern.base;
842
    cairo_surface_t *src;
843
    cairo_rectangle_int_t limit;
844
    cairo_int_status_t status;
845
    int tx, ty;
846

            
847
    src = _cairo_pattern_get_source ((cairo_surface_pattern_t *)source, &limit);
848
    if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type))
849
	return CAIRO_INT_STATUS_UNSUPPORTED;
850

            
851
    if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
852
	return CAIRO_INT_STATUS_UNSUPPORTED;
853

            
854
    /* Check that the data is entirely within the image */
855
    if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y)
856
	return CAIRO_INT_STATUS_UNSUPPORTED;
857

            
858
    if (extents->bounded.x + extents->bounded.width  + tx > limit.x + limit.width ||
859
	extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height)
860
	return CAIRO_INT_STATUS_UNSUPPORTED;
861

            
862
    tx += limit.x;
863
    ty += limit.y;
864

            
865
    if (src->type == CAIRO_SURFACE_TYPE_IMAGE)
866
	status = compositor->draw_image_boxes (dst,
867
					       (cairo_image_surface_t *)src,
868
					       boxes, tx, ty);
869
    else
870
	status = compositor->copy_boxes (dst, src, boxes, &extents->bounded,
871
					 tx, ty);
872

            
873
    return status;
874
}
875

            
876
static cairo_status_t
877
composite_boxes (const cairo_mask_compositor_t *compositor,
878
		 const cairo_composite_rectangles_t *extents,
879
		 cairo_boxes_t *boxes)
880
{
881
    cairo_surface_t *dst = extents->surface;
882
    cairo_operator_t op = extents->op;
883
    const cairo_pattern_t *source = &extents->source_pattern.base;
884
    cairo_bool_t need_clip_mask = extents->clip->path != NULL;
885
    cairo_status_t status;
886

            
887
    if (need_clip_mask &&
888
	(! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE))
889
    {
890
	return CAIRO_INT_STATUS_UNSUPPORTED;
891
    }
892

            
893
    status = compositor->acquire (dst);
894
    if (unlikely (status))
895
	return status;
896

            
897
    if (! need_clip_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) {
898
	const cairo_color_t *color;
899

            
900
	color = &((cairo_solid_pattern_t *) source)->color;
901
	status = compositor->fill_boxes (dst, op, color, boxes);
902
    } else {
903
	cairo_surface_t *src, *mask = NULL;
904
	int src_x, src_y;
905
	int mask_x = 0, mask_y = 0;
906

            
907
	if (need_clip_mask) {
908
	    mask = get_clip_source (compositor,
909
				    extents->clip, dst, &extents->bounded,
910
				    &mask_x, &mask_y);
911
	    if (unlikely (mask->status))
912
		return mask->status;
913

            
914
	    if (op == CAIRO_OPERATOR_CLEAR) {
915
		source = NULL;
916
		op = CAIRO_OPERATOR_DEST_OUT;
917
	    }
918
	}
919

            
920
	if (source || mask == NULL) {
921
	    src = compositor->pattern_to_surface (dst, source, FALSE,
922
						  &extents->bounded,
923
						  &extents->source_sample_area,
924
						  &src_x, &src_y);
925
	} else {
926
	    src = mask;
927
	    src_x = mask_x;
928
	    src_y = mask_y;
929
	    mask = NULL;
930
	}
931

            
932
	status = compositor->composite_boxes (dst, op, src, mask,
933
					      src_x, src_y,
934
					      mask_x, mask_y,
935
					      0, 0,
936
					      boxes, &extents->bounded);
937

            
938
	cairo_surface_destroy (src);
939
	cairo_surface_destroy (mask);
940
    }
941

            
942
    if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded)
943
	status = fixup_unbounded_boxes (compositor, extents, boxes);
944

            
945
    compositor->release (dst);
946

            
947
    return status;
948
}
949

            
950
static cairo_status_t
951
clip_and_composite_boxes (const cairo_mask_compositor_t *compositor,
952
			  cairo_composite_rectangles_t *extents,
953
			  cairo_boxes_t *boxes)
954
{
955
    cairo_surface_t *dst = extents->surface;
956
    cairo_int_status_t status;
957

            
958
    if (boxes->num_boxes == 0) {
959
	if (extents->is_bounded)
960
	    return CAIRO_STATUS_SUCCESS;
961

            
962
	return fixup_unbounded_boxes (compositor, extents, boxes);
963
    }
964

            
965
    if (! boxes->is_pixel_aligned)
966
	return CAIRO_INT_STATUS_UNSUPPORTED;
967

            
968
    status = trim_extents_to_boxes (extents, boxes);
969
    if (unlikely (status))
970
	return status;
971

            
972
    if (extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
973
	extents->clip->path == NULL &&
974
	(extents->op == CAIRO_OPERATOR_SOURCE ||
975
	 (dst->is_clear && (extents->op == CAIRO_OPERATOR_OVER ||
976
			    extents->op == CAIRO_OPERATOR_ADD))))
977
    {
978
	status = upload_boxes (compositor, extents, boxes);
979
	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
980
	    return status;
981
    }
982

            
983
    return composite_boxes (compositor, extents, boxes);
984
}
985

            
986
/* high-level compositor interface */
987

            
988
static cairo_int_status_t
989
_cairo_mask_compositor_paint (const cairo_compositor_t *_compositor,
990
			      cairo_composite_rectangles_t *extents)
991
{
992
    cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
993
    cairo_boxes_t boxes;
994
    cairo_int_status_t status;
995

            
996
    status = compositor->check_composite (extents);
997
    if (unlikely (status))
998
	return status;
999

            
    _cairo_clip_steal_boxes (extents->clip, &boxes);
    status = clip_and_composite_boxes (compositor, extents, &boxes);
    _cairo_clip_unsteal_boxes (extents->clip, &boxes);
    return status;
}
struct composite_opacity_info {
    const cairo_mask_compositor_t *compositor;
    uint8_t op;
    cairo_surface_t *dst;
    cairo_surface_t *src;
    int src_x, src_y;
    double opacity;
};
static void composite_opacity(void *closure,
			      int16_t x, int16_t y,
			      int16_t w, int16_t h,
			      uint16_t coverage)
{
    struct composite_opacity_info *info = closure;
    const cairo_mask_compositor_t *compositor = info->compositor;
    cairo_surface_t *mask;
    int mask_x, mask_y;
    cairo_color_t color;
    cairo_solid_pattern_t solid;
    _cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage);
    _cairo_pattern_init_solid (&solid, &color);
    mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE,
					   &_cairo_unbounded_rectangle,
					   &_cairo_unbounded_rectangle,
					   &mask_x, &mask_y);
    if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
	if (info->src) {
	    compositor->composite (info->dst, info->op, info->src, mask,
				   x + info->src_x,  y + info->src_y,
				   mask_x,           mask_y,
				   x,                y,
				   w,                h);
	} else {
	    compositor->composite (info->dst, info->op, mask, NULL,
				   mask_x,            mask_y,
				   0,                 0,
				   x,                 y,
				   w,                 h);
	}
    }
    cairo_surface_destroy (mask);
}
static cairo_int_status_t
composite_opacity_boxes (const cairo_mask_compositor_t *compositor,
			 cairo_surface_t		*dst,
			 void				*closure,
			 cairo_operator_t		 op,
			 const cairo_pattern_t		*src_pattern,
			 const cairo_rectangle_int_t	*src_sample,
			 int				 dst_x,
			 int				 dst_y,
			 const cairo_rectangle_int_t	*extents,
			 cairo_clip_t			*clip)
{
    const cairo_solid_pattern_t *mask_pattern = closure;
    struct composite_opacity_info info;
    int i;
    assert (clip);
    info.compositor = compositor;
    info.op = op;
    info.dst = dst;
    if (src_pattern != NULL) {
	info.src = compositor->pattern_to_surface (dst, src_pattern, FALSE,
						   extents, src_sample,
						   &info.src_x, &info.src_y);
	if (unlikely (info.src->status))
	    return info.src->status;
    } else
	info.src = NULL;
    info.opacity = mask_pattern->color.alpha / (double) 0xffff;
    /* XXX for lots of boxes create a clip region for the fully opaque areas */
    for (i = 0; i < clip->num_boxes; i++)
	do_unaligned_box(composite_opacity, &info,
			 &clip->boxes[i], dst_x, dst_y);
    cairo_surface_destroy (info.src);
    return CAIRO_STATUS_SUCCESS;
}
struct composite_box_info {
    const cairo_mask_compositor_t *compositor;
    cairo_surface_t *dst;
    cairo_surface_t *src;
    int src_x, src_y;
    uint8_t op;
};
static void composite_box(void *closure,
			  int16_t x, int16_t y,
			  int16_t w, int16_t h,
			  uint16_t coverage)
{
    struct composite_box_info *info = closure;
    const cairo_mask_compositor_t *compositor = info->compositor;
    if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) {
	cairo_surface_t *mask;
	cairo_color_t color;
	cairo_solid_pattern_t solid;
	int mask_x, mask_y;
	_cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff);
	_cairo_pattern_init_solid (&solid, &color);
	mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE,
					       &_cairo_unbounded_rectangle,
					       &_cairo_unbounded_rectangle,
					       &mask_x, &mask_y);
	if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
	    compositor->composite (info->dst, info->op, info->src, mask,
				   x + info->src_x,  y + info->src_y,
				   mask_x,           mask_y,
				   x,                y,
				   w,                h);
	}
	cairo_surface_destroy (mask);
    } else {
	compositor->composite (info->dst, info->op, info->src, NULL,
			       x + info->src_x,  y + info->src_y,
			       0,                0,
			       x,                y,
			       w,                h);
    }
}
static cairo_int_status_t
composite_mask_clip_boxes (const cairo_mask_compositor_t *compositor,
			   cairo_surface_t		*dst,
			   void				*closure,
			   cairo_operator_t		 op,
			   const cairo_pattern_t	*src_pattern,
			   const cairo_rectangle_int_t	*src_sample,
			   int				 dst_x,
			   int				 dst_y,
			   const cairo_rectangle_int_t	*extents,
			   cairo_clip_t			*clip)
{
    cairo_composite_rectangles_t *composite = closure;
    struct composite_box_info info;
    int i;
    assert (src_pattern == NULL);
    assert (op == CAIRO_OPERATOR_SOURCE);
    info.compositor = compositor;
    info.op = CAIRO_OPERATOR_SOURCE;
    info.dst = dst;
    info.src = compositor->pattern_to_surface (dst,
					       &composite->mask_pattern.base,
					       FALSE, extents,
					       &composite->mask_sample_area,
					       &info.src_x, &info.src_y);
    if (unlikely (info.src->status))
	return info.src->status;
    info.src_x += dst_x;
    info.src_y += dst_y;
    for (i = 0; i < clip->num_boxes; i++)
	do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y);
    cairo_surface_destroy (info.src);
    return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
composite_mask (const cairo_mask_compositor_t *compositor,
		cairo_surface_t			*dst,
		void				*closure,
		cairo_operator_t		 op,
		const cairo_pattern_t		*src_pattern,
		const cairo_rectangle_int_t	*src_sample,
		int				 dst_x,
		int				 dst_y,
		const cairo_rectangle_int_t	*extents,
		cairo_clip_t			*clip)
{
    cairo_composite_rectangles_t *composite = closure;
    cairo_surface_t *src, *mask;
    int src_x, src_y;
    int mask_x, mask_y;
    if (src_pattern != NULL) {
	src = compositor->pattern_to_surface (dst, src_pattern, FALSE,
					      extents, src_sample,
					      &src_x, &src_y);
	if (unlikely (src->status))
	    return src->status;
	mask = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, TRUE,
					       extents, &composite->mask_sample_area,
					       &mask_x, &mask_y);
	if (unlikely (mask->status)) {
	    cairo_surface_destroy (src);
	    return mask->status;
	}
	compositor->composite (dst, op, src, mask,
			       extents->x + src_x,  extents->y + src_y,
			       extents->x + mask_x, extents->y + mask_y,
			       extents->x - dst_x,  extents->y - dst_y,
			       extents->width,      extents->height);
	cairo_surface_destroy (mask);
	cairo_surface_destroy (src);
    } else {
	src = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, FALSE,
					      extents, &composite->mask_sample_area,
					      &src_x, &src_y);
	if (unlikely (src->status))
	    return src->status;
	compositor->composite (dst, op, src, NULL,
			       extents->x + src_x,  extents->y + src_y,
			       0, 0,
			       extents->x - dst_x,  extents->y - dst_y,
			       extents->width,      extents->height);
	cairo_surface_destroy (src);
    }
    return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_mask_compositor_mask (const cairo_compositor_t *_compositor,
			     cairo_composite_rectangles_t *extents)
{
    const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
    status = compositor->check_composite (extents);
    if (unlikely (status))
	return status;
    if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
	extents->clip->path == NULL &&
	_cairo_clip_is_region (extents->clip)) {
	status = clip_and_composite (compositor,
				     composite_opacity_boxes,
				     composite_opacity_boxes,
				     &extents->mask_pattern.solid,
				     extents, need_unbounded_clip (extents));
    } else {
	status = clip_and_composite (compositor,
				     composite_mask,
				     extents->clip->path == NULL ? composite_mask_clip_boxes : NULL,
				     extents,
				     extents, need_bounded_clip (extents));
    }
    return status;
}
static cairo_int_status_t
_cairo_mask_compositor_stroke (const cairo_compositor_t *_compositor,
			       cairo_composite_rectangles_t *extents,
			       const cairo_path_fixed_t	*path,
			       const cairo_stroke_style_t	*style,
			       const cairo_matrix_t	*ctm,
			       const cairo_matrix_t	*ctm_inverse,
			       double		 tolerance,
			       cairo_antialias_t	 antialias)
{
    const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
    cairo_surface_t *mask;
    cairo_surface_pattern_t pattern;
    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
    status = compositor->check_composite (extents);
    if (unlikely (status))
	return status;
    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
	cairo_boxes_t boxes;
	_cairo_boxes_init_with_clip (&boxes, extents->clip);
	status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
								style,
								ctm,
								antialias,
								&boxes);
	if (likely (status == CAIRO_INT_STATUS_SUCCESS))
	    status = clip_and_composite_boxes (compositor, extents, &boxes);
	_cairo_boxes_fini (&boxes);
    }
    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
	mask = cairo_surface_create_similar_image (extents->surface,
						   CAIRO_FORMAT_A8,
						   extents->bounded.width,
						   extents->bounded.height);
	if (unlikely (mask->status))
	    return mask->status;
	status = _cairo_surface_offset_stroke (mask,
					       extents->bounded.x,
					       extents->bounded.y,
					       CAIRO_OPERATOR_ADD,
					       &_cairo_pattern_white.base,
					       path, style, ctm, ctm_inverse,
					       tolerance, antialias,
					       extents->clip);
	if (unlikely (status)) {
	    cairo_surface_destroy (mask);
	    return status;
	}
	_cairo_pattern_init_for_surface (&pattern, mask);
	cairo_surface_destroy (mask);
	cairo_matrix_init_translate (&pattern.base.matrix,
				     -extents->bounded.x,
				     -extents->bounded.y);
	pattern.base.filter = CAIRO_FILTER_NEAREST;
	pattern.base.extend = CAIRO_EXTEND_NONE;
	status = _cairo_surface_mask (extents->surface,
				      extents->op,
				      &extents->source_pattern.base,
				      &pattern.base,
				      extents->clip);
	_cairo_pattern_fini (&pattern.base);
    }
    return status;
}
static cairo_int_status_t
_cairo_mask_compositor_fill (const cairo_compositor_t *_compositor,
			     cairo_composite_rectangles_t *extents,
			     const cairo_path_fixed_t	*path,
			     cairo_fill_rule_t	 fill_rule,
			     double			 tolerance,
			     cairo_antialias_t	 antialias)
{
    const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
    cairo_surface_t *mask;
    cairo_surface_pattern_t pattern;
    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
    status = compositor->check_composite (extents);
    if (unlikely (status))
	return status;
    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
	cairo_boxes_t boxes;
	_cairo_boxes_init_with_clip (&boxes, extents->clip);
	status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
							      fill_rule,
							      antialias,
							      &boxes);
	if (likely (status == CAIRO_INT_STATUS_SUCCESS))
	    status = clip_and_composite_boxes (compositor, extents, &boxes);
	_cairo_boxes_fini (&boxes);
    }
    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
	mask = cairo_surface_create_similar_image (extents->surface,
						   CAIRO_FORMAT_A8,
						   extents->bounded.width,
						   extents->bounded.height);
	if (unlikely (mask->status))
	    return mask->status;
	status = _cairo_surface_offset_fill (mask,
					     extents->bounded.x,
					     extents->bounded.y,
					     CAIRO_OPERATOR_ADD,
					     &_cairo_pattern_white.base,
					     path, fill_rule, tolerance, antialias,
					     extents->clip);
	if (unlikely (status)) {
	    cairo_surface_destroy (mask);
	    return status;
	}
	_cairo_pattern_init_for_surface (&pattern, mask);
	cairo_surface_destroy (mask);
	cairo_matrix_init_translate (&pattern.base.matrix,
				     -extents->bounded.x,
				     -extents->bounded.y);
	pattern.base.filter = CAIRO_FILTER_NEAREST;
	pattern.base.extend = CAIRO_EXTEND_NONE;
	status = _cairo_surface_mask (extents->surface,
				      extents->op,
				      &extents->source_pattern.base,
				      &pattern.base,
				      extents->clip);
	_cairo_pattern_fini (&pattern.base);
    }
    return status;
}
static cairo_int_status_t
_cairo_mask_compositor_glyphs (const cairo_compositor_t *_compositor,
			       cairo_composite_rectangles_t *extents,
			       cairo_scaled_font_t	*scaled_font,
			       cairo_glyph_t		*glyphs,
			       int			 num_glyphs,
			       cairo_bool_t		 overlap)
{
    const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
    cairo_surface_t *mask;
    cairo_surface_pattern_t pattern;
    cairo_int_status_t status;
    status = compositor->check_composite (extents);
    if (unlikely (status))
	return CAIRO_INT_STATUS_UNSUPPORTED;
    mask = cairo_surface_create_similar_image (extents->surface,
					       CAIRO_FORMAT_A8,
					       extents->bounded.width,
					       extents->bounded.height);
    if (unlikely (mask->status))
	return mask->status;
    status = _cairo_surface_offset_glyphs (mask,
					   extents->bounded.x,
					   extents->bounded.y,
					   CAIRO_OPERATOR_ADD,
					   &_cairo_pattern_white.base,
					   scaled_font, glyphs, num_glyphs,
					   extents->clip);
    if (unlikely (status)) {
	cairo_surface_destroy (mask);
	return status;
    }
    _cairo_pattern_init_for_surface (&pattern, mask);
    cairo_surface_destroy (mask);
    cairo_matrix_init_translate (&pattern.base.matrix,
				 -extents->bounded.x,
				 -extents->bounded.y);
    pattern.base.filter = CAIRO_FILTER_NEAREST;
    pattern.base.extend = CAIRO_EXTEND_NONE;
    status = _cairo_surface_mask (extents->surface,
				  extents->op,
				  &extents->source_pattern.base,
				  &pattern.base,
				  extents->clip);
    _cairo_pattern_fini (&pattern.base);
    return status;
}
void
4
_cairo_mask_compositor_init (cairo_mask_compositor_t *compositor,
			     const cairo_compositor_t *delegate)
{
4
    compositor->base.delegate = delegate;
4
    compositor->base.paint = _cairo_mask_compositor_paint;
4
    compositor->base.mask  = _cairo_mask_compositor_mask;
4
    compositor->base.fill  = _cairo_mask_compositor_fill;
4
    compositor->base.stroke = _cairo_mask_compositor_stroke;
4
    compositor->base.glyphs = _cairo_mask_compositor_glyphs;
4
}