beta-0.89.2
[luatex.git] / source / libs / cairo / cairo-src / src / cairo-mask-compositor.c
blob4d6b118dda5846321eab415ba4485e252d4c9460
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
4 * Copyright © 2002 University of Southern California
5 * Copyright © 2005 Red Hat, Inc.
6 * Copyright © 2011 Intel Corporation
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.
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
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/
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.
31 * The Original Code is the cairo graphics library.
33 * The Initial Developer of the Original Code is University of Southern
34 * California.
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>
42 /* This compositor renders the shape to a mask using an image surface
43 * then calls composite.
46 #include "cairoint.h"
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"
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);
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)
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++;
88 if (x2 > x1)
89 blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
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));
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)
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++;
115 if (y2 > y1)
116 do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
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);
126 struct blt_in {
127 const cairo_mask_compositor_t *compositor;
128 cairo_surface_t *dst;
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)
136 struct blt_in *info = closure;
137 cairo_color_t color;
138 cairo_rectangle_int_t rect;
140 if (coverage == 0xffff)
141 return;
143 rect.x = x;
144 rect.y = y;
145 rect.width = w;
146 rect.height = h;
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);
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)
161 cairo_surface_t *surface;
162 cairo_int_status_t status;
163 struct blt_in info;
164 int i;
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;
173 status = compositor->acquire (surface);
174 if (unlikely (status)) {
175 cairo_surface_destroy (surface);
176 return _cairo_int_surface_create_in_error (status);
179 if (!surface->is_clear) {
180 cairo_rectangle_int_t rect;
182 rect.x = rect.y = 0;
183 rect.width = extents->bounded.width;
184 rect.height = extents->bounded.height;
186 status = compositor->fill_rectangles (surface, CAIRO_OPERATOR_CLEAR,
187 CAIRO_COLOR_TRANSPARENT,
188 &rect, 1);
189 if (unlikely (status))
190 goto error;
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;
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;
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];
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))
220 do_unaligned_box(blt_in, &info, b,
221 extents->bounded.x,
222 extents->bounded.y);
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;
234 out:
235 compositor->release (surface);
236 surface->is_clear = FALSE;
237 return surface;
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);
245 return surface;
248 /* Handles compositing with a clip surface when the operator allows
249 * us to combine the clip with the mask
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)
260 cairo_surface_t *dst = extents->surface;
261 cairo_surface_t *mask, *src;
262 int src_x, src_y;
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;
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;
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);
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);
297 cairo_surface_destroy (mask);
299 return CAIRO_STATUS_SUCCESS;
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)
309 cairo_surface_pattern_t pattern;
310 cairo_rectangle_int_t r;
311 cairo_surface_t *surface;
313 surface = _cairo_clip_get_image (clip, dst, bounds);
314 if (unlikely (surface->status))
315 return surface;
317 _cairo_pattern_init_for_surface (&pattern, surface);
318 pattern.base.filter = CAIRO_FILTER_NEAREST;
319 cairo_surface_destroy (surface);
321 r.x = r.y = 0;
322 r.width = bounds->width;
323 r.height = bounds->height;
325 surface = compositor->pattern_to_surface (dst, &pattern.base, TRUE,
326 &r, &r, out_x, out_y);
327 _cairo_pattern_fini (&pattern.base);
329 *out_x += -bounds->x;
330 *out_y += -bounds->y;
331 return surface;
334 /* Handles compositing with a clip surface when we have to do the operation
335 * in two pieces and combine them together.
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)
345 cairo_surface_t *dst = extents->surface;
346 cairo_surface_t *tmp, *clip;
347 cairo_status_t status;
348 int clip_x, clip_y;
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;
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);
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;
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;
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);
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);
397 cairo_surface_destroy (clip);
399 cleanup:
400 cairo_surface_destroy (tmp);
401 return status;
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))
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)
415 cairo_surface_t *dst = extents->surface;
416 cairo_surface_t *mask, *src;
417 int src_x, src_y;
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;
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;
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);
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);
458 cairo_surface_destroy (src);
459 cairo_surface_destroy (mask);
461 return CAIRO_STATUS_SUCCESS;
464 static cairo_bool_t
465 can_reduce_alpha_op (cairo_operator_t op)
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;
478 static cairo_bool_t
479 reduce_alpha_op (cairo_surface_t *dst,
480 cairo_operator_t op,
481 const cairo_pattern_t *pattern)
483 return dst->is_clear &&
484 dst->content == CAIRO_CONTENT_ALPHA &&
485 _cairo_pattern_is_opaque_solid (pattern) &&
486 can_reduce_alpha_op (op);
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)
494 cairo_rectangle_int_t rects[4];
495 int n;
497 if (extents->bounded.width == extents->unbounded.width &&
498 extents->bounded.height == extents->unbounded.height)
500 return CAIRO_STATUS_SUCCESS;
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++;
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++;
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++;
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++;
545 return compositor->fill_rectangles (dst, CAIRO_OPERATOR_CLEAR,
546 CAIRO_COLOR_TRANSPARENT,
547 rects, n);
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)
555 cairo_surface_t *mask;
556 int mask_x, mask_y;
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;
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;
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);
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;
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);
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;
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);
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;
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);
620 cairo_surface_destroy (mask);
622 return CAIRO_STATUS_SUCCESS;
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)
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;
638 assert (boxes->is_pixel_aligned);
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;
648 if (boxes->num_boxes <= 1 && clip_region == NULL)
649 return fixup_unbounded (compositor, dst, extents);
651 _cairo_boxes_init (&clear);
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);
658 if (clip_region == NULL) {
659 cairo_boxes_t tmp;
661 _cairo_boxes_init (&tmp);
663 status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
664 assert (status == CAIRO_STATUS_SUCCESS);
666 tmp.chunks.next = &boxes->chunks;
667 tmp.num_boxes += boxes->num_boxes;
669 status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
670 CAIRO_FILL_RULE_WINDING,
671 &clear);
673 tmp.chunks.next = NULL;
674 } else {
675 pixman_box32_t *pbox;
677 pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
678 _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
680 status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
681 assert (status == CAIRO_STATUS_SUCCESS);
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;
695 status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
696 CAIRO_FILL_RULE_WINDING,
697 &clear);
700 if (likely (status == CAIRO_STATUS_SUCCESS)) {
701 status = compositor->fill_boxes (dst,
702 CAIRO_OPERATOR_CLEAR,
703 CAIRO_COLOR_TRANSPARENT,
704 &clear);
707 _cairo_boxes_fini (&clear);
709 return status;
712 enum {
713 NEED_CLIP_REGION = 0x1,
714 NEED_CLIP_SURFACE = 0x2,
715 FORCE_CLIP_REGION = 0x4,
718 static cairo_bool_t
719 need_bounded_clip (cairo_composite_rectangles_t *extents)
721 unsigned int flags = NEED_CLIP_REGION;
722 if (! _cairo_clip_is_region (extents->clip))
723 flags |= NEED_CLIP_SURFACE;
724 return flags;
727 static cairo_bool_t
728 need_unbounded_clip (cairo_composite_rectangles_t *extents)
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;
736 if (extents->clip->path != NULL)
737 flags |= NEED_CLIP_SURFACE;
738 return flags;
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)
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;
755 compositor->acquire (dst);
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;
772 if (reduce_alpha_op (dst, op, &extents->source_pattern.base)) {
773 op = CAIRO_OPERATOR_ADD;
774 src = NULL;
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;
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);
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);
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);
817 if (clip_region)
818 compositor->set_clip_region (dst, NULL);
820 compositor->release (dst);
822 return status;
825 static cairo_int_status_t
826 trim_extents_to_boxes (cairo_composite_rectangles_t *extents,
827 cairo_boxes_t *boxes)
829 cairo_box_t box;
831 _cairo_boxes_extents (boxes, &box);
832 return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
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)
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;
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;
851 if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
852 return CAIRO_INT_STATUS_UNSUPPORTED;
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;
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;
862 tx += limit.x;
863 ty += limit.y;
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);
873 return status;
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)
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;
887 if (need_clip_mask &&
888 (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE))
890 return CAIRO_INT_STATUS_UNSUPPORTED;
893 status = compositor->acquire (dst);
894 if (unlikely (status))
895 return status;
897 if (! need_clip_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) {
898 const cairo_color_t *color;
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;
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;
914 if (op == CAIRO_OPERATOR_CLEAR) {
915 source = NULL;
916 op = CAIRO_OPERATOR_DEST_OUT;
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;
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);
938 cairo_surface_destroy (src);
939 cairo_surface_destroy (mask);
942 if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded)
943 status = fixup_unbounded_boxes (compositor, extents, boxes);
945 compositor->release (dst);
947 return status;
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)
955 cairo_surface_t *dst = extents->surface;
956 cairo_int_status_t status;
958 if (boxes->num_boxes == 0) {
959 if (extents->is_bounded)
960 return CAIRO_STATUS_SUCCESS;
962 return fixup_unbounded_boxes (compositor, extents, boxes);
965 if (! boxes->is_pixel_aligned)
966 return CAIRO_INT_STATUS_UNSUPPORTED;
968 status = trim_extents_to_boxes (extents, boxes);
969 if (unlikely (status))
970 return status;
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))))
978 status = upload_boxes (compositor, extents, boxes);
979 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
980 return status;
983 return composite_boxes (compositor, extents, boxes);
986 /* high-level compositor interface */
988 static cairo_int_status_t
989 _cairo_mask_compositor_paint (const cairo_compositor_t *_compositor,
990 cairo_composite_rectangles_t *extents)
992 cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
993 cairo_boxes_t boxes;
994 cairo_int_status_t status;
996 status = compositor->check_composite (extents);
997 if (unlikely (status))
998 return status;
1000 _cairo_clip_steal_boxes (extents->clip, &boxes);
1001 status = clip_and_composite_boxes (compositor, extents, &boxes);
1002 _cairo_clip_unsteal_boxes (extents->clip, &boxes);
1004 return status;
1007 struct composite_opacity_info {
1008 const cairo_mask_compositor_t *compositor;
1009 uint8_t op;
1010 cairo_surface_t *dst;
1011 cairo_surface_t *src;
1012 int src_x, src_y;
1013 double opacity;
1016 static void composite_opacity(void *closure,
1017 int16_t x, int16_t y,
1018 int16_t w, int16_t h,
1019 uint16_t coverage)
1021 struct composite_opacity_info *info = closure;
1022 const cairo_mask_compositor_t *compositor = info->compositor;
1023 cairo_surface_t *mask;
1024 int mask_x, mask_y;
1025 cairo_color_t color;
1026 cairo_solid_pattern_t solid;
1028 _cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage);
1029 _cairo_pattern_init_solid (&solid, &color);
1030 mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE,
1031 &_cairo_unbounded_rectangle,
1032 &_cairo_unbounded_rectangle,
1033 &mask_x, &mask_y);
1034 if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
1035 if (info->src) {
1036 compositor->composite (info->dst, info->op, info->src, mask,
1037 x + info->src_x, y + info->src_y,
1038 mask_x, mask_y,
1039 x, y,
1040 w, h);
1041 } else {
1042 compositor->composite (info->dst, info->op, mask, NULL,
1043 mask_x, mask_y,
1044 0, 0,
1045 x, y,
1046 w, h);
1050 cairo_surface_destroy (mask);
1053 static cairo_int_status_t
1054 composite_opacity_boxes (const cairo_mask_compositor_t *compositor,
1055 cairo_surface_t *dst,
1056 void *closure,
1057 cairo_operator_t op,
1058 const cairo_pattern_t *src_pattern,
1059 const cairo_rectangle_int_t *src_sample,
1060 int dst_x,
1061 int dst_y,
1062 const cairo_rectangle_int_t *extents,
1063 cairo_clip_t *clip)
1065 const cairo_solid_pattern_t *mask_pattern = closure;
1066 struct composite_opacity_info info;
1067 int i;
1069 assert (clip);
1071 info.compositor = compositor;
1072 info.op = op;
1073 info.dst = dst;
1075 if (src_pattern != NULL) {
1076 info.src = compositor->pattern_to_surface (dst, src_pattern, FALSE,
1077 extents, src_sample,
1078 &info.src_x, &info.src_y);
1079 if (unlikely (info.src->status))
1080 return info.src->status;
1081 } else
1082 info.src = NULL;
1084 info.opacity = mask_pattern->color.alpha / (double) 0xffff;
1086 /* XXX for lots of boxes create a clip region for the fully opaque areas */
1087 for (i = 0; i < clip->num_boxes; i++)
1088 do_unaligned_box(composite_opacity, &info,
1089 &clip->boxes[i], dst_x, dst_y);
1090 cairo_surface_destroy (info.src);
1092 return CAIRO_STATUS_SUCCESS;
1095 struct composite_box_info {
1096 const cairo_mask_compositor_t *compositor;
1097 cairo_surface_t *dst;
1098 cairo_surface_t *src;
1099 int src_x, src_y;
1100 uint8_t op;
1103 static void composite_box(void *closure,
1104 int16_t x, int16_t y,
1105 int16_t w, int16_t h,
1106 uint16_t coverage)
1108 struct composite_box_info *info = closure;
1109 const cairo_mask_compositor_t *compositor = info->compositor;
1111 if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) {
1112 cairo_surface_t *mask;
1113 cairo_color_t color;
1114 cairo_solid_pattern_t solid;
1115 int mask_x, mask_y;
1117 _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff);
1118 _cairo_pattern_init_solid (&solid, &color);
1120 mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE,
1121 &_cairo_unbounded_rectangle,
1122 &_cairo_unbounded_rectangle,
1123 &mask_x, &mask_y);
1125 if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
1126 compositor->composite (info->dst, info->op, info->src, mask,
1127 x + info->src_x, y + info->src_y,
1128 mask_x, mask_y,
1129 x, y,
1130 w, h);
1133 cairo_surface_destroy (mask);
1134 } else {
1135 compositor->composite (info->dst, info->op, info->src, NULL,
1136 x + info->src_x, y + info->src_y,
1137 0, 0,
1138 x, y,
1139 w, h);
1143 static cairo_int_status_t
1144 composite_mask_clip_boxes (const cairo_mask_compositor_t *compositor,
1145 cairo_surface_t *dst,
1146 void *closure,
1147 cairo_operator_t op,
1148 const cairo_pattern_t *src_pattern,
1149 const cairo_rectangle_int_t *src_sample,
1150 int dst_x,
1151 int dst_y,
1152 const cairo_rectangle_int_t *extents,
1153 cairo_clip_t *clip)
1155 cairo_composite_rectangles_t *composite = closure;
1156 struct composite_box_info info;
1157 int i;
1159 assert (src_pattern == NULL);
1160 assert (op == CAIRO_OPERATOR_SOURCE);
1162 info.compositor = compositor;
1163 info.op = CAIRO_OPERATOR_SOURCE;
1164 info.dst = dst;
1165 info.src = compositor->pattern_to_surface (dst,
1166 &composite->mask_pattern.base,
1167 FALSE, extents,
1168 &composite->mask_sample_area,
1169 &info.src_x, &info.src_y);
1170 if (unlikely (info.src->status))
1171 return info.src->status;
1173 info.src_x += dst_x;
1174 info.src_y += dst_y;
1176 for (i = 0; i < clip->num_boxes; i++)
1177 do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y);
1179 cairo_surface_destroy (info.src);
1181 return CAIRO_STATUS_SUCCESS;
1184 static cairo_int_status_t
1185 composite_mask (const cairo_mask_compositor_t *compositor,
1186 cairo_surface_t *dst,
1187 void *closure,
1188 cairo_operator_t op,
1189 const cairo_pattern_t *src_pattern,
1190 const cairo_rectangle_int_t *src_sample,
1191 int dst_x,
1192 int dst_y,
1193 const cairo_rectangle_int_t *extents,
1194 cairo_clip_t *clip)
1196 cairo_composite_rectangles_t *composite = closure;
1197 cairo_surface_t *src, *mask;
1198 int src_x, src_y;
1199 int mask_x, mask_y;
1201 if (src_pattern != NULL) {
1202 src = compositor->pattern_to_surface (dst, src_pattern, FALSE,
1203 extents, src_sample,
1204 &src_x, &src_y);
1205 if (unlikely (src->status))
1206 return src->status;
1208 mask = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, TRUE,
1209 extents, &composite->mask_sample_area,
1210 &mask_x, &mask_y);
1211 if (unlikely (mask->status)) {
1212 cairo_surface_destroy (src);
1213 return mask->status;
1216 compositor->composite (dst, op, src, mask,
1217 extents->x + src_x, extents->y + src_y,
1218 extents->x + mask_x, extents->y + mask_y,
1219 extents->x - dst_x, extents->y - dst_y,
1220 extents->width, extents->height);
1222 cairo_surface_destroy (mask);
1223 cairo_surface_destroy (src);
1224 } else {
1225 src = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, FALSE,
1226 extents, &composite->mask_sample_area,
1227 &src_x, &src_y);
1228 if (unlikely (src->status))
1229 return src->status;
1231 compositor->composite (dst, op, src, NULL,
1232 extents->x + src_x, extents->y + src_y,
1233 0, 0,
1234 extents->x - dst_x, extents->y - dst_y,
1235 extents->width, extents->height);
1237 cairo_surface_destroy (src);
1240 return CAIRO_STATUS_SUCCESS;
1243 static cairo_int_status_t
1244 _cairo_mask_compositor_mask (const cairo_compositor_t *_compositor,
1245 cairo_composite_rectangles_t *extents)
1247 const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
1248 cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
1250 status = compositor->check_composite (extents);
1251 if (unlikely (status))
1252 return status;
1254 if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
1255 extents->clip->path == NULL &&
1256 _cairo_clip_is_region (extents->clip)) {
1257 status = clip_and_composite (compositor,
1258 composite_opacity_boxes,
1259 composite_opacity_boxes,
1260 &extents->mask_pattern.solid,
1261 extents, need_unbounded_clip (extents));
1262 } else {
1263 status = clip_and_composite (compositor,
1264 composite_mask,
1265 extents->clip->path == NULL ? composite_mask_clip_boxes : NULL,
1266 extents,
1267 extents, need_bounded_clip (extents));
1270 return status;
1273 static cairo_int_status_t
1274 _cairo_mask_compositor_stroke (const cairo_compositor_t *_compositor,
1275 cairo_composite_rectangles_t *extents,
1276 const cairo_path_fixed_t *path,
1277 const cairo_stroke_style_t *style,
1278 const cairo_matrix_t *ctm,
1279 const cairo_matrix_t *ctm_inverse,
1280 double tolerance,
1281 cairo_antialias_t antialias)
1283 const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
1284 cairo_surface_t *mask;
1285 cairo_surface_pattern_t pattern;
1286 cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
1288 status = compositor->check_composite (extents);
1289 if (unlikely (status))
1290 return status;
1292 if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
1293 cairo_boxes_t boxes;
1295 _cairo_boxes_init_with_clip (&boxes, extents->clip);
1296 status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
1297 style,
1298 ctm,
1299 antialias,
1300 &boxes);
1301 if (likely (status == CAIRO_INT_STATUS_SUCCESS))
1302 status = clip_and_composite_boxes (compositor, extents, &boxes);
1303 _cairo_boxes_fini (&boxes);
1307 if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
1308 mask = cairo_surface_create_similar_image (extents->surface,
1309 CAIRO_FORMAT_A8,
1310 extents->bounded.width,
1311 extents->bounded.height);
1312 if (unlikely (mask->status))
1313 return mask->status;
1315 status = _cairo_surface_offset_stroke (mask,
1316 extents->bounded.x,
1317 extents->bounded.y,
1318 CAIRO_OPERATOR_ADD,
1319 &_cairo_pattern_white.base,
1320 path, style, ctm, ctm_inverse,
1321 tolerance, antialias,
1322 extents->clip);
1323 if (unlikely (status)) {
1324 cairo_surface_destroy (mask);
1325 return status;
1328 _cairo_pattern_init_for_surface (&pattern, mask);
1329 cairo_surface_destroy (mask);
1331 cairo_matrix_init_translate (&pattern.base.matrix,
1332 -extents->bounded.x,
1333 -extents->bounded.y);
1334 pattern.base.filter = CAIRO_FILTER_NEAREST;
1335 pattern.base.extend = CAIRO_EXTEND_NONE;
1336 status = _cairo_surface_mask (extents->surface,
1337 extents->op,
1338 &extents->source_pattern.base,
1339 &pattern.base,
1340 extents->clip);
1341 _cairo_pattern_fini (&pattern.base);
1344 return status;
1347 static cairo_int_status_t
1348 _cairo_mask_compositor_fill (const cairo_compositor_t *_compositor,
1349 cairo_composite_rectangles_t *extents,
1350 const cairo_path_fixed_t *path,
1351 cairo_fill_rule_t fill_rule,
1352 double tolerance,
1353 cairo_antialias_t antialias)
1355 const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
1356 cairo_surface_t *mask;
1357 cairo_surface_pattern_t pattern;
1358 cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
1360 status = compositor->check_composite (extents);
1361 if (unlikely (status))
1362 return status;
1364 if (_cairo_path_fixed_fill_is_rectilinear (path)) {
1365 cairo_boxes_t boxes;
1367 _cairo_boxes_init_with_clip (&boxes, extents->clip);
1368 status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
1369 fill_rule,
1370 antialias,
1371 &boxes);
1372 if (likely (status == CAIRO_INT_STATUS_SUCCESS))
1373 status = clip_and_composite_boxes (compositor, extents, &boxes);
1374 _cairo_boxes_fini (&boxes);
1377 if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
1378 mask = cairo_surface_create_similar_image (extents->surface,
1379 CAIRO_FORMAT_A8,
1380 extents->bounded.width,
1381 extents->bounded.height);
1382 if (unlikely (mask->status))
1383 return mask->status;
1385 status = _cairo_surface_offset_fill (mask,
1386 extents->bounded.x,
1387 extents->bounded.y,
1388 CAIRO_OPERATOR_ADD,
1389 &_cairo_pattern_white.base,
1390 path, fill_rule, tolerance, antialias,
1391 extents->clip);
1392 if (unlikely (status)) {
1393 cairo_surface_destroy (mask);
1394 return status;
1397 _cairo_pattern_init_for_surface (&pattern, mask);
1398 cairo_surface_destroy (mask);
1400 cairo_matrix_init_translate (&pattern.base.matrix,
1401 -extents->bounded.x,
1402 -extents->bounded.y);
1403 pattern.base.filter = CAIRO_FILTER_NEAREST;
1404 pattern.base.extend = CAIRO_EXTEND_NONE;
1405 status = _cairo_surface_mask (extents->surface,
1406 extents->op,
1407 &extents->source_pattern.base,
1408 &pattern.base,
1409 extents->clip);
1410 _cairo_pattern_fini (&pattern.base);
1413 return status;
1416 static cairo_int_status_t
1417 _cairo_mask_compositor_glyphs (const cairo_compositor_t *_compositor,
1418 cairo_composite_rectangles_t *extents,
1419 cairo_scaled_font_t *scaled_font,
1420 cairo_glyph_t *glyphs,
1421 int num_glyphs,
1422 cairo_bool_t overlap)
1424 const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
1425 cairo_surface_t *mask;
1426 cairo_surface_pattern_t pattern;
1427 cairo_int_status_t status;
1429 status = compositor->check_composite (extents);
1430 if (unlikely (status))
1431 return CAIRO_INT_STATUS_UNSUPPORTED;
1433 mask = cairo_surface_create_similar_image (extents->surface,
1434 CAIRO_FORMAT_A8,
1435 extents->bounded.width,
1436 extents->bounded.height);
1437 if (unlikely (mask->status))
1438 return mask->status;
1440 status = _cairo_surface_offset_glyphs (mask,
1441 extents->bounded.x,
1442 extents->bounded.y,
1443 CAIRO_OPERATOR_ADD,
1444 &_cairo_pattern_white.base,
1445 scaled_font, glyphs, num_glyphs,
1446 extents->clip);
1447 if (unlikely (status)) {
1448 cairo_surface_destroy (mask);
1449 return status;
1452 _cairo_pattern_init_for_surface (&pattern, mask);
1453 cairo_surface_destroy (mask);
1455 cairo_matrix_init_translate (&pattern.base.matrix,
1456 -extents->bounded.x,
1457 -extents->bounded.y);
1458 pattern.base.filter = CAIRO_FILTER_NEAREST;
1459 pattern.base.extend = CAIRO_EXTEND_NONE;
1460 status = _cairo_surface_mask (extents->surface,
1461 extents->op,
1462 &extents->source_pattern.base,
1463 &pattern.base,
1464 extents->clip);
1465 _cairo_pattern_fini (&pattern.base);
1467 return status;
1470 void
1471 _cairo_mask_compositor_init (cairo_mask_compositor_t *compositor,
1472 const cairo_compositor_t *delegate)
1474 compositor->base.delegate = delegate;
1476 compositor->base.paint = _cairo_mask_compositor_paint;
1477 compositor->base.mask = _cairo_mask_compositor_mask;
1478 compositor->base.fill = _cairo_mask_compositor_fill;
1479 compositor->base.stroke = _cairo_mask_compositor_stroke;
1480 compositor->base.glyphs = _cairo_mask_compositor_glyphs;