Coverage Report

Created: 2026-06-30 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cairo/src/cairo-mask-compositor.c
Line
Count
Source
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
0
{
79
0
    int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
80
0
    int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
81
0
    if (x2 > x1) {
82
0
  if (! _cairo_fixed_is_integer (b->p1.x)) {
83
0
      blt(closure, x1, y, 1, h,
84
0
    coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
85
0
      x1++;
86
0
  }
87
88
0
  if (x2 > x1)
89
0
      blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
90
91
0
  if (! _cairo_fixed_is_integer (b->p2.x))
92
0
      blt(closure, x2, y, 1, h,
93
0
    coverage * _cairo_fixed_fractional_part (b->p2.x));
94
0
    } else
95
0
  blt(closure, x1, y, 1, h,
96
0
      coverage * (b->p2.x - b->p1.x));
97
0
}
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
0
{
106
0
    int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
107
0
    int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
108
0
    if (y2 > y1) {
109
0
  if (! _cairo_fixed_is_integer (b->p1.y)) {
110
0
      do_unaligned_row(blt, closure, b, tx, y1, 1,
111
0
           256 - _cairo_fixed_fractional_part (b->p1.y));
112
0
      y1++;
113
0
  }
114
115
0
  if (y2 > y1)
116
0
      do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
117
118
0
  if (! _cairo_fixed_is_integer (b->p2.y))
119
0
      do_unaligned_row(blt, closure, b, tx, y2, 1,
120
0
           _cairo_fixed_fractional_part (b->p2.y));
121
0
    } else
122
0
  do_unaligned_row(blt, closure, b, tx, y1, 1,
123
0
       b->p2.y - b->p1.y);
124
0
}
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
0
{
136
0
    struct blt_in *info = closure;
137
0
    cairo_color_t color;
138
0
    cairo_rectangle_int_t rect;
139
140
0
    if (coverage == 0xffff)
141
0
  return;
142
143
0
    rect.x = x;
144
0
    rect.y = y;
145
0
    rect.width  = w;
146
0
    rect.height = h;
147
148
0
    _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff);
149
0
    info->compositor->fill_rectangles (info->dst, CAIRO_OPERATOR_IN,
150
0
               &color, &rect, 1);
151
0
}
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
0
{
161
0
    cairo_surface_t *surface;
162
0
    cairo_int_status_t status;
163
0
    struct blt_in info;
164
0
    int i;
165
166
0
    surface = _cairo_surface_create_scratch (dst, CAIRO_CONTENT_ALPHA,
167
0
               extents->bounded.width,
168
0
               extents->bounded.height,
169
0
               NULL);
170
0
    if (unlikely (surface->status))
171
0
  return surface;
172
173
0
    status = compositor->acquire (surface);
174
0
    if (unlikely (status)) {
175
0
  cairo_surface_destroy (surface);
176
0
  return _cairo_int_surface_create_in_error (status);
177
0
    }
178
179
0
    if (!surface->is_clear) {
180
0
  cairo_rectangle_int_t rect;
181
182
0
  rect.x = rect.y = 0;
183
0
  rect.width = extents->bounded.width;
184
0
  rect.height = extents->bounded.height;
185
186
0
  status = compositor->fill_rectangles (surface, CAIRO_OPERATOR_CLEAR,
187
0
                CAIRO_COLOR_TRANSPARENT,
188
0
                &rect, 1);
189
0
  if (unlikely (status))
190
0
      goto error;
191
0
    }
192
193
0
    if (mask_func) {
194
0
  status = mask_func (compositor, surface, draw_closure,
195
0
          CAIRO_OPERATOR_SOURCE, NULL, NULL,
196
0
          extents->bounded.x, extents->bounded.y,
197
0
          &extents->bounded, extents->clip);
198
0
  if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
199
0
      goto out;
200
0
    }
201
202
    /* Is it worth setting the clip region here? */
203
0
    status = draw_func (compositor, surface, draw_closure,
204
0
      CAIRO_OPERATOR_ADD, NULL, NULL,
205
0
      extents->bounded.x, extents->bounded.y,
206
0
      &extents->bounded, NULL);
207
0
    if (unlikely (status))
208
0
  goto error;
209
210
0
    info.compositor = compositor;
211
0
    info.dst = surface;
212
0
    for (i = 0; i < extents->clip->num_boxes; i++) {
213
0
  cairo_box_t *b = &extents->clip->boxes[i];
214
215
0
  if (! _cairo_fixed_is_integer (b->p1.x) ||
216
0
      ! _cairo_fixed_is_integer (b->p1.y) ||
217
0
      ! _cairo_fixed_is_integer (b->p2.x) ||
218
0
      ! _cairo_fixed_is_integer (b->p2.y))
219
0
  {
220
0
      do_unaligned_box(blt_in, &info, b,
221
0
           extents->bounded.x,
222
0
           extents->bounded.y);
223
0
  }
224
0
    }
225
226
0
    if (extents->clip->path != NULL) {
227
0
  status = _cairo_clip_combine_with_surface (extents->clip, surface,
228
0
               extents->bounded.x,
229
0
               extents->bounded.y);
230
0
  if (unlikely (status))
231
0
      goto error;
232
0
    }
233
234
0
out:
235
0
    compositor->release (surface);
236
0
    surface->is_clear = FALSE;
237
0
    return surface;
238
239
0
error:
240
0
    compositor->release (surface);
241
0
    if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
242
0
  cairo_surface_destroy (surface);
243
0
  surface = _cairo_int_surface_create_in_error (status);
244
0
    }
245
0
    return surface;
246
0
}
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
0
{
260
0
    cairo_surface_t *dst = extents->surface;
261
0
    cairo_surface_t *mask, *src;
262
0
    int src_x, src_y;
263
264
0
    mask = create_composite_mask (compositor, dst, draw_closure,
265
0
          draw_func, mask_func,
266
0
          extents);
267
0
    if (unlikely (mask->status))
268
0
  return mask->status;
269
270
0
    if (pattern != NULL || dst->content != CAIRO_CONTENT_ALPHA) {
271
0
  src = compositor->pattern_to_surface (dst,
272
0
                &extents->source_pattern.base,
273
0
                FALSE,
274
0
                &extents->bounded,
275
0
                &extents->source_sample_area,
276
0
                &src_x, &src_y);
277
0
  if (unlikely (src->status)) {
278
0
      cairo_surface_destroy (mask);
279
0
      return src->status;
280
0
  }
281
282
0
  compositor->composite (dst, op, src, mask,
283
0
             extents->bounded.x + src_x,
284
0
             extents->bounded.y + src_y,
285
0
             0, 0,
286
0
             extents->bounded.x,      extents->bounded.y,
287
0
             extents->bounded.width,  extents->bounded.height);
288
289
0
  cairo_surface_destroy (src);
290
0
    } else {
291
0
  compositor->composite (dst, op, mask, NULL,
292
0
             0, 0,
293
0
             0, 0,
294
0
             extents->bounded.x,      extents->bounded.y,
295
0
             extents->bounded.width,  extents->bounded.height);
296
0
    }
297
0
    cairo_surface_destroy (mask);
298
299
0
    return CAIRO_STATUS_SUCCESS;
300
0
}
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
0
{
309
0
    cairo_surface_pattern_t pattern;
310
0
    cairo_rectangle_int_t r;
311
0
    cairo_surface_t *surface;
312
313
0
    surface = _cairo_clip_get_image (clip, dst, bounds);
314
0
    if (unlikely (surface->status))
315
0
  return surface;
316
317
0
    _cairo_pattern_init_for_surface (&pattern, surface);
318
0
    pattern.base.filter = CAIRO_FILTER_NEAREST;
319
0
    cairo_surface_destroy (surface);
320
321
0
    r.x = r.y = 0;
322
0
    r.width  = bounds->width;
323
0
    r.height = bounds->height;
324
325
0
    surface = compositor->pattern_to_surface (dst, &pattern.base, TRUE,
326
0
                &r, &r, out_x, out_y);
327
0
    _cairo_pattern_fini (&pattern.base);
328
329
0
    *out_x += -bounds->x;
330
0
    *out_y += -bounds->y;
331
0
    return surface;
332
0
}
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
0
{
345
0
    cairo_surface_t *dst = extents->surface;
346
0
    cairo_surface_t *tmp, *clip;
347
0
    cairo_status_t status;
348
0
    int clip_x, clip_y;
349
350
0
    tmp = _cairo_surface_create_scratch (dst, dst->content,
351
0
           extents->bounded.width,
352
0
           extents->bounded.height,
353
0
           NULL);
354
0
    if (unlikely (tmp->status))
355
0
  return tmp->status;
356
357
0
    compositor->composite (tmp, CAIRO_OPERATOR_SOURCE, dst, NULL,
358
0
         extents->bounded.x,      extents->bounded.y,
359
0
         0, 0,
360
0
         0, 0,
361
0
         extents->bounded.width,  extents->bounded.height);
362
363
0
    status = draw_func (compositor, tmp, draw_closure, op,
364
0
      pattern, &extents->source_sample_area,
365
0
      extents->bounded.x, extents->bounded.y,
366
0
      &extents->bounded, NULL);
367
0
    if (unlikely (status))
368
0
  goto cleanup;
369
370
0
    clip = get_clip_source (compositor,
371
0
          extents->clip, dst, &extents->bounded,
372
0
          &clip_x, &clip_y);
373
0
    if (unlikely ((status = clip->status)))
374
0
  goto cleanup;
375
376
0
    if (dst->is_clear) {
377
0
  compositor->composite (dst, CAIRO_OPERATOR_SOURCE, tmp, clip,
378
0
             0, 0,
379
0
             clip_x, clip_y,
380
0
             extents->bounded.x,      extents->bounded.y,
381
0
             extents->bounded.width,  extents->bounded.height);
382
0
    } else {
383
  /* Punch the clip out of the destination */
384
0
  compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, clip, NULL,
385
0
             clip_x, clip_y,
386
0
             0, 0,
387
0
             extents->bounded.x,     extents->bounded.y,
388
0
             extents->bounded.width, extents->bounded.height);
389
390
  /* Now add the two results together */
391
0
  compositor->composite (dst, CAIRO_OPERATOR_ADD, tmp, clip,
392
0
             0, 0,
393
0
             clip_x, clip_y,
394
0
             extents->bounded.x,     extents->bounded.y,
395
0
             extents->bounded.width, extents->bounded.height);
396
0
    }
397
0
    cairo_surface_destroy (clip);
398
399
0
cleanup:
400
0
    cairo_surface_destroy (tmp);
401
0
    return status;
402
0
}
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
0
{
415
0
    cairo_surface_t *dst = extents->surface;
416
0
    cairo_surface_t *mask, *src;
417
0
    int src_x, src_y;
418
419
    /* Create a surface that is mask IN clip */
420
0
    mask = create_composite_mask (compositor, dst, draw_closure,
421
0
          draw_func, mask_func,
422
0
          extents);
423
0
    if (unlikely (mask->status))
424
0
  return mask->status;
425
426
0
    src = compositor->pattern_to_surface (dst,
427
0
            pattern,
428
0
            FALSE,
429
0
            &extents->bounded,
430
0
            &extents->source_sample_area,
431
0
            &src_x, &src_y);
432
0
    if (unlikely (src->status)) {
433
0
  cairo_surface_destroy (mask);
434
0
  return src->status;
435
0
    }
436
437
0
    if (dst->is_clear) {
438
0
  compositor->composite (dst, CAIRO_OPERATOR_SOURCE, src, mask,
439
0
             extents->bounded.x + src_x, extents->bounded.y + src_y,
440
0
             0, 0,
441
0
             extents->bounded.x,      extents->bounded.y,
442
0
             extents->bounded.width,  extents->bounded.height);
443
0
    } else {
444
  /* Compute dest' = dest OUT (mask IN clip) */
445
0
  compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
446
0
             0, 0, 0, 0,
447
0
             extents->bounded.x,     extents->bounded.y,
448
0
             extents->bounded.width, extents->bounded.height);
449
450
  /* Now compute (src IN (mask IN clip)) ADD dest' */
451
0
  compositor->composite (dst, CAIRO_OPERATOR_ADD, src, mask,
452
0
             extents->bounded.x + src_x, extents->bounded.y + src_y,
453
0
             0, 0,
454
0
             extents->bounded.x,     extents->bounded.y,
455
0
             extents->bounded.width, extents->bounded.height);
456
0
    }
457
458
0
    cairo_surface_destroy (src);
459
0
    cairo_surface_destroy (mask);
460
461
0
    return CAIRO_STATUS_SUCCESS;
462
0
}
463
464
static cairo_bool_t
465
can_reduce_alpha_op (cairo_operator_t op)
466
0
{
467
0
    int iop = op;
468
0
    switch (iop) {
469
0
    case CAIRO_OPERATOR_OVER:
470
0
    case CAIRO_OPERATOR_SOURCE:
471
0
    case CAIRO_OPERATOR_ADD:
472
0
  return TRUE;
473
0
    default:
474
0
  return FALSE;
475
0
    }
476
0
}
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
0
{
483
0
    return dst->is_clear &&
484
0
     dst->content == CAIRO_CONTENT_ALPHA &&
485
0
     _cairo_pattern_is_opaque_solid (pattern) &&
486
0
     can_reduce_alpha_op (op);
487
0
}
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
0
{
494
0
    cairo_rectangle_int_t rects[4];
495
0
    int n;
496
497
0
    if (extents->bounded.width  == extents->unbounded.width &&
498
0
  extents->bounded.height == extents->unbounded.height)
499
0
    {
500
0
  return CAIRO_STATUS_SUCCESS;
501
0
    }
502
503
0
    n = 0;
504
0
    if (extents->bounded.width == 0 || extents->bounded.height == 0) {
505
0
  rects[n].x = extents->unbounded.x;
506
0
  rects[n].width = extents->unbounded.width;
507
0
  rects[n].y = extents->unbounded.y;
508
0
  rects[n].height = extents->unbounded.height;
509
0
  n++;
510
0
    } else {
511
  /* top */
512
0
  if (extents->bounded.y != extents->unbounded.y) {
513
0
      rects[n].x = extents->unbounded.x;
514
0
      rects[n].width = extents->unbounded.width;
515
0
      rects[n].y = extents->unbounded.y;
516
0
      rects[n].height = extents->bounded.y - extents->unbounded.y;
517
0
      n++;
518
0
  }
519
  /* left */
520
0
  if (extents->bounded.x != extents->unbounded.x) {
521
0
      rects[n].x = extents->unbounded.x;
522
0
      rects[n].width = extents->bounded.x - extents->unbounded.x;
523
0
      rects[n].y = extents->bounded.y;
524
0
      rects[n].height = extents->bounded.height;
525
0
      n++;
526
0
  }
527
  /* right */
528
0
  if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
529
0
      rects[n].x = extents->bounded.x + extents->bounded.width;
530
0
      rects[n].width = extents->unbounded.x + extents->unbounded.width - rects[n].x;
531
0
      rects[n].y = extents->bounded.y;
532
0
      rects[n].height = extents->bounded.height;
533
0
      n++;
534
0
  }
535
  /* bottom */
536
0
  if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
537
0
      rects[n].x = extents->unbounded.x;
538
0
      rects[n].width = extents->unbounded.width;
539
0
      rects[n].y = extents->bounded.y + extents->bounded.height;
540
0
      rects[n].height = extents->unbounded.y + extents->unbounded.height - rects[n].y;
541
0
      n++;
542
0
  }
543
0
    }
544
545
0
    return compositor->fill_rectangles (dst, CAIRO_OPERATOR_CLEAR,
546
0
          CAIRO_COLOR_TRANSPARENT,
547
0
          rects, n);
548
0
}
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
0
{
555
0
    cairo_surface_t *mask;
556
0
    int mask_x, mask_y;
557
558
0
    mask = get_clip_source (compositor,
559
0
          extents->clip, dst, &extents->unbounded,
560
0
          &mask_x, &mask_y);
561
0
    if (unlikely (mask->status))
562
0
  return mask->status;
563
564
    /* top */
565
0
    if (extents->bounded.y != extents->unbounded.y) {
566
0
  int x = extents->unbounded.x;
567
0
  int y = extents->unbounded.y;
568
0
  int width = extents->unbounded.width;
569
0
  int height = extents->bounded.y - y;
570
571
0
  compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
572
0
             x + mask_x, y + mask_y,
573
0
             0, 0,
574
0
             x, y,
575
0
             width, height);
576
0
    }
577
578
    /* left */
579
0
    if (extents->bounded.x != extents->unbounded.x) {
580
0
  int x = extents->unbounded.x;
581
0
  int y = extents->bounded.y;
582
0
  int width = extents->bounded.x - x;
583
0
  int height = extents->bounded.height;
584
585
0
  compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
586
0
             x + mask_x, y + mask_y,
587
0
             0, 0,
588
0
             x, y,
589
0
             width, height);
590
0
    }
591
592
    /* right */
593
0
    if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
594
0
  int x = extents->bounded.x + extents->bounded.width;
595
0
  int y = extents->bounded.y;
596
0
  int width = extents->unbounded.x + extents->unbounded.width - x;
597
0
  int height = extents->bounded.height;
598
599
0
  compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
600
0
             x + mask_x, y + mask_y,
601
0
             0, 0,
602
0
             x, y,
603
0
             width, height);
604
0
    }
605
606
    /* bottom */
607
0
    if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
608
0
  int x = extents->unbounded.x;
609
0
  int y = extents->bounded.y + extents->bounded.height;
610
0
  int width = extents->unbounded.width;
611
0
  int height = extents->unbounded.y + extents->unbounded.height - y;
612
613
0
  compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
614
0
             x + mask_x, y + mask_y,
615
0
             0, 0,
616
0
             x, y,
617
0
             width, height);
618
0
    }
619
620
0
    cairo_surface_destroy (mask);
621
622
0
    return CAIRO_STATUS_SUCCESS;
623
0
}
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
0
{
630
0
    cairo_surface_t *dst = extents->surface;
631
0
    cairo_boxes_t clear;
632
0
    cairo_region_t *clip_region;
633
0
    cairo_box_t box;
634
0
    cairo_status_t status;
635
0
    struct _cairo_boxes_chunk *chunk;
636
0
    int i;
637
638
0
    assert (boxes->is_pixel_aligned);
639
640
0
    clip_region = NULL;
641
0
    if (_cairo_clip_is_region (extents->clip) &&
642
0
  (clip_region = _cairo_clip_get_region (extents->clip)) &&
643
0
  cairo_region_contains_rectangle (clip_region,
644
0
           &extents->bounded) == CAIRO_REGION_OVERLAP_IN)
645
0
  clip_region = NULL;
646
647
648
0
    if (boxes->num_boxes <= 1 && clip_region == NULL)
649
0
  return fixup_unbounded (compositor, dst, extents);
650
651
0
    _cairo_boxes_init (&clear);
652
653
0
    box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
654
0
    box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
655
0
    box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
656
0
    box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
657
658
0
    if (clip_region == NULL) {
659
0
  cairo_boxes_t tmp;
660
661
0
  _cairo_boxes_init (&tmp);
662
663
0
  status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
664
0
  assert (status == CAIRO_STATUS_SUCCESS);
665
666
0
  tmp.chunks.next = &boxes->chunks;
667
0
  tmp.num_boxes += boxes->num_boxes;
668
669
0
  status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
670
0
                CAIRO_FILL_RULE_WINDING,
671
0
                &clear);
672
673
0
  tmp.chunks.next = NULL;
674
0
    } else {
675
0
  pixman_box32_t *pbox;
676
677
0
  pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
678
0
  _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
679
680
0
  status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
681
0
  assert (status == CAIRO_STATUS_SUCCESS);
682
683
0
  for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
684
0
      for (i = 0; i < chunk->count; i++) {
685
0
    status = _cairo_boxes_add (&clear,
686
0
             CAIRO_ANTIALIAS_DEFAULT,
687
0
             &chunk->base[i]);
688
0
    if (unlikely (status)) {
689
0
        _cairo_boxes_fini (&clear);
690
0
        return status;
691
0
    }
692
0
      }
693
0
  }
694
695
0
  status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
696
0
                CAIRO_FILL_RULE_WINDING,
697
0
                &clear);
698
0
    }
699
700
0
    if (likely (status == CAIRO_STATUS_SUCCESS)) {
701
0
  status = compositor->fill_boxes (dst,
702
0
           CAIRO_OPERATOR_CLEAR,
703
0
           CAIRO_COLOR_TRANSPARENT,
704
0
           &clear);
705
0
    }
706
707
0
    _cairo_boxes_fini (&clear);
708
709
0
    return status;
710
0
}
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
0
{
721
0
    unsigned int flags = NEED_CLIP_REGION;
722
0
    if (! _cairo_clip_is_region (extents->clip))
723
0
  flags |= NEED_CLIP_SURFACE;
724
0
    return flags;
725
0
}
726
727
static cairo_bool_t
728
need_unbounded_clip (cairo_composite_rectangles_t *extents)
729
0
{
730
0
    unsigned int flags = 0;
731
0
    if (! extents->is_bounded) {
732
0
  flags |= NEED_CLIP_REGION;
733
0
  if (! _cairo_clip_is_region (extents->clip))
734
0
      flags |= NEED_CLIP_SURFACE;
735
0
    }
736
0
    if (extents->clip->path != NULL)
737
0
  flags |= NEED_CLIP_SURFACE;
738
0
    return flags;
739
0
}
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
0
{
749
0
    cairo_surface_t *dst = extents->surface;
750
0
    cairo_operator_t op = extents->op;
751
0
    cairo_pattern_t *src = &extents->source_pattern.base;
752
0
    cairo_region_t *clip_region = NULL;
753
0
    cairo_status_t status;
754
755
0
    compositor->acquire (dst);
756
757
0
    if (need_clip & NEED_CLIP_REGION) {
758
0
  clip_region = _cairo_clip_get_region (extents->clip);
759
0
  if ((need_clip & FORCE_CLIP_REGION) == 0 &&
760
0
      _cairo_composite_rectangles_can_reduce_clip (extents,
761
0
               extents->clip))
762
0
      clip_region = NULL;
763
0
  if (clip_region != NULL) {
764
0
      status = compositor->set_clip_region (dst, clip_region);
765
0
      if (unlikely (status)) {
766
0
    compositor->release (dst);
767
0
    return status;
768
0
      }
769
0
  }
770
0
    }
771
772
0
    if (reduce_alpha_op (dst, op, &extents->source_pattern.base)) {
773
0
  op = CAIRO_OPERATOR_ADD;
774
0
  src = NULL;
775
0
    }
776
777
0
    if (op == CAIRO_OPERATOR_SOURCE) {
778
0
  status = clip_and_composite_source (compositor,
779
0
              draw_closure, draw_func, mask_func,
780
0
              src, extents);
781
0
    } else {
782
0
  if (op == CAIRO_OPERATOR_CLEAR) {
783
0
      op = CAIRO_OPERATOR_DEST_OUT;
784
0
      src = NULL;
785
0
  }
786
787
0
  if (need_clip & NEED_CLIP_SURFACE) {
788
0
      if (extents->is_bounded) {
789
0
    status = clip_and_composite_with_mask (compositor,
790
0
                   draw_closure,
791
0
                   draw_func,
792
0
                   mask_func,
793
0
                   op, src, extents);
794
0
      } else {
795
0
    status = clip_and_composite_combine (compositor,
796
0
                 draw_closure,
797
0
                 draw_func,
798
0
                 op, src, extents);
799
0
      }
800
0
  } else {
801
0
      status = draw_func (compositor,
802
0
        dst, draw_closure,
803
0
        op, src, &extents->source_sample_area,
804
0
        0, 0,
805
0
        &extents->bounded,
806
0
        extents->clip);
807
0
  }
808
0
    }
809
810
0
    if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
811
0
  if (need_clip & NEED_CLIP_SURFACE)
812
0
      status = fixup_unbounded_with_mask (compositor, dst, extents);
813
0
  else
814
0
      status = fixup_unbounded (compositor, dst, extents);
815
0
    }
816
817
0
    if (clip_region)
818
0
  compositor->set_clip_region (dst, NULL);
819
820
0
    compositor->release (dst);
821
822
0
    return status;
823
0
}
824
825
static cairo_int_status_t
826
trim_extents_to_boxes (cairo_composite_rectangles_t *extents,
827
           cairo_boxes_t *boxes)
828
0
{
829
0
    cairo_box_t box;
830
831
0
    _cairo_boxes_extents (boxes, &box);
832
0
    return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
833
0
}
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
0
{
840
0
    cairo_surface_t *dst = extents->surface;
841
0
    const cairo_pattern_t *source = &extents->source_pattern.base;
842
0
    cairo_surface_t *src;
843
0
    cairo_rectangle_int_t limit;
844
0
    cairo_int_status_t status;
845
0
    int tx, ty;
846
847
0
    src = _cairo_pattern_get_source ((cairo_surface_pattern_t *)source, &limit);
848
0
    if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type))
849
0
  return CAIRO_INT_STATUS_UNSUPPORTED;
850
851
0
    if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
852
0
  return CAIRO_INT_STATUS_UNSUPPORTED;
853
854
    /* Check that the data is entirely within the image */
855
0
    if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y)
856
0
  return CAIRO_INT_STATUS_UNSUPPORTED;
857
858
0
    if (extents->bounded.x + extents->bounded.width  + tx > limit.x + limit.width ||
859
0
  extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height)
860
0
  return CAIRO_INT_STATUS_UNSUPPORTED;
861
862
0
    tx += limit.x;
863
0
    ty += limit.y;
864
865
0
    if (src->type == CAIRO_SURFACE_TYPE_IMAGE)
866
0
  status = compositor->draw_image_boxes (dst,
867
0
                 (cairo_image_surface_t *)src,
868
0
                 boxes, tx, ty);
869
0
    else
870
0
  status = compositor->copy_boxes (dst, src, boxes, &extents->bounded,
871
0
           tx, ty);
872
873
0
    return status;
874
0
}
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
0
{
881
0
    cairo_surface_t *dst = extents->surface;
882
0
    cairo_operator_t op = extents->op;
883
0
    const cairo_pattern_t *source = &extents->source_pattern.base;
884
0
    cairo_bool_t need_clip_mask = extents->clip->path != NULL;
885
0
    cairo_status_t status;
886
887
0
    if (need_clip_mask &&
888
0
  (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE))
889
0
    {
890
0
  return CAIRO_INT_STATUS_UNSUPPORTED;
891
0
    }
892
893
0
    status = compositor->acquire (dst);
894
0
    if (unlikely (status))
895
0
  return status;
896
897
0
    if (! need_clip_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) {
898
0
  const cairo_color_t *color;
899
900
0
  color = &((cairo_solid_pattern_t *) source)->color;
901
0
  status = compositor->fill_boxes (dst, op, color, boxes);
902
0
    } else {
903
0
  cairo_surface_t *src, *mask = NULL;
904
0
  int src_x, src_y;
905
0
  int mask_x = 0, mask_y = 0;
906
907
0
  if (need_clip_mask) {
908
0
      mask = get_clip_source (compositor,
909
0
            extents->clip, dst, &extents->bounded,
910
0
            &mask_x, &mask_y);
911
0
      if (unlikely (mask->status))
912
0
    return mask->status;
913
914
0
      if (op == CAIRO_OPERATOR_CLEAR) {
915
0
    source = NULL;
916
0
    op = CAIRO_OPERATOR_DEST_OUT;
917
0
      }
918
0
  }
919
920
0
  if (source || mask == NULL) {
921
0
      src = compositor->pattern_to_surface (dst, source, FALSE,
922
0
              &extents->bounded,
923
0
              &extents->source_sample_area,
924
0
              &src_x, &src_y);
925
0
  } else {
926
0
      src = mask;
927
0
      src_x = mask_x;
928
0
      src_y = mask_y;
929
0
      mask = NULL;
930
0
  }
931
932
0
  status = compositor->composite_boxes (dst, op, src, mask,
933
0
                src_x, src_y,
934
0
                mask_x, mask_y,
935
0
                0, 0,
936
0
                boxes, &extents->bounded);
937
938
0
  cairo_surface_destroy (src);
939
0
  cairo_surface_destroy (mask);
940
0
    }
941
942
0
    if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded)
943
0
  status = fixup_unbounded_boxes (compositor, extents, boxes);
944
945
0
    compositor->release (dst);
946
947
0
    return status;
948
0
}
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
0
{
955
0
    cairo_surface_t *dst = extents->surface;
956
0
    cairo_int_status_t status;
957
958
0
    if (boxes->num_boxes == 0) {
959
0
  if (extents->is_bounded)
960
0
      return CAIRO_STATUS_SUCCESS;
961
962
0
  return fixup_unbounded_boxes (compositor, extents, boxes);
963
0
    }
964
965
0
    if (! boxes->is_pixel_aligned)
966
0
  return CAIRO_INT_STATUS_UNSUPPORTED;
967
968
0
    status = trim_extents_to_boxes (extents, boxes);
969
0
    if (unlikely (status))
970
0
  return status;
971
972
0
    if (extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
973
0
  extents->clip->path == NULL &&
974
0
  (extents->op == CAIRO_OPERATOR_SOURCE ||
975
0
   (dst->is_clear && (extents->op == CAIRO_OPERATOR_OVER ||
976
0
          extents->op == CAIRO_OPERATOR_ADD))))
977
0
    {
978
0
  status = upload_boxes (compositor, extents, boxes);
979
0
  if (status != CAIRO_INT_STATUS_UNSUPPORTED)
980
0
      return status;
981
0
    }
982
983
0
    return composite_boxes (compositor, extents, boxes);
984
0
}
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
0
{
992
0
    cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
993
0
    cairo_boxes_t boxes;
994
0
    cairo_int_status_t status;
995
996
0
    status = compositor->check_composite (extents);
997
0
    if (unlikely (status))
998
0
  return status;
999
1000
0
    _cairo_clip_steal_boxes (extents->clip, &boxes);
1001
0
    status = clip_and_composite_boxes (compositor, extents, &boxes);
1002
0
    _cairo_clip_unsteal_boxes (extents->clip, &boxes);
1003
1004
0
    return status;
1005
0
}
1006
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;
1014
};
1015
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)
1020
0
{
1021
0
    struct composite_opacity_info *info = closure;
1022
0
    const cairo_mask_compositor_t *compositor = info->compositor;
1023
0
    cairo_surface_t *mask;
1024
0
    int mask_x, mask_y;
1025
0
    cairo_color_t color;
1026
0
    cairo_solid_pattern_t solid;
1027
1028
0
    _cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage);
1029
0
    _cairo_pattern_init_solid (&solid, &color);
1030
0
    mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE,
1031
0
             &_cairo_unbounded_rectangle,
1032
0
             &_cairo_unbounded_rectangle,
1033
0
             &mask_x, &mask_y);
1034
0
    if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
1035
0
  if (info->src) {
1036
0
      compositor->composite (info->dst, info->op, info->src, mask,
1037
0
           x + info->src_x,  y + info->src_y,
1038
0
           mask_x,           mask_y,
1039
0
           x,                y,
1040
0
           w,                h);
1041
0
  } else {
1042
0
      compositor->composite (info->dst, info->op, mask, NULL,
1043
0
           mask_x,            mask_y,
1044
0
           0,                 0,
1045
0
           x,                 y,
1046
0
           w,                 h);
1047
0
  }
1048
0
    }
1049
1050
0
    cairo_surface_destroy (mask);
1051
0
}
1052
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)
1064
0
{
1065
0
    const cairo_solid_pattern_t *mask_pattern = closure;
1066
0
    struct composite_opacity_info info;
1067
0
    int i;
1068
1069
0
    assert (clip);
1070
1071
0
    info.compositor = compositor;
1072
0
    info.op = op;
1073
0
    info.dst = dst;
1074
1075
0
    if (src_pattern != NULL) {
1076
0
  info.src = compositor->pattern_to_surface (dst, src_pattern, FALSE,
1077
0
               extents, src_sample,
1078
0
               &info.src_x, &info.src_y);
1079
0
  if (unlikely (info.src->status))
1080
0
      return info.src->status;
1081
0
    } else
1082
0
  info.src = NULL;
1083
1084
0
    info.opacity = mask_pattern->color.alpha / (double) 0xffff;
1085
1086
    /* XXX for lots of boxes create a clip region for the fully opaque areas */
1087
0
    for (i = 0; i < clip->num_boxes; i++)
1088
0
  do_unaligned_box(composite_opacity, &info,
1089
0
       &clip->boxes[i], dst_x, dst_y);
1090
0
    cairo_surface_destroy (info.src);
1091
1092
0
    return CAIRO_STATUS_SUCCESS;
1093
0
}
1094
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;
1101
};
1102
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)
1107
0
{
1108
0
    struct composite_box_info *info = closure;
1109
0
    const cairo_mask_compositor_t *compositor = info->compositor;
1110
1111
0
    if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) {
1112
0
  cairo_surface_t *mask;
1113
0
  cairo_color_t color;
1114
0
  cairo_solid_pattern_t solid;
1115
0
  int mask_x, mask_y;
1116
1117
0
  _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff);
1118
0
  _cairo_pattern_init_solid (&solid, &color);
1119
1120
0
  mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE,
1121
0
                 &_cairo_unbounded_rectangle,
1122
0
                 &_cairo_unbounded_rectangle,
1123
0
                 &mask_x, &mask_y);
1124
1125
0
  if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
1126
0
      compositor->composite (info->dst, info->op, info->src, mask,
1127
0
           x + info->src_x,  y + info->src_y,
1128
0
           mask_x,           mask_y,
1129
0
           x,                y,
1130
0
           w,                h);
1131
0
  }
1132
1133
0
  cairo_surface_destroy (mask);
1134
0
    } else {
1135
0
  compositor->composite (info->dst, info->op, info->src, NULL,
1136
0
             x + info->src_x,  y + info->src_y,
1137
0
             0,                0,
1138
0
             x,                y,
1139
0
             w,                h);
1140
0
    }
1141
0
}
1142
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)
1154
0
{
1155
0
    cairo_composite_rectangles_t *composite = closure;
1156
0
    struct composite_box_info info;
1157
0
    int i;
1158
1159
0
    assert (src_pattern == NULL);
1160
0
    assert (op == CAIRO_OPERATOR_SOURCE);
1161
1162
0
    info.compositor = compositor;
1163
0
    info.op = CAIRO_OPERATOR_SOURCE;
1164
0
    info.dst = dst;
1165
0
    info.src = compositor->pattern_to_surface (dst,
1166
0
                 &composite->mask_pattern.base,
1167
0
                 FALSE, extents,
1168
0
                 &composite->mask_sample_area,
1169
0
                 &info.src_x, &info.src_y);
1170
0
    if (unlikely (info.src->status))
1171
0
  return info.src->status;
1172
1173
0
    info.src_x += dst_x;
1174
0
    info.src_y += dst_y;
1175
1176
0
    for (i = 0; i < clip->num_boxes; i++)
1177
0
  do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y);
1178
1179
0
    cairo_surface_destroy (info.src);
1180
1181
0
    return CAIRO_STATUS_SUCCESS;
1182
0
}
1183
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)
1195
0
{
1196
0
    cairo_composite_rectangles_t *composite = closure;
1197
0
    cairo_surface_t *src, *mask;
1198
0
    int src_x, src_y;
1199
0
    int mask_x, mask_y;
1200
1201
0
    if (src_pattern != NULL) {
1202
0
  src = compositor->pattern_to_surface (dst, src_pattern, FALSE,
1203
0
                extents, src_sample,
1204
0
                &src_x, &src_y);
1205
0
  if (unlikely (src->status))
1206
0
      return src->status;
1207
1208
0
  mask = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, TRUE,
1209
0
                 extents, &composite->mask_sample_area,
1210
0
                 &mask_x, &mask_y);
1211
0
  if (unlikely (mask->status)) {
1212
0
      cairo_surface_destroy (src);
1213
0
      return mask->status;
1214
0
  }
1215
1216
0
  compositor->composite (dst, op, src, mask,
1217
0
             extents->x + src_x,  extents->y + src_y,
1218
0
             extents->x + mask_x, extents->y + mask_y,
1219
0
             extents->x - dst_x,  extents->y - dst_y,
1220
0
             extents->width,      extents->height);
1221
1222
0
  cairo_surface_destroy (mask);
1223
0
  cairo_surface_destroy (src);
1224
0
    } else {
1225
0
  src = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, FALSE,
1226
0
                extents, &composite->mask_sample_area,
1227
0
                &src_x, &src_y);
1228
0
  if (unlikely (src->status))
1229
0
      return src->status;
1230
1231
0
  compositor->composite (dst, op, src, NULL,
1232
0
             extents->x + src_x,  extents->y + src_y,
1233
0
             0, 0,
1234
0
             extents->x - dst_x,  extents->y - dst_y,
1235
0
             extents->width,      extents->height);
1236
1237
0
  cairo_surface_destroy (src);
1238
0
    }
1239
1240
0
    return CAIRO_STATUS_SUCCESS;
1241
0
}
1242
1243
static cairo_int_status_t
1244
_cairo_mask_compositor_mask (const cairo_compositor_t *_compositor,
1245
           cairo_composite_rectangles_t *extents)
1246
0
{
1247
0
    const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
1248
0
    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
1249
1250
0
    status = compositor->check_composite (extents);
1251
0
    if (unlikely (status))
1252
0
  return status;
1253
1254
0
    if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
1255
0
  extents->clip->path == NULL &&
1256
0
  _cairo_clip_is_region (extents->clip)) {
1257
0
  status = clip_and_composite (compositor,
1258
0
             composite_opacity_boxes,
1259
0
             composite_opacity_boxes,
1260
0
             &extents->mask_pattern.solid,
1261
0
             extents, need_unbounded_clip (extents));
1262
0
    } else {
1263
0
  status = clip_and_composite (compositor,
1264
0
             composite_mask,
1265
0
             extents->clip->path == NULL ? composite_mask_clip_boxes : NULL,
1266
0
             extents,
1267
0
             extents, need_bounded_clip (extents));
1268
0
    }
1269
1270
0
    return status;
1271
0
}
1272
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)
1282
0
{
1283
0
    const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
1284
0
    cairo_surface_t *mask;
1285
0
    cairo_surface_pattern_t pattern;
1286
0
    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
1287
1288
0
    status = compositor->check_composite (extents);
1289
0
    if (unlikely (status))
1290
0
  return status;
1291
1292
0
    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
1293
0
  cairo_boxes_t boxes;
1294
1295
0
  _cairo_boxes_init_with_clip (&boxes, extents->clip);
1296
0
  status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
1297
0
                style,
1298
0
                ctm,
1299
0
                antialias,
1300
0
                &boxes);
1301
0
  if (likely (status == CAIRO_INT_STATUS_SUCCESS))
1302
0
      status = clip_and_composite_boxes (compositor, extents, &boxes);
1303
0
  _cairo_boxes_fini (&boxes);
1304
0
    }
1305
1306
1307
0
    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
1308
0
  mask = cairo_surface_create_similar_image (extents->surface,
1309
0
               CAIRO_FORMAT_A8,
1310
0
               extents->bounded.width,
1311
0
               extents->bounded.height);
1312
0
  if (unlikely (mask->status))
1313
0
      return mask->status;
1314
1315
0
  status = _cairo_surface_offset_stroke (mask,
1316
0
                 extents->bounded.x,
1317
0
                 extents->bounded.y,
1318
0
                 CAIRO_OPERATOR_ADD,
1319
0
                 &_cairo_pattern_white.base,
1320
0
                 path, style, ctm, ctm_inverse,
1321
0
                 tolerance, antialias,
1322
0
                 extents->clip);
1323
0
  if (unlikely (status)) {
1324
0
      cairo_surface_destroy (mask);
1325
0
      return status;
1326
0
  }
1327
1328
0
  _cairo_pattern_init_for_surface (&pattern, mask);
1329
0
  cairo_surface_destroy (mask);
1330
1331
0
  cairo_matrix_init_translate (&pattern.base.matrix,
1332
0
             -extents->bounded.x,
1333
0
             -extents->bounded.y);
1334
0
  pattern.base.filter = CAIRO_FILTER_NEAREST;
1335
0
  pattern.base.extend = CAIRO_EXTEND_NONE;
1336
0
  status = _cairo_surface_mask (extents->surface,
1337
0
              extents->op,
1338
0
              &extents->source_pattern.base,
1339
0
              &pattern.base,
1340
0
              extents->clip);
1341
0
  _cairo_pattern_fini (&pattern.base);
1342
0
    }
1343
1344
0
    return status;
1345
0
}
1346
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)
1354
0
{
1355
0
    const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
1356
0
    cairo_surface_t *mask;
1357
0
    cairo_surface_pattern_t pattern;
1358
0
    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
1359
1360
0
    status = compositor->check_composite (extents);
1361
0
    if (unlikely (status))
1362
0
  return status;
1363
1364
0
    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
1365
0
  cairo_boxes_t boxes;
1366
1367
0
  _cairo_boxes_init_with_clip (&boxes, extents->clip);
1368
0
  status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
1369
0
                    fill_rule,
1370
0
                    antialias,
1371
0
                    &boxes);
1372
0
  if (likely (status == CAIRO_INT_STATUS_SUCCESS))
1373
0
      status = clip_and_composite_boxes (compositor, extents, &boxes);
1374
0
  _cairo_boxes_fini (&boxes);
1375
0
    }
1376
1377
0
    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
1378
0
  mask = cairo_surface_create_similar_image (extents->surface,
1379
0
               CAIRO_FORMAT_A8,
1380
0
               extents->bounded.width,
1381
0
               extents->bounded.height);
1382
0
  if (unlikely (mask->status))
1383
0
      return mask->status;
1384
1385
0
  status = _cairo_surface_offset_fill (mask,
1386
0
               extents->bounded.x,
1387
0
               extents->bounded.y,
1388
0
               CAIRO_OPERATOR_ADD,
1389
0
               &_cairo_pattern_white.base,
1390
0
               path, fill_rule, tolerance, antialias,
1391
0
               extents->clip);
1392
0
  if (unlikely (status)) {
1393
0
      cairo_surface_destroy (mask);
1394
0
      return status;
1395
0
  }
1396
1397
0
  _cairo_pattern_init_for_surface (&pattern, mask);
1398
0
  cairo_surface_destroy (mask);
1399
1400
0
  cairo_matrix_init_translate (&pattern.base.matrix,
1401
0
             -extents->bounded.x,
1402
0
             -extents->bounded.y);
1403
0
  pattern.base.filter = CAIRO_FILTER_NEAREST;
1404
0
  pattern.base.extend = CAIRO_EXTEND_NONE;
1405
0
  status = _cairo_surface_mask (extents->surface,
1406
0
              extents->op,
1407
0
              &extents->source_pattern.base,
1408
0
              &pattern.base,
1409
0
              extents->clip);
1410
0
  _cairo_pattern_fini (&pattern.base);
1411
0
    }
1412
1413
0
    return status;
1414
0
}
1415
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)
1423
0
{
1424
0
    const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
1425
0
    cairo_surface_t *mask;
1426
0
    cairo_surface_pattern_t pattern;
1427
0
    cairo_int_status_t status;
1428
1429
0
    status = compositor->check_composite (extents);
1430
0
    if (unlikely (status))
1431
0
  return CAIRO_INT_STATUS_UNSUPPORTED;
1432
1433
0
    mask = cairo_surface_create_similar_image (extents->surface,
1434
0
                 CAIRO_FORMAT_A8,
1435
0
                 extents->bounded.width,
1436
0
                 extents->bounded.height);
1437
0
    if (unlikely (mask->status))
1438
0
  return mask->status;
1439
1440
0
    status = _cairo_surface_offset_glyphs (mask,
1441
0
             extents->bounded.x,
1442
0
             extents->bounded.y,
1443
0
             CAIRO_OPERATOR_ADD,
1444
0
             &_cairo_pattern_white.base,
1445
0
             scaled_font, glyphs, num_glyphs,
1446
0
             extents->clip);
1447
0
    if (unlikely (status)) {
1448
0
  cairo_surface_destroy (mask);
1449
0
  return status;
1450
0
    }
1451
1452
0
    _cairo_pattern_init_for_surface (&pattern, mask);
1453
0
    cairo_surface_destroy (mask);
1454
1455
0
    cairo_matrix_init_translate (&pattern.base.matrix,
1456
0
         -extents->bounded.x,
1457
0
         -extents->bounded.y);
1458
0
    pattern.base.filter = CAIRO_FILTER_NEAREST;
1459
0
    pattern.base.extend = CAIRO_EXTEND_NONE;
1460
0
    status = _cairo_surface_mask (extents->surface,
1461
0
          extents->op,
1462
0
          &extents->source_pattern.base,
1463
0
          &pattern.base,
1464
0
          extents->clip);
1465
0
    _cairo_pattern_fini (&pattern.base);
1466
1467
0
    return status;
1468
0
}
1469
1470
void
1471
_cairo_mask_compositor_init (cairo_mask_compositor_t *compositor,
1472
           const cairo_compositor_t *delegate)
1473
0
{
1474
0
    compositor->base.delegate = delegate;
1475
1476
0
    compositor->base.paint = _cairo_mask_compositor_paint;
1477
0
    compositor->base.mask  = _cairo_mask_compositor_mask;
1478
0
    compositor->base.fill  = _cairo_mask_compositor_fill;
1479
0
    compositor->base.stroke = _cairo_mask_compositor_stroke;
1480
0
    compositor->base.glyphs = _cairo_mask_compositor_glyphs;
1481
0
}