Rev 14952 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14952 | fschmid | 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 © 2005 Red Hat, Inc |
||
5 | * Copyright © 2007 Adrian Johnson |
||
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 |
||
18122 | mrdocs | 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
14952 | fschmid | 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 | * Kristian Høgsberg <krh@redhat.com> |
||
36 | * Carl Worth <cworth@cworth.org> |
||
37 | * Adrian Johnson <ajohnson@redneon.com> |
||
38 | */ |
||
39 | |||
40 | /* A meta surface is a surface that records all drawing operations at |
||
41 | * the highest level of the surface backend interface, (that is, the |
||
42 | * level of paint, mask, stroke, fill, and show_text_glyphs). The meta |
||
43 | * surface can then be "replayed" against any target surface with: |
||
44 | * |
||
45 | * <informalexample><programlisting> |
||
46 | * cairo_meta_surface_replay (meta, target); |
||
47 | * </programlisting></informalexample> |
||
48 | * |
||
49 | * after which the results in target will be identical to the results |
||
50 | * that would have been obtained if the original operations applied to |
||
51 | * the meta surface had instead been applied to the target surface. |
||
52 | * |
||
53 | * A meta surface is logically unbounded, i.e. it has no implicit constraint |
||
54 | * on the size of the drawing surface. However, in practice this is rarely |
||
55 | * useful as you wish to replay against a particular target surface with |
||
56 | * known bounds. For this case, it is more efficient to specify the target |
||
57 | * extents to the meta surface upon creation. |
||
58 | * |
||
59 | * The recording phase of the meta surface is careful to snapshot all |
||
60 | * necessary objects (paths, patterns, etc.), in order to achieve |
||
61 | * accurate replay. The efficiency of the meta surface could be |
||
62 | * improved by improving the implementation of snapshot for the |
||
63 | * various objects. For example, it would be nice to have a |
||
64 | * copy-on-write implementation for _cairo_surface_snapshot. |
||
65 | */ |
||
66 | |||
67 | /* XXX Rename to recording surface */ |
||
68 | |||
69 | #include "cairoint.h" |
||
70 | #include "cairo-analysis-surface-private.h" |
||
71 | #include "cairo-meta-surface-private.h" |
||
72 | #include "cairo-clip-private.h" |
||
73 | #include "cairo-surface-wrapper-private.h" |
||
74 | |||
75 | typedef enum { |
||
76 | CAIRO_META_REPLAY, |
||
77 | CAIRO_META_CREATE_REGIONS |
||
78 | } cairo_meta_replay_type_t; |
||
79 | |||
80 | static const cairo_surface_backend_t cairo_meta_surface_backend; |
||
81 | |||
82 | /* Currently all meta surfaces do have a size which should be passed |
||
83 | * in as the maximum size of any target surface against which the |
||
84 | * meta-surface will ever be replayed. |
||
85 | * |
||
86 | * XXX: The naming of "pixels" in the size here is a misnomer. It's |
||
87 | * actually a size in whatever device-space units are desired (again, |
||
88 | * according to the intended replay target). |
||
89 | */ |
||
90 | |||
91 | /** |
||
92 | * cairo_meta_surface_create: |
||
93 | * @content: the content of the meta surface |
||
94 | * @extents_pixels: the extents to record in pixels, can be %NULL to record |
||
95 | * unbounded operations. |
||
96 | * |
||
97 | * Creates a meta-surface which can be used to record all drawing operations |
||
98 | * at the highest level (that is, the level of paint, mask, stroke, fill |
||
99 | * and show_text_glyphs). The meta surface can then be "replayed" against |
||
100 | * any target surface with: |
||
101 | * |
||
102 | * <informalexample><programlisting> |
||
103 | * cairo_meta_surface_replay (meta, target); |
||
104 | * </programlisting></informalexample> |
||
105 | * |
||
106 | * after which the results in target will be identical to the results |
||
107 | * that would have been obtained if the original operations applied to |
||
108 | * the meta surface had instead been applied to the target surface. |
||
109 | * |
||
110 | * The recording phase of the meta surface is careful to snapshot all |
||
111 | * necessary objects (paths, patterns, etc.), in order to achieve |
||
112 | * accurate replay. |
||
113 | * |
||
114 | * Since 1.10 |
||
115 | **/ |
||
116 | cairo_surface_t * |
||
117 | cairo_meta_surface_create (cairo_content_t content, |
||
118 | const cairo_rectangle_t *extents) |
||
119 | { |
||
120 | cairo_meta_surface_t *meta; |
||
121 | cairo_status_t status; |
||
122 | |||
123 | meta = malloc (sizeof (cairo_meta_surface_t)); |
||
124 | if (unlikely (meta == NULL)) |
||
125 | return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
||
126 | |||
127 | _cairo_surface_init (&meta->base, &cairo_meta_surface_backend, content); |
||
128 | |||
129 | meta->content = content; |
||
130 | |||
131 | /* unbounded -> 'infinite' extents */ |
||
132 | if (extents != NULL) { |
||
133 | meta->extents_pixels = *extents; |
||
134 | |||
135 | /* XXX check for overflow */ |
||
136 | meta->extents.x = floor (extents->x); |
||
137 | meta->extents.y = floor (extents->y); |
||
138 | meta->extents.width = ceil (extents->x + extents->width) - meta->extents.x; |
||
139 | meta->extents.height = ceil (extents->y + extents->height) - meta->extents.y; |
||
140 | |||
141 | status = _cairo_clip_init_rectangle (&meta->clip, &meta->extents); |
||
142 | if (unlikely (status)) { |
||
143 | free (meta); |
||
144 | return _cairo_surface_create_in_error (status); |
||
145 | } |
||
146 | |||
147 | meta->unbounded = FALSE; |
||
148 | } else { |
||
149 | meta->unbounded = TRUE; |
||
150 | _cairo_clip_init (&meta->clip); |
||
151 | } |
||
152 | |||
153 | _cairo_array_init (&meta->commands, sizeof (cairo_command_t *)); |
||
154 | meta->commands_owner = NULL; |
||
155 | |||
156 | meta->replay_start_idx = 0; |
||
157 | |||
158 | return &meta->base; |
||
159 | } |
||
160 | slim_hidden_def (cairo_meta_surface_create); |
||
161 | |||
162 | static cairo_surface_t * |
||
163 | _cairo_meta_surface_create_similar (void *abstract_surface, |
||
164 | cairo_content_t content, |
||
165 | int width, |
||
166 | int height) |
||
167 | { |
||
168 | cairo_rectangle_t extents; |
||
169 | extents.x = extents.y = 0; |
||
170 | extents.width = width; |
||
171 | extents.height = height; |
||
172 | return cairo_meta_surface_create (content, &extents); |
||
173 | } |
||
174 | |||
175 | static cairo_status_t |
||
176 | _cairo_meta_surface_finish (void *abstract_surface) |
||
177 | { |
||
178 | cairo_meta_surface_t *meta = abstract_surface; |
||
179 | cairo_command_t **elements; |
||
180 | int i, num_elements; |
||
181 | |||
182 | if (meta->commands_owner) { |
||
183 | cairo_surface_destroy (meta->commands_owner); |
||
184 | return CAIRO_STATUS_SUCCESS; |
||
185 | } |
||
186 | |||
187 | num_elements = meta->commands.num_elements; |
||
188 | elements = _cairo_array_index (&meta->commands, 0); |
||
189 | for (i = 0; i < num_elements; i++) { |
||
190 | cairo_command_t *command = elements[i]; |
||
191 | |||
192 | _cairo_clip_reset (&command->header.clip); |
||
193 | |||
194 | switch (command->header.type) { |
||
195 | case CAIRO_COMMAND_PAINT: |
||
196 | _cairo_pattern_fini_snapshot (&command->paint.source.base); |
||
197 | free (command); |
||
198 | break; |
||
199 | |||
200 | case CAIRO_COMMAND_MASK: |
||
201 | _cairo_pattern_fini_snapshot (&command->mask.source.base); |
||
202 | _cairo_pattern_fini_snapshot (&command->mask.mask.base); |
||
203 | free (command); |
||
204 | break; |
||
205 | |||
206 | case CAIRO_COMMAND_STROKE: |
||
207 | _cairo_pattern_fini_snapshot (&command->stroke.source.base); |
||
208 | _cairo_path_fixed_fini (&command->stroke.path); |
||
209 | _cairo_stroke_style_fini (&command->stroke.style); |
||
210 | free (command); |
||
211 | break; |
||
212 | |||
213 | case CAIRO_COMMAND_FILL: |
||
214 | _cairo_pattern_fini_snapshot (&command->fill.source.base); |
||
215 | _cairo_path_fixed_fini (&command->fill.path); |
||
216 | free (command); |
||
217 | break; |
||
218 | |||
219 | case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: |
||
220 | _cairo_pattern_fini_snapshot (&command->show_text_glyphs.source.base); |
||
221 | free (command->show_text_glyphs.utf8); |
||
222 | free (command->show_text_glyphs.glyphs); |
||
223 | free (command->show_text_glyphs.clusters); |
||
224 | cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font); |
||
225 | free (command); |
||
226 | break; |
||
227 | |||
228 | default: |
||
229 | ASSERT_NOT_REACHED; |
||
230 | } |
||
231 | } |
||
232 | |||
233 | _cairo_array_fini (&meta->commands); |
||
234 | _cairo_clip_reset (&meta->clip); |
||
235 | |||
236 | return CAIRO_STATUS_SUCCESS; |
||
237 | } |
||
238 | |||
239 | static cairo_status_t |
||
240 | _cairo_meta_surface_acquire_source_image (void *abstract_surface, |
||
241 | cairo_image_surface_t **image_out, |
||
242 | void **image_extra) |
||
243 | { |
||
244 | cairo_status_t status; |
||
245 | cairo_meta_surface_t *surface = abstract_surface; |
||
246 | cairo_surface_t *image; |
||
247 | |||
248 | image = _cairo_surface_has_snapshot (&surface->base, |
||
249 | &_cairo_image_surface_backend, |
||
250 | surface->content); |
||
251 | if (image != NULL) { |
||
252 | *image_out = (cairo_image_surface_t *) cairo_surface_reference (image); |
||
253 | *image_extra = NULL; |
||
254 | return CAIRO_STATUS_SUCCESS; |
||
255 | } |
||
256 | |||
257 | image = _cairo_image_surface_create_with_content (surface->content, |
||
258 | surface->extents.width, |
||
259 | surface->extents.height); |
||
260 | if (unlikely (image->status)) |
||
261 | return image->status; |
||
262 | |||
263 | cairo_surface_set_device_offset (image, |
||
264 | -surface->extents.x, |
||
265 | -surface->extents.y); |
||
266 | |||
267 | status = cairo_meta_surface_replay (&surface->base, image); |
||
268 | if (unlikely (status)) { |
||
269 | cairo_surface_destroy (image); |
||
270 | return status; |
||
271 | } |
||
272 | |||
273 | status = _cairo_surface_attach_snapshot (&surface->base, image, NULL); |
||
274 | if (unlikely (status)) { |
||
275 | cairo_surface_destroy (image); |
||
276 | return status; |
||
277 | } |
||
278 | |||
279 | *image_out = (cairo_image_surface_t *) image; |
||
280 | *image_extra = NULL; |
||
281 | return CAIRO_STATUS_SUCCESS; |
||
282 | } |
||
283 | |||
284 | static void |
||
285 | _cairo_meta_surface_release_source_image (void *abstract_surface, |
||
286 | cairo_image_surface_t *image, |
||
287 | void *image_extra) |
||
288 | { |
||
289 | cairo_surface_destroy (&image->base); |
||
290 | } |
||
291 | |||
292 | static cairo_status_t |
||
293 | _command_init (cairo_meta_surface_t *meta, |
||
294 | cairo_command_header_t *command, |
||
295 | cairo_command_type_t type, |
||
296 | cairo_operator_t op, |
||
297 | cairo_clip_t *clip) |
||
298 | { |
||
299 | cairo_status_t status = CAIRO_STATUS_SUCCESS; |
||
300 | |||
301 | command->type = type; |
||
302 | command->op = op; |
||
303 | command->region = CAIRO_META_REGION_ALL; |
||
304 | _cairo_clip_init_copy (&command->clip, clip); |
||
305 | if (meta->clip.path != NULL) |
||
306 | status = _cairo_clip_apply_clip (&command->clip, &meta->clip); |
||
307 | |||
308 | return status; |
||
309 | } |
||
310 | |||
311 | static cairo_int_status_t |
||
312 | _cairo_meta_surface_paint (void *abstract_surface, |
||
313 | cairo_operator_t op, |
||
314 | const cairo_pattern_t *source, |
||
315 | cairo_clip_t *clip) |
||
316 | { |
||
317 | cairo_status_t status; |
||
318 | cairo_meta_surface_t *meta = abstract_surface; |
||
319 | cairo_command_paint_t *command; |
||
320 | |||
321 | command = malloc (sizeof (cairo_command_paint_t)); |
||
322 | if (unlikely (command == NULL)) |
||
323 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
324 | |||
325 | status = _command_init (meta, |
||
326 | &command->header, CAIRO_COMMAND_PAINT, op, clip); |
||
327 | if (unlikely (status)) |
||
328 | goto CLEANUP_COMMAND; |
||
329 | |||
330 | status = _cairo_pattern_init_snapshot (&command->source.base, source); |
||
331 | if (unlikely (status)) |
||
332 | goto CLEANUP_COMMAND; |
||
333 | |||
334 | status = _cairo_array_append (&meta->commands, &command); |
||
335 | if (unlikely (status)) |
||
336 | goto CLEANUP_SOURCE; |
||
337 | |||
338 | /* An optimisation that takes care to not replay what was done |
||
339 | * before surface is cleared. We don't erase recorded commands |
||
340 | * since we may have earlier snapshots of this surface. */ |
||
341 | if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) |
||
342 | meta->replay_start_idx = meta->commands.num_elements; |
||
343 | |||
344 | return CAIRO_STATUS_SUCCESS; |
||
345 | |||
346 | CLEANUP_SOURCE: |
||
347 | _cairo_pattern_fini_snapshot (&command->source.base); |
||
348 | CLEANUP_COMMAND: |
||
349 | free (command); |
||
350 | return status; |
||
351 | } |
||
352 | |||
353 | static cairo_int_status_t |
||
354 | _cairo_meta_surface_mask (void *abstract_surface, |
||
355 | cairo_operator_t op, |
||
356 | const cairo_pattern_t *source, |
||
357 | const cairo_pattern_t *mask, |
||
358 | cairo_clip_t *clip) |
||
359 | { |
||
360 | cairo_status_t status; |
||
361 | cairo_meta_surface_t *meta = abstract_surface; |
||
362 | cairo_command_mask_t *command; |
||
363 | |||
364 | command = malloc (sizeof (cairo_command_mask_t)); |
||
365 | if (unlikely (command == NULL)) |
||
366 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
367 | |||
368 | status = _command_init (meta, |
||
369 | &command->header, CAIRO_COMMAND_MASK, op, clip); |
||
370 | if (unlikely (status)) |
||
371 | goto CLEANUP_COMMAND; |
||
372 | |||
373 | status = _cairo_pattern_init_snapshot (&command->source.base, source); |
||
374 | if (unlikely (status)) |
||
375 | goto CLEANUP_COMMAND; |
||
376 | |||
377 | status = _cairo_pattern_init_snapshot (&command->mask.base, mask); |
||
378 | if (unlikely (status)) |
||
379 | goto CLEANUP_SOURCE; |
||
380 | |||
381 | status = _cairo_array_append (&meta->commands, &command); |
||
382 | if (unlikely (status)) |
||
383 | goto CLEANUP_MASK; |
||
384 | |||
385 | return CAIRO_STATUS_SUCCESS; |
||
386 | |||
387 | CLEANUP_MASK: |
||
388 | _cairo_pattern_fini_snapshot (&command->mask.base); |
||
389 | CLEANUP_SOURCE: |
||
390 | _cairo_pattern_fini_snapshot (&command->source.base); |
||
391 | CLEANUP_COMMAND: |
||
392 | free (command); |
||
393 | return status; |
||
394 | } |
||
395 | |||
396 | static cairo_int_status_t |
||
397 | _cairo_meta_surface_stroke (void *abstract_surface, |
||
398 | cairo_operator_t op, |
||
399 | const cairo_pattern_t *source, |
||
400 | cairo_path_fixed_t *path, |
||
401 | cairo_stroke_style_t *style, |
||
402 | cairo_matrix_t *ctm, |
||
403 | cairo_matrix_t *ctm_inverse, |
||
404 | double tolerance, |
||
405 | cairo_antialias_t antialias, |
||
406 | cairo_clip_t *clip) |
||
407 | { |
||
408 | cairo_status_t status; |
||
409 | cairo_meta_surface_t *meta = abstract_surface; |
||
410 | cairo_command_stroke_t *command; |
||
411 | |||
412 | command = malloc (sizeof (cairo_command_stroke_t)); |
||
413 | if (unlikely (command == NULL)) |
||
414 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
415 | |||
416 | status = _command_init (meta, |
||
417 | &command->header, CAIRO_COMMAND_STROKE, op, clip); |
||
418 | if (unlikely (status)) |
||
419 | goto CLEANUP_COMMAND; |
||
420 | |||
421 | status = _cairo_pattern_init_snapshot (&command->source.base, source); |
||
422 | if (unlikely (status)) |
||
423 | goto CLEANUP_COMMAND; |
||
424 | |||
425 | status = _cairo_path_fixed_init_copy (&command->path, path); |
||
426 | if (unlikely (status)) |
||
427 | goto CLEANUP_SOURCE; |
||
428 | |||
429 | status = _cairo_stroke_style_init_copy (&command->style, style); |
||
430 | if (unlikely (status)) |
||
431 | goto CLEANUP_PATH; |
||
432 | |||
433 | command->ctm = *ctm; |
||
434 | command->ctm_inverse = *ctm_inverse; |
||
435 | command->tolerance = tolerance; |
||
436 | command->antialias = antialias; |
||
437 | |||
438 | status = _cairo_array_append (&meta->commands, &command); |
||
439 | if (unlikely (status)) |
||
440 | goto CLEANUP_STYLE; |
||
441 | |||
442 | return CAIRO_STATUS_SUCCESS; |
||
443 | |||
444 | CLEANUP_STYLE: |
||
445 | _cairo_stroke_style_fini (&command->style); |
||
446 | CLEANUP_PATH: |
||
447 | _cairo_path_fixed_fini (&command->path); |
||
448 | CLEANUP_SOURCE: |
||
449 | _cairo_pattern_fini_snapshot (&command->source.base); |
||
450 | CLEANUP_COMMAND: |
||
451 | free (command); |
||
452 | return status; |
||
453 | } |
||
454 | |||
455 | static cairo_int_status_t |
||
456 | _cairo_meta_surface_fill (void *abstract_surface, |
||
457 | cairo_operator_t op, |
||
458 | const cairo_pattern_t *source, |
||
459 | cairo_path_fixed_t *path, |
||
460 | cairo_fill_rule_t fill_rule, |
||
461 | double tolerance, |
||
462 | cairo_antialias_t antialias, |
||
463 | cairo_clip_t *clip) |
||
464 | { |
||
465 | cairo_status_t status; |
||
466 | cairo_meta_surface_t *meta = abstract_surface; |
||
467 | cairo_command_fill_t *command; |
||
468 | |||
469 | command = malloc (sizeof (cairo_command_fill_t)); |
||
470 | if (unlikely (command == NULL)) |
||
471 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
472 | |||
473 | status =_command_init (meta, |
||
474 | &command->header, CAIRO_COMMAND_FILL, op, clip); |
||
475 | if (unlikely (status)) |
||
476 | goto CLEANUP_COMMAND; |
||
477 | |||
478 | status = _cairo_pattern_init_snapshot (&command->source.base, source); |
||
479 | if (unlikely (status)) |
||
480 | goto CLEANUP_COMMAND; |
||
481 | |||
482 | status = _cairo_path_fixed_init_copy (&command->path, path); |
||
483 | if (unlikely (status)) |
||
484 | goto CLEANUP_SOURCE; |
||
485 | |||
486 | command->fill_rule = fill_rule; |
||
487 | command->tolerance = tolerance; |
||
488 | command->antialias = antialias; |
||
489 | |||
490 | status = _cairo_array_append (&meta->commands, &command); |
||
491 | if (unlikely (status)) |
||
492 | goto CLEANUP_PATH; |
||
493 | |||
494 | return CAIRO_STATUS_SUCCESS; |
||
495 | |||
496 | CLEANUP_PATH: |
||
497 | _cairo_path_fixed_fini (&command->path); |
||
498 | CLEANUP_SOURCE: |
||
499 | _cairo_pattern_fini_snapshot (&command->source.base); |
||
500 | CLEANUP_COMMAND: |
||
501 | free (command); |
||
502 | return status; |
||
503 | } |
||
504 | |||
505 | static cairo_bool_t |
||
506 | _cairo_meta_surface_has_show_text_glyphs (void *abstract_surface) |
||
507 | { |
||
508 | return TRUE; |
||
509 | } |
||
510 | |||
511 | static cairo_int_status_t |
||
512 | _cairo_meta_surface_show_text_glyphs (void *abstract_surface, |
||
513 | cairo_operator_t op, |
||
514 | const cairo_pattern_t *source, |
||
515 | const char *utf8, |
||
516 | int utf8_len, |
||
517 | cairo_glyph_t *glyphs, |
||
518 | int num_glyphs, |
||
519 | const cairo_text_cluster_t *clusters, |
||
520 | int num_clusters, |
||
521 | cairo_text_cluster_flags_t cluster_flags, |
||
522 | cairo_scaled_font_t *scaled_font, |
||
523 | cairo_clip_t *clip) |
||
524 | { |
||
525 | cairo_status_t status; |
||
526 | cairo_meta_surface_t *meta = abstract_surface; |
||
527 | cairo_command_show_text_glyphs_t *command; |
||
528 | |||
529 | command = malloc (sizeof (cairo_command_show_text_glyphs_t)); |
||
530 | if (unlikely (command == NULL)) |
||
531 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
532 | |||
533 | status = _command_init (meta, |
||
534 | &command->header, CAIRO_COMMAND_SHOW_TEXT_GLYPHS, |
||
535 | op, clip); |
||
536 | if (unlikely (status)) |
||
537 | goto CLEANUP_COMMAND; |
||
538 | |||
539 | status = _cairo_pattern_init_snapshot (&command->source.base, source); |
||
540 | if (unlikely (status)) |
||
541 | goto CLEANUP_COMMAND; |
||
542 | |||
543 | command->utf8 = NULL; |
||
544 | command->utf8_len = utf8_len; |
||
545 | command->glyphs = NULL; |
||
546 | command->num_glyphs = num_glyphs; |
||
547 | command->clusters = NULL; |
||
548 | command->num_clusters = num_clusters; |
||
549 | |||
550 | if (utf8_len) { |
||
551 | command->utf8 = malloc (utf8_len); |
||
552 | if (unlikely (command->utf8 == NULL)) { |
||
553 | status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
554 | goto CLEANUP_ARRAYS; |
||
555 | } |
||
556 | memcpy (command->utf8, utf8, utf8_len); |
||
557 | } |
||
558 | if (num_glyphs) { |
||
559 | command->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (glyphs[0])); |
||
560 | if (unlikely (command->glyphs == NULL)) { |
||
561 | status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
562 | goto CLEANUP_ARRAYS; |
||
563 | } |
||
564 | memcpy (command->glyphs, glyphs, sizeof (glyphs[0]) * num_glyphs); |
||
565 | } |
||
566 | if (num_clusters) { |
||
567 | command->clusters = _cairo_malloc_ab (num_clusters, sizeof (clusters[0])); |
||
568 | if (unlikely (command->clusters == NULL)) { |
||
569 | status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
570 | goto CLEANUP_ARRAYS; |
||
571 | } |
||
572 | memcpy (command->clusters, clusters, sizeof (clusters[0]) * num_clusters); |
||
573 | } |
||
574 | |||
575 | command->cluster_flags = cluster_flags; |
||
576 | |||
577 | command->scaled_font = cairo_scaled_font_reference (scaled_font); |
||
578 | |||
579 | status = _cairo_array_append (&meta->commands, &command); |
||
580 | if (unlikely (status)) |
||
581 | goto CLEANUP_SCALED_FONT; |
||
582 | |||
583 | return CAIRO_STATUS_SUCCESS; |
||
584 | |||
585 | CLEANUP_SCALED_FONT: |
||
586 | cairo_scaled_font_destroy (command->scaled_font); |
||
587 | CLEANUP_ARRAYS: |
||
588 | free (command->utf8); |
||
589 | free (command->glyphs); |
||
590 | free (command->clusters); |
||
591 | |||
592 | _cairo_pattern_fini_snapshot (&command->source.base); |
||
593 | CLEANUP_COMMAND: |
||
594 | free (command); |
||
595 | return status; |
||
596 | } |
||
597 | |||
598 | /** |
||
599 | * _cairo_meta_surface_snapshot |
||
600 | * @surface: a #cairo_surface_t which must be a meta surface |
||
601 | * |
||
602 | * Make an immutable copy of @surface. It is an error to call a |
||
603 | * surface-modifying function on the result of this function. |
||
604 | * |
||
605 | * The caller owns the return value and should call |
||
606 | * cairo_surface_destroy() when finished with it. This function will not |
||
607 | * return %NULL, but will return a nil surface instead. |
||
608 | * |
||
609 | * Return value: The snapshot surface. |
||
610 | **/ |
||
611 | static cairo_surface_t * |
||
612 | _cairo_meta_surface_snapshot (void *abstract_other) |
||
613 | { |
||
614 | cairo_meta_surface_t *other = abstract_other; |
||
615 | cairo_meta_surface_t *meta; |
||
616 | |||
617 | meta = malloc (sizeof (cairo_meta_surface_t)); |
||
618 | if (unlikely (meta == NULL)) |
||
619 | return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
||
620 | |||
621 | _cairo_surface_init (&meta->base, &cairo_meta_surface_backend, |
||
622 | other->base.content); |
||
623 | |||
624 | meta->extents_pixels = other->extents_pixels; |
||
625 | meta->extents = other->extents; |
||
626 | meta->unbounded = other->unbounded; |
||
627 | meta->replay_start_idx = other->replay_start_idx; |
||
628 | meta->content = other->content; |
||
629 | |||
630 | _cairo_array_init_snapshot (&meta->commands, &other->commands); |
||
631 | meta->commands_owner = cairo_surface_reference (&other->base); |
||
632 | |||
633 | _cairo_clip_init_copy (&meta->clip, &other->clip); |
||
634 | |||
635 | return &meta->base; |
||
636 | } |
||
637 | |||
638 | static cairo_bool_t |
||
639 | _cairo_meta_surface_get_extents (void *abstract_surface, |
||
640 | cairo_rectangle_int_t *rectangle) |
||
641 | { |
||
642 | cairo_meta_surface_t *surface = abstract_surface; |
||
643 | |||
644 | if (surface->unbounded) |
||
645 | return FALSE; |
||
646 | |||
647 | *rectangle = surface->extents; |
||
648 | return TRUE; |
||
649 | } |
||
650 | |||
651 | /** |
||
652 | * _cairo_surface_is_meta: |
||
653 | * @surface: a #cairo_surface_t |
||
654 | * |
||
655 | * Checks if a surface is a #cairo_meta_surface_t |
||
656 | * |
||
657 | * Return value: %TRUE if the surface is a meta surface |
||
658 | **/ |
||
659 | cairo_bool_t |
||
660 | _cairo_surface_is_meta (const cairo_surface_t *surface) |
||
661 | { |
||
662 | return surface->backend == &cairo_meta_surface_backend; |
||
663 | } |
||
664 | |||
665 | static const cairo_surface_backend_t cairo_meta_surface_backend = { |
||
666 | CAIRO_SURFACE_TYPE_META, |
||
667 | _cairo_meta_surface_create_similar, |
||
668 | _cairo_meta_surface_finish, |
||
669 | _cairo_meta_surface_acquire_source_image, |
||
670 | _cairo_meta_surface_release_source_image, |
||
671 | NULL, /* acquire_dest_image */ |
||
672 | NULL, /* release_dest_image */ |
||
673 | NULL, /* clone_similar */ |
||
674 | NULL, /* composite */ |
||
675 | NULL, /* fill_rectangles */ |
||
676 | NULL, /* composite_trapezoids */ |
||
677 | NULL, /* create_span_renderer */ |
||
678 | NULL, /* check_span_renderer */ |
||
679 | NULL, /* copy_page */ |
||
680 | NULL, /* show_page */ |
||
681 | _cairo_meta_surface_get_extents, |
||
682 | NULL, /* old_show_glyphs */ |
||
683 | NULL, /* get_font_options */ |
||
684 | NULL, /* flush */ |
||
685 | NULL, /* mark_dirty_rectangle */ |
||
686 | NULL, /* scaled_font_fini */ |
||
687 | NULL, /* scaled_glyph_fini */ |
||
688 | |||
689 | /* Here are the 5 basic drawing operations, (which are in some |
||
690 | * sense the only things that cairo_meta_surface should need to |
||
691 | * implement). However, we implement the more generic show_text_glyphs |
||
692 | * instead of show_glyphs. One or the other is eough. */ |
||
693 | |||
694 | _cairo_meta_surface_paint, |
||
695 | _cairo_meta_surface_mask, |
||
696 | _cairo_meta_surface_stroke, |
||
697 | _cairo_meta_surface_fill, |
||
698 | NULL, |
||
699 | |||
700 | _cairo_meta_surface_snapshot, |
||
701 | |||
702 | NULL, /* is_similar */ |
||
703 | NULL, /* fill_stroke */ |
||
704 | NULL, /* create_solid_pattern_surface */ |
||
705 | NULL, /* can_repaint_solid_pattern_surface */ |
||
706 | |||
707 | _cairo_meta_surface_has_show_text_glyphs, |
||
708 | _cairo_meta_surface_show_text_glyphs |
||
709 | }; |
||
710 | |||
711 | cairo_int_status_t |
||
712 | _cairo_meta_surface_get_path (cairo_surface_t *surface, |
||
713 | cairo_path_fixed_t *path) |
||
714 | { |
||
715 | cairo_meta_surface_t *meta; |
||
716 | cairo_command_t **elements; |
||
717 | int i, num_elements; |
||
718 | cairo_int_status_t status; |
||
719 | |||
720 | if (surface->status) |
||
721 | return surface->status; |
||
722 | |||
723 | meta = (cairo_meta_surface_t *) surface; |
||
724 | status = CAIRO_STATUS_SUCCESS; |
||
725 | |||
726 | num_elements = meta->commands.num_elements; |
||
727 | elements = _cairo_array_index (&meta->commands, 0); |
||
728 | for (i = meta->replay_start_idx; i < num_elements; i++) { |
||
729 | cairo_command_t *command = elements[i]; |
||
730 | |||
731 | switch (command->header.type) { |
||
732 | case CAIRO_COMMAND_PAINT: |
||
733 | case CAIRO_COMMAND_MASK: |
||
734 | status = CAIRO_INT_STATUS_UNSUPPORTED; |
||
735 | break; |
||
736 | |||
737 | case CAIRO_COMMAND_STROKE: |
||
738 | { |
||
739 | cairo_traps_t traps; |
||
740 | |||
741 | _cairo_traps_init (&traps); |
||
742 | |||
743 | /* XXX call cairo_stroke_to_path() when that is implemented */ |
||
744 | status = _cairo_path_fixed_stroke_to_traps (&command->stroke.path, |
||
745 | &command->stroke.style, |
||
746 | &command->stroke.ctm, |
||
747 | &command->stroke.ctm_inverse, |
||
748 | command->stroke.tolerance, |
||
749 | &traps); |
||
750 | |||
751 | if (status == CAIRO_STATUS_SUCCESS) |
||
752 | status = _cairo_traps_path (&traps, path); |
||
753 | |||
754 | _cairo_traps_fini (&traps); |
||
755 | break; |
||
756 | } |
||
757 | case CAIRO_COMMAND_FILL: |
||
758 | { |
||
759 | status = _cairo_path_fixed_append (path, |
||
760 | &command->fill.path, CAIRO_DIRECTION_FORWARD, |
||
761 | 0, 0); |
||
762 | break; |
||
763 | } |
||
764 | case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: |
||
765 | { |
||
766 | status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font, |
||
767 | command->show_text_glyphs.glyphs, |
||
768 | command->show_text_glyphs.num_glyphs, |
||
769 | path); |
||
770 | break; |
||
771 | } |
||
772 | |||
773 | default: |
||
774 | ASSERT_NOT_REACHED; |
||
775 | } |
||
776 | |||
777 | if (unlikely (status)) |
||
778 | break; |
||
779 | } |
||
780 | |||
781 | return _cairo_surface_set_error (surface, status); |
||
782 | } |
||
783 | |||
784 | #define _clip(c) ((c)->header.clip.path ? &(c)->header.clip : NULL) |
||
785 | static cairo_status_t |
||
786 | _cairo_meta_surface_replay_internal (cairo_surface_t *surface, |
||
787 | cairo_surface_t *target, |
||
788 | cairo_meta_replay_type_t type, |
||
789 | cairo_meta_region_type_t region) |
||
790 | { |
||
791 | cairo_meta_surface_t *meta; |
||
792 | cairo_command_t **elements; |
||
793 | int i, num_elements; |
||
794 | cairo_int_status_t status; |
||
795 | cairo_surface_wrapper_t wrapper; |
||
796 | |||
797 | if (unlikely (surface->status)) |
||
798 | return surface->status; |
||
799 | |||
800 | if (unlikely (target->status)) |
||
801 | return _cairo_surface_set_error (surface, target->status); |
||
802 | |||
803 | _cairo_surface_wrapper_init (&wrapper, target); |
||
804 | |||
805 | meta = (cairo_meta_surface_t *) surface; |
||
806 | status = CAIRO_STATUS_SUCCESS; |
||
807 | |||
808 | num_elements = meta->commands.num_elements; |
||
809 | elements = _cairo_array_index (&meta->commands, 0); |
||
810 | for (i = meta->replay_start_idx; i < num_elements; i++) { |
||
811 | cairo_command_t *command = elements[i]; |
||
812 | |||
813 | if (type == CAIRO_META_REPLAY && region != CAIRO_META_REGION_ALL) { |
||
814 | if (command->header.region != region) |
||
815 | continue; |
||
816 | } |
||
817 | |||
818 | switch (command->header.type) { |
||
819 | case CAIRO_COMMAND_PAINT: |
||
820 | status = _cairo_surface_wrapper_paint (&wrapper, |
||
821 | command->header.op, |
||
822 | &command->paint.source.base, |
||
823 | _clip (command)); |
||
824 | break; |
||
825 | |||
826 | case CAIRO_COMMAND_MASK: |
||
827 | status = _cairo_surface_wrapper_mask (&wrapper, |
||
828 | command->header.op, |
||
829 | &command->mask.source.base, |
||
830 | &command->mask.mask.base, |
||
831 | _clip (command)); |
||
832 | break; |
||
833 | |||
834 | case CAIRO_COMMAND_STROKE: |
||
835 | { |
||
836 | status = _cairo_surface_wrapper_stroke (&wrapper, |
||
837 | command->header.op, |
||
838 | &command->stroke.source.base, |
||
839 | &command->stroke.path, |
||
840 | &command->stroke.style, |
||
841 | &command->stroke.ctm, |
||
842 | &command->stroke.ctm_inverse, |
||
843 | command->stroke.tolerance, |
||
844 | command->stroke.antialias, |
||
845 | _clip (command)); |
||
846 | break; |
||
847 | } |
||
848 | case CAIRO_COMMAND_FILL: |
||
849 | { |
||
850 | cairo_command_t *stroke_command; |
||
851 | |||
852 | stroke_command = NULL; |
||
853 | if (type != CAIRO_META_CREATE_REGIONS && i < num_elements - 1) |
||
854 | stroke_command = elements[i + 1]; |
||
855 | |||
856 | if (stroke_command != NULL && |
||
857 | type == CAIRO_META_REPLAY && |
||
858 | region != CAIRO_META_REGION_ALL) |
||
859 | { |
||
860 | if (stroke_command->header.region != region) |
||
861 | stroke_command = NULL; |
||
862 | } |
||
863 | |||
864 | if (stroke_command != NULL && |
||
865 | stroke_command->header.type == CAIRO_COMMAND_STROKE && |
||
866 | _cairo_path_fixed_is_equal (&command->fill.path, |
||
867 | &stroke_command->stroke.path)) |
||
868 | { |
||
869 | status = _cairo_surface_wrapper_fill_stroke (&wrapper, |
||
870 | command->header.op, |
||
871 | &command->fill.source.base, |
||
872 | command->fill.fill_rule, |
||
873 | command->fill.tolerance, |
||
874 | command->fill.antialias, |
||
875 | &command->fill.path, |
||
876 | stroke_command->header.op, |
||
877 | &stroke_command->stroke.source.base, |
||
878 | &stroke_command->stroke.style, |
||
879 | &stroke_command->stroke.ctm, |
||
880 | &stroke_command->stroke.ctm_inverse, |
||
881 | stroke_command->stroke.tolerance, |
||
882 | stroke_command->stroke.antialias, |
||
883 | _clip (command)); |
||
884 | i++; |
||
885 | } |
||
886 | else |
||
887 | { |
||
888 | status = _cairo_surface_wrapper_fill (&wrapper, |
||
889 | command->header.op, |
||
890 | &command->fill.source.base, |
||
891 | &command->fill.path, |
||
892 | command->fill.fill_rule, |
||
893 | command->fill.tolerance, |
||
894 | command->fill.antialias, |
||
895 | _clip (command)); |
||
896 | } |
||
897 | break; |
||
898 | } |
||
899 | case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: |
||
900 | { |
||
901 | cairo_glyph_t *glyphs = command->show_text_glyphs.glyphs; |
||
902 | cairo_glyph_t *glyphs_copy; |
||
903 | int num_glyphs = command->show_text_glyphs.num_glyphs; |
||
904 | |||
905 | /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed |
||
906 | * to modify the glyph array that's passed in. We must always |
||
907 | * copy the array before handing it to the backend. |
||
908 | */ |
||
909 | glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); |
||
910 | if (unlikely (glyphs_copy == NULL)) { |
||
911 | status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
912 | break; |
||
913 | } |
||
914 | |||
915 | memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs); |
||
916 | |||
917 | status = _cairo_surface_wrapper_show_text_glyphs (&wrapper, |
||
918 | command->header.op, |
||
919 | &command->show_text_glyphs.source.base, |
||
920 | command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len, |
||
921 | glyphs_copy, num_glyphs, |
||
922 | command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters, |
||
923 | command->show_text_glyphs.cluster_flags, |
||
924 | command->show_text_glyphs.scaled_font, |
||
925 | _clip (command)); |
||
926 | free (glyphs_copy); |
||
927 | break; |
||
928 | } |
||
929 | default: |
||
930 | ASSERT_NOT_REACHED; |
||
931 | } |
||
932 | |||
933 | if (type == CAIRO_META_CREATE_REGIONS) { |
||
934 | if (status == CAIRO_STATUS_SUCCESS) { |
||
935 | command->header.region = CAIRO_META_REGION_NATIVE; |
||
936 | } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) { |
||
937 | command->header.region = CAIRO_META_REGION_IMAGE_FALLBACK; |
||
938 | status = CAIRO_STATUS_SUCCESS; |
||
939 | } else { |
||
940 | assert (_cairo_status_is_error (status)); |
||
941 | } |
||
942 | } |
||
943 | |||
944 | if (unlikely (status)) |
||
945 | break; |
||
946 | } |
||
947 | |||
948 | /* free up any caches */ |
||
949 | for (i = meta->replay_start_idx; i < num_elements; i++) { |
||
950 | cairo_command_t *command = elements[i]; |
||
951 | |||
952 | _cairo_clip_drop_cache (&command->header.clip); |
||
953 | } |
||
954 | |||
955 | _cairo_surface_wrapper_fini (&wrapper); |
||
956 | |||
957 | return _cairo_surface_set_error (surface, status); |
||
958 | } |
||
959 | |||
960 | /** |
||
961 | * cairo_meta_surface_replay: |
||
962 | * @surface: the #cairo_meta_surface_t |
||
963 | * @target: a target #cairo_surface_t onto which to replay the operations |
||
964 | * @width_pixels: width of the surface, in pixels |
||
965 | * @height_pixels: height of the surface, in pixels |
||
966 | * |
||
967 | * A meta surface can be "replayed" against any target surface, |
||
968 | * after which the results in target will be identical to the results |
||
969 | * that would have been obtained if the original operations applied to |
||
970 | * the meta surface had instead been applied to the target surface. |
||
971 | * |
||
972 | * Since 1.10 |
||
973 | **/ |
||
974 | cairo_status_t |
||
975 | cairo_meta_surface_replay (cairo_surface_t *surface, |
||
976 | cairo_surface_t *target) |
||
977 | { |
||
978 | return _cairo_meta_surface_replay_internal (surface, |
||
979 | target, |
||
980 | CAIRO_META_REPLAY, |
||
981 | CAIRO_META_REGION_ALL); |
||
982 | } |
||
983 | slim_hidden_def (cairo_meta_surface_replay); |
||
984 | |||
985 | /* Replay meta to surface. When the return status of each operation is |
||
986 | * one of %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED, or |
||
987 | * %CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY the status of each operation |
||
988 | * will be stored in the meta surface. Any other status will abort the |
||
989 | * replay and return the status. |
||
990 | */ |
||
991 | cairo_status_t |
||
992 | _cairo_meta_surface_replay_and_create_regions (cairo_surface_t *surface, |
||
993 | cairo_surface_t *target) |
||
994 | { |
||
995 | return _cairo_meta_surface_replay_internal (surface, |
||
996 | target, |
||
997 | CAIRO_META_CREATE_REGIONS, |
||
998 | CAIRO_META_REGION_ALL); |
||
999 | } |
||
1000 | |||
1001 | cairo_status_t |
||
1002 | _cairo_meta_surface_replay_region (cairo_surface_t *surface, |
||
1003 | cairo_surface_t *target, |
||
1004 | cairo_meta_region_type_t region) |
||
1005 | { |
||
1006 | return _cairo_meta_surface_replay_internal (surface, |
||
1007 | target, |
||
1008 | CAIRO_META_REPLAY, |
||
1009 | region); |
||
1010 | } |
||
1011 | |||
1012 | static cairo_status_t |
||
1013 | _meta_surface_get_ink_bbox (cairo_meta_surface_t *surface, |
||
1014 | cairo_box_t *bbox, |
||
1015 | const cairo_matrix_t *transform) |
||
1016 | { |
||
1017 | cairo_surface_t *null_surface; |
||
1018 | cairo_surface_t *analysis_surface; |
||
1019 | cairo_status_t status; |
||
1020 | |||
1021 | null_surface = _cairo_null_surface_create (surface->content); |
||
1022 | analysis_surface = _cairo_analysis_surface_create (null_surface); |
||
1023 | cairo_surface_destroy (null_surface); |
||
1024 | |||
1025 | status = analysis_surface->status; |
||
1026 | if (unlikely (status)) |
||
1027 | return status; |
||
1028 | |||
1029 | if (transform != NULL) |
||
1030 | _cairo_analysis_surface_set_ctm (analysis_surface, transform); |
||
1031 | |||
1032 | status = cairo_meta_surface_replay (&surface->base, analysis_surface); |
||
1033 | _cairo_analysis_surface_get_bounding_box (analysis_surface, bbox); |
||
1034 | cairo_surface_destroy (analysis_surface); |
||
1035 | |||
1036 | return status; |
||
1037 | } |
||
1038 | |||
1039 | /** |
||
1040 | * cairo_meta_surface_ink_extents: |
||
1041 | * @surface: a #cairo_meta_surface_t |
||
1042 | * @x0: the x-coordinate of the top-left of the ink bounding box |
||
1043 | * @y0: the y-coordinate of the top-left of the ink bounding box |
||
1044 | * @width: the width of the ink bounding box |
||
1045 | * @height: the height of the ink bounding box |
||
1046 | * |
||
1047 | * Measures the extents of the operations stored within the meta-surface. |
||
1048 | * This is useful to compute the required size of an image surface (or |
||
1049 | * equivalent) into which to replay the full sequence of drawing operations. |
||
1050 | * |
||
1051 | * Since: 1.10 |
||
1052 | **/ |
||
1053 | void |
||
1054 | cairo_meta_surface_ink_extents (cairo_surface_t *surface, |
||
1055 | double *x0, |
||
1056 | double *y0, |
||
1057 | double *width, |
||
1058 | double *height) |
||
1059 | { |
||
1060 | cairo_status_t status; |
||
1061 | cairo_box_t bbox; |
||
1062 | |||
1063 | memset (&bbox, 0, sizeof (bbox)); |
||
1064 | |||
1065 | if (! _cairo_surface_is_meta (surface)) { |
||
1066 | _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); |
||
1067 | goto DONE; |
||
1068 | } |
||
1069 | |||
1070 | status = _meta_surface_get_ink_bbox ((cairo_meta_surface_t *) surface, |
||
1071 | &bbox, |
||
1072 | NULL); |
||
1073 | if (unlikely (status)) |
||
1074 | status = _cairo_surface_set_error (surface, status); |
||
1075 | |||
1076 | DONE: |
||
1077 | if (x0) |
||
1078 | *x0 = _cairo_fixed_to_double (bbox.p1.x); |
||
1079 | if (y0) |
||
1080 | *y0 = _cairo_fixed_to_double (bbox.p1.y); |
||
1081 | if (width) |
||
1082 | *width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); |
||
1083 | if (height) |
||
1084 | *height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); |
||
1085 | } |
||
1086 | |||
1087 | cairo_status_t |
||
1088 | _cairo_meta_surface_get_bbox (cairo_meta_surface_t *surface, |
||
1089 | cairo_box_t *bbox, |
||
1090 | const cairo_matrix_t *transform) |
||
1091 | { |
||
1092 | if (! surface->unbounded) { |
||
1093 | _cairo_box_from_rectangle (bbox, &surface->extents); |
||
1094 | if (transform != NULL) |
||
1095 | _cairo_matrix_transform_bounding_box_fixed (transform, bbox, NULL); |
||
1096 | |||
1097 | return CAIRO_STATUS_SUCCESS; |
||
1098 | } |
||
1099 | |||
1100 | return _meta_surface_get_ink_bbox (surface, bbox, transform); |
||
1101 | } |