1 /* cairo - a vector graphics library with display and print output
3 * Copyright © 2005 Red Hat, Inc
4 * Copyright © 2007 Adrian Johnson
6 * This library is free software; you can redistribute it and/or
7 * modify it either under the terms of the GNU Lesser General Public
8 * License version 2.1 as published by the Free Software Foundation
9 * (the "LGPL") or, at your option, under the terms of the Mozilla
10 * Public License Version 1.1 (the "MPL"). If you do not alter this
11 * notice, a recipient may use your version of this file under either
12 * the MPL or the LGPL.
14 * You should have received a copy of the LGPL along with this library
15 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17 * You should have received a copy of the MPL along with this library
18 * in the file COPYING-MPL-1.1
20 * The contents of this file are subject to the Mozilla Public License
21 * Version 1.1 (the "License"); you may not use this file except in
22 * compliance with the License. You may obtain a copy of the License at
23 * http://www.mozilla.org/MPL/
25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27 * the specific language governing rights and limitations.
29 * The Original Code is the cairo graphics library.
31 * The Initial Developer of the Original Code is Red Hat, Inc.
34 * Carl Worth <cworth@cworth.org>
35 * Keith Packard <keithp@keithp.com>
36 * Adrian Johnson <ajohnson@redneon.com>
39 /* The paginated surface layer exists to provide as much code sharing
40 * as possible for the various paginated surface backends in cairo
41 * (PostScript, PDF, etc.). See cairo-paginated-private.h for
42 * more details on how it works and how to use it.
47 #include "cairo-paginated-private.h"
48 #include "cairo-paginated-surface-private.h"
49 #include "cairo-recording-surface-private.h"
50 #include "cairo-analysis-surface-private.h"
51 #include "cairo-error-private.h"
52 #include "cairo-image-surface-private.h"
53 #include "cairo-surface-subsurface-inline.h"
55 static const cairo_surface_backend_t cairo_paginated_surface_backend
;
57 static cairo_int_status_t
58 _cairo_paginated_surface_show_page (void *abstract_surface
);
60 static cairo_surface_t
*
61 _cairo_paginated_surface_create_similar (void *abstract_surface
,
62 cairo_content_t content
,
66 cairo_rectangle_t rect
;
70 return cairo_recording_surface_create (content
, &rect
);
73 static cairo_surface_t
*
74 _create_recording_surface_for_target (cairo_surface_t
*target
,
75 cairo_content_t content
)
77 cairo_rectangle_int_t rect
;
79 if (_cairo_surface_get_extents (target
, &rect
)) {
80 cairo_rectangle_t recording_extents
;
82 recording_extents
.x
= rect
.x
;
83 recording_extents
.y
= rect
.y
;
84 recording_extents
.width
= rect
.width
;
85 recording_extents
.height
= rect
.height
;
87 return cairo_recording_surface_create (content
, &recording_extents
);
89 return cairo_recording_surface_create (content
, NULL
);
94 _cairo_paginated_surface_create (cairo_surface_t
*target
,
95 cairo_content_t content
,
96 const cairo_paginated_surface_backend_t
*backend
)
98 cairo_paginated_surface_t
*surface
;
99 cairo_status_t status
;
101 surface
= malloc (sizeof (cairo_paginated_surface_t
));
102 if (unlikely (surface
== NULL
)) {
103 status
= _cairo_error (CAIRO_STATUS_NO_MEMORY
);
107 _cairo_surface_init (&surface
->base
,
108 &cairo_paginated_surface_backend
,
112 /* Override surface->base.type with target's type so we don't leak
113 * evidence of the paginated wrapper out to the user. */
114 surface
->base
.type
= target
->type
;
116 surface
->target
= cairo_surface_reference (target
);
118 surface
->content
= content
;
119 surface
->backend
= backend
;
121 surface
->recording_surface
= _create_recording_surface_for_target (target
, content
);
122 status
= surface
->recording_surface
->status
;
123 if (unlikely (status
))
124 goto FAIL_CLEANUP_SURFACE
;
126 surface
->page_num
= 1;
127 surface
->base
.is_clear
= TRUE
;
129 return &surface
->base
;
131 FAIL_CLEANUP_SURFACE
:
132 cairo_surface_destroy (target
);
135 return _cairo_surface_create_in_error (status
);
139 _cairo_surface_is_paginated (cairo_surface_t
*surface
)
141 return surface
->backend
== &cairo_paginated_surface_backend
;
145 _cairo_paginated_surface_get_target (cairo_surface_t
*surface
)
147 cairo_paginated_surface_t
*paginated_surface
;
149 assert (_cairo_surface_is_paginated (surface
));
151 paginated_surface
= (cairo_paginated_surface_t
*) surface
;
152 return paginated_surface
->target
;
156 _cairo_paginated_surface_get_recording (cairo_surface_t
*surface
)
158 cairo_paginated_surface_t
*paginated_surface
;
160 assert (_cairo_surface_is_paginated (surface
));
162 paginated_surface
= (cairo_paginated_surface_t
*) surface
;
163 return paginated_surface
->recording_surface
;
167 _cairo_paginated_surface_set_size (cairo_surface_t
*surface
,
171 cairo_paginated_surface_t
*paginated_surface
;
172 cairo_status_t status
;
173 cairo_rectangle_t recording_extents
;
175 assert (_cairo_surface_is_paginated (surface
));
177 paginated_surface
= (cairo_paginated_surface_t
*) surface
;
179 recording_extents
.x
= 0;
180 recording_extents
.y
= 0;
181 recording_extents
.width
= width
;
182 recording_extents
.height
= height
;
184 cairo_surface_destroy (paginated_surface
->recording_surface
);
185 paginated_surface
->recording_surface
= cairo_recording_surface_create (paginated_surface
->content
,
187 status
= paginated_surface
->recording_surface
->status
;
188 if (unlikely (status
))
189 return _cairo_surface_set_error (surface
, status
);
191 return CAIRO_STATUS_SUCCESS
;
194 static cairo_status_t
195 _cairo_paginated_surface_finish (void *abstract_surface
)
197 cairo_paginated_surface_t
*surface
= abstract_surface
;
198 cairo_status_t status
= CAIRO_STATUS_SUCCESS
;
200 if (! surface
->base
.is_clear
|| surface
->page_num
== 1) {
201 /* Bypass some of the sanity checking in cairo-surface.c, as we
202 * know that the surface is finished...
204 status
= _cairo_paginated_surface_show_page (surface
);
207 /* XXX We want to propagate any errors from destroy(), but those are not
208 * returned via the api. So we need to explicitly finish the target,
209 * and check the status afterwards. However, we can only call finish()
210 * on the target, if we own it.
212 if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface
->target
->ref_count
) == 1)
213 cairo_surface_finish (surface
->target
);
214 if (status
== CAIRO_STATUS_SUCCESS
)
215 status
= cairo_surface_status (surface
->target
);
216 cairo_surface_destroy (surface
->target
);
218 cairo_surface_finish (surface
->recording_surface
);
219 if (status
== CAIRO_STATUS_SUCCESS
)
220 status
= cairo_surface_status (surface
->recording_surface
);
221 cairo_surface_destroy (surface
->recording_surface
);
226 static cairo_surface_t
*
227 _cairo_paginated_surface_create_image_surface (void *abstract_surface
,
231 cairo_paginated_surface_t
*surface
= abstract_surface
;
232 cairo_surface_t
*image
;
233 cairo_font_options_t options
;
235 image
= _cairo_image_surface_create_with_content (surface
->content
,
239 cairo_surface_get_font_options (&surface
->base
, &options
);
240 _cairo_surface_set_font_options (image
, &options
);
245 static cairo_surface_t
*
246 _cairo_paginated_surface_source (void *abstract_surface
,
247 cairo_rectangle_int_t
*extents
)
249 cairo_paginated_surface_t
*surface
= abstract_surface
;
250 return _cairo_surface_get_source (surface
->target
, extents
);
253 static cairo_status_t
254 _cairo_paginated_surface_acquire_source_image (void *abstract_surface
,
255 cairo_image_surface_t
**image_out
,
258 cairo_paginated_surface_t
*surface
= abstract_surface
;
259 cairo_bool_t is_bounded
;
260 cairo_surface_t
*image
;
261 cairo_status_t status
;
262 cairo_rectangle_int_t extents
;
264 is_bounded
= _cairo_surface_get_extents (surface
->target
, &extents
);
266 return CAIRO_INT_STATUS_UNSUPPORTED
;
268 image
= _cairo_paginated_surface_create_image_surface (surface
,
272 status
= _cairo_recording_surface_replay (surface
->recording_surface
, image
);
273 if (unlikely (status
)) {
274 cairo_surface_destroy (image
);
278 *image_out
= (cairo_image_surface_t
*) image
;
281 return CAIRO_STATUS_SUCCESS
;
285 _cairo_paginated_surface_release_source_image (void *abstract_surface
,
286 cairo_image_surface_t
*image
,
289 cairo_surface_destroy (&image
->base
);
292 static cairo_int_status_t
293 _paint_fallback_image (cairo_paginated_surface_t
*surface
,
294 cairo_rectangle_int_t
*rect
)
296 double x_scale
= surface
->base
.x_fallback_resolution
/ surface
->target
->x_resolution
;
297 double y_scale
= surface
->base
.y_fallback_resolution
/ surface
->target
->y_resolution
;
298 int x
, y
, width
, height
;
299 cairo_status_t status
;
300 cairo_surface_t
*image
;
301 cairo_surface_pattern_t pattern
;
307 height
= rect
->height
;
308 image
= _cairo_paginated_surface_create_image_surface (surface
,
309 ceil (width
* x_scale
),
310 ceil (height
* y_scale
));
311 cairo_surface_set_device_scale (image
, x_scale
, y_scale
);
312 /* set_device_offset just sets the x0/y0 components of the matrix;
313 * so we have to do the scaling manually. */
314 cairo_surface_set_device_offset (image
, -x
*x_scale
, -y
*y_scale
);
316 status
= _cairo_recording_surface_replay (surface
->recording_surface
, image
);
317 if (unlikely (status
))
320 _cairo_pattern_init_for_surface (&pattern
, image
);
321 cairo_matrix_init (&pattern
.base
.matrix
,
322 x_scale
, 0, 0, y_scale
, -x
*x_scale
, -y
*y_scale
);
323 /* the fallback should be rendered at native resolution, so disable
324 * filtering (if possible) to avoid introducing potential artifacts. */
325 pattern
.base
.filter
= CAIRO_FILTER_NEAREST
;
327 clip
= _cairo_clip_intersect_rectangle (NULL
, rect
);
328 status
= _cairo_surface_paint (surface
->target
,
329 CAIRO_OPERATOR_SOURCE
,
330 &pattern
.base
, clip
);
331 _cairo_clip_destroy (clip
);
332 _cairo_pattern_fini (&pattern
.base
);
335 cairo_surface_destroy (image
);
340 static cairo_int_status_t
341 _paint_page (cairo_paginated_surface_t
*surface
)
343 cairo_surface_t
*analysis
;
344 cairo_int_status_t status
;
345 cairo_bool_t has_supported
, has_page_fallback
, has_finegrained_fallback
;
347 if (unlikely (surface
->target
->status
))
348 return surface
->target
->status
;
350 analysis
= _cairo_analysis_surface_create (surface
->target
);
351 if (unlikely (analysis
->status
))
352 return _cairo_surface_set_error (surface
->target
, analysis
->status
);
354 surface
->backend
->set_paginated_mode (surface
->target
,
355 CAIRO_PAGINATED_MODE_ANALYZE
);
356 status
= _cairo_recording_surface_replay_and_create_regions (surface
->recording_surface
,
361 assert (analysis
->status
== CAIRO_STATUS_SUCCESS
);
363 if (surface
->backend
->set_bounding_box
) {
366 _cairo_analysis_surface_get_bounding_box (analysis
, &bbox
);
367 status
= surface
->backend
->set_bounding_box (surface
->target
, &bbox
);
368 if (unlikely (status
))
372 if (surface
->backend
->set_fallback_images_required
) {
373 cairo_bool_t has_fallbacks
= _cairo_analysis_surface_has_unsupported (analysis
);
375 status
= surface
->backend
->set_fallback_images_required (surface
->target
,
377 if (unlikely (status
))
381 /* Finer grained fallbacks are currently only supported for some
383 if (surface
->backend
->supports_fine_grained_fallbacks
!= NULL
&&
384 surface
->backend
->supports_fine_grained_fallbacks (surface
->target
))
386 has_supported
= _cairo_analysis_surface_has_supported (analysis
);
387 has_page_fallback
= FALSE
;
388 has_finegrained_fallback
= _cairo_analysis_surface_has_unsupported (analysis
);
392 if (_cairo_analysis_surface_has_unsupported (analysis
)) {
393 has_supported
= FALSE
;
394 has_page_fallback
= TRUE
;
396 has_supported
= TRUE
;
397 has_page_fallback
= FALSE
;
399 has_finegrained_fallback
= FALSE
;
403 surface
->backend
->set_paginated_mode (surface
->target
,
404 CAIRO_PAGINATED_MODE_RENDER
);
406 status
= _cairo_recording_surface_replay_region (surface
->recording_surface
,
409 CAIRO_RECORDING_REGION_NATIVE
);
410 assert (status
!= CAIRO_INT_STATUS_UNSUPPORTED
);
411 if (unlikely (status
))
415 if (has_page_fallback
) {
416 cairo_rectangle_int_t extents
;
417 cairo_bool_t is_bounded
;
419 surface
->backend
->set_paginated_mode (surface
->target
,
420 CAIRO_PAGINATED_MODE_FALLBACK
);
422 is_bounded
= _cairo_surface_get_extents (surface
->target
, &extents
);
424 status
= CAIRO_INT_STATUS_UNSUPPORTED
;
428 status
= _paint_fallback_image (surface
, &extents
);
429 if (unlikely (status
))
433 if (has_finegrained_fallback
) {
434 cairo_region_t
*region
;
437 surface
->backend
->set_paginated_mode (surface
->target
,
438 CAIRO_PAGINATED_MODE_FALLBACK
);
440 region
= _cairo_analysis_surface_get_unsupported (analysis
);
442 num_rects
= cairo_region_num_rectangles (region
);
443 for (i
= 0; i
< num_rects
; i
++) {
444 cairo_rectangle_int_t rect
;
446 cairo_region_get_rectangle (region
, i
, &rect
);
447 status
= _paint_fallback_image (surface
, &rect
);
448 if (unlikely (status
))
454 cairo_surface_destroy (analysis
);
456 return _cairo_surface_set_error (surface
->target
, status
);
459 static cairo_status_t
460 _start_page (cairo_paginated_surface_t
*surface
)
462 if (surface
->target
->status
)
463 return surface
->target
->status
;
465 if (! surface
->backend
->start_page
)
466 return CAIRO_STATUS_SUCCESS
;
468 return _cairo_surface_set_error (surface
->target
,
469 surface
->backend
->start_page (surface
->target
));
472 static cairo_int_status_t
473 _cairo_paginated_surface_copy_page (void *abstract_surface
)
475 cairo_status_t status
;
476 cairo_paginated_surface_t
*surface
= abstract_surface
;
478 status
= _start_page (surface
);
479 if (unlikely (status
))
482 status
= _paint_page (surface
);
483 if (unlikely (status
))
488 /* XXX: It might make sense to add some support here for calling
489 * cairo_surface_copy_page on the target surface. It would be an
490 * optimization for the output, but the interaction with image
491 * fallbacks gets tricky. For now, we just let the target see a
492 * show_page and we implement the copying by simply not destroying
493 * the recording-surface. */
495 cairo_surface_show_page (surface
->target
);
496 return cairo_surface_status (surface
->target
);
499 static cairo_int_status_t
500 _cairo_paginated_surface_show_page (void *abstract_surface
)
502 cairo_status_t status
;
503 cairo_paginated_surface_t
*surface
= abstract_surface
;
505 status
= _start_page (surface
);
506 if (unlikely (status
))
509 status
= _paint_page (surface
);
510 if (unlikely (status
))
513 cairo_surface_show_page (surface
->target
);
514 status
= surface
->target
->status
;
515 if (unlikely (status
))
518 status
= surface
->recording_surface
->status
;
519 if (unlikely (status
))
522 if (! surface
->base
.finished
) {
523 cairo_surface_destroy (surface
->recording_surface
);
525 surface
->recording_surface
= _create_recording_surface_for_target (surface
->target
,
527 status
= surface
->recording_surface
->status
;
528 if (unlikely (status
))
532 surface
->base
.is_clear
= TRUE
;
535 return CAIRO_STATUS_SUCCESS
;
539 _cairo_paginated_surface_get_extents (void *abstract_surface
,
540 cairo_rectangle_int_t
*rectangle
)
542 cairo_paginated_surface_t
*surface
= abstract_surface
;
544 return _cairo_surface_get_extents (surface
->target
, rectangle
);
548 _cairo_paginated_surface_get_font_options (void *abstract_surface
,
549 cairo_font_options_t
*options
)
551 cairo_paginated_surface_t
*surface
= abstract_surface
;
553 cairo_surface_get_font_options (surface
->target
, options
);
556 static cairo_int_status_t
557 _cairo_paginated_surface_paint (void *abstract_surface
,
559 const cairo_pattern_t
*source
,
560 const cairo_clip_t
*clip
)
562 cairo_paginated_surface_t
*surface
= abstract_surface
;
564 return _cairo_surface_paint (surface
->recording_surface
, op
, source
, clip
);
567 static cairo_int_status_t
568 _cairo_paginated_surface_mask (void *abstract_surface
,
570 const cairo_pattern_t
*source
,
571 const cairo_pattern_t
*mask
,
572 const cairo_clip_t
*clip
)
574 cairo_paginated_surface_t
*surface
= abstract_surface
;
576 return _cairo_surface_mask (surface
->recording_surface
, op
, source
, mask
, clip
);
579 static cairo_int_status_t
580 _cairo_paginated_surface_stroke (void *abstract_surface
,
582 const cairo_pattern_t
*source
,
583 const cairo_path_fixed_t
*path
,
584 const cairo_stroke_style_t
*style
,
585 const cairo_matrix_t
*ctm
,
586 const cairo_matrix_t
*ctm_inverse
,
588 cairo_antialias_t antialias
,
589 const cairo_clip_t
*clip
)
591 cairo_paginated_surface_t
*surface
= abstract_surface
;
593 return _cairo_surface_stroke (surface
->recording_surface
, op
, source
,
596 tolerance
, antialias
,
600 static cairo_int_status_t
601 _cairo_paginated_surface_fill (void *abstract_surface
,
603 const cairo_pattern_t
*source
,
604 const cairo_path_fixed_t
*path
,
605 cairo_fill_rule_t fill_rule
,
607 cairo_antialias_t antialias
,
608 const cairo_clip_t
*clip
)
610 cairo_paginated_surface_t
*surface
= abstract_surface
;
612 return _cairo_surface_fill (surface
->recording_surface
, op
, source
,
614 tolerance
, antialias
,
619 _cairo_paginated_surface_has_show_text_glyphs (void *abstract_surface
)
621 cairo_paginated_surface_t
*surface
= abstract_surface
;
623 return cairo_surface_has_show_text_glyphs (surface
->target
);
626 static cairo_int_status_t
627 _cairo_paginated_surface_show_text_glyphs (void *abstract_surface
,
629 const cairo_pattern_t
*source
,
632 cairo_glyph_t
*glyphs
,
634 const cairo_text_cluster_t
*clusters
,
636 cairo_text_cluster_flags_t cluster_flags
,
637 cairo_scaled_font_t
*scaled_font
,
638 const cairo_clip_t
*clip
)
640 cairo_paginated_surface_t
*surface
= abstract_surface
;
642 return _cairo_surface_show_text_glyphs (surface
->recording_surface
, op
, source
,
645 clusters
, num_clusters
,
652 _cairo_paginated_surface_get_supported_mime_types (void *abstract_surface
)
654 cairo_paginated_surface_t
*surface
= abstract_surface
;
656 if (surface
->target
->backend
->get_supported_mime_types
)
657 return surface
->target
->backend
->get_supported_mime_types (surface
->target
);
662 static cairo_surface_t
*
663 _cairo_paginated_surface_snapshot (void *abstract_other
)
665 cairo_paginated_surface_t
*other
= abstract_other
;
667 return other
->recording_surface
->backend
->snapshot (other
->recording_surface
);
671 _cairo_paginated_context_create (void *target
)
673 cairo_paginated_surface_t
*surface
= target
;
675 if (_cairo_surface_is_subsurface (&surface
->base
))
676 surface
= (cairo_paginated_surface_t
*)
677 _cairo_surface_subsurface_get_target (&surface
->base
);
679 return surface
->recording_surface
->backend
->create_context (target
);
682 static const cairo_surface_backend_t cairo_paginated_surface_backend
= {
683 CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED
,
684 _cairo_paginated_surface_finish
,
686 _cairo_paginated_context_create
,
688 _cairo_paginated_surface_create_similar
,
689 NULL
, /* create simlar image */
690 NULL
, /* map to image */
691 NULL
, /* unmap image */
693 _cairo_paginated_surface_source
,
694 _cairo_paginated_surface_acquire_source_image
,
695 _cairo_paginated_surface_release_source_image
,
696 _cairo_paginated_surface_snapshot
,
698 _cairo_paginated_surface_copy_page
,
699 _cairo_paginated_surface_show_page
,
701 _cairo_paginated_surface_get_extents
,
702 _cairo_paginated_surface_get_font_options
,
705 NULL
, /* mark_dirty_rectangle */
707 _cairo_paginated_surface_paint
,
708 _cairo_paginated_surface_mask
,
709 _cairo_paginated_surface_stroke
,
710 _cairo_paginated_surface_fill
,
711 NULL
, /* fill_stroke */
712 NULL
, /* show_glyphs */
713 _cairo_paginated_surface_has_show_text_glyphs
,
714 _cairo_paginated_surface_show_text_glyphs
,
715 _cairo_paginated_surface_get_supported_mime_types
,