/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 | } |