Initialize Tizen 2.3
[framework/graphics/cairo.git] / src / cairo-surface-wrapper.c
1 /* cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2005 Red Hat, Inc
4  * Copyright © 2007 Adrian Johnson
5  * Copyright © 2009 Chris Wilson
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 Red Hat, Inc.
33  *
34  * Contributor(s):
35  *      Chris Wilson <chris@chris-wilson.co.uk>
36  */
37
38 #include "cairoint.h"
39
40 #include "cairo-clip-inline.h"
41 #include "cairo-error-private.h"
42 #include "cairo-pattern-private.h"
43 #include "cairo-surface-wrapper-private.h"
44
45 /* A collection of routines to facilitate surface wrapping */
46
47 static void
48 _copy_transformed_pattern (cairo_pattern_t *pattern,
49                            const cairo_pattern_t *original,
50                            const cairo_matrix_t  *ctm_inverse)
51 {
52     _cairo_pattern_init_static_copy (pattern, original);
53
54     if (! _cairo_matrix_is_identity (ctm_inverse))
55         _cairo_pattern_transform (pattern, ctm_inverse);
56 }
57
58 cairo_status_t
59 _cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper,
60                                              cairo_image_surface_t  **image_out,
61                                              void                   **image_extra)
62 {
63     if (unlikely (wrapper->target->status))
64         return wrapper->target->status;
65
66     return _cairo_surface_acquire_source_image (wrapper->target,
67                                                 image_out, image_extra);
68 }
69
70 void
71 _cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper,
72                                              cairo_image_surface_t  *image,
73                                              void                   *image_extra)
74 {
75     _cairo_surface_release_source_image (wrapper->target, image, image_extra);
76 }
77
78 static void
79 _cairo_surface_wrapper_get_transform (cairo_surface_wrapper_t *wrapper,
80                                       cairo_matrix_t *m)
81 {
82     cairo_matrix_init_identity (m);
83
84     if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y))
85         cairo_matrix_translate (m, -wrapper->extents.x, -wrapper->extents.y);
86
87     if (! _cairo_matrix_is_identity (&wrapper->transform))
88         cairo_matrix_multiply (m, &wrapper->transform, m);
89
90     if (! _cairo_matrix_is_identity (&wrapper->target->device_transform))
91         cairo_matrix_multiply (m, &wrapper->target->device_transform, m);
92 }
93
94 static void
95 _cairo_surface_wrapper_get_inverse_transform (cairo_surface_wrapper_t *wrapper,
96                                               cairo_matrix_t *m)
97 {
98     cairo_matrix_init_identity (m);
99
100     if (! _cairo_matrix_is_identity (&wrapper->target->device_transform_inverse))
101         cairo_matrix_multiply (m, &wrapper->target->device_transform_inverse, m);
102
103     if (! _cairo_matrix_is_identity (&wrapper->transform)) {
104         cairo_matrix_t inv;
105         cairo_status_t status;
106
107         inv = wrapper->transform;
108         status = cairo_matrix_invert (&inv);
109         assert (status == CAIRO_STATUS_SUCCESS);
110         cairo_matrix_multiply (m, &inv, m);
111     }
112
113     if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y))
114         cairo_matrix_translate (m, wrapper->extents.x, wrapper->extents.y);
115 }
116
117 static cairo_clip_t *
118 _cairo_surface_wrapper_get_clip (cairo_surface_wrapper_t *wrapper,
119                                  const cairo_clip_t *clip)
120 {
121     cairo_clip_t *copy;
122
123     copy = _cairo_clip_copy (clip);
124     if (wrapper->has_extents) {
125         copy = _cairo_clip_intersect_rectangle (copy, &wrapper->extents);
126     }
127     copy = _cairo_clip_transform (copy, &wrapper->transform);
128     if (! _cairo_matrix_is_identity (&wrapper->target->device_transform))
129         copy = _cairo_clip_transform (copy, &wrapper->target->device_transform);
130     if (wrapper->clip)
131         copy = _cairo_clip_intersect_clip (copy, wrapper->clip);
132
133     return copy;
134 }
135
136 cairo_status_t
137 _cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
138                               cairo_operator_t   op,
139                               const cairo_pattern_t *source,
140                               const cairo_clip_t    *clip)
141 {
142     cairo_status_t status;
143     cairo_clip_t *dev_clip;
144     cairo_pattern_union_t source_copy;
145
146     if (unlikely (wrapper->target->status))
147         return wrapper->target->status;
148
149     dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
150     if (_cairo_clip_is_all_clipped (dev_clip))
151         return CAIRO_INT_STATUS_NOTHING_TO_DO;
152
153     if (wrapper->needs_transform) {
154         cairo_matrix_t m;
155
156         _cairo_surface_wrapper_get_transform (wrapper, &m);
157
158         status = cairo_matrix_invert (&m);
159         assert (status == CAIRO_STATUS_SUCCESS);
160
161         _copy_transformed_pattern (&source_copy.base, source, &m);
162         source = &source_copy.base;
163     }
164
165     status = _cairo_surface_paint (wrapper->target, op, source, dev_clip);
166
167     _cairo_clip_destroy (dev_clip);
168     return status;
169 }
170
171
172 cairo_status_t
173 _cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
174                              cairo_operator_t    op,
175                              const cairo_pattern_t *source,
176                              const cairo_pattern_t *mask,
177                              const cairo_clip_t     *clip)
178 {
179     cairo_status_t status;
180     cairo_clip_t *dev_clip;
181     cairo_pattern_union_t source_copy;
182     cairo_pattern_union_t mask_copy;
183
184     if (unlikely (wrapper->target->status))
185         return wrapper->target->status;
186
187     dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
188     if (_cairo_clip_is_all_clipped (dev_clip))
189         return CAIRO_INT_STATUS_NOTHING_TO_DO;
190
191     if (wrapper->needs_transform) {
192         cairo_matrix_t m;
193
194         _cairo_surface_wrapper_get_transform (wrapper, &m);
195
196         status = cairo_matrix_invert (&m);
197         assert (status == CAIRO_STATUS_SUCCESS);
198
199         _copy_transformed_pattern (&source_copy.base, source, &m);
200         source = &source_copy.base;
201
202         _copy_transformed_pattern (&mask_copy.base, mask, &m);
203         mask = &mask_copy.base;
204     }
205
206     status = _cairo_surface_mask (wrapper->target, op, source, mask, dev_clip);
207
208     _cairo_clip_destroy (dev_clip);
209     return status;
210 }
211
212 cairo_status_t
213 _cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
214                                cairo_operator_t          op,
215                                const cairo_pattern_t    *source,
216                                const cairo_path_fixed_t *path,
217                                const cairo_stroke_style_t       *stroke_style,
218                                const cairo_matrix_t             *ctm,
219                                const cairo_matrix_t             *ctm_inverse,
220                                double                    tolerance,
221                                cairo_antialias_t         antialias,
222                                const cairo_clip_t               *clip)
223 {
224     cairo_status_t status;
225     cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
226     cairo_clip_t *dev_clip;
227     cairo_matrix_t dev_ctm = *ctm;
228     cairo_matrix_t dev_ctm_inverse = *ctm_inverse;
229     cairo_pattern_union_t source_copy;
230
231     if (unlikely (wrapper->target->status))
232         return wrapper->target->status;
233
234     dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
235     if (_cairo_clip_is_all_clipped (dev_clip))
236         return CAIRO_INT_STATUS_NOTHING_TO_DO;
237
238     if (wrapper->needs_transform) {
239         cairo_matrix_t m;
240
241         _cairo_surface_wrapper_get_transform (wrapper, &m);
242
243         status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
244         if (unlikely (status))
245             goto FINISH;
246
247         _cairo_path_fixed_transform (&path_copy, &m);
248         dev_path = &path_copy;
249
250         cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
251
252         status = cairo_matrix_invert (&m);
253         assert (status == CAIRO_STATUS_SUCCESS);
254
255         cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse);
256
257         _copy_transformed_pattern (&source_copy.base, source, &m);
258         source = &source_copy.base;
259     }
260
261     status = _cairo_surface_stroke (wrapper->target, op, source,
262                                     dev_path, stroke_style,
263                                     &dev_ctm, &dev_ctm_inverse,
264                                     tolerance, antialias,
265                                     dev_clip);
266
267  FINISH:
268     if (dev_path != path)
269         _cairo_path_fixed_fini (dev_path);
270     _cairo_clip_destroy (dev_clip);
271     return status;
272 }
273
274 cairo_status_t
275 _cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
276                                     cairo_operator_t         fill_op,
277                                     const cairo_pattern_t   *fill_source,
278                                     cairo_fill_rule_t        fill_rule,
279                                     double                   fill_tolerance,
280                                     cairo_antialias_t        fill_antialias,
281                                     const cairo_path_fixed_t*path,
282                                     cairo_operator_t         stroke_op,
283                                     const cairo_pattern_t   *stroke_source,
284                                     const cairo_stroke_style_t    *stroke_style,
285                                     const cairo_matrix_t            *stroke_ctm,
286                                     const cairo_matrix_t            *stroke_ctm_inverse,
287                                     double                   stroke_tolerance,
288                                     cairo_antialias_t        stroke_antialias,
289                                     const cairo_clip_t      *clip)
290 {
291     cairo_status_t status;
292     cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *)path;
293     cairo_matrix_t dev_ctm = *stroke_ctm;
294     cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse;
295     cairo_clip_t *dev_clip;
296     cairo_pattern_union_t stroke_source_copy;
297     cairo_pattern_union_t fill_source_copy;
298
299     if (unlikely (wrapper->target->status))
300         return wrapper->target->status;
301
302     dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
303     if (_cairo_clip_is_all_clipped (dev_clip))
304         return CAIRO_INT_STATUS_NOTHING_TO_DO;
305
306     if (wrapper->needs_transform) {
307         cairo_matrix_t m;
308
309         _cairo_surface_wrapper_get_transform (wrapper, &m);
310
311         status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
312         if (unlikely (status))
313             goto FINISH;
314
315         _cairo_path_fixed_transform (&path_copy, &m);
316         dev_path = &path_copy;
317
318         cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
319
320         status = cairo_matrix_invert (&m);
321         assert (status == CAIRO_STATUS_SUCCESS);
322
323         cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse);
324
325         _copy_transformed_pattern (&stroke_source_copy.base, stroke_source, &m);
326         stroke_source = &stroke_source_copy.base;
327
328         _copy_transformed_pattern (&fill_source_copy.base, fill_source, &m);
329         fill_source = &fill_source_copy.base;
330     }
331
332     status = _cairo_surface_fill_stroke (wrapper->target,
333                                          fill_op, fill_source, fill_rule,
334                                          fill_tolerance, fill_antialias,
335                                          dev_path,
336                                          stroke_op, stroke_source,
337                                          stroke_style,
338                                          &dev_ctm, &dev_ctm_inverse,
339                                          stroke_tolerance, stroke_antialias,
340                                          dev_clip);
341
342   FINISH:
343     if (dev_path != path)
344         _cairo_path_fixed_fini (dev_path);
345     _cairo_clip_destroy (dev_clip);
346     return status;
347 }
348
349 cairo_status_t
350 _cairo_surface_wrapper_fill (cairo_surface_wrapper_t    *wrapper,
351                              cairo_operator_t    op,
352                              const cairo_pattern_t *source,
353                              const cairo_path_fixed_t   *path,
354                              cairo_fill_rule_t   fill_rule,
355                              double              tolerance,
356                              cairo_antialias_t   antialias,
357                              const cairo_clip_t *clip)
358 {
359     cairo_status_t status;
360     cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
361     cairo_pattern_union_t source_copy;
362     cairo_clip_t *dev_clip;
363
364     if (unlikely (wrapper->target->status))
365         return wrapper->target->status;
366
367     dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
368     if (_cairo_clip_is_all_clipped (dev_clip))
369         return CAIRO_INT_STATUS_NOTHING_TO_DO;
370
371     if (wrapper->needs_transform) {
372         cairo_matrix_t m;
373
374         _cairo_surface_wrapper_get_transform (wrapper, &m);
375
376         status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
377         if (unlikely (status))
378             goto FINISH;
379
380         _cairo_path_fixed_transform (&path_copy, &m);
381         dev_path = &path_copy;
382
383         status = cairo_matrix_invert (&m);
384         assert (status == CAIRO_STATUS_SUCCESS);
385
386         _copy_transformed_pattern (&source_copy.base, source, &m);
387         source = &source_copy.base;
388     }
389
390     status = _cairo_surface_fill (wrapper->target, op, source,
391                                   dev_path, fill_rule,
392                                   tolerance, antialias,
393                                   dev_clip);
394
395  FINISH:
396     if (dev_path != path)
397         _cairo_path_fixed_fini (dev_path);
398     _cairo_clip_destroy (dev_clip);
399     return status;
400 }
401
402 cairo_status_t
403 _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
404                                          cairo_operator_t            op,
405                                          const cairo_pattern_t      *source,
406                                          const char                 *utf8,
407                                          int                         utf8_len,
408                                          const cairo_glyph_t        *glyphs,
409                                          int                         num_glyphs,
410                                          const cairo_text_cluster_t *clusters,
411                                          int                         num_clusters,
412                                          cairo_text_cluster_flags_t  cluster_flags,
413                                          cairo_scaled_font_t        *scaled_font,
414                                          const cairo_clip_t         *clip)
415 {
416     cairo_status_t status;
417     cairo_clip_t *dev_clip;
418     cairo_glyph_t stack_glyphs [CAIRO_STACK_ARRAY_LENGTH(cairo_glyph_t)];
419     cairo_glyph_t *dev_glyphs = stack_glyphs;
420     cairo_scaled_font_t *dev_scaled_font = scaled_font;
421     cairo_pattern_union_t source_copy;
422     cairo_font_options_t options;
423
424     if (unlikely (wrapper->target->status))
425         return wrapper->target->status;
426
427     dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
428     if (_cairo_clip_is_all_clipped (dev_clip))
429         return CAIRO_INT_STATUS_NOTHING_TO_DO;
430
431     cairo_surface_get_font_options (wrapper->target, &options);
432     cairo_font_options_merge (&options, &scaled_font->options);
433
434     if (wrapper->needs_transform) {
435         cairo_matrix_t m;
436         int i;
437
438         _cairo_surface_wrapper_get_transform (wrapper, &m);
439
440         if (! _cairo_matrix_is_translation (&wrapper->transform)) {
441             cairo_matrix_t ctm;
442
443             /* XXX No device-transform? A bug in the tangle of layers? */
444             _cairo_matrix_multiply (&ctm,
445                                     &wrapper->transform,
446                                     &scaled_font->ctm);
447             dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face,
448                                                         &scaled_font->font_matrix,
449                                                         &ctm, &options);
450         }
451
452         if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) {
453             dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
454             if (unlikely (dev_glyphs == NULL)) {
455                 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
456                 goto FINISH;
457             }
458         }
459
460         for (i = 0; i < num_glyphs; i++) {
461             dev_glyphs[i] = glyphs[i];
462             cairo_matrix_transform_point (&m,
463                                           &dev_glyphs[i].x,
464                                           &dev_glyphs[i].y);
465         }
466
467         status = cairo_matrix_invert (&m);
468         assert (status == CAIRO_STATUS_SUCCESS);
469
470         _copy_transformed_pattern (&source_copy.base, source, &m);
471         source = &source_copy.base;
472     } else {
473         if (! cairo_font_options_equal (&options, &scaled_font->options)) {
474             dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face,
475                                                         &scaled_font->font_matrix,
476                                                         &scaled_font->ctm,
477                                                         &options);
478         }
479
480         /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed
481          * to modify the glyph array that's passed in.  We must always
482          * copy the array before handing it to the backend.
483          */
484         if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) {
485             dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
486             if (unlikely (dev_glyphs == NULL)) {
487                 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
488                 goto FINISH;
489             }
490         }
491
492         memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
493     }
494
495     status = _cairo_surface_show_text_glyphs (wrapper->target, op, source,
496                                               utf8, utf8_len,
497                                               dev_glyphs, num_glyphs,
498                                               clusters, num_clusters,
499                                               cluster_flags,
500                                               dev_scaled_font,
501                                               dev_clip);
502  FINISH:
503     _cairo_clip_destroy (dev_clip);
504     if (dev_glyphs != stack_glyphs)
505         free (dev_glyphs);
506     if (dev_scaled_font != scaled_font)
507         cairo_scaled_font_destroy (dev_scaled_font);
508     return status;
509 }
510
511 cairo_surface_t *
512 _cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper,
513                                        cairo_content_t  content,
514                                        int              width,
515                                        int              height)
516 {
517     return _cairo_surface_create_similar_scratch (wrapper->target,
518                                                   content, width, height);
519 }
520
521 cairo_bool_t
522 _cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper,
523                                     cairo_rectangle_int_t   *extents)
524 {
525     if (wrapper->has_extents) {
526         if (_cairo_surface_get_extents (wrapper->target, extents))
527             _cairo_rectangle_intersect (extents, &wrapper->extents);
528         else
529             *extents = wrapper->extents;
530
531         return TRUE;
532     } else {
533         return _cairo_surface_get_extents (wrapper->target, extents);
534     }
535 }
536
537 static cairo_bool_t
538 _cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper)
539 {
540     return
541         (wrapper->has_extents && (wrapper->extents.x | wrapper->extents.y)) ||
542         ! _cairo_matrix_is_identity (&wrapper->transform) ||
543         ! _cairo_matrix_is_identity (&wrapper->target->device_transform);
544 }
545
546 void
547 _cairo_surface_wrapper_intersect_extents (cairo_surface_wrapper_t *wrapper,
548                                           const cairo_rectangle_int_t *extents)
549 {
550     if (! wrapper->has_extents) {
551         wrapper->extents = *extents;
552         wrapper->has_extents = TRUE;
553     } else
554         _cairo_rectangle_intersect (&wrapper->extents, extents);
555
556     wrapper->needs_transform =
557         _cairo_surface_wrapper_needs_device_transform (wrapper);
558 }
559
560 void
561 _cairo_surface_wrapper_set_inverse_transform (cairo_surface_wrapper_t *wrapper,
562                                               const cairo_matrix_t *transform)
563 {
564     cairo_status_t status;
565
566     if (transform == NULL || _cairo_matrix_is_identity (transform)) {
567         cairo_matrix_init_identity (&wrapper->transform);
568
569         wrapper->needs_transform =
570             _cairo_surface_wrapper_needs_device_transform (wrapper);
571     } else {
572         wrapper->transform = *transform;
573         status = cairo_matrix_invert (&wrapper->transform);
574         /* should always be invertible unless given pathological input */
575         assert (status == CAIRO_STATUS_SUCCESS);
576
577         wrapper->needs_transform = TRUE;
578     }
579 }
580
581 void
582 _cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper,
583                                  const cairo_clip_t *clip)
584 {
585     wrapper->clip = clip;
586 }
587
588 void
589 _cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t    *wrapper,
590                                          cairo_font_options_t       *options)
591 {
592     cairo_surface_get_font_options (wrapper->target, options);
593 }
594
595 cairo_surface_t *
596 _cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper)
597 {
598     if (wrapper->target->backend->snapshot)
599         return wrapper->target->backend->snapshot (wrapper->target);
600
601     return NULL;
602 }
603
604 cairo_bool_t
605 _cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper)
606 {
607     return cairo_surface_has_show_text_glyphs (wrapper->target);
608 }
609
610 void
611 _cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper,
612                              cairo_surface_t *target)
613 {
614     wrapper->target = cairo_surface_reference (target);
615     cairo_matrix_init_identity (&wrapper->transform);
616     wrapper->has_extents = FALSE;
617     wrapper->extents.x = wrapper->extents.y = 0;
618     wrapper->clip = NULL;
619
620     wrapper->needs_transform = FALSE;
621     if (target) {
622         wrapper->needs_transform =
623             ! _cairo_matrix_is_identity (&target->device_transform);
624     }
625 }
626
627 void
628 _cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper)
629 {
630     cairo_surface_destroy (wrapper->target);
631 }
632
633 cairo_bool_t
634 _cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper,
635                                            cairo_rectangle_int_t *extents)
636 {
637     cairo_rectangle_int_t clip;
638     cairo_bool_t has_clip;
639
640     has_clip = _cairo_surface_get_extents (wrapper->target, &clip);
641     if (wrapper->clip) {
642         if (has_clip) {
643             if (! _cairo_rectangle_intersect (&clip,
644                                               _cairo_clip_get_extents (wrapper->clip)))
645                 return FALSE;
646         } else {
647             has_clip = TRUE;
648             clip = *_cairo_clip_get_extents (wrapper->clip);
649         }
650     }
651
652     if (has_clip && wrapper->needs_transform) {
653         cairo_matrix_t m;
654         double x1, y1, x2, y2;
655
656         _cairo_surface_wrapper_get_inverse_transform (wrapper, &m);
657
658         x1 = clip.x;
659         y1 = clip.y;
660         x2 = clip.x + clip.width;
661         y2 = clip.y + clip.height;
662
663         _cairo_matrix_transform_bounding_box (&m, &x1, &y1, &x2, &y2, NULL);
664
665         clip.x = floor (x1);
666         clip.y = floor (y1);
667         clip.width  = ceil (x2) - clip.x;
668         clip.height = ceil (y2) - clip.y;
669     }
670
671     if (has_clip) {
672         if (wrapper->has_extents) {
673             *extents = wrapper->extents;
674             return _cairo_rectangle_intersect (extents, &clip);
675         } else {
676             *extents = clip;
677             return TRUE;
678         }
679     } else if (wrapper->has_extents) {
680         *extents = wrapper->extents;
681         return TRUE;
682     } else {
683         _cairo_unbounded_rectangle_init (extents);
684         return TRUE;
685     }
686 }