/src/cairo/src/cairo-spans-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 | | #include "cairoint.h" |
43 | | |
44 | | #include "cairo-compositor-private.h" |
45 | | #include "cairo-clip-inline.h" |
46 | | #include "cairo-clip-private.h" |
47 | | #include "cairo-image-surface-private.h" |
48 | | #include "cairo-paginated-private.h" |
49 | | #include "cairo-pattern-inline.h" |
50 | | #include "cairo-region-private.h" |
51 | | #include "cairo-recording-surface-inline.h" |
52 | | #include "cairo-spans-compositor-private.h" |
53 | | #include "cairo-surface-subsurface-private.h" |
54 | | #include "cairo-surface-snapshot-private.h" |
55 | | #include "cairo-surface-observer-private.h" |
56 | | |
57 | | typedef struct { |
58 | | cairo_polygon_t *polygon; |
59 | | cairo_fill_rule_t fill_rule; |
60 | | cairo_antialias_t antialias; |
61 | | } composite_spans_info_t; |
62 | | |
63 | | static cairo_int_status_t |
64 | | composite_polygon (const cairo_spans_compositor_t *compositor, |
65 | | cairo_composite_rectangles_t *extents, |
66 | | cairo_polygon_t *polygon, |
67 | | cairo_fill_rule_t fill_rule, |
68 | | cairo_antialias_t antialias); |
69 | | |
70 | | static cairo_int_status_t |
71 | | composite_boxes (const cairo_spans_compositor_t *compositor, |
72 | | cairo_composite_rectangles_t *extents, |
73 | | cairo_boxes_t *boxes); |
74 | | |
75 | | static cairo_int_status_t |
76 | | clip_and_composite_polygon (const cairo_spans_compositor_t *compositor, |
77 | | cairo_composite_rectangles_t *extents, |
78 | | cairo_polygon_t *polygon, |
79 | | cairo_fill_rule_t fill_rule, |
80 | | cairo_antialias_t antialias); |
81 | | static cairo_surface_t * |
82 | | get_clip_surface (const cairo_spans_compositor_t *compositor, |
83 | | cairo_surface_t *dst, |
84 | | const cairo_clip_t *clip, |
85 | | const cairo_rectangle_int_t *extents) |
86 | 0 | { |
87 | 0 | cairo_composite_rectangles_t composite; |
88 | 0 | cairo_surface_t *surface; |
89 | 0 | cairo_box_t box; |
90 | 0 | cairo_polygon_t polygon; |
91 | 0 | const cairo_clip_path_t *clip_path; |
92 | 0 | cairo_antialias_t antialias; |
93 | 0 | cairo_fill_rule_t fill_rule; |
94 | 0 | cairo_int_status_t status; |
95 | |
|
96 | 0 | assert (clip->path); |
97 | |
|
98 | 0 | surface = _cairo_surface_create_scratch (dst, |
99 | 0 | CAIRO_CONTENT_ALPHA, |
100 | 0 | extents->width, |
101 | 0 | extents->height, |
102 | 0 | CAIRO_COLOR_TRANSPARENT); |
103 | |
|
104 | 0 | _cairo_box_from_rectangle (&box, extents); |
105 | 0 | _cairo_polygon_init (&polygon, &box, 1); |
106 | |
|
107 | 0 | clip_path = clip->path; |
108 | 0 | status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, |
109 | 0 | clip_path->tolerance, |
110 | 0 | &polygon); |
111 | 0 | if (unlikely (status)) |
112 | 0 | goto cleanup_polygon; |
113 | | |
114 | 0 | polygon.num_limits = 0; |
115 | |
|
116 | 0 | antialias = clip_path->antialias; |
117 | 0 | fill_rule = clip_path->fill_rule; |
118 | |
|
119 | 0 | if (clip->boxes) { |
120 | 0 | cairo_polygon_t intersect; |
121 | 0 | cairo_boxes_t tmp; |
122 | |
|
123 | 0 | _cairo_boxes_init_for_array (&tmp, clip->boxes, clip->num_boxes); |
124 | 0 | status= _cairo_polygon_init_boxes (&intersect, &tmp); |
125 | 0 | if (unlikely (status)) |
126 | 0 | goto cleanup_polygon; |
127 | | |
128 | 0 | status = _cairo_polygon_intersect (&polygon, fill_rule, |
129 | 0 | &intersect, CAIRO_FILL_RULE_WINDING); |
130 | 0 | _cairo_polygon_fini (&intersect); |
131 | |
|
132 | 0 | if (unlikely (status)) |
133 | 0 | goto cleanup_polygon; |
134 | | |
135 | 0 | fill_rule = CAIRO_FILL_RULE_WINDING; |
136 | 0 | } |
137 | | |
138 | 0 | polygon.limits = NULL; |
139 | 0 | polygon.num_limits = 0; |
140 | |
|
141 | 0 | clip_path = clip_path->prev; |
142 | 0 | while (clip_path) { |
143 | 0 | if (clip_path->antialias == antialias) { |
144 | 0 | cairo_polygon_t next; |
145 | |
|
146 | 0 | _cairo_polygon_init (&next, NULL, 0); |
147 | 0 | status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, |
148 | 0 | clip_path->tolerance, |
149 | 0 | &next); |
150 | 0 | if (likely (status == CAIRO_INT_STATUS_SUCCESS)) |
151 | 0 | status = _cairo_polygon_intersect (&polygon, fill_rule, |
152 | 0 | &next, clip_path->fill_rule); |
153 | 0 | _cairo_polygon_fini (&next); |
154 | 0 | if (unlikely (status)) |
155 | 0 | goto cleanup_polygon; |
156 | | |
157 | 0 | fill_rule = CAIRO_FILL_RULE_WINDING; |
158 | 0 | } |
159 | | |
160 | 0 | clip_path = clip_path->prev; |
161 | 0 | } |
162 | | |
163 | 0 | _cairo_polygon_translate (&polygon, -extents->x, -extents->y); |
164 | 0 | status = _cairo_composite_rectangles_init_for_polygon (&composite, surface, |
165 | 0 | CAIRO_OPERATOR_ADD, |
166 | 0 | &_cairo_pattern_white.base, |
167 | 0 | &polygon, |
168 | 0 | NULL); |
169 | 0 | if (unlikely (status)) |
170 | 0 | goto cleanup_polygon; |
171 | | |
172 | 0 | status = composite_polygon (compositor, &composite, |
173 | 0 | &polygon, fill_rule, antialias); |
174 | 0 | _cairo_composite_rectangles_fini (&composite); |
175 | 0 | _cairo_polygon_fini (&polygon); |
176 | 0 | if (unlikely (status)) |
177 | 0 | goto error; |
178 | | |
179 | 0 | _cairo_polygon_init (&polygon, &box, 1); |
180 | |
|
181 | 0 | clip_path = clip->path; |
182 | 0 | antialias = clip_path->antialias == CAIRO_ANTIALIAS_DEFAULT ? CAIRO_ANTIALIAS_NONE : CAIRO_ANTIALIAS_DEFAULT; |
183 | 0 | clip_path = clip_path->prev; |
184 | 0 | while (clip_path) { |
185 | 0 | if (clip_path->antialias == antialias) { |
186 | 0 | if (polygon.num_edges == 0) { |
187 | 0 | status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, |
188 | 0 | clip_path->tolerance, |
189 | 0 | &polygon); |
190 | |
|
191 | 0 | fill_rule = clip_path->fill_rule; |
192 | 0 | polygon.limits = NULL; |
193 | 0 | polygon.num_limits = 0; |
194 | 0 | } else { |
195 | 0 | cairo_polygon_t next; |
196 | |
|
197 | 0 | _cairo_polygon_init (&next, NULL, 0); |
198 | 0 | status = _cairo_path_fixed_fill_to_polygon (&clip_path->path, |
199 | 0 | clip_path->tolerance, |
200 | 0 | &next); |
201 | 0 | if (likely (status == CAIRO_INT_STATUS_SUCCESS)) |
202 | 0 | status = _cairo_polygon_intersect (&polygon, fill_rule, |
203 | 0 | &next, clip_path->fill_rule); |
204 | 0 | _cairo_polygon_fini (&next); |
205 | 0 | fill_rule = CAIRO_FILL_RULE_WINDING; |
206 | 0 | } |
207 | 0 | if (unlikely (status)) |
208 | 0 | goto error; |
209 | 0 | } |
210 | | |
211 | 0 | clip_path = clip_path->prev; |
212 | 0 | } |
213 | | |
214 | 0 | if (polygon.num_edges) { |
215 | 0 | _cairo_polygon_translate (&polygon, -extents->x, -extents->y); |
216 | 0 | status = _cairo_composite_rectangles_init_for_polygon (&composite, surface, |
217 | 0 | CAIRO_OPERATOR_IN, |
218 | 0 | &_cairo_pattern_white.base, |
219 | 0 | &polygon, |
220 | 0 | NULL); |
221 | 0 | if (unlikely (status)) |
222 | 0 | goto cleanup_polygon; |
223 | | |
224 | 0 | status = composite_polygon (compositor, &composite, |
225 | 0 | &polygon, fill_rule, antialias); |
226 | 0 | _cairo_composite_rectangles_fini (&composite); |
227 | 0 | _cairo_polygon_fini (&polygon); |
228 | 0 | if (unlikely (status)) |
229 | 0 | goto error; |
230 | 0 | } |
231 | | |
232 | 0 | return surface; |
233 | | |
234 | 0 | cleanup_polygon: |
235 | 0 | _cairo_polygon_fini (&polygon); |
236 | 0 | error: |
237 | 0 | cairo_surface_destroy (surface); |
238 | 0 | return _cairo_int_surface_create_in_error (status); |
239 | 0 | } |
240 | | |
241 | | static cairo_int_status_t |
242 | | fixup_unbounded_mask (const cairo_spans_compositor_t *compositor, |
243 | | const cairo_composite_rectangles_t *extents, |
244 | | cairo_boxes_t *boxes) |
245 | 0 | { |
246 | 0 | cairo_composite_rectangles_t composite; |
247 | 0 | cairo_surface_t *clip; |
248 | 0 | cairo_int_status_t status; |
249 | |
|
250 | 0 | TRACE((stderr, "%s\n", __FUNCTION__)); |
251 | |
|
252 | 0 | clip = get_clip_surface (compositor, extents->surface, extents->clip, |
253 | 0 | &extents->unbounded); |
254 | 0 | if (unlikely (clip->status)) { |
255 | 0 | if ((cairo_int_status_t)clip->status == CAIRO_INT_STATUS_NOTHING_TO_DO) |
256 | 0 | return CAIRO_STATUS_SUCCESS; |
257 | | |
258 | 0 | return clip->status; |
259 | 0 | } |
260 | | |
261 | 0 | status = _cairo_composite_rectangles_init_for_boxes (&composite, |
262 | 0 | extents->surface, |
263 | 0 | CAIRO_OPERATOR_CLEAR, |
264 | 0 | &_cairo_pattern_clear.base, |
265 | 0 | boxes, |
266 | 0 | NULL); |
267 | 0 | if (unlikely (status)) |
268 | 0 | goto cleanup_clip; |
269 | | |
270 | 0 | _cairo_pattern_init_for_surface (&composite.mask_pattern.surface, clip); |
271 | 0 | composite.mask_pattern.base.filter = CAIRO_FILTER_NEAREST; |
272 | 0 | composite.mask_pattern.base.extend = CAIRO_EXTEND_NONE; |
273 | |
|
274 | 0 | status = composite_boxes (compositor, &composite, boxes); |
275 | |
|
276 | 0 | _cairo_pattern_fini (&composite.mask_pattern.base); |
277 | 0 | _cairo_composite_rectangles_fini (&composite); |
278 | |
|
279 | 0 | cleanup_clip: |
280 | 0 | cairo_surface_destroy (clip); |
281 | 0 | return status; |
282 | 0 | } |
283 | | |
284 | | static cairo_int_status_t |
285 | | fixup_unbounded_polygon (const cairo_spans_compositor_t *compositor, |
286 | | const cairo_composite_rectangles_t *extents, |
287 | | cairo_boxes_t *boxes) |
288 | 0 | { |
289 | 0 | cairo_polygon_t polygon, intersect; |
290 | 0 | cairo_composite_rectangles_t composite; |
291 | 0 | cairo_fill_rule_t fill_rule; |
292 | 0 | cairo_antialias_t antialias; |
293 | 0 | cairo_int_status_t status; |
294 | |
|
295 | 0 | TRACE((stderr, "%s\n", __FUNCTION__)); |
296 | | |
297 | | /* Can we treat the clip as a regular clear-polygon and use it to fill? */ |
298 | 0 | status = _cairo_clip_get_polygon (extents->clip, &polygon, |
299 | 0 | &fill_rule, &antialias); |
300 | 0 | if (status == CAIRO_INT_STATUS_UNSUPPORTED) |
301 | 0 | return status; |
302 | | |
303 | 0 | status= _cairo_polygon_init_boxes (&intersect, boxes); |
304 | 0 | if (unlikely (status)) |
305 | 0 | goto cleanup_polygon; |
306 | | |
307 | 0 | status = _cairo_polygon_intersect (&polygon, fill_rule, |
308 | 0 | &intersect, CAIRO_FILL_RULE_WINDING); |
309 | 0 | _cairo_polygon_fini (&intersect); |
310 | |
|
311 | 0 | if (unlikely (status)) |
312 | 0 | goto cleanup_polygon; |
313 | | |
314 | 0 | status = _cairo_composite_rectangles_init_for_polygon (&composite, |
315 | 0 | extents->surface, |
316 | 0 | CAIRO_OPERATOR_CLEAR, |
317 | 0 | &_cairo_pattern_clear.base, |
318 | 0 | &polygon, |
319 | 0 | NULL); |
320 | 0 | if (unlikely (status)) |
321 | 0 | goto cleanup_polygon; |
322 | | |
323 | 0 | status = composite_polygon (compositor, &composite, |
324 | 0 | &polygon, fill_rule, antialias); |
325 | |
|
326 | 0 | _cairo_composite_rectangles_fini (&composite); |
327 | 0 | cleanup_polygon: |
328 | 0 | _cairo_polygon_fini (&polygon); |
329 | |
|
330 | 0 | return status; |
331 | 0 | } |
332 | | |
333 | | static cairo_int_status_t |
334 | | fixup_unbounded_boxes (const cairo_spans_compositor_t *compositor, |
335 | | const cairo_composite_rectangles_t *extents, |
336 | | cairo_boxes_t *boxes) |
337 | 0 | { |
338 | 0 | cairo_boxes_t tmp, clear; |
339 | 0 | cairo_box_t box; |
340 | 0 | cairo_int_status_t status; |
341 | |
|
342 | 0 | assert (boxes->is_pixel_aligned); |
343 | |
|
344 | 0 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
345 | 0 | if (extents->bounded.width == extents->unbounded.width && |
346 | 0 | extents->bounded.height == extents->unbounded.height) |
347 | 0 | { |
348 | 0 | return CAIRO_STATUS_SUCCESS; |
349 | 0 | } |
350 | | |
351 | | /* subtract the drawn boxes from the unbounded area */ |
352 | 0 | _cairo_boxes_init (&clear); |
353 | |
|
354 | 0 | box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); |
355 | 0 | box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); |
356 | 0 | box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); |
357 | 0 | box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); |
358 | |
|
359 | 0 | if (boxes->num_boxes) { |
360 | 0 | _cairo_boxes_init (&tmp); |
361 | |
|
362 | 0 | status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box); |
363 | 0 | assert (status == CAIRO_INT_STATUS_SUCCESS); |
364 | |
|
365 | 0 | tmp.chunks.next = &boxes->chunks; |
366 | 0 | tmp.num_boxes += boxes->num_boxes; |
367 | |
|
368 | 0 | status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, |
369 | 0 | CAIRO_FILL_RULE_WINDING, |
370 | 0 | &clear); |
371 | 0 | tmp.chunks.next = NULL; |
372 | 0 | if (unlikely (status)) |
373 | 0 | goto error; |
374 | 0 | } else { |
375 | 0 | box.p1.x = _cairo_fixed_from_int (extents->unbounded.x); |
376 | 0 | box.p2.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); |
377 | |
|
378 | 0 | status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box); |
379 | 0 | assert (status == CAIRO_INT_STATUS_SUCCESS); |
380 | 0 | } |
381 | | |
382 | | /* If we have a clip polygon, we need to intersect with that as well */ |
383 | 0 | if (extents->clip->path) { |
384 | 0 | status = fixup_unbounded_polygon (compositor, extents, &clear); |
385 | 0 | if (status == CAIRO_INT_STATUS_UNSUPPORTED) |
386 | 0 | status = fixup_unbounded_mask (compositor, extents, &clear); |
387 | 0 | } else { |
388 | | /* Otherwise just intersect with the clip boxes */ |
389 | 0 | if (extents->clip->num_boxes) { |
390 | 0 | _cairo_boxes_init_for_array (&tmp, |
391 | 0 | extents->clip->boxes, |
392 | 0 | extents->clip->num_boxes); |
393 | 0 | status = _cairo_boxes_intersect (&clear, &tmp, &clear); |
394 | 0 | if (unlikely (status)) |
395 | 0 | goto error; |
396 | 0 | } |
397 | | |
398 | 0 | if (clear.is_pixel_aligned) { |
399 | 0 | status = compositor->fill_boxes (extents->surface, |
400 | 0 | CAIRO_OPERATOR_CLEAR, |
401 | 0 | CAIRO_COLOR_TRANSPARENT, |
402 | 0 | &clear); |
403 | 0 | } else { |
404 | 0 | cairo_composite_rectangles_t composite; |
405 | |
|
406 | 0 | status = _cairo_composite_rectangles_init_for_boxes (&composite, |
407 | 0 | extents->surface, |
408 | 0 | CAIRO_OPERATOR_CLEAR, |
409 | 0 | &_cairo_pattern_clear.base, |
410 | 0 | &clear, |
411 | 0 | NULL); |
412 | 0 | if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { |
413 | 0 | status = composite_boxes (compositor, &composite, &clear); |
414 | 0 | _cairo_composite_rectangles_fini (&composite); |
415 | 0 | } |
416 | 0 | } |
417 | 0 | } |
418 | | |
419 | 0 | error: |
420 | 0 | _cairo_boxes_fini (&clear); |
421 | 0 | return status; |
422 | 0 | } |
423 | | |
424 | | static cairo_surface_t * |
425 | | unwrap_source (const cairo_pattern_t *pattern) |
426 | 0 | { |
427 | 0 | cairo_rectangle_int_t limit; |
428 | |
|
429 | 0 | return _cairo_pattern_get_source ((cairo_surface_pattern_t *)pattern, |
430 | 0 | &limit); |
431 | 0 | } |
432 | | |
433 | | static cairo_bool_t |
434 | | is_recording_pattern (const cairo_pattern_t *pattern) |
435 | 333 | { |
436 | 333 | cairo_surface_t *surface; |
437 | | |
438 | 333 | if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) |
439 | 329 | return FALSE; |
440 | | |
441 | 4 | surface = ((const cairo_surface_pattern_t *) pattern)->surface; |
442 | 4 | return _cairo_surface_is_recording (surface); |
443 | 333 | } |
444 | | |
445 | | static cairo_bool_t |
446 | | recording_pattern_contains_sample (const cairo_pattern_t *pattern, |
447 | | const cairo_rectangle_int_t *sample) |
448 | 333 | { |
449 | 333 | cairo_recording_surface_t *surface; |
450 | | |
451 | 333 | if (! is_recording_pattern (pattern)) |
452 | 333 | return FALSE; |
453 | | |
454 | 0 | if (pattern->extend == CAIRO_EXTEND_NONE) |
455 | 0 | return TRUE; |
456 | | |
457 | 0 | surface = (cairo_recording_surface_t *) unwrap_source (pattern); |
458 | 0 | if (surface->unbounded) |
459 | 0 | return TRUE; |
460 | | |
461 | 0 | return _cairo_rectangle_contains_rectangle (&surface->extents, sample); |
462 | 0 | } |
463 | | |
464 | | static cairo_bool_t |
465 | | op_reduces_to_source (const cairo_composite_rectangles_t *extents, |
466 | | cairo_bool_t no_mask) |
467 | 3.99k | { |
468 | 3.99k | if (extents->op == CAIRO_OPERATOR_SOURCE) |
469 | 0 | return TRUE; |
470 | | |
471 | 3.99k | if (extents->surface->is_clear) |
472 | 3.70k | return extents->op == CAIRO_OPERATOR_OVER || extents->op == CAIRO_OPERATOR_ADD; |
473 | | |
474 | 292 | if (no_mask && extents->op == CAIRO_OPERATOR_OVER) |
475 | 282 | return _cairo_pattern_is_opaque (&extents->source_pattern.base, |
476 | 282 | &extents->source_sample_area); |
477 | | |
478 | 10 | return FALSE; |
479 | 292 | } |
480 | | |
481 | | static cairo_status_t |
482 | | upload_boxes (const cairo_spans_compositor_t *compositor, |
483 | | const cairo_composite_rectangles_t *extents, |
484 | | cairo_boxes_t *boxes) |
485 | 4 | { |
486 | 4 | cairo_surface_t *dst = extents->surface; |
487 | 4 | const cairo_surface_pattern_t *source = &extents->source_pattern.surface; |
488 | 4 | cairo_surface_t *src; |
489 | 4 | cairo_rectangle_int_t limit; |
490 | 4 | cairo_int_status_t status; |
491 | 4 | int tx, ty; |
492 | | |
493 | 4 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
494 | | |
495 | 4 | src = _cairo_pattern_get_source(source, &limit); |
496 | 4 | if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type)) |
497 | 0 | return CAIRO_INT_STATUS_UNSUPPORTED; |
498 | | |
499 | 4 | if (! _cairo_matrix_is_integer_translation (&source->base.matrix, &tx, &ty)) |
500 | 4 | return CAIRO_INT_STATUS_UNSUPPORTED; |
501 | | |
502 | | /* Check that the data is entirely within the image */ |
503 | 0 | if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y) |
504 | 0 | return CAIRO_INT_STATUS_UNSUPPORTED; |
505 | | |
506 | 0 | if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width || |
507 | 0 | extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height) |
508 | 0 | return CAIRO_INT_STATUS_UNSUPPORTED; |
509 | | |
510 | 0 | tx += limit.x; |
511 | 0 | ty += limit.y; |
512 | |
|
513 | 0 | if (src->type == CAIRO_SURFACE_TYPE_IMAGE) |
514 | 0 | status = compositor->draw_image_boxes (dst, |
515 | 0 | (cairo_image_surface_t *)src, |
516 | 0 | boxes, tx, ty); |
517 | 0 | else |
518 | 0 | status = compositor->copy_boxes (dst, src, boxes, &extents->bounded, |
519 | 0 | tx, ty); |
520 | |
|
521 | 0 | return status; |
522 | 0 | } |
523 | | |
524 | | static cairo_bool_t |
525 | | _clip_is_region (const cairo_clip_t *clip) |
526 | 3.99k | { |
527 | 3.99k | int i; |
528 | | |
529 | 3.99k | if (clip->is_region) |
530 | 3.99k | return TRUE; |
531 | | |
532 | 0 | if (clip->path) |
533 | 0 | return FALSE; |
534 | | |
535 | 0 | for (i = 0; i < clip->num_boxes; i++) { |
536 | 0 | const cairo_box_t *b = &clip->boxes[i]; |
537 | 0 | if (!_cairo_fixed_is_integer (b->p1.x | b->p1.y | b->p2.x | b->p2.y)) |
538 | 0 | return FALSE; |
539 | 0 | } |
540 | | |
541 | 0 | return TRUE; |
542 | 0 | } |
543 | | |
544 | | static cairo_int_status_t |
545 | | composite_aligned_boxes (const cairo_spans_compositor_t *compositor, |
546 | | const cairo_composite_rectangles_t *extents, |
547 | | cairo_boxes_t *boxes) |
548 | 3.99k | { |
549 | 3.99k | cairo_surface_t *dst = extents->surface; |
550 | 3.99k | cairo_operator_t op = extents->op; |
551 | 3.99k | const cairo_pattern_t *source = &extents->source_pattern.base; |
552 | 3.99k | cairo_int_status_t status; |
553 | 3.99k | cairo_bool_t need_clip_mask = ! _clip_is_region (extents->clip); |
554 | 3.99k | cairo_bool_t op_is_source; |
555 | 3.99k | cairo_bool_t no_mask; |
556 | 3.99k | cairo_bool_t inplace; |
557 | | |
558 | 3.99k | TRACE ((stderr, "%s: need_clip_mask=%d, is-bounded=%d\n", |
559 | 3.99k | __FUNCTION__, need_clip_mask, extents->is_bounded)); |
560 | 3.99k | if (need_clip_mask && ! extents->is_bounded) { |
561 | 0 | TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__)); |
562 | 0 | return CAIRO_INT_STATUS_UNSUPPORTED; |
563 | 0 | } |
564 | | |
565 | 3.99k | no_mask = extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID && |
566 | 3.99k | CAIRO_COLOR_IS_OPAQUE (&extents->mask_pattern.solid.color); |
567 | 3.99k | op_is_source = op_reduces_to_source (extents, no_mask); |
568 | 3.99k | inplace = ! need_clip_mask && op_is_source && no_mask; |
569 | | |
570 | 3.99k | TRACE ((stderr, "%s: op-is-source=%d [op=%d], no-mask=%d, inplace=%d\n", |
571 | 3.99k | __FUNCTION__, op_is_source, op, no_mask, inplace)); |
572 | | |
573 | 3.99k | if (op == CAIRO_OPERATOR_SOURCE && (need_clip_mask || ! no_mask)) { |
574 | | /* SOURCE with a mask is actually a LERP in cairo semantics */ |
575 | 0 | if ((compositor->flags & CAIRO_SPANS_COMPOSITOR_HAS_LERP) == 0) { |
576 | 0 | TRACE ((stderr, "%s: unsupported lerp\n", __FUNCTION__)); |
577 | 0 | return CAIRO_INT_STATUS_UNSUPPORTED; |
578 | 0 | } |
579 | 0 | } |
580 | | |
581 | | /* Are we just copying a recording surface? */ |
582 | 3.99k | if (inplace && |
583 | 333 | recording_pattern_contains_sample (&extents->source_pattern.base, |
584 | 333 | &extents->source_sample_area)) |
585 | 0 | { |
586 | 0 | cairo_clip_t *recording_clip; |
587 | 0 | const cairo_pattern_t *source = &extents->source_pattern.base; |
588 | 0 | const cairo_matrix_t *m; |
589 | 0 | cairo_matrix_t matrix; |
590 | | |
591 | | /* XXX could also do tiling repeat modes... */ |
592 | | |
593 | | /* first clear the area about to be overwritten */ |
594 | 0 | if (! dst->is_clear) { |
595 | 0 | status = compositor->fill_boxes (dst, |
596 | 0 | CAIRO_OPERATOR_CLEAR, |
597 | 0 | CAIRO_COLOR_TRANSPARENT, |
598 | 0 | boxes); |
599 | 0 | if (unlikely (status)) |
600 | 0 | return status; |
601 | | |
602 | 0 | dst->is_clear = TRUE; |
603 | 0 | } |
604 | | |
605 | 0 | m = &source->matrix; |
606 | 0 | if (_cairo_surface_has_device_transform (dst)) { |
607 | 0 | cairo_matrix_multiply (&matrix, |
608 | 0 | &source->matrix, |
609 | 0 | &dst->device_transform); |
610 | 0 | m = &matrix; |
611 | 0 | } |
612 | |
|
613 | 0 | recording_clip = _cairo_clip_from_boxes (boxes); |
614 | 0 | status = _cairo_recording_surface_replay_with_clip (unwrap_source (source), |
615 | 0 | m, dst, recording_clip); |
616 | 0 | _cairo_clip_destroy (recording_clip); |
617 | |
|
618 | 0 | return status; |
619 | 0 | } |
620 | | |
621 | 3.99k | status = CAIRO_INT_STATUS_UNSUPPORTED; |
622 | 3.99k | if (! need_clip_mask && no_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) { |
623 | 329 | const cairo_color_t *color; |
624 | | |
625 | 329 | color = &((cairo_solid_pattern_t *) source)->color; |
626 | 329 | if (op_is_source) |
627 | 329 | op = CAIRO_OPERATOR_SOURCE; |
628 | 329 | status = compositor->fill_boxes (dst, op, color, boxes); |
629 | 3.67k | } else if (inplace && source->type == CAIRO_PATTERN_TYPE_SURFACE) { |
630 | 4 | status = upload_boxes (compositor, extents, boxes); |
631 | 4 | } |
632 | 3.99k | if (status == CAIRO_INT_STATUS_UNSUPPORTED) { |
633 | 3.67k | cairo_surface_t *src; |
634 | 3.67k | cairo_surface_t *mask = NULL; |
635 | 3.67k | int src_x, src_y; |
636 | 3.67k | int mask_x = 0, mask_y = 0; |
637 | | |
638 | | /* All typical cases will have been resolved before now... */ |
639 | 3.67k | if (need_clip_mask) { |
640 | 0 | mask = get_clip_surface (compositor, dst, extents->clip, |
641 | 0 | &extents->bounded); |
642 | 0 | if (unlikely (mask->status)) |
643 | 0 | return mask->status; |
644 | | |
645 | 0 | mask_x = -extents->bounded.x; |
646 | 0 | mask_y = -extents->bounded.y; |
647 | 0 | } |
648 | | |
649 | | /* XXX but this is still ugly */ |
650 | 3.67k | if (! no_mask) { |
651 | 3.38k | src = compositor->pattern_to_surface (dst, |
652 | 3.38k | &extents->mask_pattern.base, |
653 | 3.38k | TRUE, |
654 | 3.38k | &extents->bounded, |
655 | 3.38k | &extents->mask_sample_area, |
656 | 3.38k | &src_x, &src_y); |
657 | 3.38k | if (unlikely (src->status)) { |
658 | 2 | cairo_surface_destroy (mask); |
659 | 2 | return src->status; |
660 | 2 | } |
661 | | |
662 | 3.38k | if (mask != NULL) { |
663 | 0 | status = compositor->composite_boxes (mask, CAIRO_OPERATOR_IN, |
664 | 0 | src, NULL, |
665 | 0 | src_x, src_y, |
666 | 0 | 0, 0, |
667 | 0 | mask_x, mask_y, |
668 | 0 | boxes, &extents->bounded); |
669 | |
|
670 | 0 | cairo_surface_destroy (src); |
671 | 3.38k | } else { |
672 | 3.38k | mask = src; |
673 | 3.38k | mask_x = src_x; |
674 | 3.38k | mask_y = src_y; |
675 | 3.38k | } |
676 | 3.38k | } |
677 | | |
678 | 3.66k | src = compositor->pattern_to_surface (dst, source, FALSE, |
679 | 3.66k | &extents->bounded, |
680 | 3.66k | &extents->source_sample_area, |
681 | 3.66k | &src_x, &src_y); |
682 | 3.66k | if (likely (src->status == CAIRO_STATUS_SUCCESS)) { |
683 | 3.66k | status = compositor->composite_boxes (dst, op, src, mask, |
684 | 3.66k | src_x, src_y, |
685 | 3.66k | mask_x, mask_y, |
686 | 3.66k | 0, 0, |
687 | 3.66k | boxes, &extents->bounded); |
688 | 3.66k | cairo_surface_destroy (src); |
689 | 3.66k | } else |
690 | 0 | status = src->status; |
691 | | |
692 | 3.66k | cairo_surface_destroy (mask); |
693 | 3.66k | } |
694 | | |
695 | 3.99k | if (status == CAIRO_INT_STATUS_SUCCESS && ! extents->is_bounded) |
696 | 0 | status = fixup_unbounded_boxes (compositor, extents, boxes); |
697 | | |
698 | 3.99k | return status; |
699 | 3.99k | } |
700 | | |
701 | | static cairo_bool_t |
702 | | composite_needs_clip (const cairo_composite_rectangles_t *composite, |
703 | | const cairo_box_t *extents) |
704 | 397 | { |
705 | 397 | return !_cairo_clip_contains_box (composite->clip, extents); |
706 | 397 | } |
707 | | |
708 | | static cairo_int_status_t |
709 | | composite_boxes (const cairo_spans_compositor_t *compositor, |
710 | | cairo_composite_rectangles_t *extents, |
711 | | cairo_boxes_t *boxes) |
712 | 397 | { |
713 | 397 | cairo_abstract_span_renderer_t renderer; |
714 | 397 | cairo_rectangular_scan_converter_t converter; |
715 | 397 | const struct _cairo_boxes_chunk *chunk; |
716 | 397 | cairo_int_status_t status; |
717 | 397 | cairo_box_t box; |
718 | | |
719 | 397 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
720 | 397 | _cairo_box_from_rectangle (&box, &extents->unbounded); |
721 | 397 | if (composite_needs_clip (extents, &box)) { |
722 | 85 | TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__)); |
723 | 85 | return CAIRO_INT_STATUS_UNSUPPORTED; |
724 | 85 | } |
725 | | |
726 | 312 | _cairo_rectangular_scan_converter_init (&converter, &extents->unbounded); |
727 | 624 | for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { |
728 | 312 | const cairo_box_t *box = chunk->base; |
729 | 312 | int i; |
730 | | |
731 | 658 | for (i = 0; i < chunk->count; i++) { |
732 | 346 | status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1); |
733 | 346 | if (unlikely (status)) |
734 | 0 | goto cleanup_converter; |
735 | 346 | } |
736 | 312 | } |
737 | | |
738 | 312 | status = compositor->renderer_init (&renderer, extents, |
739 | 312 | CAIRO_ANTIALIAS_DEFAULT, FALSE); |
740 | 312 | if (likely (status == CAIRO_INT_STATUS_SUCCESS)) |
741 | 310 | status = converter.base.generate (&converter.base, &renderer.base); |
742 | 312 | compositor->renderer_fini (&renderer, status); |
743 | | |
744 | 312 | cleanup_converter: |
745 | 312 | converter.base.destroy (&converter.base); |
746 | 312 | return status; |
747 | 312 | } |
748 | | |
749 | | static cairo_int_status_t |
750 | | composite_polygon (const cairo_spans_compositor_t *compositor, |
751 | | cairo_composite_rectangles_t *extents, |
752 | | cairo_polygon_t *polygon, |
753 | | cairo_fill_rule_t fill_rule, |
754 | | cairo_antialias_t antialias) |
755 | 338 | { |
756 | 338 | cairo_abstract_span_renderer_t renderer; |
757 | 338 | cairo_scan_converter_t *converter; |
758 | 338 | cairo_bool_t needs_clip; |
759 | 338 | cairo_int_status_t status; |
760 | | |
761 | 338 | if (extents->is_bounded) |
762 | 338 | needs_clip = extents->clip->path != NULL; |
763 | 0 | else |
764 | 0 | needs_clip = !_clip_is_region (extents->clip) || extents->clip->num_boxes > 1; |
765 | 338 | TRACE ((stderr, "%s - needs_clip=%d\n", __FUNCTION__, needs_clip)); |
766 | 338 | if (needs_clip) { |
767 | 6 | TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__)); |
768 | 6 | return CAIRO_INT_STATUS_UNSUPPORTED; |
769 | 0 | converter = _cairo_clip_tor_scan_converter_create (extents->clip, |
770 | 0 | polygon, |
771 | 0 | fill_rule, antialias); |
772 | 332 | } else { |
773 | 332 | const cairo_rectangle_int_t *r = &extents->unbounded; |
774 | | |
775 | 332 | if (antialias == CAIRO_ANTIALIAS_FAST) { |
776 | 0 | converter = _cairo_tor22_scan_converter_create (r->x, r->y, |
777 | 0 | r->x + r->width, |
778 | 0 | r->y + r->height, |
779 | 0 | fill_rule, antialias); |
780 | 0 | status = _cairo_tor22_scan_converter_add_polygon (converter, polygon); |
781 | 332 | } else if (antialias == CAIRO_ANTIALIAS_NONE) { |
782 | 0 | converter = _cairo_mono_scan_converter_create (r->x, r->y, |
783 | 0 | r->x + r->width, |
784 | 0 | r->y + r->height, |
785 | 0 | fill_rule); |
786 | 0 | status = _cairo_mono_scan_converter_add_polygon (converter, polygon); |
787 | 332 | } else { |
788 | 332 | converter = _cairo_tor_scan_converter_create (r->x, r->y, |
789 | 332 | r->x + r->width, |
790 | 332 | r->y + r->height, |
791 | 332 | fill_rule, antialias); |
792 | 332 | status = _cairo_tor_scan_converter_add_polygon (converter, polygon); |
793 | 332 | } |
794 | 332 | } |
795 | 332 | if (unlikely (status)) |
796 | 0 | goto cleanup_converter; |
797 | | |
798 | 332 | status = compositor->renderer_init (&renderer, extents, |
799 | 332 | antialias, needs_clip); |
800 | 332 | if (likely (status == CAIRO_INT_STATUS_SUCCESS)) |
801 | 324 | status = converter->generate (converter, &renderer.base); |
802 | 332 | compositor->renderer_fini (&renderer, status); |
803 | | |
804 | 332 | cleanup_converter: |
805 | 332 | converter->destroy (converter); |
806 | 332 | return status; |
807 | 332 | } |
808 | | |
809 | | static cairo_int_status_t |
810 | | trim_extents_to_boxes (cairo_composite_rectangles_t *extents, |
811 | | cairo_boxes_t *boxes) |
812 | 4.42k | { |
813 | 4.42k | cairo_box_t box; |
814 | | |
815 | 4.42k | _cairo_boxes_extents (boxes, &box); |
816 | 4.42k | return _cairo_composite_rectangles_intersect_mask_extents (extents, &box); |
817 | 4.42k | } |
818 | | |
819 | | static cairo_int_status_t |
820 | | trim_extents_to_polygon (cairo_composite_rectangles_t *extents, |
821 | | cairo_polygon_t *polygon) |
822 | 269 | { |
823 | 269 | return _cairo_composite_rectangles_intersect_mask_extents (extents, |
824 | 269 | &polygon->extents); |
825 | 269 | } |
826 | | |
827 | | static cairo_int_status_t |
828 | | clip_and_composite_boxes (const cairo_spans_compositor_t *compositor, |
829 | | cairo_composite_rectangles_t *extents, |
830 | | cairo_boxes_t *boxes) |
831 | 4.42k | { |
832 | 4.42k | cairo_int_status_t status; |
833 | 4.42k | cairo_polygon_t polygon; |
834 | | |
835 | 4.42k | TRACE ((stderr, "%s\n", __FUNCTION__)); |
836 | 4.42k | status = trim_extents_to_boxes (extents, boxes); |
837 | 4.42k | if (unlikely (status)) |
838 | 10 | return status; |
839 | | |
840 | 4.41k | if (boxes->num_boxes == 0) { |
841 | 0 | if (extents->is_bounded) |
842 | 0 | return CAIRO_STATUS_SUCCESS; |
843 | | |
844 | 0 | return fixup_unbounded_boxes (compositor, extents, boxes); |
845 | 0 | } |
846 | | |
847 | | /* Can we reduce drawing through a clip-mask to simply drawing the clip? */ |
848 | 4.41k | if (extents->clip->path != NULL && extents->is_bounded) { |
849 | 20 | cairo_polygon_t polygon; |
850 | 20 | cairo_fill_rule_t fill_rule; |
851 | 20 | cairo_antialias_t antialias; |
852 | 20 | cairo_clip_t *clip; |
853 | | |
854 | 20 | clip = _cairo_clip_copy (extents->clip); |
855 | 20 | clip = _cairo_clip_intersect_boxes (clip, boxes); |
856 | 20 | if (_cairo_clip_is_all_clipped (clip)) |
857 | 0 | return CAIRO_INT_STATUS_NOTHING_TO_DO; |
858 | | |
859 | 20 | status = _cairo_clip_get_polygon (clip, &polygon, |
860 | 20 | &fill_rule, &antialias); |
861 | 20 | _cairo_clip_path_destroy (clip->path); |
862 | 20 | clip->path = NULL; |
863 | 20 | if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { |
864 | 20 | cairo_clip_t *saved_clip = extents->clip; |
865 | 20 | extents->clip = clip; |
866 | | |
867 | 20 | status = clip_and_composite_polygon (compositor, extents, &polygon, |
868 | 20 | fill_rule, antialias); |
869 | | |
870 | 20 | clip = extents->clip; |
871 | 20 | extents->clip = saved_clip; |
872 | | |
873 | 20 | _cairo_polygon_fini (&polygon); |
874 | 20 | } |
875 | 20 | _cairo_clip_destroy (clip); |
876 | | |
877 | 20 | if (status != CAIRO_INT_STATUS_UNSUPPORTED) |
878 | 14 | return status; |
879 | 20 | } |
880 | | |
881 | 4.39k | if (boxes->is_pixel_aligned) { |
882 | 3.99k | status = composite_aligned_boxes (compositor, extents, boxes); |
883 | 3.99k | if (status != CAIRO_INT_STATUS_UNSUPPORTED) |
884 | 3.99k | return status; |
885 | 3.99k | } |
886 | | |
887 | 397 | status = composite_boxes (compositor, extents, boxes); |
888 | 397 | if (status != CAIRO_INT_STATUS_UNSUPPORTED) |
889 | 310 | return status; |
890 | | |
891 | 87 | status = _cairo_polygon_init_boxes (&polygon, boxes); |
892 | 87 | if (unlikely (status)) |
893 | 0 | return status; |
894 | | |
895 | 87 | status = composite_polygon (compositor, extents, &polygon, |
896 | 87 | CAIRO_FILL_RULE_WINDING, |
897 | 87 | CAIRO_ANTIALIAS_DEFAULT); |
898 | 87 | _cairo_polygon_fini (&polygon); |
899 | | |
900 | 87 | return status; |
901 | 87 | } |
902 | | |
903 | | static cairo_int_status_t |
904 | | clip_and_composite_polygon (const cairo_spans_compositor_t *compositor, |
905 | | cairo_composite_rectangles_t *extents, |
906 | | cairo_polygon_t *polygon, |
907 | | cairo_fill_rule_t fill_rule, |
908 | | cairo_antialias_t antialias) |
909 | 251 | { |
910 | 251 | cairo_int_status_t status; |
911 | | |
912 | 251 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
913 | | |
914 | | /* XXX simply uses polygon limits.point extemities, tessellation? */ |
915 | 251 | status = trim_extents_to_polygon (extents, polygon); |
916 | 251 | if (unlikely (status)) |
917 | 0 | return status; |
918 | | |
919 | 251 | if (_cairo_polygon_is_empty (polygon)) { |
920 | 0 | cairo_boxes_t boxes; |
921 | |
|
922 | 0 | if (extents->is_bounded) |
923 | 0 | return CAIRO_STATUS_SUCCESS; |
924 | | |
925 | 0 | _cairo_boxes_init (&boxes); |
926 | 0 | extents->bounded.width = extents->bounded.height = 0; |
927 | 0 | return fixup_unbounded_boxes (compositor, extents, &boxes); |
928 | 0 | } |
929 | | |
930 | 251 | if (extents->is_bounded && extents->clip->path) { |
931 | 18 | cairo_polygon_t clipper; |
932 | 18 | cairo_antialias_t clip_antialias; |
933 | 18 | cairo_fill_rule_t clip_fill_rule; |
934 | | |
935 | 18 | TRACE((stderr, "%s - combining shape with clip polygon\n", |
936 | 18 | __FUNCTION__)); |
937 | | |
938 | 18 | status = _cairo_clip_get_polygon (extents->clip, |
939 | 18 | &clipper, |
940 | 18 | &clip_fill_rule, |
941 | 18 | &clip_antialias); |
942 | 18 | if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { |
943 | 18 | cairo_clip_t *old_clip; |
944 | | |
945 | 18 | if (clip_antialias == antialias) { |
946 | 18 | status = _cairo_polygon_intersect (polygon, fill_rule, |
947 | 18 | &clipper, clip_fill_rule); |
948 | 18 | _cairo_polygon_fini (&clipper); |
949 | 18 | if (unlikely (status)) |
950 | 0 | return status; |
951 | | |
952 | 18 | old_clip = extents->clip; |
953 | 18 | extents->clip = _cairo_clip_copy_region (extents->clip); |
954 | 18 | _cairo_clip_destroy (old_clip); |
955 | | |
956 | 18 | status = trim_extents_to_polygon (extents, polygon); |
957 | 18 | if (unlikely (status)) |
958 | 0 | return status; |
959 | | |
960 | 18 | fill_rule = CAIRO_FILL_RULE_WINDING; |
961 | 18 | } else { |
962 | 0 | _cairo_polygon_fini (&clipper); |
963 | 0 | } |
964 | 18 | } |
965 | 18 | } |
966 | | |
967 | 251 | return composite_polygon (compositor, extents, |
968 | 251 | polygon, fill_rule, antialias); |
969 | 251 | } |
970 | | |
971 | | /* high-level compositor interface */ |
972 | | |
973 | | static cairo_int_status_t |
974 | | _cairo_spans_compositor_paint (const cairo_compositor_t *_compositor, |
975 | | cairo_composite_rectangles_t *extents) |
976 | 860 | { |
977 | 860 | const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor; |
978 | 860 | cairo_boxes_t boxes; |
979 | 860 | cairo_int_status_t status; |
980 | | |
981 | 860 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
982 | 860 | _cairo_clip_steal_boxes (extents->clip, &boxes); |
983 | 860 | status = clip_and_composite_boxes (compositor, extents, &boxes); |
984 | 860 | _cairo_clip_unsteal_boxes (extents->clip, &boxes); |
985 | | |
986 | 860 | return status; |
987 | 860 | } |
988 | | |
989 | | static cairo_int_status_t |
990 | | _cairo_spans_compositor_mask (const cairo_compositor_t *_compositor, |
991 | | cairo_composite_rectangles_t *extents) |
992 | 3.42k | { |
993 | 3.42k | const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor; |
994 | 3.42k | cairo_int_status_t status; |
995 | 3.42k | cairo_boxes_t boxes; |
996 | | |
997 | 3.42k | TRACE ((stderr, "%s\n", __FUNCTION__)); |
998 | 3.42k | _cairo_clip_steal_boxes (extents->clip, &boxes); |
999 | 3.42k | status = clip_and_composite_boxes (compositor, extents, &boxes); |
1000 | 3.42k | _cairo_clip_unsteal_boxes (extents->clip, &boxes); |
1001 | | |
1002 | 3.42k | return status; |
1003 | 3.42k | } |
1004 | | |
1005 | | static cairo_int_status_t |
1006 | | _cairo_spans_compositor_stroke (const cairo_compositor_t *_compositor, |
1007 | | cairo_composite_rectangles_t *extents, |
1008 | | const cairo_path_fixed_t *path, |
1009 | | const cairo_stroke_style_t *style, |
1010 | | const cairo_matrix_t *ctm, |
1011 | | const cairo_matrix_t *ctm_inverse, |
1012 | | double tolerance, |
1013 | | cairo_antialias_t antialias) |
1014 | 94 | { |
1015 | 94 | const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor; |
1016 | 94 | cairo_int_status_t status; |
1017 | | |
1018 | 94 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
1019 | 94 | TRACE_ (_cairo_debug_print_path (stderr, path)); |
1020 | 94 | TRACE_ (_cairo_debug_print_clip (stderr, extents->clip)); |
1021 | | |
1022 | 94 | status = CAIRO_INT_STATUS_UNSUPPORTED; |
1023 | 94 | if (_cairo_path_fixed_stroke_is_rectilinear (path)) { |
1024 | 82 | cairo_boxes_t boxes; |
1025 | | |
1026 | 82 | _cairo_boxes_init (&boxes); |
1027 | 82 | if (! _cairo_clip_contains_rectangle (extents->clip, &extents->mask)) |
1028 | 18 | _cairo_boxes_limit (&boxes, |
1029 | 18 | extents->clip->boxes, |
1030 | 18 | extents->clip->num_boxes); |
1031 | | |
1032 | 82 | status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, |
1033 | 82 | style, |
1034 | 82 | ctm, |
1035 | 82 | antialias, |
1036 | 82 | &boxes); |
1037 | 82 | if (likely (status == CAIRO_INT_STATUS_SUCCESS)) |
1038 | 60 | status = clip_and_composite_boxes (compositor, extents, &boxes); |
1039 | 82 | _cairo_boxes_fini (&boxes); |
1040 | 82 | } |
1041 | | |
1042 | 94 | if (status == CAIRO_INT_STATUS_UNSUPPORTED) { |
1043 | 34 | cairo_polygon_t polygon; |
1044 | 34 | cairo_box_t limits; |
1045 | 34 | cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING; |
1046 | | |
1047 | 34 | if (! _cairo_rectangle_contains_rectangle (&extents->unbounded, |
1048 | 34 | &extents->mask)) |
1049 | 0 | { |
1050 | 0 | if (extents->clip->num_boxes == 1) { |
1051 | 0 | _cairo_polygon_init (&polygon, extents->clip->boxes, 1); |
1052 | 0 | } else { |
1053 | 0 | _cairo_box_from_rectangle (&limits, &extents->unbounded); |
1054 | 0 | _cairo_polygon_init (&polygon, &limits, 1); |
1055 | 0 | } |
1056 | 0 | } |
1057 | 34 | else |
1058 | 34 | { |
1059 | 34 | _cairo_polygon_init (&polygon, NULL, 0); |
1060 | 34 | } |
1061 | 34 | status = _cairo_path_fixed_stroke_to_polygon (path, |
1062 | 34 | style, |
1063 | 34 | ctm, ctm_inverse, |
1064 | 34 | tolerance, |
1065 | 34 | &polygon); |
1066 | 34 | TRACE_ (_cairo_debug_print_polygon (stderr, &polygon)); |
1067 | 34 | polygon.num_limits = 0; |
1068 | | |
1069 | 34 | if (status == CAIRO_INT_STATUS_SUCCESS && extents->clip->num_boxes > 1) { |
1070 | 0 | status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule, |
1071 | 0 | extents->clip->boxes, |
1072 | 0 | extents->clip->num_boxes); |
1073 | 0 | } |
1074 | 34 | if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { |
1075 | 34 | cairo_clip_t *saved_clip = extents->clip; |
1076 | | |
1077 | 34 | if (extents->is_bounded) { |
1078 | 34 | extents->clip = _cairo_clip_copy_path (extents->clip); |
1079 | 34 | extents->clip = _cairo_clip_intersect_box(extents->clip, |
1080 | 34 | &polygon.extents); |
1081 | 34 | } |
1082 | | |
1083 | 34 | status = clip_and_composite_polygon (compositor, extents, &polygon, |
1084 | 34 | fill_rule, antialias); |
1085 | | |
1086 | 34 | if (extents->is_bounded) { |
1087 | 34 | _cairo_clip_destroy (extents->clip); |
1088 | 34 | extents->clip = saved_clip; |
1089 | 34 | } |
1090 | 34 | } |
1091 | 34 | _cairo_polygon_fini (&polygon); |
1092 | 34 | } |
1093 | | |
1094 | 94 | return status; |
1095 | 94 | } |
1096 | | |
1097 | | static cairo_int_status_t |
1098 | | _cairo_spans_compositor_fill (const cairo_compositor_t *_compositor, |
1099 | | cairo_composite_rectangles_t *extents, |
1100 | | const cairo_path_fixed_t *path, |
1101 | | cairo_fill_rule_t fill_rule, |
1102 | | double tolerance, |
1103 | | cairo_antialias_t antialias) |
1104 | 277 | { |
1105 | 277 | const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor; |
1106 | 277 | cairo_int_status_t status; |
1107 | | |
1108 | 277 | TRACE((stderr, "%s op=%d, antialias=%d\n", __FUNCTION__, extents->op, antialias)); |
1109 | | |
1110 | 277 | status = CAIRO_INT_STATUS_UNSUPPORTED; |
1111 | 277 | if (_cairo_path_fixed_fill_is_rectilinear (path)) { |
1112 | 80 | cairo_boxes_t boxes; |
1113 | | |
1114 | 80 | TRACE((stderr, "%s - rectilinear\n", __FUNCTION__)); |
1115 | | |
1116 | 80 | _cairo_boxes_init (&boxes); |
1117 | 80 | if (! _cairo_clip_contains_rectangle (extents->clip, &extents->mask)) |
1118 | 74 | _cairo_boxes_limit (&boxes, |
1119 | 74 | extents->clip->boxes, |
1120 | 74 | extents->clip->num_boxes); |
1121 | 80 | status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, |
1122 | 80 | fill_rule, |
1123 | 80 | antialias, |
1124 | 80 | &boxes); |
1125 | 80 | if (likely (status == CAIRO_INT_STATUS_SUCCESS)) |
1126 | 80 | status = clip_and_composite_boxes (compositor, extents, &boxes); |
1127 | 80 | _cairo_boxes_fini (&boxes); |
1128 | 80 | } |
1129 | 277 | if (status == CAIRO_INT_STATUS_UNSUPPORTED) { |
1130 | 197 | cairo_polygon_t polygon; |
1131 | 197 | cairo_box_t limits; |
1132 | | |
1133 | 197 | TRACE((stderr, "%s - polygon\n", __FUNCTION__)); |
1134 | | |
1135 | 197 | if (! _cairo_rectangle_contains_rectangle (&extents->unbounded, |
1136 | 197 | &extents->mask)) |
1137 | 23 | { |
1138 | 23 | TRACE((stderr, "%s - clipping to bounds\n", __FUNCTION__)); |
1139 | 23 | if (extents->clip->num_boxes == 1) { |
1140 | 23 | _cairo_polygon_init (&polygon, extents->clip->boxes, 1); |
1141 | 23 | } else { |
1142 | 0 | _cairo_box_from_rectangle (&limits, &extents->unbounded); |
1143 | 0 | _cairo_polygon_init (&polygon, &limits, 1); |
1144 | 0 | } |
1145 | 23 | } |
1146 | 174 | else |
1147 | 174 | { |
1148 | 174 | _cairo_polygon_init (&polygon, NULL, 0); |
1149 | 174 | } |
1150 | | |
1151 | 197 | status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); |
1152 | 197 | TRACE_ (_cairo_debug_print_polygon (stderr, &polygon)); |
1153 | 197 | polygon.num_limits = 0; |
1154 | | |
1155 | 197 | if (status == CAIRO_INT_STATUS_SUCCESS && extents->clip->num_boxes > 1) { |
1156 | 0 | TRACE((stderr, "%s - polygon intersect with %d clip boxes\n", |
1157 | 0 | __FUNCTION__, extents->clip->num_boxes)); |
1158 | 0 | status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule, |
1159 | 0 | extents->clip->boxes, |
1160 | 0 | extents->clip->num_boxes); |
1161 | 0 | } |
1162 | 197 | TRACE_ (_cairo_debug_print_polygon (stderr, &polygon)); |
1163 | 197 | if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { |
1164 | 197 | cairo_clip_t *saved_clip = extents->clip; |
1165 | | |
1166 | 197 | if (extents->is_bounded) { |
1167 | 197 | TRACE((stderr, "%s - polygon discard clip boxes\n", |
1168 | 197 | __FUNCTION__)); |
1169 | 197 | extents->clip = _cairo_clip_copy_path (extents->clip); |
1170 | 197 | extents->clip = _cairo_clip_intersect_box(extents->clip, |
1171 | 197 | &polygon.extents); |
1172 | 197 | } |
1173 | | |
1174 | 197 | status = clip_and_composite_polygon (compositor, extents, &polygon, |
1175 | 197 | fill_rule, antialias); |
1176 | | |
1177 | 197 | if (extents->is_bounded) { |
1178 | 197 | _cairo_clip_destroy (extents->clip); |
1179 | 197 | extents->clip = saved_clip; |
1180 | 197 | } |
1181 | 197 | } |
1182 | 197 | _cairo_polygon_fini (&polygon); |
1183 | | |
1184 | 197 | TRACE((stderr, "%s - polygon status=%d\n", __FUNCTION__, status)); |
1185 | 197 | } |
1186 | | |
1187 | 277 | return status; |
1188 | 277 | } |
1189 | | |
1190 | | void |
1191 | | _cairo_spans_compositor_init (cairo_spans_compositor_t *compositor, |
1192 | | const cairo_compositor_t *delegate) |
1193 | 2 | { |
1194 | 2 | compositor->base.delegate = delegate; |
1195 | | |
1196 | 2 | compositor->base.paint = _cairo_spans_compositor_paint; |
1197 | 2 | compositor->base.mask = _cairo_spans_compositor_mask; |
1198 | 2 | compositor->base.fill = _cairo_spans_compositor_fill; |
1199 | 2 | compositor->base.stroke = _cairo_spans_compositor_stroke; |
1200 | | compositor->base.glyphs = NULL; |
1201 | 2 | } |