Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1892 | serge | 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 | * |
||
7 | * This library is free software; you can redistribute it and/or |
||
8 | * modify it either under the terms of the GNU Lesser General Public |
||
9 | * License version 2.1 as published by the Free Software Foundation |
||
10 | * (the "LGPL") or, at your option, under the terms of the Mozilla |
||
11 | * Public License Version 1.1 (the "MPL"). If you do not alter this |
||
12 | * notice, a recipient may use your version of this file under either |
||
13 | * the MPL or the LGPL. |
||
14 | * |
||
15 | * You should have received a copy of the LGPL along with this library |
||
16 | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
||
17 | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
||
18 | * You should have received a copy of the MPL along with this library |
||
19 | * in the file COPYING-MPL-1.1 |
||
20 | * |
||
21 | * The contents of this file are subject to the Mozilla Public License |
||
22 | * Version 1.1 (the "License"); you may not use this file except in |
||
23 | * compliance with the License. You may obtain a copy of the License at |
||
24 | * http://www.mozilla.org/MPL/ |
||
25 | * |
||
26 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
||
27 | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
||
28 | * the specific language governing rights and limitations. |
||
29 | * |
||
30 | * The Original Code is the cairo graphics library. |
||
31 | * |
||
32 | * The Initial Developer of the Original Code is University of Southern |
||
33 | * California. |
||
34 | * |
||
35 | * Contributor(s): |
||
36 | * Carl D. Worth |
||
37 | * Joonas Pihlaja |
||
38 | * Chris Wilson |
||
39 | */ |
||
40 | |||
41 | #include "cairoint.h" |
||
42 | |||
43 | #include "cairo-boxes-private.h" |
||
44 | #include "cairo-clip-private.h" |
||
45 | #include "cairo-composite-rectangles-private.h" |
||
46 | #include "cairo-error-private.h" |
||
47 | #include "cairo-region-private.h" |
||
48 | #include "cairo-spans-private.h" |
||
49 | #include "cairo-surface-fallback-private.h" |
||
50 | |||
51 | typedef struct { |
||
52 | cairo_surface_t *dst; |
||
53 | cairo_rectangle_int_t extents; |
||
54 | cairo_image_surface_t *image; |
||
55 | cairo_rectangle_int_t image_rect; |
||
56 | void *image_extra; |
||
57 | } fallback_state_t; |
||
58 | |||
59 | /** |
||
60 | * _fallback_init: |
||
61 | * |
||
62 | * Acquire destination image surface needed for an image-based |
||
63 | * fallback. |
||
64 | * |
||
65 | * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the extents are not |
||
66 | * visible, %CAIRO_STATUS_SUCCESS if some portion is visible and all |
||
67 | * went well, or some error status otherwise. |
||
68 | **/ |
||
69 | static cairo_int_status_t |
||
70 | _fallback_init (fallback_state_t *state, |
||
71 | cairo_surface_t *dst, |
||
72 | int x, |
||
73 | int y, |
||
74 | int width, |
||
75 | int height) |
||
76 | { |
||
77 | cairo_status_t status; |
||
78 | |||
79 | state->extents.x = x; |
||
80 | state->extents.y = y; |
||
81 | state->extents.width = width; |
||
82 | state->extents.height = height; |
||
83 | |||
84 | state->dst = dst; |
||
85 | |||
86 | status = _cairo_surface_acquire_dest_image (dst, &state->extents, |
||
87 | &state->image, &state->image_rect, |
||
88 | &state->image_extra); |
||
89 | if (unlikely (status)) |
||
90 | return status; |
||
91 | |||
92 | |||
93 | /* XXX: This NULL value tucked away in state->image is a rather |
||
94 | * ugly interface. Cleaner would be to push the |
||
95 | * CAIRO_INT_STATUS_NOTHING_TO_DO value down into |
||
96 | * _cairo_surface_acquire_dest_image and its backend |
||
97 | * counterparts. */ |
||
98 | assert (state->image != NULL); |
||
99 | |||
100 | return CAIRO_STATUS_SUCCESS; |
||
101 | } |
||
102 | |||
103 | static void |
||
104 | _fallback_fini (fallback_state_t *state) |
||
105 | { |
||
106 | _cairo_surface_release_dest_image (state->dst, &state->extents, |
||
107 | state->image, &state->image_rect, |
||
108 | state->image_extra); |
||
109 | } |
||
110 | |||
111 | typedef cairo_status_t |
||
112 | (*cairo_draw_func_t) (void *closure, |
||
113 | cairo_operator_t op, |
||
114 | const cairo_pattern_t *src, |
||
115 | cairo_surface_t *dst, |
||
116 | int dst_x, |
||
117 | int dst_y, |
||
118 | const cairo_rectangle_int_t *extents, |
||
119 | cairo_region_t *clip_region); |
||
120 | |||
121 | static cairo_status_t |
||
122 | _create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern, |
||
123 | cairo_clip_t *clip, |
||
124 | cairo_draw_func_t draw_func, |
||
125 | void *draw_closure, |
||
126 | cairo_surface_t *dst, |
||
127 | const cairo_rectangle_int_t *extents) |
||
128 | { |
||
129 | cairo_surface_t *mask; |
||
130 | cairo_region_t *clip_region = NULL, *fallback_region = NULL; |
||
131 | cairo_status_t status; |
||
132 | cairo_bool_t clip_surface = FALSE; |
||
133 | |||
134 | if (clip != NULL) { |
||
135 | status = _cairo_clip_get_region (clip, &clip_region); |
||
136 | if (unlikely (_cairo_status_is_error (status) || |
||
137 | status == CAIRO_INT_STATUS_NOTHING_TO_DO)) |
||
138 | { |
||
139 | return status; |
||
140 | } |
||
141 | |||
142 | clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; |
||
143 | } |
||
144 | |||
145 | /* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with |
||
146 | * a mask (as called via _cairo_surface_mask) triggers assertion failures. |
||
147 | */ |
||
148 | mask = _cairo_surface_create_similar_solid (dst, |
||
149 | CAIRO_CONTENT_ALPHA, |
||
150 | extents->width, |
||
151 | extents->height, |
||
152 | CAIRO_COLOR_TRANSPARENT, |
||
153 | TRUE); |
||
154 | if (unlikely (mask->status)) |
||
155 | return mask->status; |
||
156 | |||
157 | if (clip_region && (extents->x || extents->y)) { |
||
158 | fallback_region = cairo_region_copy (clip_region); |
||
159 | status = fallback_region->status; |
||
160 | if (unlikely (status)) |
||
161 | goto CLEANUP_SURFACE; |
||
162 | |||
163 | cairo_region_translate (fallback_region, |
||
164 | -extents->x, |
||
165 | -extents->y); |
||
166 | clip_region = fallback_region; |
||
167 | } |
||
168 | |||
169 | status = draw_func (draw_closure, CAIRO_OPERATOR_ADD, |
||
170 | &_cairo_pattern_white.base, mask, |
||
171 | extents->x, extents->y, |
||
172 | extents, |
||
173 | clip_region); |
||
174 | if (unlikely (status)) |
||
175 | goto CLEANUP_SURFACE; |
||
176 | |||
177 | if (clip_surface) |
||
178 | status = _cairo_clip_combine_with_surface (clip, mask, extents->x, extents->y); |
||
179 | |||
180 | _cairo_pattern_init_for_surface (mask_pattern, mask); |
||
181 | |||
182 | CLEANUP_SURFACE: |
||
183 | if (fallback_region) |
||
184 | cairo_region_destroy (fallback_region); |
||
185 | cairo_surface_destroy (mask); |
||
186 | |||
187 | return status; |
||
188 | } |
||
189 | |||
190 | /* Handles compositing with a clip surface when the operator allows |
||
191 | * us to combine the clip with the mask |
||
192 | */ |
||
193 | static cairo_status_t |
||
194 | _clip_and_composite_with_mask (cairo_clip_t *clip, |
||
195 | cairo_operator_t op, |
||
196 | const cairo_pattern_t *src, |
||
197 | cairo_draw_func_t draw_func, |
||
198 | void *draw_closure, |
||
199 | cairo_surface_t *dst, |
||
200 | const cairo_rectangle_int_t *extents) |
||
201 | { |
||
202 | cairo_surface_pattern_t mask_pattern; |
||
203 | cairo_status_t status; |
||
204 | |||
205 | status = _create_composite_mask_pattern (&mask_pattern, |
||
206 | clip, |
||
207 | draw_func, draw_closure, |
||
208 | dst, extents); |
||
209 | if (likely (status == CAIRO_STATUS_SUCCESS)) { |
||
210 | status = _cairo_surface_composite (op, |
||
211 | src, &mask_pattern.base, dst, |
||
212 | extents->x, extents->y, |
||
213 | 0, 0, |
||
214 | extents->x, extents->y, |
||
215 | extents->width, extents->height, |
||
216 | NULL); |
||
217 | |||
218 | _cairo_pattern_fini (&mask_pattern.base); |
||
219 | } |
||
220 | |||
221 | return status; |
||
222 | } |
||
223 | |||
224 | /* Handles compositing with a clip surface when we have to do the operation |
||
225 | * in two pieces and combine them together. |
||
226 | */ |
||
227 | static cairo_status_t |
||
228 | _clip_and_composite_combine (cairo_clip_t *clip, |
||
229 | cairo_operator_t op, |
||
230 | const cairo_pattern_t *src, |
||
231 | cairo_draw_func_t draw_func, |
||
232 | void *draw_closure, |
||
233 | cairo_surface_t *dst, |
||
234 | const cairo_rectangle_int_t *extents) |
||
235 | { |
||
236 | cairo_surface_t *intermediate; |
||
237 | cairo_surface_pattern_t pattern; |
||
238 | cairo_surface_pattern_t clip_pattern; |
||
239 | cairo_surface_t *clip_surface; |
||
240 | int clip_x, clip_y; |
||
241 | cairo_status_t status; |
||
242 | |||
243 | /* We'd be better off here creating a surface identical in format |
||
244 | * to dst, but we have no way of getting that information. Instead |
||
245 | * we ask the backend to create a similar surface of identical content, |
||
246 | * in the belief that the backend will do something useful - like use |
||
247 | * an identical format. For example, the xlib backend will endeavor to |
||
248 | * use a compatible depth to enable core protocol routines. |
||
249 | */ |
||
250 | intermediate = |
||
251 | _cairo_surface_create_similar_scratch (dst, dst->content, |
||
252 | extents->width, |
||
253 | extents->height); |
||
254 | if (intermediate == NULL) { |
||
255 | intermediate = |
||
256 | _cairo_image_surface_create_with_content (dst->content, |
||
257 | extents->width, |
||
258 | extents->width); |
||
259 | } |
||
260 | if (unlikely (intermediate->status)) |
||
261 | return intermediate->status; |
||
262 | |||
263 | /* Initialize the intermediate surface from the destination surface */ |
||
264 | _cairo_pattern_init_for_surface (&pattern, dst); |
||
265 | status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, |
||
266 | &pattern.base, NULL, intermediate, |
||
267 | extents->x, extents->y, |
||
268 | 0, 0, |
||
269 | 0, 0, |
||
270 | extents->width, extents->height, |
||
271 | NULL); |
||
272 | _cairo_pattern_fini (&pattern.base); |
||
273 | if (unlikely (status)) |
||
274 | goto CLEANUP_SURFACE; |
||
275 | |||
276 | status = (*draw_func) (draw_closure, op, |
||
277 | src, intermediate, |
||
278 | extents->x, extents->y, |
||
279 | extents, |
||
280 | NULL); |
||
281 | if (unlikely (status)) |
||
282 | goto CLEANUP_SURFACE; |
||
283 | |||
284 | assert (clip->path != NULL); |
||
285 | clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); |
||
286 | if (unlikely (clip_surface->status)) |
||
287 | goto CLEANUP_SURFACE; |
||
288 | |||
289 | _cairo_pattern_init_for_surface (&clip_pattern, clip_surface); |
||
290 | |||
291 | /* Combine that with the clip */ |
||
292 | status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN, |
||
293 | &clip_pattern.base, NULL, intermediate, |
||
294 | extents->x - clip_x, |
||
295 | extents->y - clip_y, |
||
296 | 0, 0, |
||
297 | 0, 0, |
||
298 | extents->width, extents->height, |
||
299 | NULL); |
||
300 | if (unlikely (status)) |
||
301 | goto CLEANUP_CLIP; |
||
302 | |||
303 | /* Punch the clip out of the destination */ |
||
304 | status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, |
||
305 | &clip_pattern.base, NULL, dst, |
||
306 | extents->x - clip_x, |
||
307 | extents->y - clip_y, |
||
308 | 0, 0, |
||
309 | extents->x, extents->y, |
||
310 | extents->width, extents->height, |
||
311 | NULL); |
||
312 | if (unlikely (status)) |
||
313 | goto CLEANUP_CLIP; |
||
314 | |||
315 | /* Now add the two results together */ |
||
316 | _cairo_pattern_init_for_surface (&pattern, intermediate); |
||
317 | status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, |
||
318 | &pattern.base, NULL, dst, |
||
319 | 0, 0, |
||
320 | 0, 0, |
||
321 | extents->x, extents->y, |
||
322 | extents->width, extents->height, |
||
323 | NULL); |
||
324 | _cairo_pattern_fini (&pattern.base); |
||
325 | |||
326 | CLEANUP_CLIP: |
||
327 | _cairo_pattern_fini (&clip_pattern.base); |
||
328 | CLEANUP_SURFACE: |
||
329 | cairo_surface_destroy (intermediate); |
||
330 | |||
331 | return status; |
||
332 | } |
||
333 | |||
334 | /* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's |
||
335 | * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip)) |
||
336 | */ |
||
337 | static cairo_status_t |
||
338 | _clip_and_composite_source (cairo_clip_t *clip, |
||
339 | const cairo_pattern_t *src, |
||
340 | cairo_draw_func_t draw_func, |
||
341 | void *draw_closure, |
||
342 | cairo_surface_t *dst, |
||
343 | const cairo_rectangle_int_t *extents) |
||
344 | { |
||
345 | cairo_surface_pattern_t mask_pattern; |
||
346 | cairo_region_t *clip_region = NULL; |
||
347 | cairo_status_t status; |
||
348 | |||
349 | if (clip != NULL) { |
||
350 | status = _cairo_clip_get_region (clip, &clip_region); |
||
351 | if (unlikely (_cairo_status_is_error (status) || |
||
352 | status == CAIRO_INT_STATUS_NOTHING_TO_DO)) |
||
353 | { |
||
354 | return status; |
||
355 | } |
||
356 | } |
||
357 | |||
358 | /* Create a surface that is mask IN clip */ |
||
359 | status = _create_composite_mask_pattern (&mask_pattern, |
||
360 | clip, |
||
361 | draw_func, draw_closure, |
||
362 | dst, extents); |
||
363 | if (unlikely (status)) |
||
364 | return status; |
||
365 | |||
366 | /* Compute dest' = dest OUT (mask IN clip) */ |
||
367 | status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT, |
||
368 | &mask_pattern.base, NULL, dst, |
||
369 | 0, 0, |
||
370 | 0, 0, |
||
371 | extents->x, extents->y, |
||
372 | extents->width, extents->height, |
||
373 | clip_region); |
||
374 | |||
375 | if (unlikely (status)) |
||
376 | goto CLEANUP_MASK_PATTERN; |
||
377 | |||
378 | /* Now compute (src IN (mask IN clip)) ADD dest' */ |
||
379 | status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, |
||
380 | src, &mask_pattern.base, dst, |
||
381 | extents->x, extents->y, |
||
382 | 0, 0, |
||
383 | extents->x, extents->y, |
||
384 | extents->width, extents->height, |
||
385 | clip_region); |
||
386 | |||
387 | CLEANUP_MASK_PATTERN: |
||
388 | _cairo_pattern_fini (&mask_pattern.base); |
||
389 | return status; |
||
390 | } |
||
391 | |||
392 | static int |
||
393 | _cairo_rectangle_empty (const cairo_rectangle_int_t *rect) |
||
394 | { |
||
395 | return rect->width == 0 || rect->height == 0; |
||
396 | } |
||
397 | |||
398 | /** |
||
399 | * _clip_and_composite: |
||
400 | * @clip: a #cairo_clip_t |
||
401 | * @op: the operator to draw with |
||
402 | * @src: source pattern |
||
403 | * @draw_func: function that can be called to draw with the mask onto a surface. |
||
404 | * @draw_closure: data to pass to @draw_func. |
||
405 | * @dst: destination surface |
||
406 | * @extents: rectangle holding a bounding box for the operation; this |
||
407 | * rectangle will be used as the size for the temporary |
||
408 | * surface. |
||
409 | * |
||
410 | * When there is a surface clip, we typically need to create an intermediate |
||
411 | * surface. This function handles the logic of creating a temporary surface |
||
412 | * drawing to it, then compositing the result onto the target surface. |
||
413 | * |
||
414 | * @draw_func is to called to draw the mask; it will be called no more |
||
415 | * than once. |
||
416 | * |
||
417 | * Return value: %CAIRO_STATUS_SUCCESS if the drawing succeeded. |
||
418 | **/ |
||
419 | static cairo_status_t |
||
420 | _clip_and_composite (cairo_clip_t *clip, |
||
421 | cairo_operator_t op, |
||
422 | const cairo_pattern_t *src, |
||
423 | cairo_draw_func_t draw_func, |
||
424 | void *draw_closure, |
||
425 | cairo_surface_t *dst, |
||
426 | const cairo_rectangle_int_t *extents) |
||
427 | { |
||
428 | cairo_status_t status; |
||
429 | |||
430 | if (_cairo_rectangle_empty (extents)) |
||
431 | /* Nothing to do */ |
||
432 | return CAIRO_STATUS_SUCCESS; |
||
433 | |||
434 | if (op == CAIRO_OPERATOR_CLEAR) { |
||
435 | src = &_cairo_pattern_white.base; |
||
436 | op = CAIRO_OPERATOR_DEST_OUT; |
||
437 | } |
||
438 | |||
439 | if (op == CAIRO_OPERATOR_SOURCE) { |
||
440 | status = _clip_and_composite_source (clip, |
||
441 | src, |
||
442 | draw_func, draw_closure, |
||
443 | dst, extents); |
||
444 | } else { |
||
445 | cairo_bool_t clip_surface = FALSE; |
||
446 | cairo_region_t *clip_region = NULL; |
||
447 | |||
448 | if (clip != NULL) { |
||
449 | status = _cairo_clip_get_region (clip, &clip_region); |
||
450 | if (unlikely (_cairo_status_is_error (status) || |
||
451 | status == CAIRO_INT_STATUS_NOTHING_TO_DO)) |
||
452 | { |
||
453 | return status; |
||
454 | } |
||
455 | |||
456 | clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; |
||
457 | } |
||
458 | |||
459 | if (clip_surface) { |
||
460 | if (_cairo_operator_bounded_by_mask (op)) { |
||
461 | status = _clip_and_composite_with_mask (clip, op, |
||
462 | src, |
||
463 | draw_func, draw_closure, |
||
464 | dst, extents); |
||
465 | } else { |
||
466 | status = _clip_and_composite_combine (clip, op, |
||
467 | src, |
||
468 | draw_func, draw_closure, |
||
469 | dst, extents); |
||
470 | } |
||
471 | } else { |
||
472 | status = draw_func (draw_closure, op, |
||
473 | src, dst, |
||
474 | 0, 0, |
||
475 | extents, |
||
476 | clip_region); |
||
477 | } |
||
478 | } |
||
479 | |||
480 | return status; |
||
481 | } |
||
482 | |||
483 | /* Composites a region representing a set of trapezoids. |
||
484 | */ |
||
485 | static cairo_status_t |
||
486 | _composite_trap_region (cairo_clip_t *clip, |
||
487 | const cairo_pattern_t *src, |
||
488 | cairo_operator_t op, |
||
489 | cairo_surface_t *dst, |
||
490 | cairo_region_t *trap_region, |
||
491 | const cairo_rectangle_int_t *extents) |
||
492 | { |
||
493 | cairo_status_t status; |
||
494 | cairo_surface_pattern_t mask_pattern; |
||
495 | cairo_pattern_t *mask = NULL; |
||
496 | int mask_x = 0, mask_y =0; |
||
497 | |||
498 | if (clip != NULL) { |
||
499 | cairo_surface_t *clip_surface = NULL; |
||
500 | int clip_x, clip_y; |
||
501 | |||
502 | clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y); |
||
503 | if (unlikely (clip_surface->status)) |
||
504 | return clip_surface->status; |
||
505 | |||
506 | if (op == CAIRO_OPERATOR_CLEAR) { |
||
507 | src = &_cairo_pattern_white.base; |
||
508 | op = CAIRO_OPERATOR_DEST_OUT; |
||
509 | } |
||
510 | |||
511 | _cairo_pattern_init_for_surface (&mask_pattern, clip_surface); |
||
512 | mask_x = extents->x - clip_x; |
||
513 | mask_y = extents->y - clip_y; |
||
514 | mask = &mask_pattern.base; |
||
515 | } |
||
516 | |||
517 | status = _cairo_surface_composite (op, src, mask, dst, |
||
518 | extents->x, extents->y, |
||
519 | mask_x, mask_y, |
||
520 | extents->x, extents->y, |
||
521 | extents->width, extents->height, |
||
522 | trap_region); |
||
523 | |||
524 | if (mask != NULL) |
||
525 | _cairo_pattern_fini (mask); |
||
526 | |||
527 | return status; |
||
528 | } |
||
529 | |||
530 | typedef struct { |
||
531 | cairo_traps_t *traps; |
||
532 | cairo_antialias_t antialias; |
||
533 | } cairo_composite_traps_info_t; |
||
534 | |||
535 | static cairo_status_t |
||
536 | _composite_traps_draw_func (void *closure, |
||
537 | cairo_operator_t op, |
||
538 | const cairo_pattern_t *src, |
||
539 | cairo_surface_t *dst, |
||
540 | int dst_x, |
||
541 | int dst_y, |
||
542 | const cairo_rectangle_int_t *extents, |
||
543 | cairo_region_t *clip_region) |
||
544 | { |
||
545 | cairo_composite_traps_info_t *info = closure; |
||
546 | cairo_status_t status; |
||
547 | cairo_region_t *extents_region = NULL; |
||
548 | |||
549 | if (dst_x != 0 || dst_y != 0) |
||
550 | _cairo_traps_translate (info->traps, - dst_x, - dst_y); |
||
551 | |||
552 | if (clip_region == NULL && |
||
553 | !_cairo_operator_bounded_by_source (op)) { |
||
554 | extents_region = cairo_region_create_rectangle (extents); |
||
555 | if (unlikely (extents_region->status)) |
||
556 | return extents_region->status; |
||
557 | cairo_region_translate (extents_region, -dst_x, -dst_y); |
||
558 | clip_region = extents_region; |
||
559 | } |
||
560 | |||
561 | status = _cairo_surface_composite_trapezoids (op, |
||
562 | src, dst, info->antialias, |
||
563 | extents->x, extents->y, |
||
564 | extents->x - dst_x, extents->y - dst_y, |
||
565 | extents->width, extents->height, |
||
566 | info->traps->traps, |
||
567 | info->traps->num_traps, |
||
568 | clip_region); |
||
569 | |||
570 | if (extents_region) |
||
571 | cairo_region_destroy (extents_region); |
||
572 | |||
573 | return status; |
||
574 | } |
||
575 | |||
576 | enum { |
||
577 | HAS_CLEAR_REGION = 0x1, |
||
578 | }; |
||
579 | |||
580 | static cairo_status_t |
||
581 | _clip_and_composite_region (const cairo_pattern_t *src, |
||
582 | cairo_operator_t op, |
||
583 | cairo_surface_t *dst, |
||
584 | cairo_region_t *trap_region, |
||
585 | cairo_clip_t *clip, |
||
586 | cairo_rectangle_int_t *extents) |
||
587 | { |
||
588 | cairo_region_t clear_region; |
||
589 | unsigned int has_region = 0; |
||
590 | cairo_status_t status; |
||
591 | |||
592 | if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) { |
||
593 | /* If we optimize drawing with an unbounded operator to |
||
594 | * _cairo_surface_fill_rectangles() or to drawing with a |
||
595 | * clip region, then we have an additional region to clear. |
||
596 | */ |
||
597 | _cairo_region_init_rectangle (&clear_region, extents); |
||
598 | status = cairo_region_subtract (&clear_region, trap_region); |
||
599 | if (unlikely (status)) |
||
600 | return status; |
||
601 | |||
602 | if (! cairo_region_is_empty (&clear_region)) |
||
603 | has_region |= HAS_CLEAR_REGION; |
||
604 | } |
||
605 | |||
606 | if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) && |
||
607 | clip == NULL) |
||
608 | { |
||
609 | const cairo_color_t *color; |
||
610 | |||
611 | if (op == CAIRO_OPERATOR_CLEAR) |
||
612 | color = CAIRO_COLOR_TRANSPARENT; |
||
613 | else |
||
614 | color = &((cairo_solid_pattern_t *)src)->color; |
||
615 | |||
616 | /* Solid rectangles special case */ |
||
617 | status = _cairo_surface_fill_region (dst, op, color, trap_region); |
||
618 | } else { |
||
619 | /* For a simple rectangle, we can just use composite(), for more |
||
620 | * rectangles, we have to set a clip region. The cost of rasterizing |
||
621 | * trapezoids is pretty high for most backends currently, so it's |
||
622 | * worthwhile even if a region is needed. |
||
623 | * |
||
624 | * If we have a clip surface, we set it as the mask; this only works |
||
625 | * for bounded operators other than SOURCE; for unbounded operators, |
||
626 | * clip and mask cannot be interchanged. For SOURCE, the operator |
||
627 | * as implemented by the backends is different in its handling |
||
628 | * of the mask then what we want. |
||
629 | * |
||
630 | * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has |
||
631 | * more than rectangle and the destination doesn't support clip |
||
632 | * regions. In that case, we fall through. |
||
633 | */ |
||
634 | status = _composite_trap_region (clip, src, op, dst, |
||
635 | trap_region, extents); |
||
636 | } |
||
637 | |||
638 | if (has_region & HAS_CLEAR_REGION) { |
||
639 | if (status == CAIRO_STATUS_SUCCESS) { |
||
640 | status = _cairo_surface_fill_region (dst, |
||
641 | CAIRO_OPERATOR_CLEAR, |
||
642 | CAIRO_COLOR_TRANSPARENT, |
||
643 | &clear_region); |
||
644 | } |
||
645 | _cairo_region_fini (&clear_region); |
||
646 | } |
||
647 | |||
648 | return status; |
||
649 | } |
||
650 | |||
651 | /* avoid using region code to re-validate boxes */ |
||
652 | static cairo_status_t |
||
653 | _fill_rectangles (cairo_surface_t *dst, |
||
654 | cairo_operator_t op, |
||
655 | const cairo_pattern_t *src, |
||
656 | cairo_traps_t *traps, |
||
657 | cairo_clip_t *clip) |
||
658 | { |
||
659 | const cairo_color_t *color; |
||
660 | cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; |
||
661 | cairo_rectangle_int_t *rects = stack_rects; |
||
662 | cairo_status_t status; |
||
663 | int i; |
||
664 | |||
665 | if (! traps->is_rectilinear || ! traps->maybe_region) |
||
666 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
667 | |||
668 | /* XXX: convert clip region to geometric boxes? */ |
||
669 | if (clip != NULL) |
||
670 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
671 | |||
672 | /* XXX: fallback for the region_subtract() operation */ |
||
673 | if (! _cairo_operator_bounded_by_mask (op)) |
||
674 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
675 | |||
676 | if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR)) |
||
677 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
678 | |||
679 | if (traps->has_intersections) { |
||
680 | if (traps->is_rectangular) { |
||
681 | status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); |
||
682 | } else { |
||
683 | status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); |
||
684 | } |
||
685 | if (unlikely (status)) |
||
686 | return status; |
||
687 | } |
||
688 | |||
689 | for (i = 0; i < traps->num_traps; i++) { |
||
690 | if (! _cairo_fixed_is_integer (traps->traps[i].top) || |
||
691 | ! _cairo_fixed_is_integer (traps->traps[i].bottom) || |
||
692 | ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) || |
||
693 | ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x)) |
||
694 | { |
||
695 | traps->maybe_region = FALSE; |
||
696 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
697 | } |
||
698 | } |
||
699 | |||
700 | if (traps->num_traps > ARRAY_LENGTH (stack_rects)) { |
||
701 | rects = _cairo_malloc_ab (traps->num_traps, |
||
702 | sizeof (cairo_rectangle_int_t)); |
||
703 | if (unlikely (rects == NULL)) |
||
704 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
705 | } |
||
706 | |||
707 | for (i = 0; i < traps->num_traps; i++) { |
||
708 | int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); |
||
709 | int y1 = _cairo_fixed_integer_part (traps->traps[i].top); |
||
710 | int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); |
||
711 | int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom); |
||
712 | |||
713 | rects[i].x = x1; |
||
714 | rects[i].y = y1; |
||
715 | rects[i].width = x2 - x1; |
||
716 | rects[i].height = y2 - y1; |
||
717 | } |
||
718 | |||
719 | if (op == CAIRO_OPERATOR_CLEAR) |
||
720 | color = CAIRO_COLOR_TRANSPARENT; |
||
721 | else |
||
722 | color = &((cairo_solid_pattern_t *)src)->color; |
||
723 | |||
724 | status = _cairo_surface_fill_rectangles (dst, op, color, rects, i); |
||
725 | |||
726 | if (rects != stack_rects) |
||
727 | free (rects); |
||
728 | |||
729 | return status; |
||
730 | } |
||
731 | |||
732 | /* fast-path for very common composite of a single rectangle */ |
||
733 | static cairo_status_t |
||
734 | _composite_rectangle (cairo_surface_t *dst, |
||
735 | cairo_operator_t op, |
||
736 | const cairo_pattern_t *src, |
||
737 | cairo_traps_t *traps, |
||
738 | cairo_clip_t *clip) |
||
739 | { |
||
740 | cairo_rectangle_int_t rect; |
||
741 | |||
742 | if (clip != NULL) |
||
743 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
744 | |||
745 | if (traps->num_traps > 1 || ! traps->is_rectilinear || ! traps->maybe_region) |
||
746 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
747 | |||
748 | if (! _cairo_fixed_is_integer (traps->traps[0].top) || |
||
749 | ! _cairo_fixed_is_integer (traps->traps[0].bottom) || |
||
750 | ! _cairo_fixed_is_integer (traps->traps[0].left.p1.x) || |
||
751 | ! _cairo_fixed_is_integer (traps->traps[0].right.p1.x)) |
||
752 | { |
||
753 | traps->maybe_region = FALSE; |
||
754 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
755 | } |
||
756 | |||
757 | rect.x = _cairo_fixed_integer_part (traps->traps[0].left.p1.x); |
||
758 | rect.y = _cairo_fixed_integer_part (traps->traps[0].top); |
||
759 | rect.width = _cairo_fixed_integer_part (traps->traps[0].right.p1.x) - rect.x; |
||
760 | rect.height = _cairo_fixed_integer_part (traps->traps[0].bottom) - rect.y; |
||
761 | |||
762 | return _cairo_surface_composite (op, src, NULL, dst, |
||
763 | rect.x, rect.y, |
||
764 | 0, 0, |
||
765 | rect.x, rect.y, |
||
766 | rect.width, rect.height, |
||
767 | NULL); |
||
768 | } |
||
769 | |||
770 | /* Warning: This call modifies the coordinates of traps */ |
||
771 | static cairo_status_t |
||
772 | _clip_and_composite_trapezoids (const cairo_pattern_t *src, |
||
773 | cairo_operator_t op, |
||
774 | cairo_surface_t *dst, |
||
775 | cairo_traps_t *traps, |
||
776 | cairo_antialias_t antialias, |
||
777 | cairo_clip_t *clip, |
||
778 | cairo_rectangle_int_t *extents) |
||
779 | { |
||
780 | cairo_composite_traps_info_t traps_info; |
||
781 | cairo_region_t *clip_region = NULL; |
||
782 | cairo_bool_t clip_surface = FALSE; |
||
783 | cairo_status_t status; |
||
784 | |||
785 | if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op)) |
||
786 | return CAIRO_STATUS_SUCCESS; |
||
787 | |||
788 | if (clip != NULL) { |
||
789 | status = _cairo_clip_get_region (clip, &clip_region); |
||
790 | if (unlikely (_cairo_status_is_error (status))) |
||
791 | return status; |
||
792 | if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) |
||
793 | return CAIRO_STATUS_SUCCESS; |
||
794 | |||
795 | clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED; |
||
796 | } |
||
797 | |||
798 | /* Use a fast path if the trapezoids consist of a simple region, |
||
799 | * but we can only do this if we do not have a clip surface, or can |
||
800 | * substitute the mask with the clip. |
||
801 | */ |
||
802 | if (! clip_surface || |
||
803 | (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE)) |
||
804 | { |
||
805 | cairo_region_t *trap_region = NULL; |
||
806 | |||
807 | if (_cairo_operator_bounded_by_source (op)) { |
||
808 | status = _fill_rectangles (dst, op, src, traps, clip); |
||
809 | if (status != CAIRO_INT_STATUS_UNSUPPORTED) |
||
810 | return status; |
||
811 | |||
812 | status = _composite_rectangle (dst, op, src, traps, clip); |
||
813 | if (status != CAIRO_INT_STATUS_UNSUPPORTED) |
||
814 | return status; |
||
815 | } |
||
816 | |||
817 | status = _cairo_traps_extract_region (traps, &trap_region); |
||
818 | if (unlikely (_cairo_status_is_error (status))) |
||
819 | return status; |
||
820 | |||
821 | if (trap_region != NULL) { |
||
822 | status = cairo_region_intersect_rectangle (trap_region, extents); |
||
823 | if (unlikely (status)) { |
||
824 | cairo_region_destroy (trap_region); |
||
825 | return status; |
||
826 | } |
||
827 | |||
828 | if (clip_region != NULL) { |
||
829 | status = cairo_region_intersect (trap_region, clip_region); |
||
830 | if (unlikely (status)) { |
||
831 | cairo_region_destroy (trap_region); |
||
832 | return status; |
||
833 | } |
||
834 | } |
||
835 | |||
836 | if (_cairo_operator_bounded_by_mask (op)) { |
||
837 | cairo_rectangle_int_t trap_extents; |
||
838 | |||
839 | cairo_region_get_extents (trap_region, &trap_extents); |
||
840 | if (! _cairo_rectangle_intersect (extents, &trap_extents)) { |
||
841 | cairo_region_destroy (trap_region); |
||
842 | return CAIRO_STATUS_SUCCESS; |
||
843 | } |
||
844 | } |
||
845 | |||
846 | status = _clip_and_composite_region (src, op, dst, |
||
847 | trap_region, |
||
848 | clip_surface ? clip : NULL, |
||
849 | extents); |
||
850 | cairo_region_destroy (trap_region); |
||
851 | |||
852 | if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED)) |
||
853 | return status; |
||
854 | } |
||
855 | } |
||
856 | |||
857 | /* No fast path, exclude self-intersections and clip trapezoids. */ |
||
858 | if (traps->has_intersections) { |
||
859 | if (traps->is_rectangular) |
||
860 | status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING); |
||
861 | else if (traps->is_rectilinear) |
||
862 | status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING); |
||
863 | else |
||
864 | status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING); |
||
865 | if (unlikely (status)) |
||
866 | return status; |
||
867 | } |
||
868 | |||
869 | /* Otherwise render the trapezoids to a mask and composite in the usual |
||
870 | * fashion. |
||
871 | */ |
||
872 | traps_info.traps = traps; |
||
873 | traps_info.antialias = antialias; |
||
874 | |||
875 | return _clip_and_composite (clip, op, src, |
||
876 | _composite_traps_draw_func, |
||
877 | &traps_info, dst, extents); |
||
878 | } |
||
879 | |||
880 | cairo_status_t |
||
881 | _cairo_surface_fallback_paint (cairo_surface_t *surface, |
||
882 | cairo_operator_t op, |
||
883 | const cairo_pattern_t *source, |
||
884 | cairo_clip_t *clip) |
||
885 | { |
||
886 | cairo_composite_rectangles_t extents; |
||
887 | cairo_rectangle_int_t rect; |
||
888 | cairo_clip_path_t *clip_path = clip ? clip->path : NULL; |
||
889 | cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; |
||
890 | cairo_boxes_t boxes; |
||
891 | int num_boxes = ARRAY_LENGTH (boxes_stack); |
||
892 | cairo_status_t status; |
||
893 | cairo_traps_t traps; |
||
894 | |||
895 | if (!_cairo_surface_get_extents (surface, &rect)) |
||
896 | ASSERT_NOT_REACHED; |
||
897 | |||
898 | status = _cairo_composite_rectangles_init_for_paint (&extents, |
||
899 | rect.width, |
||
900 | rect.height, |
||
901 | op, source, |
||
902 | clip); |
||
903 | if (unlikely (status)) |
||
904 | return status; |
||
905 | |||
906 | if (_cairo_clip_contains_extents (clip, &extents)) |
||
907 | clip = NULL; |
||
908 | |||
909 | status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); |
||
910 | if (unlikely (status)) |
||
911 | return status; |
||
912 | |||
913 | /* If the clip cannot be reduced to a set of boxes, we will need to |
||
914 | * use a clipmask. Paint is special as it is the only operation that |
||
915 | * does not implicitly use a mask, so we may be able to reduce this |
||
916 | * operation to a fill... |
||
917 | */ |
||
918 | if (clip != NULL && clip_path->prev == NULL && |
||
919 | _cairo_operator_bounded_by_mask (op)) |
||
920 | { |
||
921 | return _cairo_surface_fill (surface, op, source, |
||
922 | &clip_path->path, |
||
923 | clip_path->fill_rule, |
||
924 | clip_path->tolerance, |
||
925 | clip_path->antialias, |
||
926 | NULL); |
||
927 | } |
||
928 | |||
929 | /* meh, surface-fallback is dying anyway... */ |
||
930 | _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes); |
||
931 | status = _cairo_traps_init_boxes (&traps, &boxes); |
||
932 | if (unlikely (status)) |
||
933 | goto CLEANUP_BOXES; |
||
934 | |||
935 | status = _clip_and_composite_trapezoids (source, op, surface, |
||
936 | &traps, CAIRO_ANTIALIAS_DEFAULT, |
||
937 | clip, |
||
938 | extents.is_bounded ? &extents.bounded : &extents.unbounded); |
||
939 | _cairo_traps_fini (&traps); |
||
940 | |||
941 | CLEANUP_BOXES: |
||
942 | if (clip_boxes != boxes_stack) |
||
943 | free (clip_boxes); |
||
944 | |||
945 | return status; |
||
946 | } |
||
947 | |||
948 | static cairo_status_t |
||
949 | _cairo_surface_mask_draw_func (void *closure, |
||
950 | cairo_operator_t op, |
||
951 | const cairo_pattern_t *src, |
||
952 | cairo_surface_t *dst, |
||
953 | int dst_x, |
||
954 | int dst_y, |
||
955 | const cairo_rectangle_int_t *extents, |
||
956 | cairo_region_t *clip_region) |
||
957 | { |
||
958 | cairo_pattern_t *mask = closure; |
||
959 | cairo_status_t status; |
||
960 | cairo_region_t *extents_region = NULL; |
||
961 | |||
962 | if (clip_region == NULL && |
||
963 | !_cairo_operator_bounded_by_source (op)) { |
||
964 | extents_region = cairo_region_create_rectangle (extents); |
||
965 | if (unlikely (extents_region->status)) |
||
966 | return extents_region->status; |
||
967 | cairo_region_translate (extents_region, -dst_x, -dst_y); |
||
968 | clip_region = extents_region; |
||
969 | } |
||
970 | |||
971 | if (src) { |
||
972 | status = _cairo_surface_composite (op, |
||
973 | src, mask, dst, |
||
974 | extents->x, extents->y, |
||
975 | extents->x, extents->y, |
||
976 | extents->x - dst_x, extents->y - dst_y, |
||
977 | extents->width, extents->height, |
||
978 | clip_region); |
||
979 | } else { |
||
980 | status = _cairo_surface_composite (op, |
||
981 | mask, NULL, dst, |
||
982 | extents->x, extents->y, |
||
983 | 0, 0, /* unused */ |
||
984 | extents->x - dst_x, extents->y - dst_y, |
||
985 | extents->width, extents->height, |
||
986 | clip_region); |
||
987 | } |
||
988 | |||
989 | if (extents_region) |
||
990 | cairo_region_destroy (extents_region); |
||
991 | |||
992 | return status; |
||
993 | } |
||
994 | |||
995 | cairo_status_t |
||
996 | _cairo_surface_fallback_mask (cairo_surface_t *surface, |
||
997 | cairo_operator_t op, |
||
998 | const cairo_pattern_t *source, |
||
999 | const cairo_pattern_t *mask, |
||
1000 | cairo_clip_t *clip) |
||
1001 | { |
||
1002 | cairo_composite_rectangles_t extents; |
||
1003 | cairo_rectangle_int_t rect; |
||
1004 | cairo_status_t status; |
||
1005 | |||
1006 | if (!_cairo_surface_get_extents (surface, &rect)) |
||
1007 | ASSERT_NOT_REACHED; |
||
1008 | |||
1009 | status = _cairo_composite_rectangles_init_for_mask (&extents, |
||
1010 | rect.width, rect.height, |
||
1011 | op, source, mask, clip); |
||
1012 | if (unlikely (status)) |
||
1013 | return status; |
||
1014 | |||
1015 | if (_cairo_clip_contains_extents (clip, &extents)) |
||
1016 | clip = NULL; |
||
1017 | |||
1018 | if (clip != NULL && extents.is_bounded) { |
||
1019 | status = _cairo_clip_rectangle (clip, &extents.bounded); |
||
1020 | if (unlikely (status)) |
||
1021 | return status; |
||
1022 | } |
||
1023 | |||
1024 | return _clip_and_composite (clip, op, source, |
||
1025 | _cairo_surface_mask_draw_func, |
||
1026 | (void *) mask, |
||
1027 | surface, |
||
1028 | extents.is_bounded ? &extents.bounded : &extents.unbounded); |
||
1029 | } |
||
1030 | |||
1031 | cairo_status_t |
||
1032 | _cairo_surface_fallback_stroke (cairo_surface_t *surface, |
||
1033 | cairo_operator_t op, |
||
1034 | const cairo_pattern_t *source, |
||
1035 | cairo_path_fixed_t *path, |
||
1036 | const cairo_stroke_style_t *stroke_style, |
||
1037 | const cairo_matrix_t *ctm, |
||
1038 | const cairo_matrix_t *ctm_inverse, |
||
1039 | double tolerance, |
||
1040 | cairo_antialias_t antialias, |
||
1041 | cairo_clip_t *clip) |
||
1042 | { |
||
1043 | cairo_polygon_t polygon; |
||
1044 | cairo_traps_t traps; |
||
1045 | cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; |
||
1046 | int num_boxes = ARRAY_LENGTH (boxes_stack); |
||
1047 | cairo_composite_rectangles_t extents; |
||
1048 | cairo_rectangle_int_t rect; |
||
1049 | cairo_status_t status; |
||
1050 | |||
1051 | if (!_cairo_surface_get_extents (surface, &rect)) |
||
1052 | ASSERT_NOT_REACHED; |
||
1053 | |||
1054 | status = _cairo_composite_rectangles_init_for_stroke (&extents, |
||
1055 | rect.width, |
||
1056 | rect.height, |
||
1057 | op, source, |
||
1058 | path, stroke_style, ctm, |
||
1059 | clip); |
||
1060 | if (unlikely (status)) |
||
1061 | return status; |
||
1062 | |||
1063 | if (_cairo_clip_contains_extents (clip, &extents)) |
||
1064 | clip = NULL; |
||
1065 | |||
1066 | status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); |
||
1067 | if (unlikely (status)) |
||
1068 | return status; |
||
1069 | |||
1070 | _cairo_polygon_init (&polygon); |
||
1071 | _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); |
||
1072 | |||
1073 | _cairo_traps_init (&traps); |
||
1074 | _cairo_traps_limit (&traps, clip_boxes, num_boxes); |
||
1075 | |||
1076 | if (path->is_rectilinear) { |
||
1077 | status = _cairo_path_fixed_stroke_rectilinear_to_traps (path, |
||
1078 | stroke_style, |
||
1079 | ctm, |
||
1080 | &traps); |
||
1081 | if (likely (status == CAIRO_STATUS_SUCCESS)) |
||
1082 | goto DO_TRAPS; |
||
1083 | |||
1084 | if (_cairo_status_is_error (status)) |
||
1085 | goto CLEANUP; |
||
1086 | } |
||
1087 | |||
1088 | status = _cairo_path_fixed_stroke_to_polygon (path, |
||
1089 | stroke_style, |
||
1090 | ctm, ctm_inverse, |
||
1091 | tolerance, |
||
1092 | &polygon); |
||
1093 | if (unlikely (status)) |
||
1094 | goto CLEANUP; |
||
1095 | |||
1096 | if (polygon.num_edges == 0) |
||
1097 | goto DO_TRAPS; |
||
1098 | |||
1099 | if (_cairo_operator_bounded_by_mask (op)) { |
||
1100 | _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); |
||
1101 | if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) |
||
1102 | goto CLEANUP; |
||
1103 | } |
||
1104 | |||
1105 | /* Fall back to trapezoid fills. */ |
||
1106 | status = _cairo_bentley_ottmann_tessellate_polygon (&traps, |
||
1107 | &polygon, |
||
1108 | CAIRO_FILL_RULE_WINDING); |
||
1109 | if (unlikely (status)) |
||
1110 | goto CLEANUP; |
||
1111 | |||
1112 | DO_TRAPS: |
||
1113 | status = _clip_and_composite_trapezoids (source, op, surface, |
||
1114 | &traps, antialias, |
||
1115 | clip, |
||
1116 | extents.is_bounded ? &extents.bounded : &extents.unbounded); |
||
1117 | CLEANUP: |
||
1118 | _cairo_traps_fini (&traps); |
||
1119 | _cairo_polygon_fini (&polygon); |
||
1120 | if (clip_boxes != boxes_stack) |
||
1121 | free (clip_boxes); |
||
1122 | |||
1123 | return status; |
||
1124 | } |
||
1125 | |||
1126 | cairo_status_t |
||
1127 | _cairo_surface_fallback_fill (cairo_surface_t *surface, |
||
1128 | cairo_operator_t op, |
||
1129 | const cairo_pattern_t *source, |
||
1130 | cairo_path_fixed_t *path, |
||
1131 | cairo_fill_rule_t fill_rule, |
||
1132 | double tolerance, |
||
1133 | cairo_antialias_t antialias, |
||
1134 | cairo_clip_t *clip) |
||
1135 | { |
||
1136 | cairo_polygon_t polygon; |
||
1137 | cairo_traps_t traps; |
||
1138 | cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack; |
||
1139 | int num_boxes = ARRAY_LENGTH (boxes_stack); |
||
1140 | cairo_bool_t is_rectilinear; |
||
1141 | cairo_composite_rectangles_t extents; |
||
1142 | cairo_rectangle_int_t rect; |
||
1143 | cairo_status_t status; |
||
1144 | |||
1145 | if (!_cairo_surface_get_extents (surface, &rect)) |
||
1146 | ASSERT_NOT_REACHED; |
||
1147 | |||
1148 | status = _cairo_composite_rectangles_init_for_fill (&extents, |
||
1149 | rect.width, |
||
1150 | rect.height, |
||
1151 | op, source, path, |
||
1152 | clip); |
||
1153 | if (unlikely (status)) |
||
1154 | return status; |
||
1155 | |||
1156 | if (_cairo_clip_contains_extents (clip, &extents)) |
||
1157 | clip = NULL; |
||
1158 | |||
1159 | status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes); |
||
1160 | if (unlikely (status)) |
||
1161 | return status; |
||
1162 | |||
1163 | _cairo_traps_init (&traps); |
||
1164 | _cairo_traps_limit (&traps, clip_boxes, num_boxes); |
||
1165 | |||
1166 | _cairo_polygon_init (&polygon); |
||
1167 | _cairo_polygon_limit (&polygon, clip_boxes, num_boxes); |
||
1168 | |||
1169 | if (path->is_empty_fill) |
||
1170 | goto DO_TRAPS; |
||
1171 | |||
1172 | is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path); |
||
1173 | if (is_rectilinear) { |
||
1174 | status = _cairo_path_fixed_fill_rectilinear_to_traps (path, |
||
1175 | fill_rule, |
||
1176 | &traps); |
||
1177 | if (likely (status == CAIRO_STATUS_SUCCESS)) |
||
1178 | goto DO_TRAPS; |
||
1179 | |||
1180 | if (_cairo_status_is_error (status)) |
||
1181 | goto CLEANUP; |
||
1182 | } |
||
1183 | |||
1184 | status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); |
||
1185 | if (unlikely (status)) |
||
1186 | goto CLEANUP; |
||
1187 | |||
1188 | if (polygon.num_edges == 0) |
||
1189 | goto DO_TRAPS; |
||
1190 | |||
1191 | if (_cairo_operator_bounded_by_mask (op)) { |
||
1192 | _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask); |
||
1193 | if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) |
||
1194 | goto CLEANUP; |
||
1195 | } |
||
1196 | |||
1197 | if (is_rectilinear) { |
||
1198 | status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps, |
||
1199 | &polygon, |
||
1200 | fill_rule); |
||
1201 | if (likely (status == CAIRO_STATUS_SUCCESS)) |
||
1202 | goto DO_TRAPS; |
||
1203 | |||
1204 | if (unlikely (_cairo_status_is_error (status))) |
||
1205 | goto CLEANUP; |
||
1206 | } |
||
1207 | |||
1208 | /* Fall back to trapezoid fills. */ |
||
1209 | status = _cairo_bentley_ottmann_tessellate_polygon (&traps, |
||
1210 | &polygon, |
||
1211 | fill_rule); |
||
1212 | if (unlikely (status)) |
||
1213 | goto CLEANUP; |
||
1214 | |||
1215 | DO_TRAPS: |
||
1216 | status = _clip_and_composite_trapezoids (source, op, surface, |
||
1217 | &traps, antialias, |
||
1218 | clip, |
||
1219 | extents.is_bounded ? &extents.bounded : &extents.unbounded); |
||
1220 | CLEANUP: |
||
1221 | _cairo_traps_fini (&traps); |
||
1222 | _cairo_polygon_fini (&polygon); |
||
1223 | if (clip_boxes != boxes_stack) |
||
1224 | free (clip_boxes); |
||
1225 | |||
1226 | return status; |
||
1227 | } |
||
1228 | |||
1229 | typedef struct { |
||
1230 | cairo_scaled_font_t *font; |
||
1231 | cairo_glyph_t *glyphs; |
||
1232 | int num_glyphs; |
||
1233 | } cairo_show_glyphs_info_t; |
||
1234 | |||
1235 | static cairo_status_t |
||
1236 | _cairo_surface_old_show_glyphs_draw_func (void *closure, |
||
1237 | cairo_operator_t op, |
||
1238 | const cairo_pattern_t *src, |
||
1239 | cairo_surface_t *dst, |
||
1240 | int dst_x, |
||
1241 | int dst_y, |
||
1242 | const cairo_rectangle_int_t *extents, |
||
1243 | cairo_region_t *clip_region) |
||
1244 | { |
||
1245 | cairo_show_glyphs_info_t *glyph_info = closure; |
||
1246 | cairo_status_t status; |
||
1247 | cairo_region_t *extents_region = NULL; |
||
1248 | |||
1249 | if (clip_region == NULL && |
||
1250 | !_cairo_operator_bounded_by_source (op)) { |
||
1251 | extents_region = cairo_region_create_rectangle (extents); |
||
1252 | if (unlikely (extents_region->status)) |
||
1253 | return extents_region->status; |
||
1254 | cairo_region_translate (extents_region, -dst_x, -dst_y); |
||
1255 | clip_region = extents_region; |
||
1256 | } |
||
1257 | |||
1258 | /* Modifying the glyph array is fine because we know that this function |
||
1259 | * will be called only once, and we've already made a copy of the |
||
1260 | * glyphs in the wrapper. |
||
1261 | */ |
||
1262 | if (dst_x != 0 || dst_y != 0) { |
||
1263 | int i; |
||
1264 | |||
1265 | for (i = 0; i < glyph_info->num_glyphs; ++i) { |
||
1266 | ((cairo_glyph_t *) glyph_info->glyphs)[i].x -= dst_x; |
||
1267 | ((cairo_glyph_t *) glyph_info->glyphs)[i].y -= dst_y; |
||
1268 | } |
||
1269 | } |
||
1270 | |||
1271 | status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src, |
||
1272 | dst, |
||
1273 | extents->x, extents->y, |
||
1274 | extents->x - dst_x, |
||
1275 | extents->y - dst_y, |
||
1276 | extents->width, |
||
1277 | extents->height, |
||
1278 | glyph_info->glyphs, |
||
1279 | glyph_info->num_glyphs, |
||
1280 | clip_region); |
||
1281 | |||
1282 | if (status == CAIRO_INT_STATUS_UNSUPPORTED) { |
||
1283 | status = _cairo_scaled_font_show_glyphs (glyph_info->font, |
||
1284 | op, |
||
1285 | src, dst, |
||
1286 | extents->x, extents->y, |
||
1287 | extents->x - dst_x, |
||
1288 | extents->y - dst_y, |
||
1289 | extents->width, extents->height, |
||
1290 | glyph_info->glyphs, |
||
1291 | glyph_info->num_glyphs, |
||
1292 | clip_region); |
||
1293 | } |
||
1294 | |||
1295 | if (extents_region) |
||
1296 | cairo_region_destroy (extents_region); |
||
1297 | |||
1298 | return status; |
||
1299 | } |
||
1300 | |||
1301 | cairo_status_t |
||
1302 | _cairo_surface_fallback_show_glyphs (cairo_surface_t *surface, |
||
1303 | cairo_operator_t op, |
||
1304 | const cairo_pattern_t *source, |
||
1305 | cairo_glyph_t *glyphs, |
||
1306 | int num_glyphs, |
||
1307 | cairo_scaled_font_t *scaled_font, |
||
1308 | cairo_clip_t *clip) |
||
1309 | { |
||
1310 | cairo_show_glyphs_info_t glyph_info; |
||
1311 | cairo_composite_rectangles_t extents; |
||
1312 | cairo_rectangle_int_t rect; |
||
1313 | cairo_status_t status; |
||
1314 | |||
1315 | if (!_cairo_surface_get_extents (surface, &rect)) |
||
1316 | ASSERT_NOT_REACHED; |
||
1317 | |||
1318 | status = _cairo_composite_rectangles_init_for_glyphs (&extents, |
||
1319 | rect.width, |
||
1320 | rect.height, |
||
1321 | op, source, |
||
1322 | scaled_font, |
||
1323 | glyphs, num_glyphs, |
||
1324 | clip, |
||
1325 | NULL); |
||
1326 | if (unlikely (status)) |
||
1327 | return status; |
||
1328 | |||
1329 | if (_cairo_clip_contains_rectangle (clip, &extents.mask)) |
||
1330 | clip = NULL; |
||
1331 | |||
1332 | if (clip != NULL && extents.is_bounded) { |
||
1333 | status = _cairo_clip_rectangle (clip, &extents.bounded); |
||
1334 | if (unlikely (status)) |
||
1335 | return status; |
||
1336 | } |
||
1337 | |||
1338 | glyph_info.font = scaled_font; |
||
1339 | glyph_info.glyphs = glyphs; |
||
1340 | glyph_info.num_glyphs = num_glyphs; |
||
1341 | |||
1342 | return _clip_and_composite (clip, op, source, |
||
1343 | _cairo_surface_old_show_glyphs_draw_func, |
||
1344 | &glyph_info, |
||
1345 | surface, |
||
1346 | extents.is_bounded ? &extents.bounded : &extents.unbounded); |
||
1347 | } |
||
1348 | |||
1349 | cairo_surface_t * |
||
1350 | _cairo_surface_fallback_snapshot (cairo_surface_t *surface) |
||
1351 | { |
||
1352 | cairo_surface_t *snapshot; |
||
1353 | cairo_status_t status; |
||
1354 | cairo_format_t format; |
||
1355 | cairo_surface_pattern_t pattern; |
||
1356 | cairo_image_surface_t *image; |
||
1357 | void *image_extra; |
||
1358 | |||
1359 | status = _cairo_surface_acquire_source_image (surface, |
||
1360 | &image, &image_extra); |
||
1361 | if (unlikely (status)) |
||
1362 | return _cairo_surface_create_in_error (status); |
||
1363 | |||
1364 | format = image->format; |
||
1365 | if (format == CAIRO_FORMAT_INVALID) { |
||
1366 | /* Non-standard images formats can be generated when retrieving |
||
1367 | * images from unusual xservers, for example. |
||
1368 | */ |
||
1369 | format = _cairo_format_from_content (image->base.content); |
||
1370 | } |
||
1371 | snapshot = cairo_image_surface_create (format, |
||
1372 | image->width, |
||
1373 | image->height); |
||
1374 | if (cairo_surface_status (snapshot)) { |
||
1375 | _cairo_surface_release_source_image (surface, image, image_extra); |
||
1376 | return snapshot; |
||
1377 | } |
||
1378 | |||
1379 | _cairo_pattern_init_for_surface (&pattern, &image->base); |
||
1380 | status = _cairo_surface_paint (snapshot, |
||
1381 | CAIRO_OPERATOR_SOURCE, |
||
1382 | &pattern.base, |
||
1383 | NULL); |
||
1384 | _cairo_pattern_fini (&pattern.base); |
||
1385 | _cairo_surface_release_source_image (surface, image, image_extra); |
||
1386 | if (unlikely (status)) { |
||
1387 | cairo_surface_destroy (snapshot); |
||
1388 | return _cairo_surface_create_in_error (status); |
||
1389 | } |
||
1390 | |||
1391 | return snapshot; |
||
1392 | } |
||
1393 | |||
1394 | cairo_status_t |
||
1395 | _cairo_surface_fallback_composite (cairo_operator_t op, |
||
1396 | const cairo_pattern_t *src, |
||
1397 | const cairo_pattern_t *mask, |
||
1398 | cairo_surface_t *dst, |
||
1399 | int src_x, |
||
1400 | int src_y, |
||
1401 | int mask_x, |
||
1402 | int mask_y, |
||
1403 | int dst_x, |
||
1404 | int dst_y, |
||
1405 | unsigned int width, |
||
1406 | unsigned int height, |
||
1407 | cairo_region_t *clip_region) |
||
1408 | { |
||
1409 | fallback_state_t state; |
||
1410 | cairo_region_t *fallback_region = NULL; |
||
1411 | cairo_status_t status; |
||
1412 | |||
1413 | status = _fallback_init (&state, dst, dst_x, dst_y, width, height); |
||
1414 | if (unlikely (status)) |
||
1415 | return status; |
||
1416 | |||
1417 | /* We know this will never fail with the image backend; but |
||
1418 | * instead of calling into it directly, we call |
||
1419 | * _cairo_surface_composite so that we get the correct device |
||
1420 | * offset handling. |
||
1421 | */ |
||
1422 | |||
1423 | if (clip_region != NULL && (state.image_rect.x || state.image_rect.y)) { |
||
1424 | fallback_region = cairo_region_copy (clip_region); |
||
1425 | status = fallback_region->status; |
||
1426 | if (unlikely (status)) |
||
1427 | goto FAIL; |
||
1428 | |||
1429 | cairo_region_translate (fallback_region, |
||
1430 | -state.image_rect.x, |
||
1431 | -state.image_rect.y); |
||
1432 | clip_region = fallback_region; |
||
1433 | } |
||
1434 | |||
1435 | status = _cairo_surface_composite (op, src, mask, |
||
1436 | &state.image->base, |
||
1437 | src_x, src_y, mask_x, mask_y, |
||
1438 | dst_x - state.image_rect.x, |
||
1439 | dst_y - state.image_rect.y, |
||
1440 | width, height, |
||
1441 | clip_region); |
||
1442 | FAIL: |
||
1443 | if (fallback_region != NULL) |
||
1444 | cairo_region_destroy (fallback_region); |
||
1445 | _fallback_fini (&state); |
||
1446 | |||
1447 | return status; |
||
1448 | } |
||
1449 | |||
1450 | cairo_status_t |
||
1451 | _cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface, |
||
1452 | cairo_operator_t op, |
||
1453 | const cairo_color_t *color, |
||
1454 | cairo_rectangle_int_t *rects, |
||
1455 | int num_rects) |
||
1456 | { |
||
1457 | fallback_state_t state; |
||
1458 | cairo_rectangle_int_t *offset_rects = NULL; |
||
1459 | cairo_status_t status; |
||
1460 | int x1, y1, x2, y2; |
||
1461 | int i; |
||
1462 | |||
1463 | assert (surface->snapshot_of == NULL); |
||
1464 | |||
1465 | if (num_rects <= 0) |
||
1466 | return CAIRO_STATUS_SUCCESS; |
||
1467 | |||
1468 | /* Compute the bounds of the rectangles, so that we know what area of the |
||
1469 | * destination surface to fetch |
||
1470 | */ |
||
1471 | x1 = rects[0].x; |
||
1472 | y1 = rects[0].y; |
||
1473 | x2 = rects[0].x + rects[0].width; |
||
1474 | y2 = rects[0].y + rects[0].height; |
||
1475 | |||
1476 | for (i = 1; i < num_rects; i++) { |
||
1477 | if (rects[i].x < x1) |
||
1478 | x1 = rects[i].x; |
||
1479 | if (rects[i].y < y1) |
||
1480 | y1 = rects[i].y; |
||
1481 | |||
1482 | if ((int) (rects[i].x + rects[i].width) > x2) |
||
1483 | x2 = rects[i].x + rects[i].width; |
||
1484 | if ((int) (rects[i].y + rects[i].height) > y2) |
||
1485 | y2 = rects[i].y + rects[i].height; |
||
1486 | } |
||
1487 | |||
1488 | status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); |
||
1489 | if (unlikely (status)) |
||
1490 | return status; |
||
1491 | |||
1492 | /* If the fetched image isn't at 0,0, we need to offset the rectangles */ |
||
1493 | |||
1494 | if (state.image_rect.x != 0 || state.image_rect.y != 0) { |
||
1495 | offset_rects = _cairo_malloc_ab (num_rects, sizeof (cairo_rectangle_int_t)); |
||
1496 | if (unlikely (offset_rects == NULL)) { |
||
1497 | status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
1498 | goto DONE; |
||
1499 | } |
||
1500 | |||
1501 | for (i = 0; i < num_rects; i++) { |
||
1502 | offset_rects[i].x = rects[i].x - state.image_rect.x; |
||
1503 | offset_rects[i].y = rects[i].y - state.image_rect.y; |
||
1504 | offset_rects[i].width = rects[i].width; |
||
1505 | offset_rects[i].height = rects[i].height; |
||
1506 | } |
||
1507 | |||
1508 | rects = offset_rects; |
||
1509 | } |
||
1510 | |||
1511 | status = _cairo_surface_fill_rectangles (&state.image->base, |
||
1512 | op, color, |
||
1513 | rects, num_rects); |
||
1514 | |||
1515 | free (offset_rects); |
||
1516 | |||
1517 | DONE: |
||
1518 | _fallback_fini (&state); |
||
1519 | |||
1520 | return status; |
||
1521 | } |
||
1522 | |||
1523 | cairo_status_t |
||
1524 | _cairo_surface_fallback_composite_trapezoids (cairo_operator_t op, |
||
1525 | const cairo_pattern_t *pattern, |
||
1526 | cairo_surface_t *dst, |
||
1527 | cairo_antialias_t antialias, |
||
1528 | int src_x, |
||
1529 | int src_y, |
||
1530 | int dst_x, |
||
1531 | int dst_y, |
||
1532 | unsigned int width, |
||
1533 | unsigned int height, |
||
1534 | cairo_trapezoid_t *traps, |
||
1535 | int num_traps, |
||
1536 | cairo_region_t *clip_region) |
||
1537 | { |
||
1538 | fallback_state_t state; |
||
1539 | cairo_region_t *fallback_region = NULL; |
||
1540 | cairo_trapezoid_t *offset_traps = NULL; |
||
1541 | cairo_status_t status; |
||
1542 | |||
1543 | status = _fallback_init (&state, dst, dst_x, dst_y, width, height); |
||
1544 | if (unlikely (status)) |
||
1545 | return status; |
||
1546 | |||
1547 | /* If the destination image isn't at 0,0, we need to offset the trapezoids */ |
||
1548 | |||
1549 | if (state.image_rect.x != 0 || state.image_rect.y != 0) { |
||
1550 | offset_traps = _cairo_malloc_ab (num_traps, sizeof (cairo_trapezoid_t)); |
||
1551 | if (offset_traps == NULL) { |
||
1552 | status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
1553 | goto FAIL; |
||
1554 | } |
||
1555 | |||
1556 | _cairo_trapezoid_array_translate_and_scale (offset_traps, traps, num_traps, |
||
1557 | - state.image_rect.x, - state.image_rect.y, |
||
1558 | 1.0, 1.0); |
||
1559 | traps = offset_traps; |
||
1560 | |||
1561 | /* similarly we need to adjust the region */ |
||
1562 | if (clip_region != NULL) { |
||
1563 | fallback_region = cairo_region_copy (clip_region); |
||
1564 | status = fallback_region->status; |
||
1565 | if (unlikely (status)) |
||
1566 | goto FAIL; |
||
1567 | |||
1568 | cairo_region_translate (fallback_region, |
||
1569 | -state.image_rect.x, |
||
1570 | -state.image_rect.y); |
||
1571 | clip_region = fallback_region; |
||
1572 | } |
||
1573 | } |
||
1574 | |||
1575 | status = _cairo_surface_composite_trapezoids (op, pattern, |
||
1576 | &state.image->base, |
||
1577 | antialias, |
||
1578 | src_x, src_y, |
||
1579 | dst_x - state.image_rect.x, |
||
1580 | dst_y - state.image_rect.y, |
||
1581 | width, height, |
||
1582 | traps, num_traps, |
||
1583 | clip_region); |
||
1584 | FAIL: |
||
1585 | if (offset_traps != NULL) |
||
1586 | free (offset_traps); |
||
1587 | |||
1588 | if (fallback_region != NULL) |
||
1589 | cairo_region_destroy (fallback_region); |
||
1590 | |||
1591 | _fallback_fini (&state); |
||
1592 | |||
1593 | return status; |
||
1594 | } |
||
1595 | |||
1596 | cairo_status_t |
||
1597 | _cairo_surface_fallback_clone_similar (cairo_surface_t *surface, |
||
1598 | cairo_surface_t *src, |
||
1599 | int src_x, |
||
1600 | int src_y, |
||
1601 | int width, |
||
1602 | int height, |
||
1603 | int *clone_offset_x, |
||
1604 | int *clone_offset_y, |
||
1605 | cairo_surface_t **clone_out) |
||
1606 | { |
||
1607 | cairo_surface_t *new_surface; |
||
1608 | cairo_surface_pattern_t pattern; |
||
1609 | cairo_status_t status; |
||
1610 | |||
1611 | new_surface = _cairo_surface_create_similar_scratch (surface, |
||
1612 | src->content, |
||
1613 | width, height); |
||
1614 | if (new_surface == NULL) |
||
1615 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
1616 | if (unlikely (new_surface->status)) |
||
1617 | return new_surface->status; |
||
1618 | |||
1619 | /* We have to copy these here, so that the coordinate spaces are correct */ |
||
1620 | new_surface->device_transform = src->device_transform; |
||
1621 | new_surface->device_transform_inverse = src->device_transform_inverse; |
||
1622 | |||
1623 | _cairo_pattern_init_for_surface (&pattern, src); |
||
1624 | cairo_matrix_init_translate (&pattern.base.matrix, src_x, src_y); |
||
1625 | pattern.base.filter = CAIRO_FILTER_NEAREST; |
||
1626 | |||
1627 | status = _cairo_surface_paint (new_surface, |
||
1628 | CAIRO_OPERATOR_SOURCE, |
||
1629 | &pattern.base, |
||
1630 | NULL); |
||
1631 | _cairo_pattern_fini (&pattern.base); |
||
1632 | |||
1633 | if (unlikely (status)) { |
||
1634 | cairo_surface_destroy (new_surface); |
||
1635 | return status; |
||
1636 | } |
||
1637 | |||
1638 | *clone_offset_x = src_x; |
||
1639 | *clone_offset_y = src_y; |
||
1640 | *clone_out = new_surface; |
||
1641 | return CAIRO_STATUS_SUCCESS; |
||
1642 | }>>>>=>>>> |