Changes.
[cairo/gpu.git] / src / cairo-meta-surface.c
blob9f60007a535ff42796019ce5e20abcaeb07dc806
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
4 * Copyright © 2005 Red Hat, Inc
5 * Copyright © 2007 Adrian Johnson
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * You should have received a copy of the MPL along with this library
19 * in the file COPYING-MPL-1.1
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/
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.
30 * The Original Code is the cairo graphics library.
32 * The Initial Developer of the Original Code is Red Hat, Inc.
34 * Contributor(s):
35 * Kristian Høgsberg <krh@redhat.com>
36 * Carl Worth <cworth@cworth.org>
37 * Adrian Johnson <ajohnson@redneon.com>
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:
45 * _cairo_meta_surface_replay (meta, target);
47 * after which the results in target will be identical to the results
48 * that would have been obtained if the original operations applied to
49 * the meta surface had instead been applied to the target surface.
51 * The recording phase of the meta surface is careful to snapshot all
52 * necessary objects (paths, patterns, etc.), in order to achieve
53 * accurate replay. The efficiency of the meta surface could be
54 * improved by improving the implementation of snapshot for the
55 * various objects. For example, it would be nice to have a
56 * copy-on-write implementation for _cairo_surface_snapshot.
59 #include "cairoint.h"
60 #include "cairo-meta-surface-private.h"
61 #include "cairo-clip-private.h"
63 typedef enum {
64 CAIRO_META_REPLAY,
65 CAIRO_META_CREATE_REGIONS
66 } cairo_meta_replay_type_t;
68 static const cairo_surface_backend_t cairo_meta_surface_backend;
70 /* Currently all meta surfaces do have a size which should be passed
71 * in as the maximum size of any target surface against which the
72 * meta-surface will ever be replayed.
74 * XXX: The naming of "pixels" in the size here is a misnomer. It's
75 * actually a size in whatever device-space units are desired (again,
76 * according to the intended replay target). This should likely also
77 * be changed to use doubles not ints.
79 cairo_surface_t *
80 _cairo_meta_surface_create (cairo_content_t content,
81 int width_pixels,
82 int height_pixels)
84 cairo_meta_surface_t *meta;
86 meta = malloc (sizeof (cairo_meta_surface_t));
87 if (unlikely (meta == NULL))
88 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
90 _cairo_surface_init (&meta->base, &cairo_meta_surface_backend,
91 content);
93 meta->content = content;
94 meta->width_pixels = width_pixels;
95 meta->height_pixels = height_pixels;
97 _cairo_array_init (&meta->commands, sizeof (cairo_command_t *));
98 meta->commands_owner = NULL;
100 meta->is_clipped = FALSE;
101 meta->replay_start_idx = 0;
103 return &meta->base;
106 static cairo_surface_t *
107 _cairo_meta_surface_create_similar (void *abstract_surface,
108 cairo_content_t content,
109 int width,
110 int height)
112 return _cairo_meta_surface_create (content, width, height);
115 static cairo_status_t
116 _cairo_meta_surface_finish (void *abstract_surface)
118 cairo_meta_surface_t *meta = abstract_surface;
119 cairo_command_t *command;
120 cairo_command_t **elements;
121 int i, num_elements;
123 if (meta->commands_owner) {
124 cairo_surface_destroy (meta->commands_owner);
125 return CAIRO_STATUS_SUCCESS;
128 num_elements = meta->commands.num_elements;
129 elements = _cairo_array_index (&meta->commands, 0);
130 for (i = 0; i < num_elements; i++) {
131 command = elements[i];
132 switch (command->header.type) {
134 /* 5 basic drawing operations */
136 case CAIRO_COMMAND_PAINT:
137 _cairo_pattern_fini_snapshot (&command->paint.source.base);
138 free (command);
139 break;
141 case CAIRO_COMMAND_MASK:
142 _cairo_pattern_fini_snapshot (&command->mask.source.base);
143 _cairo_pattern_fini_snapshot (&command->mask.mask.base);
144 free (command);
145 break;
147 case CAIRO_COMMAND_STROKE:
148 _cairo_pattern_fini_snapshot (&command->stroke.source.base);
149 _cairo_path_fixed_fini (&command->stroke.path);
150 _cairo_stroke_style_fini (&command->stroke.style);
151 free (command);
152 break;
154 case CAIRO_COMMAND_FILL:
155 _cairo_pattern_fini_snapshot (&command->fill.source.base);
156 _cairo_path_fixed_fini (&command->fill.path);
157 free (command);
158 break;
160 case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
161 _cairo_pattern_fini_snapshot (&command->show_text_glyphs.source.base);
162 free (command->show_text_glyphs.utf8);
163 free (command->show_text_glyphs.glyphs);
164 free (command->show_text_glyphs.clusters);
165 cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font);
166 free (command);
167 break;
169 /* Other junk. */
170 case CAIRO_COMMAND_INTERSECT_CLIP_PATH:
171 if (command->intersect_clip_path.path_pointer)
172 _cairo_path_fixed_fini (&command->intersect_clip_path.path);
173 free (command);
174 break;
176 default:
177 ASSERT_NOT_REACHED;
181 _cairo_array_fini (&meta->commands);
183 return CAIRO_STATUS_SUCCESS;
186 static cairo_status_t
187 _cairo_meta_surface_acquire_source_image (void *abstract_surface,
188 cairo_image_surface_t **image_out,
189 void **image_extra)
191 cairo_status_t status;
192 cairo_meta_surface_t *surface = abstract_surface;
193 cairo_surface_t *image;
195 image = _cairo_image_surface_create_with_content (surface->content,
196 surface->width_pixels,
197 surface->height_pixels);
199 status = _cairo_meta_surface_replay (&surface->base, image);
200 if (unlikely (status)) {
201 cairo_surface_destroy (image);
202 return status;
205 *image_out = (cairo_image_surface_t *) image;
206 *image_extra = NULL;
208 return status;
211 static void
212 _cairo_meta_surface_release_source_image (void *abstract_surface,
213 cairo_image_surface_t *image,
214 void *image_extra)
216 cairo_surface_destroy (&image->base);
219 static cairo_int_status_t
220 _cairo_meta_surface_paint (void *abstract_surface,
221 cairo_operator_t op,
222 const cairo_pattern_t *source,
223 cairo_rectangle_int_t *extents)
225 cairo_status_t status;
226 cairo_meta_surface_t *meta = abstract_surface;
227 cairo_command_paint_t *command;
229 command = malloc (sizeof (cairo_command_paint_t));
230 if (unlikely (command == NULL))
231 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
233 command->header.type = CAIRO_COMMAND_PAINT;
234 command->header.region = CAIRO_META_REGION_ALL;
235 command->header.extents.x = 0;
236 command->header.extents.y = 0;
237 command->header.extents.width = meta->width_pixels;
238 command->header.extents.height = meta->height_pixels;
239 command->op = op;
241 status = _cairo_pattern_init_snapshot (&command->source.base, source);
242 if (unlikely (status))
243 goto CLEANUP_COMMAND;
245 status = _cairo_array_append (&meta->commands, &command);
246 if (unlikely (status))
247 goto CLEANUP_SOURCE;
249 /* An optimisation that takes care to not replay what was done
250 * before surface is cleared. We don't erase recorded commands
251 * since we may have earlier snapshots of this surface. */
252 if (op == CAIRO_OPERATOR_CLEAR && !meta->is_clipped)
253 meta->replay_start_idx = meta->commands.num_elements;
255 return CAIRO_STATUS_SUCCESS;
257 CLEANUP_SOURCE:
258 _cairo_pattern_fini_snapshot (&command->source.base);
259 CLEANUP_COMMAND:
260 free (command);
261 return status;
264 static cairo_int_status_t
265 _cairo_meta_surface_mask (void *abstract_surface,
266 cairo_operator_t op,
267 const cairo_pattern_t *source,
268 const cairo_pattern_t *mask,
269 cairo_rectangle_int_t *extents)
271 cairo_status_t status;
272 cairo_meta_surface_t *meta = abstract_surface;
273 cairo_command_mask_t *command;
275 command = malloc (sizeof (cairo_command_mask_t));
276 if (unlikely (command == NULL))
277 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
279 command->header.type = CAIRO_COMMAND_MASK;
280 command->header.region = CAIRO_META_REGION_ALL;
281 command->header.extents.x = 0;
282 command->header.extents.y = 0;
283 command->header.extents.width = meta->width_pixels;
284 command->header.extents.height = meta->height_pixels;
285 command->op = op;
287 status = _cairo_pattern_init_snapshot (&command->source.base, source);
288 if (unlikely (status))
289 goto CLEANUP_COMMAND;
291 status = _cairo_pattern_init_snapshot (&command->mask.base, mask);
292 if (unlikely (status))
293 goto CLEANUP_SOURCE;
295 status = _cairo_array_append (&meta->commands, &command);
296 if (unlikely (status))
297 goto CLEANUP_MASK;
299 return CAIRO_STATUS_SUCCESS;
301 CLEANUP_MASK:
302 _cairo_pattern_fini_snapshot (&command->mask.base);
303 CLEANUP_SOURCE:
304 _cairo_pattern_fini_snapshot (&command->source.base);
305 CLEANUP_COMMAND:
306 free (command);
307 return status;
310 static cairo_int_status_t
311 _cairo_meta_surface_stroke (void *abstract_surface,
312 cairo_operator_t op,
313 const cairo_pattern_t *source,
314 cairo_path_fixed_t *path,
315 cairo_stroke_style_t *style,
316 cairo_matrix_t *ctm,
317 cairo_matrix_t *ctm_inverse,
318 double tolerance,
319 cairo_antialias_t antialias,
320 cairo_rectangle_int_t *extents)
322 cairo_status_t status;
323 cairo_meta_surface_t *meta = abstract_surface;
324 cairo_command_stroke_t *command;
326 command = malloc (sizeof (cairo_command_stroke_t));
327 if (unlikely (command == NULL))
328 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
330 command->header.type = CAIRO_COMMAND_STROKE;
331 command->header.region = CAIRO_META_REGION_ALL;
332 command->header.extents.x = 0;
333 command->header.extents.y = 0;
334 command->header.extents.width = meta->width_pixels;
335 command->header.extents.height = meta->height_pixels;
336 command->op = op;
338 status = _cairo_pattern_init_snapshot (&command->source.base, source);
339 if (unlikely (status))
340 goto CLEANUP_COMMAND;
342 status = _cairo_path_fixed_init_copy (&command->path, path);
343 if (unlikely (status))
344 goto CLEANUP_SOURCE;
346 status = _cairo_stroke_style_init_copy (&command->style, style);
347 if (unlikely (status))
348 goto CLEANUP_PATH;
350 command->ctm = *ctm;
351 command->ctm_inverse = *ctm_inverse;
352 command->tolerance = tolerance;
353 command->antialias = antialias;
355 status = _cairo_array_append (&meta->commands, &command);
356 if (unlikely (status))
357 goto CLEANUP_STYLE;
359 return CAIRO_STATUS_SUCCESS;
361 CLEANUP_STYLE:
362 _cairo_stroke_style_fini (&command->style);
363 CLEANUP_PATH:
364 _cairo_path_fixed_fini (&command->path);
365 CLEANUP_SOURCE:
366 _cairo_pattern_fini_snapshot (&command->source.base);
367 CLEANUP_COMMAND:
368 free (command);
369 return status;
372 static cairo_int_status_t
373 _cairo_meta_surface_fill (void *abstract_surface,
374 cairo_operator_t op,
375 const cairo_pattern_t *source,
376 cairo_path_fixed_t *path,
377 cairo_fill_rule_t fill_rule,
378 double tolerance,
379 cairo_antialias_t antialias,
380 cairo_rectangle_int_t *extents)
382 cairo_status_t status;
383 cairo_meta_surface_t *meta = abstract_surface;
384 cairo_command_fill_t *command;
386 command = malloc (sizeof (cairo_command_fill_t));
387 if (unlikely (command == NULL))
388 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
390 command->header.type = CAIRO_COMMAND_FILL;
391 command->header.region = CAIRO_META_REGION_ALL;
392 command->header.extents.x = 0;
393 command->header.extents.y = 0;
394 command->header.extents.width = meta->width_pixels;
395 command->header.extents.height = meta->height_pixels;
396 command->op = op;
398 status = _cairo_pattern_init_snapshot (&command->source.base, source);
399 if (unlikely (status))
400 goto CLEANUP_COMMAND;
402 status = _cairo_path_fixed_init_copy (&command->path, path);
403 if (unlikely (status))
404 goto CLEANUP_SOURCE;
406 command->fill_rule = fill_rule;
407 command->tolerance = tolerance;
408 command->antialias = antialias;
410 status = _cairo_array_append (&meta->commands, &command);
411 if (unlikely (status))
412 goto CLEANUP_PATH;
414 return CAIRO_STATUS_SUCCESS;
416 CLEANUP_PATH:
417 _cairo_path_fixed_fini (&command->path);
418 CLEANUP_SOURCE:
419 _cairo_pattern_fini_snapshot (&command->source.base);
420 CLEANUP_COMMAND:
421 free (command);
422 return status;
425 static cairo_bool_t
426 _cairo_meta_surface_has_show_text_glyphs (void *abstract_surface)
428 return TRUE;
431 static cairo_int_status_t
432 _cairo_meta_surface_show_text_glyphs (void *abstract_surface,
433 cairo_operator_t op,
434 const cairo_pattern_t *source,
435 const char *utf8,
436 int utf8_len,
437 cairo_glyph_t *glyphs,
438 int num_glyphs,
439 const cairo_text_cluster_t *clusters,
440 int num_clusters,
441 cairo_text_cluster_flags_t cluster_flags,
442 cairo_scaled_font_t *scaled_font,
443 cairo_rectangle_int_t *extents)
445 cairo_status_t status;
446 cairo_meta_surface_t *meta = abstract_surface;
447 cairo_command_show_text_glyphs_t *command;
449 command = malloc (sizeof (cairo_command_show_text_glyphs_t));
450 if (unlikely (command == NULL))
451 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
453 command->header.type = CAIRO_COMMAND_SHOW_TEXT_GLYPHS;
454 command->header.region = CAIRO_META_REGION_ALL;
455 command->header.extents.x = 0;
456 command->header.extents.y = 0;
457 command->header.extents.width = meta->width_pixels;
458 command->header.extents.height = meta->height_pixels;
459 command->op = op;
461 status = _cairo_pattern_init_snapshot (&command->source.base, source);
462 if (unlikely (status))
463 goto CLEANUP_COMMAND;
465 command->utf8 = NULL;
466 command->utf8_len = utf8_len;
467 command->glyphs = NULL;
468 command->num_glyphs = num_glyphs;
469 command->clusters = NULL;
470 command->num_clusters = num_clusters;
472 if (utf8_len) {
473 command->utf8 = malloc (utf8_len);
474 if (unlikely (command->utf8 == NULL)) {
475 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
476 goto CLEANUP_ARRAYS;
478 memcpy (command->utf8, utf8, utf8_len);
480 if (num_glyphs) {
481 command->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (glyphs[0]));
482 if (unlikely (command->glyphs == NULL)) {
483 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
484 goto CLEANUP_ARRAYS;
486 memcpy (command->glyphs, glyphs, sizeof (glyphs[0]) * num_glyphs);
488 if (num_clusters) {
489 command->clusters = _cairo_malloc_ab (num_clusters, sizeof (clusters[0]));
490 if (unlikely (command->clusters == NULL)) {
491 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
492 goto CLEANUP_ARRAYS;
494 memcpy (command->clusters, clusters, sizeof (clusters[0]) * num_clusters);
497 command->cluster_flags = cluster_flags;
499 command->scaled_font = cairo_scaled_font_reference (scaled_font);
501 status = _cairo_array_append (&meta->commands, &command);
502 if (unlikely (status))
503 goto CLEANUP_SCALED_FONT;
505 return CAIRO_STATUS_SUCCESS;
507 CLEANUP_SCALED_FONT:
508 cairo_scaled_font_destroy (command->scaled_font);
509 CLEANUP_ARRAYS:
510 free (command->utf8);
511 free (command->glyphs);
512 free (command->clusters);
514 _cairo_pattern_fini_snapshot (&command->source.base);
515 CLEANUP_COMMAND:
516 free (command);
517 return status;
521 * _cairo_meta_surface_snapshot
522 * @surface: a #cairo_surface_t which must be a meta surface
524 * Make an immutable copy of @surface. It is an error to call a
525 * surface-modifying function on the result of this function.
527 * The caller owns the return value and should call
528 * cairo_surface_destroy() when finished with it. This function will not
529 * return %NULL, but will return a nil surface instead.
531 * Return value: The snapshot surface.
533 static cairo_surface_t *
534 _cairo_meta_surface_snapshot (void *abstract_other)
536 cairo_meta_surface_t *other = abstract_other;
537 cairo_meta_surface_t *meta;
539 meta = malloc (sizeof (cairo_meta_surface_t));
540 if (unlikely (meta == NULL))
541 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
543 _cairo_surface_init (&meta->base, &cairo_meta_surface_backend,
544 other->base.content);
546 meta->width_pixels = other->width_pixels;
547 meta->height_pixels = other->height_pixels;
548 meta->replay_start_idx = other->replay_start_idx;
549 meta->content = other->content;
551 _cairo_array_init_snapshot (&meta->commands, &other->commands);
552 meta->commands_owner = cairo_surface_reference (&other->base);
554 return &meta->base;
557 static cairo_int_status_t
558 _cairo_meta_surface_intersect_clip_path (void *dst,
559 cairo_path_fixed_t *path,
560 cairo_fill_rule_t fill_rule,
561 double tolerance,
562 cairo_antialias_t antialias)
564 cairo_meta_surface_t *meta = dst;
565 cairo_command_intersect_clip_path_t *command;
566 cairo_status_t status;
568 command = malloc (sizeof (cairo_command_intersect_clip_path_t));
569 if (unlikely (command == NULL))
570 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
572 command->header.type = CAIRO_COMMAND_INTERSECT_CLIP_PATH;
573 command->header.region = CAIRO_META_REGION_ALL;
575 if (path) {
576 status = _cairo_path_fixed_init_copy (&command->path, path);
577 if (unlikely (status)) {
578 free (command);
579 return status;
581 command->path_pointer = &command->path;
582 meta->is_clipped = TRUE;
583 } else {
584 command->path_pointer = NULL;
585 meta->is_clipped = FALSE;
587 command->fill_rule = fill_rule;
588 command->tolerance = tolerance;
589 command->antialias = antialias;
591 status = _cairo_array_append (&meta->commands, &command);
592 if (unlikely (status)) {
593 if (path)
594 _cairo_path_fixed_fini (&command->path);
595 free (command);
596 return status;
599 return CAIRO_STATUS_SUCCESS;
602 /* Currently, we're using as the "size" of a meta surface the largest
603 * surface size against which the meta-surface is expected to be
604 * replayed, (as passed in to _cairo_meta_surface_create).
606 static cairo_int_status_t
607 _cairo_meta_surface_get_extents (void *abstract_surface,
608 cairo_rectangle_int_t *rectangle)
610 cairo_meta_surface_t *surface = abstract_surface;
612 if (surface->width_pixels == -1 && surface->height_pixels == -1)
613 return CAIRO_INT_STATUS_UNSUPPORTED;
615 rectangle->x = 0;
616 rectangle->y = 0;
617 rectangle->width = surface->width_pixels;
618 rectangle->height = surface->height_pixels;
620 return CAIRO_STATUS_SUCCESS;
624 * _cairo_surface_is_meta:
625 * @surface: a #cairo_surface_t
627 * Checks if a surface is a #cairo_meta_surface_t
629 * Return value: %TRUE if the surface is a meta surface
631 cairo_bool_t
632 _cairo_surface_is_meta (const cairo_surface_t *surface)
634 return surface->backend == &cairo_meta_surface_backend;
637 static const cairo_surface_backend_t cairo_meta_surface_backend = {
638 CAIRO_INTERNAL_SURFACE_TYPE_META,
639 _cairo_meta_surface_create_similar,
640 _cairo_meta_surface_finish,
641 _cairo_meta_surface_acquire_source_image,
642 _cairo_meta_surface_release_source_image,
643 NULL, /* acquire_dest_image */
644 NULL, /* release_dest_image */
645 NULL, /* clone_similar */
646 NULL, /* composite */
647 NULL, /* fill_rectangles */
648 NULL, /* composite_trapezoids */
649 NULL, /* create_span_renderer */
650 NULL, /* check_span_renderer */
651 NULL, /* copy_page */
652 NULL, /* show_page */
653 NULL, /* set_clip_region */
654 _cairo_meta_surface_intersect_clip_path,
655 _cairo_meta_surface_get_extents,
656 NULL, /* old_show_glyphs */
657 NULL, /* get_font_options */
658 NULL, /* flush */
659 NULL, /* mark_dirty_rectangle */
660 NULL, /* scaled_font_fini */
661 NULL, /* scaled_glyph_fini */
663 /* Here are the 5 basic drawing operations, (which are in some
664 * sense the only things that cairo_meta_surface should need to
665 * implement). However, we implement the more generic show_text_glyphs
666 * instead of show_glyphs. One or the other is eough. */
668 _cairo_meta_surface_paint,
669 _cairo_meta_surface_mask,
670 _cairo_meta_surface_stroke,
671 _cairo_meta_surface_fill,
672 NULL,
674 _cairo_meta_surface_snapshot,
676 NULL, /* is_similar */
677 NULL, /* reset */
678 NULL, /* fill_stroke */
679 NULL, /* create_solid_pattern_surface */
680 NULL, /* can_repaint_solid_pattern_surface */
682 _cairo_meta_surface_has_show_text_glyphs,
683 _cairo_meta_surface_show_text_glyphs
686 static cairo_path_fixed_t *
687 _cairo_command_get_path (cairo_command_t *command)
689 switch (command->header.type) {
690 case CAIRO_COMMAND_PAINT:
691 case CAIRO_COMMAND_MASK:
692 case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
693 return NULL;
694 case CAIRO_COMMAND_STROKE:
695 return &command->stroke.path;
696 case CAIRO_COMMAND_FILL:
697 return &command->fill.path;
698 case CAIRO_COMMAND_INTERSECT_CLIP_PATH:
699 return command->intersect_clip_path.path_pointer;
702 ASSERT_NOT_REACHED;
703 return NULL;
706 cairo_int_status_t
707 _cairo_meta_surface_get_path (cairo_surface_t *surface,
708 cairo_path_fixed_t *path)
710 cairo_meta_surface_t *meta;
711 cairo_command_t *command, **elements;
712 int i, num_elements;
713 cairo_int_status_t status;
715 if (surface->status)
716 return surface->status;
718 meta = (cairo_meta_surface_t *) surface;
719 status = CAIRO_STATUS_SUCCESS;
721 num_elements = meta->commands.num_elements;
722 elements = _cairo_array_index (&meta->commands, 0);
723 for (i = meta->replay_start_idx; i < num_elements; i++) {
724 command = elements[i];
726 switch (command->header.type) {
727 case CAIRO_COMMAND_PAINT:
728 case CAIRO_COMMAND_MASK:
729 case CAIRO_COMMAND_INTERSECT_CLIP_PATH:
730 status = CAIRO_INT_STATUS_UNSUPPORTED;
731 break;
733 case CAIRO_COMMAND_STROKE:
735 cairo_traps_t traps;
737 _cairo_traps_init (&traps);
739 /* XXX call cairo_stroke_to_path() when that is implemented */
740 status = _cairo_path_fixed_stroke_to_traps (&command->stroke.path,
741 &command->stroke.style,
742 &command->stroke.ctm,
743 &command->stroke.ctm_inverse,
744 command->stroke.tolerance,
745 &traps);
747 if (status == CAIRO_STATUS_SUCCESS)
748 status = _cairo_traps_path (&traps, path);
750 _cairo_traps_fini (&traps);
751 break;
753 case CAIRO_COMMAND_FILL:
755 status = _cairo_path_fixed_append (path, &command->fill.path, CAIRO_DIRECTION_FORWARD);
756 break;
758 case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
760 status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font,
761 command->show_text_glyphs.glyphs,
762 command->show_text_glyphs.num_glyphs,
763 path);
764 break;
767 default:
768 ASSERT_NOT_REACHED;
771 if (unlikely (status))
772 break;
775 return _cairo_surface_set_error (surface, status);
778 static cairo_status_t
779 _cairo_meta_surface_replay_internal (cairo_surface_t *surface,
780 cairo_surface_t *target,
781 cairo_meta_replay_type_t type,
782 cairo_meta_region_type_t region)
784 cairo_meta_surface_t *meta;
785 cairo_command_t *command, **elements;
786 int i, num_elements;
787 cairo_int_status_t status, status2;
788 cairo_clip_t clip, *old_clip;
789 cairo_bool_t has_device_transform = _cairo_surface_has_device_transform (target);
790 cairo_matrix_t *device_transform = &target->device_transform;
791 cairo_path_fixed_t path_copy, *dev_path;
793 if (surface->status)
794 return surface->status;
796 if (target->status)
797 return _cairo_surface_set_error (surface, target->status);
799 meta = (cairo_meta_surface_t *) surface;
800 status = CAIRO_STATUS_SUCCESS;
802 _cairo_clip_init (&clip, target);
803 old_clip = _cairo_surface_get_clip (target);
805 num_elements = meta->commands.num_elements;
806 elements = _cairo_array_index (&meta->commands, 0);
807 for (i = meta->replay_start_idx; i < num_elements; i++) {
808 command = elements[i];
810 if (type == CAIRO_META_REPLAY && region != CAIRO_META_REGION_ALL) {
811 if (command->header.region != region)
812 continue;
815 /* For all commands except intersect_clip_path, we have to
816 * ensure the current clip gets set on the surface. */
817 if (command->header.type != CAIRO_COMMAND_INTERSECT_CLIP_PATH) {
818 status = _cairo_surface_set_clip (target, &clip);
819 if (unlikely (status))
820 break;
823 dev_path = _cairo_command_get_path (command);
824 if (dev_path && has_device_transform) {
825 status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
826 if (unlikely (status))
827 break;
828 _cairo_path_fixed_transform (&path_copy, device_transform);
829 dev_path = &path_copy;
832 switch (command->header.type) {
833 case CAIRO_COMMAND_PAINT:
834 status = _cairo_surface_paint (target,
835 command->paint.op,
836 &command->paint.source.base, &command->header.extents);
837 break;
838 case CAIRO_COMMAND_MASK:
839 status = _cairo_surface_mask (target,
840 command->mask.op,
841 &command->mask.source.base,
842 &command->mask.mask.base, &command->header.extents);
843 break;
844 case CAIRO_COMMAND_STROKE:
846 cairo_matrix_t dev_ctm = command->stroke.ctm;
847 cairo_matrix_t dev_ctm_inverse = command->stroke.ctm_inverse;
849 if (has_device_transform) {
850 cairo_matrix_multiply (&dev_ctm, &dev_ctm, device_transform);
851 cairo_matrix_multiply (&dev_ctm_inverse,
852 &target->device_transform_inverse,
853 &dev_ctm_inverse);
856 status = _cairo_surface_stroke (target,
857 command->stroke.op,
858 &command->stroke.source.base,
859 dev_path,
860 &command->stroke.style,
861 &dev_ctm,
862 &dev_ctm_inverse,
863 command->stroke.tolerance,
864 command->stroke.antialias, &command->header.extents);
865 break;
867 case CAIRO_COMMAND_FILL:
869 cairo_command_t *stroke_command;
871 if (type != CAIRO_META_CREATE_REGIONS)
872 stroke_command = (i < num_elements - 1) ? elements[i + 1] : NULL;
873 else
874 stroke_command = NULL;
876 if (stroke_command != NULL &&
877 type == CAIRO_META_REPLAY && region != CAIRO_META_REGION_ALL)
879 if (stroke_command->header.region != region)
880 stroke_command = NULL;
882 if (stroke_command != NULL &&
883 stroke_command->header.type == CAIRO_COMMAND_STROKE &&
884 _cairo_path_fixed_is_equal (dev_path, _cairo_command_get_path (stroke_command))) {
885 cairo_matrix_t dev_ctm;
886 cairo_matrix_t dev_ctm_inverse;
888 dev_ctm = stroke_command->stroke.ctm;
889 dev_ctm_inverse = stroke_command->stroke.ctm_inverse;
891 if (has_device_transform) {
892 cairo_matrix_multiply (&dev_ctm, &dev_ctm, device_transform);
893 cairo_matrix_multiply (&dev_ctm_inverse,
894 &surface->device_transform_inverse,
895 &dev_ctm_inverse);
898 status = _cairo_surface_fill_stroke (target,
899 command->fill.op,
900 &command->fill.source.base,
901 command->fill.fill_rule,
902 command->fill.tolerance,
903 command->fill.antialias,
904 dev_path,
905 stroke_command->stroke.op,
906 &stroke_command->stroke.source.base,
907 &stroke_command->stroke.style,
908 &dev_ctm,
909 &dev_ctm_inverse,
910 stroke_command->stroke.tolerance,
911 stroke_command->stroke.antialias,
912 &stroke_command->header.extents);
913 i++;
914 } else
915 status = _cairo_surface_fill (target,
916 command->fill.op,
917 &command->fill.source.base,
918 dev_path,
919 command->fill.fill_rule,
920 command->fill.tolerance,
921 command->fill.antialias, &command->header.extents);
922 break;
924 case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
926 cairo_glyph_t *glyphs = command->show_text_glyphs.glyphs;
927 cairo_glyph_t *dev_glyphs;
928 int i, num_glyphs = command->show_text_glyphs.num_glyphs;
930 /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed
931 * to modify the glyph array that's passed in. We must always
932 * copy the array before handing it to the backend.
934 dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
935 if (unlikely (dev_glyphs == NULL)) {
936 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
937 break;
940 if (has_device_transform) {
941 for (i = 0; i < num_glyphs; i++) {
942 dev_glyphs[i] = glyphs[i];
943 cairo_matrix_transform_point (device_transform,
944 &dev_glyphs[i].x,
945 &dev_glyphs[i].y);
947 } else {
948 memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
951 status = _cairo_surface_show_text_glyphs (target,
952 command->show_text_glyphs.op,
953 &command->show_text_glyphs.source.base,
954 command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
955 dev_glyphs, num_glyphs,
956 command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
957 command->show_text_glyphs.cluster_flags,
958 command->show_text_glyphs.scaled_font, &command->header.extents);
960 free (dev_glyphs);
961 break;
963 case CAIRO_COMMAND_INTERSECT_CLIP_PATH:
964 /* XXX Meta surface clipping is broken and requires some
965 * cairo-gstate.c rewriting. Work around it for now. */
966 if (dev_path == NULL)
967 _cairo_clip_reset (&clip);
968 else
969 status = _cairo_clip_clip (&clip, dev_path,
970 command->intersect_clip_path.fill_rule,
971 command->intersect_clip_path.tolerance,
972 command->intersect_clip_path.antialias,
973 target);
974 break;
975 default:
976 ASSERT_NOT_REACHED;
979 if (dev_path == &path_copy)
980 _cairo_path_fixed_fini (&path_copy);
982 if (type == CAIRO_META_CREATE_REGIONS) {
983 if (status == CAIRO_STATUS_SUCCESS) {
984 command->header.region = CAIRO_META_REGION_NATIVE;
985 } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) {
986 command->header.region = CAIRO_META_REGION_IMAGE_FALLBACK;
987 status = CAIRO_STATUS_SUCCESS;
991 if (unlikely (status))
992 break;
995 _cairo_clip_reset (&clip);
996 status2 = _cairo_surface_set_clip (target, old_clip);
997 if (status == CAIRO_STATUS_SUCCESS)
998 status = status2;
1000 return _cairo_surface_set_error (surface, status);
1003 cairo_status_t
1004 _cairo_meta_surface_replay (cairo_surface_t *surface,
1005 cairo_surface_t *target)
1007 return _cairo_meta_surface_replay_internal (surface,
1008 target,
1009 CAIRO_META_REPLAY,
1010 CAIRO_META_REGION_ALL);
1013 /* Replay meta to surface. When the return status of each operation is
1014 * one of %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED, or
1015 * %CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY the status of each operation
1016 * will be stored in the meta surface. Any other status will abort the
1017 * replay and return the status.
1019 cairo_status_t
1020 _cairo_meta_surface_replay_and_create_regions (cairo_surface_t *surface,
1021 cairo_surface_t *target)
1023 return _cairo_meta_surface_replay_internal (surface,
1024 target,
1025 CAIRO_META_CREATE_REGIONS,
1026 CAIRO_META_REGION_ALL);
1029 cairo_status_t
1030 _cairo_meta_surface_replay_region (cairo_surface_t *surface,
1031 cairo_surface_t *target,
1032 cairo_meta_region_type_t region)
1034 return _cairo_meta_surface_replay_internal (surface,
1035 target,
1036 CAIRO_META_REPLAY,
1037 region);