1 /*
2 * Copyright © 2006 Keith Packard
3 * Copyright © 2007 Adrian Johnson
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it either under the terms of the GNU Lesser General Public
7 * License version 2.1 as published by the Free Software Foundation
8 * (the "LGPL") or, at your option, under the terms of the Mozilla
9 * Public License Version 1.1 (the "MPL"). If you do not alter this
10 * notice, a recipient may use your version of this file under either
11 * the MPL or the LGPL.
12 *
13 * You should have received a copy of the LGPL along with this library
14 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16 * You should have received a copy of the MPL along with this library
17 * in the file COPYING-MPL-1.1
18 *
19 * The contents of this file are subject to the Mozilla Public License
20 * Version 1.1 (the "License"); you may not use this file except in
21 * compliance with the License. You may obtain a copy of the License at
22 * http://www.mozilla.org/MPL/
23 *
24 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26 * the specific language governing rights and limitations.
27 *
28 * The Original Code is the cairo graphics library.
29 *
30 * The Initial Developer of the Original Code is Keith Packard
31 *
32 * Contributor(s):
33 * Keith Packard <keithp@keithp.com>
34 * Adrian Johnson <ajohnson@redneon.com>
35 */
36
37 #include "cairoint.h"
38
39 #include "cairo-analysis-surface-private.h"
40 #include "cairo-box-inline.h"
41 #include "cairo-default-context-private.h"
42 #include "cairo-error-private.h"
43 #include "cairo-paginated-private.h"
44 #include "cairo-recording-surface-inline.h"
45 #include "cairo-surface-snapshot-inline.h"
46 #include "cairo-surface-subsurface-inline.h"
47 #include "cairo-region-private.h"
48
49 typedef struct {
50 cairo_surface_t base;
51
52 cairo_surface_t *target;
53
54 cairo_bool_t first_op;
55 cairo_bool_t has_supported;
56 cairo_bool_t has_unsupported;
57
58 cairo_region_t supported_region;
59 cairo_region_t fallback_region;
60 cairo_box_t page_bbox;
61
62 cairo_bool_t has_ctm;
63 cairo_matrix_t ctm;
64
65 } cairo_analysis_surface_t;
66
67 cairo_int_status_t
_cairo_analysis_surface_merge_status(cairo_int_status_t status_a,cairo_int_status_t status_b)68 _cairo_analysis_surface_merge_status (cairo_int_status_t status_a,
69 cairo_int_status_t status_b)
70 {
71 /* fatal errors should be checked and propagated at source */
72 assert (! _cairo_int_status_is_error (status_a));
73 assert (! _cairo_int_status_is_error (status_b));
74
75 /* return the most important status */
76 if (status_a == CAIRO_INT_STATUS_UNSUPPORTED ||
77 status_b == CAIRO_INT_STATUS_UNSUPPORTED)
78 return CAIRO_INT_STATUS_UNSUPPORTED;
79
80 if (status_a == CAIRO_INT_STATUS_IMAGE_FALLBACK ||
81 status_b == CAIRO_INT_STATUS_IMAGE_FALLBACK)
82 return CAIRO_INT_STATUS_IMAGE_FALLBACK;
83
84 if (status_a == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN ||
85 status_b == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
86 return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
87
88 if (status_a == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
89 status_b == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
90 return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
91
92 /* at this point we have checked all the valid internal codes, so... */
93 assert (status_a == CAIRO_INT_STATUS_SUCCESS &&
94 status_b == CAIRO_INT_STATUS_SUCCESS);
95
96 return CAIRO_INT_STATUS_SUCCESS;
97 }
98
99 struct proxy {
100 cairo_surface_t base;
101 cairo_surface_t *target;
102 };
103
104 static cairo_status_t
proxy_finish(void * abstract_surface)105 proxy_finish (void *abstract_surface)
106 {
107 return CAIRO_STATUS_SUCCESS;
108 }
109
110 static const cairo_surface_backend_t proxy_backend = {
111 CAIRO_INTERNAL_SURFACE_TYPE_NULL,
112 proxy_finish,
113 };
114
115 static cairo_surface_t *
attach_proxy(cairo_surface_t * source,cairo_surface_t * target)116 attach_proxy (cairo_surface_t *source,
117 cairo_surface_t *target)
118 {
119 struct proxy *proxy;
120
121 proxy = _cairo_malloc (sizeof (*proxy));
122 if (unlikely (proxy == NULL))
123 return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
124
125 _cairo_surface_init (&proxy->base, &proxy_backend, NULL, target->content, target->is_vector);
126
127 proxy->target = target;
128 _cairo_surface_attach_snapshot (source, &proxy->base, NULL);
129
130 return &proxy->base;
131 }
132
133 static void
detach_proxy(cairo_surface_t * proxy)134 detach_proxy (cairo_surface_t *proxy)
135 {
136 cairo_surface_finish (proxy);
137 cairo_surface_destroy (proxy);
138 }
139
140 static cairo_int_status_t
_add_operation(cairo_analysis_surface_t * surface,cairo_rectangle_int_t * rect,cairo_int_status_t backend_status)141 _add_operation (cairo_analysis_surface_t *surface,
142 cairo_rectangle_int_t *rect,
143 cairo_int_status_t backend_status)
144 {
145 cairo_int_status_t status;
146 cairo_box_t bbox;
147
148 if (rect->width == 0 || rect->height == 0) {
149 /* Even though the operation is not visible we must be careful
150 * to not allow unsupported operations to be replayed to the
151 * backend during CAIRO_PAGINATED_MODE_RENDER */
152 if (backend_status == CAIRO_INT_STATUS_SUCCESS ||
153 backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
154 backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO)
155 {
156 return CAIRO_INT_STATUS_SUCCESS;
157 }
158 else
159 {
160 return CAIRO_INT_STATUS_IMAGE_FALLBACK;
161 }
162 }
163
164 _cairo_box_from_rectangle (&bbox, rect);
165
166 if (surface->has_ctm) {
167 int tx, ty;
168
169 if (_cairo_matrix_is_integer_translation (&surface->ctm, &tx, &ty)) {
170 rect->x += tx;
171 rect->y += ty;
172
173 tx = _cairo_fixed_from_int (tx);
174 bbox.p1.x += tx;
175 bbox.p2.x += tx;
176
177 ty = _cairo_fixed_from_int (ty);
178 bbox.p1.y += ty;
179 bbox.p2.y += ty;
180 } else {
181 _cairo_matrix_transform_bounding_box_fixed (&surface->ctm,
182 &bbox, NULL);
183
184 if (bbox.p1.x == bbox.p2.x || bbox.p1.y == bbox.p2.y) {
185 /* Even though the operation is not visible we must be
186 * careful to not allow unsupported operations to be
187 * replayed to the backend during
188 * CAIRO_PAGINATED_MODE_RENDER */
189 if (backend_status == CAIRO_INT_STATUS_SUCCESS ||
190 backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
191 backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO)
192 {
193 return CAIRO_INT_STATUS_SUCCESS;
194 }
195 else
196 {
197 return CAIRO_INT_STATUS_IMAGE_FALLBACK;
198 }
199 }
200
201 _cairo_box_round_to_rectangle (&bbox, rect);
202 }
203 }
204
205 if (surface->first_op) {
206 surface->first_op = FALSE;
207 surface->page_bbox = bbox;
208 } else
209 _cairo_box_add_box(&surface->page_bbox, &bbox);
210
211 /* If the operation is completely enclosed within the fallback
212 * region there is no benefit in emitting a native operation as
213 * the fallback image will be painted on top.
214 */
215 if (cairo_region_contains_rectangle (&surface->fallback_region, rect) == CAIRO_REGION_OVERLAP_IN)
216 return CAIRO_INT_STATUS_IMAGE_FALLBACK;
217
218 if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) {
219 /* A status of CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY indicates
220 * that the backend only supports this operation if the
221 * transparency removed. If the extents of this operation does
222 * not intersect any other native operation, the operation is
223 * natively supported and the backend will blend the
224 * transparency into the white background.
225 */
226 if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT)
227 backend_status = CAIRO_INT_STATUS_SUCCESS;
228 }
229
230 if (backend_status == CAIRO_INT_STATUS_SUCCESS) {
231 /* Add the operation to the supported region. Operations in
232 * this region will be emitted as native operations.
233 */
234 surface->has_supported = TRUE;
235 return cairo_region_union_rectangle (&surface->supported_region, rect);
236 }
237
238 /* Add the operation to the unsupported region. This region will
239 * be painted as an image after all native operations have been
240 * emitted.
241 */
242 surface->has_unsupported = TRUE;
243 status = cairo_region_union_rectangle (&surface->fallback_region, rect);
244
245 /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate
246 * unsupported operations to the recording surface as using
247 * CAIRO_INT_STATUS_UNSUPPORTED would cause cairo-surface to
248 * invoke the cairo-surface-fallback path then return
249 * CAIRO_STATUS_SUCCESS.
250 */
251 if (status == CAIRO_INT_STATUS_SUCCESS)
252 return CAIRO_INT_STATUS_IMAGE_FALLBACK;
253 else
254 return status;
255 }
256
257 static cairo_int_status_t
_analyze_recording_surface_pattern(cairo_analysis_surface_t * surface,const cairo_pattern_t * pattern,cairo_rectangle_int_t * extents)258 _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
259 const cairo_pattern_t *pattern,
260 cairo_rectangle_int_t *extents)
261 {
262 const cairo_surface_pattern_t *surface_pattern;
263 cairo_analysis_surface_t *tmp;
264 cairo_surface_t *source, *proxy;
265 cairo_matrix_t p2d;
266 cairo_int_status_t status;
267 cairo_int_status_t analysis_status = CAIRO_INT_STATUS_SUCCESS;
268 cairo_bool_t surface_is_unbounded;
269 cairo_bool_t unused;
270
271 assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
272 surface_pattern = (const cairo_surface_pattern_t *) pattern;
273 assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING);
274 source = surface_pattern->surface;
275
276 proxy = _cairo_surface_has_snapshot (source, &proxy_backend);
277 if (proxy != NULL) {
278 /* nothing untoward found so far */
279 return CAIRO_STATUS_SUCCESS;
280 }
281
282 tmp = (cairo_analysis_surface_t *)
283 _cairo_analysis_surface_create (surface->target);
284 if (unlikely (tmp->base.status)) {
285 status =tmp->base.status;
286 goto cleanup1;
287 }
288 proxy = attach_proxy (source, &tmp->base);
289
290 p2d = pattern->matrix;
291 status = cairo_matrix_invert (&p2d);
292 assert (status == CAIRO_INT_STATUS_SUCCESS);
293 _cairo_analysis_surface_set_ctm (&tmp->base, &p2d);
294
295
296 source = _cairo_surface_get_source (source, NULL);
297 surface_is_unbounded = (pattern->extend == CAIRO_EXTEND_REPEAT
298 || pattern->extend == CAIRO_EXTEND_REFLECT);
299 status = _cairo_recording_surface_replay_and_create_regions (source,
300 &pattern->matrix,
301 &tmp->base,
302 surface_is_unbounded);
303 if (unlikely (status))
304 goto cleanup2;
305
306 /* black background or mime data fills entire extents */
307 if (!(source->content & CAIRO_CONTENT_ALPHA) || _cairo_surface_has_mime_image (source)) {
308 cairo_rectangle_int_t rect;
309
310 if (_cairo_surface_get_extents (source, &rect)) {
311 cairo_box_t bbox;
312
313 _cairo_box_from_rectangle (&bbox, &rect);
314 _cairo_matrix_transform_bounding_box_fixed (&p2d, &bbox, NULL);
315 _cairo_box_round_to_rectangle (&bbox, &rect);
316 status = _add_operation (tmp, &rect, CAIRO_INT_STATUS_SUCCESS);
317 if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
318 status = CAIRO_INT_STATUS_SUCCESS;
319 if (unlikely (status))
320 goto cleanup2;
321 }
322 }
323
324 if (tmp->has_supported) {
325 surface->has_supported = TRUE;
326 unused = cairo_region_union (&surface->supported_region, &tmp->supported_region);
327 }
328
329 if (tmp->has_unsupported) {
330 surface->has_unsupported = TRUE;
331 unused = cairo_region_union (&surface->fallback_region, &tmp->fallback_region);
332 }
333
334 analysis_status = tmp->has_unsupported ? CAIRO_INT_STATUS_IMAGE_FALLBACK : CAIRO_INT_STATUS_SUCCESS;
335 if (pattern->extend != CAIRO_EXTEND_NONE) {
336 _cairo_unbounded_rectangle_init (extents);
337 } else {
338 status = cairo_matrix_invert (&tmp->ctm);
339 _cairo_matrix_transform_bounding_box_fixed (&tmp->ctm,
340 &tmp->page_bbox, NULL);
341 _cairo_box_round_to_rectangle (&tmp->page_bbox, extents);
342 }
343
344 cleanup2:
345 detach_proxy (proxy);
346 cleanup1:
347 cairo_surface_destroy (&tmp->base);
348
349 if (unlikely (status))
350 return status;
351 else
352 return analysis_status;
353 }
354
355 static cairo_status_t
_cairo_analysis_surface_finish(void * abstract_surface)356 _cairo_analysis_surface_finish (void *abstract_surface)
357 {
358 cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
359
360 _cairo_region_fini (&surface->supported_region);
361 _cairo_region_fini (&surface->fallback_region);
362
363 cairo_surface_destroy (surface->target);
364
365 return CAIRO_STATUS_SUCCESS;
366 }
367
368 static cairo_bool_t
_cairo_analysis_surface_get_extents(void * abstract_surface,cairo_rectangle_int_t * rectangle)369 _cairo_analysis_surface_get_extents (void *abstract_surface,
370 cairo_rectangle_int_t *rectangle)
371 {
372 cairo_analysis_surface_t *surface = abstract_surface;
373
374 return _cairo_surface_get_extents (surface->target, rectangle);
375 }
376
377 static void
_rectangle_intersect_clip(cairo_rectangle_int_t * extents,const cairo_clip_t * clip)378 _rectangle_intersect_clip (cairo_rectangle_int_t *extents, const cairo_clip_t *clip)
379 {
380 if (clip != NULL)
381 _cairo_rectangle_intersect (extents, _cairo_clip_get_extents (clip));
382 }
383
384 static void
_cairo_analysis_surface_operation_extents(cairo_analysis_surface_t * surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_clip_t * clip,cairo_rectangle_int_t * extents)385 _cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface,
386 cairo_operator_t op,
387 const cairo_pattern_t *source,
388 const cairo_clip_t *clip,
389 cairo_rectangle_int_t *extents)
390 {
391 cairo_bool_t is_empty;
392
393 is_empty = _cairo_surface_get_extents (&surface->base, extents);
394
395 if (_cairo_operator_bounded_by_source (op)) {
396 cairo_rectangle_int_t source_extents;
397
398 _cairo_pattern_get_extents (source, &source_extents, surface->target->is_vector);
399 _cairo_rectangle_intersect (extents, &source_extents);
400 }
401
402 _rectangle_intersect_clip (extents, clip);
403 }
404
405 static cairo_int_status_t
_cairo_analysis_surface_paint(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_clip_t * clip)406 _cairo_analysis_surface_paint (void *abstract_surface,
407 cairo_operator_t op,
408 const cairo_pattern_t *source,
409 const cairo_clip_t *clip)
410 {
411 cairo_analysis_surface_t *surface = abstract_surface;
412 cairo_int_status_t backend_status;
413 cairo_rectangle_int_t extents;
414
415 if (surface->target->backend->paint == NULL) {
416 backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
417 } else {
418 backend_status =
419 surface->target->backend->paint (surface->target,
420 op, source, clip);
421 if (_cairo_int_status_is_error (backend_status))
422 return backend_status;
423 }
424
425 _cairo_analysis_surface_operation_extents (surface,
426 op, source, clip,
427 &extents);
428 if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
429 cairo_rectangle_int_t rec_extents;
430 backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents);
431 _cairo_rectangle_intersect (&extents, &rec_extents);
432 }
433
434 return _add_operation (surface, &extents, backend_status);
435 }
436
437 static cairo_int_status_t
_cairo_analysis_surface_mask(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_pattern_t * mask,const cairo_clip_t * clip)438 _cairo_analysis_surface_mask (void *abstract_surface,
439 cairo_operator_t op,
440 const cairo_pattern_t *source,
441 const cairo_pattern_t *mask,
442 const cairo_clip_t *clip)
443 {
444 cairo_analysis_surface_t *surface = abstract_surface;
445 cairo_int_status_t backend_status;
446 cairo_rectangle_int_t extents;
447
448 if (surface->target->backend->mask == NULL) {
449 backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
450 } else {
451 backend_status =
452 surface->target->backend->mask (surface->target,
453 op, source, mask, clip);
454 if (_cairo_int_status_is_error (backend_status))
455 return backend_status;
456 }
457
458 _cairo_analysis_surface_operation_extents (surface,
459 op, source, clip,
460 &extents);
461 if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
462 cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS;
463 cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS;
464 cairo_rectangle_int_t rec_extents;
465
466 if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
467 cairo_surface_t *src_surface = ((cairo_surface_pattern_t *)source)->surface;
468 src_surface = _cairo_surface_get_source (src_surface, NULL);
469 if (_cairo_surface_is_recording (src_surface)) {
470 backend_source_status =
471 _analyze_recording_surface_pattern (surface, source, &rec_extents);
472 if (_cairo_int_status_is_error (backend_source_status))
473 return backend_source_status;
474
475 _cairo_rectangle_intersect (&extents, &rec_extents);
476 }
477 }
478
479 if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
480 cairo_surface_t *mask_surface = ((cairo_surface_pattern_t *)mask)->surface;
481 mask_surface = _cairo_surface_get_source (mask_surface, NULL);
482 if (_cairo_surface_is_recording (mask_surface)) {
483 backend_mask_status =
484 _analyze_recording_surface_pattern (surface, mask, &rec_extents);
485 if (_cairo_int_status_is_error (backend_mask_status))
486 return backend_mask_status;
487
488 _cairo_rectangle_intersect (&extents, &rec_extents);
489 }
490 }
491
492 backend_status =
493 _cairo_analysis_surface_merge_status (backend_source_status,
494 backend_mask_status);
495 }
496
497 if (_cairo_operator_bounded_by_mask (op)) {
498 cairo_rectangle_int_t mask_extents;
499
500 _cairo_pattern_get_extents (mask, &mask_extents, surface->target->is_vector);
501 _cairo_rectangle_intersect (&extents, &mask_extents);
502 }
503
504 return _add_operation (surface, &extents, backend_status);
505 }
506
507 static cairo_int_status_t
_cairo_analysis_surface_stroke(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_path_fixed_t * path,const cairo_stroke_style_t * style,const cairo_matrix_t * ctm,const cairo_matrix_t * ctm_inverse,double tolerance,cairo_antialias_t antialias,const cairo_clip_t * clip)508 _cairo_analysis_surface_stroke (void *abstract_surface,
509 cairo_operator_t op,
510 const cairo_pattern_t *source,
511 const cairo_path_fixed_t *path,
512 const cairo_stroke_style_t *style,
513 const cairo_matrix_t *ctm,
514 const cairo_matrix_t *ctm_inverse,
515 double tolerance,
516 cairo_antialias_t antialias,
517 const cairo_clip_t *clip)
518 {
519 cairo_analysis_surface_t *surface = abstract_surface;
520 cairo_int_status_t backend_status;
521 cairo_rectangle_int_t extents;
522
523 if (surface->target->backend->stroke == NULL) {
524 backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
525 } else {
526 backend_status =
527 surface->target->backend->stroke (surface->target, op,
528 source, path, style,
529 ctm, ctm_inverse,
530 tolerance, antialias,
531 clip);
532 if (_cairo_int_status_is_error (backend_status))
533 return backend_status;
534 }
535
536 _cairo_analysis_surface_operation_extents (surface,
537 op, source, clip,
538 &extents);
539 if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
540 cairo_rectangle_int_t rec_extents;
541 backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents);
542 _cairo_rectangle_intersect (&extents, &rec_extents);
543 }
544
545 if (_cairo_operator_bounded_by_mask (op)) {
546 cairo_rectangle_int_t mask_extents;
547 cairo_int_status_t status;
548
549 status = _cairo_path_fixed_stroke_extents (path, style,
550 ctm, ctm_inverse,
551 tolerance,
552 &mask_extents);
553 if (unlikely (status))
554 return status;
555
556 _cairo_rectangle_intersect (&extents, &mask_extents);
557 }
558
559 return _add_operation (surface, &extents, backend_status);
560 }
561
562 static cairo_int_status_t
_cairo_analysis_surface_fill(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_path_fixed_t * path,cairo_fill_rule_t fill_rule,double tolerance,cairo_antialias_t antialias,const cairo_clip_t * clip)563 _cairo_analysis_surface_fill (void *abstract_surface,
564 cairo_operator_t op,
565 const cairo_pattern_t *source,
566 const cairo_path_fixed_t *path,
567 cairo_fill_rule_t fill_rule,
568 double tolerance,
569 cairo_antialias_t antialias,
570 const cairo_clip_t *clip)
571 {
572 cairo_analysis_surface_t *surface = abstract_surface;
573 cairo_int_status_t backend_status;
574 cairo_rectangle_int_t extents;
575
576 if (surface->target->backend->fill == NULL) {
577 backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
578 } else {
579 backend_status =
580 surface->target->backend->fill (surface->target, op,
581 source, path, fill_rule,
582 tolerance, antialias,
583 clip);
584 if (_cairo_int_status_is_error (backend_status))
585 return backend_status;
586 }
587
588 _cairo_analysis_surface_operation_extents (surface,
589 op, source, clip,
590 &extents);
591 if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
592 cairo_rectangle_int_t rec_extents;
593 backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents);
594 _cairo_rectangle_intersect (&extents, &rec_extents);
595 }
596
597 if (_cairo_operator_bounded_by_mask (op)) {
598 cairo_rectangle_int_t mask_extents;
599
600 _cairo_path_fixed_fill_extents (path, fill_rule, tolerance,
601 &mask_extents);
602
603 _cairo_rectangle_intersect (&extents, &mask_extents);
604 }
605
606 return _add_operation (surface, &extents, backend_status);
607 }
608
609 static cairo_int_status_t
_cairo_analysis_surface_show_glyphs(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_glyph_t * glyphs,int num_glyphs,cairo_scaled_font_t * scaled_font,const cairo_clip_t * clip)610 _cairo_analysis_surface_show_glyphs (void *abstract_surface,
611 cairo_operator_t op,
612 const cairo_pattern_t *source,
613 cairo_glyph_t *glyphs,
614 int num_glyphs,
615 cairo_scaled_font_t *scaled_font,
616 const cairo_clip_t *clip)
617 {
618 cairo_analysis_surface_t *surface = abstract_surface;
619 cairo_int_status_t status, backend_status;
620 cairo_rectangle_int_t extents, glyph_extents;
621
622 /* Adapted from _cairo_surface_show_glyphs */
623 if (surface->target->backend->show_glyphs != NULL) {
624 backend_status =
625 surface->target->backend->show_glyphs (surface->target, op,
626 source,
627 glyphs, num_glyphs,
628 scaled_font,
629 clip);
630 if (_cairo_int_status_is_error (backend_status))
631 return backend_status;
632 }
633 else if (surface->target->backend->show_text_glyphs != NULL)
634 {
635 backend_status =
636 surface->target->backend->show_text_glyphs (surface->target, op,
637 source,
638 NULL, 0,
639 glyphs, num_glyphs,
640 NULL, 0,
641 FALSE,
642 scaled_font,
643 clip);
644 if (_cairo_int_status_is_error (backend_status))
645 return backend_status;
646 }
647 else
648 {
649 backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
650 }
651
652 _cairo_analysis_surface_operation_extents (surface,
653 op, source, clip,
654 &extents);
655 if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
656 cairo_rectangle_int_t rec_extents;
657 backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents);
658 _cairo_rectangle_intersect (&extents, &rec_extents);
659 }
660
661 if (_cairo_operator_bounded_by_mask (op)) {
662 status = _cairo_scaled_font_glyph_device_extents (scaled_font,
663 glyphs,
664 num_glyphs,
665 &glyph_extents,
666 NULL);
667 if (unlikely (status))
668 return status;
669
670 _cairo_rectangle_intersect (&extents, &glyph_extents);
671 }
672
673 return _add_operation (surface, &extents, backend_status);
674 }
675
676 static cairo_bool_t
_cairo_analysis_surface_has_show_text_glyphs(void * abstract_surface)677 _cairo_analysis_surface_has_show_text_glyphs (void *abstract_surface)
678 {
679 cairo_analysis_surface_t *surface = abstract_surface;
680
681 return cairo_surface_has_show_text_glyphs (surface->target);
682 }
683
684 static cairo_int_status_t
_cairo_analysis_surface_show_text_glyphs(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const char * utf8,int utf8_len,cairo_glyph_t * glyphs,int num_glyphs,const cairo_text_cluster_t * clusters,int num_clusters,cairo_text_cluster_flags_t cluster_flags,cairo_scaled_font_t * scaled_font,const cairo_clip_t * clip)685 _cairo_analysis_surface_show_text_glyphs (void *abstract_surface,
686 cairo_operator_t op,
687 const cairo_pattern_t *source,
688 const char *utf8,
689 int utf8_len,
690 cairo_glyph_t *glyphs,
691 int num_glyphs,
692 const cairo_text_cluster_t *clusters,
693 int num_clusters,
694 cairo_text_cluster_flags_t cluster_flags,
695 cairo_scaled_font_t *scaled_font,
696 const cairo_clip_t *clip)
697 {
698 cairo_analysis_surface_t *surface = abstract_surface;
699 cairo_int_status_t status, backend_status;
700 cairo_rectangle_int_t extents, glyph_extents;
701
702 /* Adapted from _cairo_surface_show_glyphs */
703 backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
704 if (surface->target->backend->show_text_glyphs != NULL) {
705 backend_status =
706 surface->target->backend->show_text_glyphs (surface->target, op,
707 source,
708 utf8, utf8_len,
709 glyphs, num_glyphs,
710 clusters, num_clusters,
711 cluster_flags,
712 scaled_font,
713 clip);
714 if (_cairo_int_status_is_error (backend_status))
715 return backend_status;
716 }
717 if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED &&
718 surface->target->backend->show_glyphs != NULL)
719 {
720 backend_status =
721 surface->target->backend->show_glyphs (surface->target, op,
722 source,
723 glyphs, num_glyphs,
724 scaled_font,
725 clip);
726 if (_cairo_int_status_is_error (backend_status))
727 return backend_status;
728 }
729
730 _cairo_analysis_surface_operation_extents (surface,
731 op, source, clip,
732 &extents);
733 if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
734 cairo_rectangle_int_t rec_extents;
735 backend_status = _analyze_recording_surface_pattern (surface, source, &rec_extents);
736 _cairo_rectangle_intersect (&extents, &rec_extents);
737 }
738
739 if (_cairo_operator_bounded_by_mask (op)) {
740 status = _cairo_scaled_font_glyph_device_extents (scaled_font,
741 glyphs,
742 num_glyphs,
743 &glyph_extents,
744 NULL);
745 if (unlikely (status))
746 return status;
747
748 _cairo_rectangle_intersect (&extents, &glyph_extents);
749 }
750
751 return _add_operation (surface, &extents, backend_status);
752 }
753
754 static cairo_int_status_t
_cairo_analysis_surface_tag(void * abstract_surface,cairo_bool_t begin,const char * tag_name,const char * attributes)755 _cairo_analysis_surface_tag (void *abstract_surface,
756 cairo_bool_t begin,
757 const char *tag_name,
758 const char *attributes)
759 {
760 cairo_analysis_surface_t *surface = abstract_surface;
761 cairo_int_status_t backend_status;
762
763 backend_status = CAIRO_INT_STATUS_SUCCESS;
764 if (surface->target->backend->tag != NULL) {
765 backend_status =
766 surface->target->backend->tag (surface->target,
767 begin,
768 tag_name,
769 attributes);
770 if (backend_status == CAIRO_INT_STATUS_SUCCESS)
771 surface->has_supported = TRUE;
772 }
773
774 return backend_status;
775 }
776
777 static const cairo_surface_backend_t cairo_analysis_surface_backend = {
778 CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
779
780 _cairo_analysis_surface_finish,
781 NULL,
782
783 NULL, /* create_similar */
784 NULL, /* create_similar_image */
785 NULL, /* map_to_image */
786 NULL, /* unmap */
787
788 NULL, /* source */
789 NULL, /* acquire_source_image */
790 NULL, /* release_source_image */
791 NULL, /* snapshot */
792
793 NULL, /* copy_page */
794 NULL, /* show_page */
795
796 _cairo_analysis_surface_get_extents,
797 NULL, /* get_font_options */
798
799 NULL, /* flush */
800 NULL, /* mark_dirty_rectangle */
801
802 _cairo_analysis_surface_paint,
803 _cairo_analysis_surface_mask,
804 _cairo_analysis_surface_stroke,
805 _cairo_analysis_surface_fill,
806 NULL, /* fill_stroke */
807 _cairo_analysis_surface_show_glyphs,
808 _cairo_analysis_surface_has_show_text_glyphs,
809 _cairo_analysis_surface_show_text_glyphs,
810 NULL, /* get_supported_mime_types */
811 _cairo_analysis_surface_tag
812 };
813
814 cairo_surface_t *
_cairo_analysis_surface_create(cairo_surface_t * target)815 _cairo_analysis_surface_create (cairo_surface_t *target)
816 {
817 cairo_analysis_surface_t *surface;
818 cairo_status_t status;
819
820 status = target->status;
821 if (unlikely (status))
822 return _cairo_surface_create_in_error (status);
823
824 surface = _cairo_malloc (sizeof (cairo_analysis_surface_t));
825 if (unlikely (surface == NULL))
826 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
827
828 /* I believe the content type here is truly arbitrary. I'm quite
829 * sure nothing will ever use this value. */
830 _cairo_surface_init (&surface->base,
831 &cairo_analysis_surface_backend,
832 NULL, /* device */
833 CAIRO_CONTENT_COLOR_ALPHA,
834 target->is_vector);
835
836 cairo_matrix_init_identity (&surface->ctm);
837 surface->has_ctm = FALSE;
838
839 surface->target = cairo_surface_reference (target);
840 surface->first_op = TRUE;
841 surface->has_supported = FALSE;
842 surface->has_unsupported = FALSE;
843
844 _cairo_region_init (&surface->supported_region);
845 _cairo_region_init (&surface->fallback_region);
846
847 surface->page_bbox.p1.x = 0;
848 surface->page_bbox.p1.y = 0;
849 surface->page_bbox.p2.x = 0;
850 surface->page_bbox.p2.y = 0;
851
852 return &surface->base;
853 }
854
855 void
_cairo_analysis_surface_set_ctm(cairo_surface_t * abstract_surface,const cairo_matrix_t * ctm)856 _cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface,
857 const cairo_matrix_t *ctm)
858 {
859 cairo_analysis_surface_t *surface;
860
861 if (abstract_surface->status)
862 return;
863
864 surface = (cairo_analysis_surface_t *) abstract_surface;
865
866 surface->ctm = *ctm;
867 surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm);
868 }
869
870 void
_cairo_analysis_surface_get_ctm(cairo_surface_t * abstract_surface,cairo_matrix_t * ctm)871 _cairo_analysis_surface_get_ctm (cairo_surface_t *abstract_surface,
872 cairo_matrix_t *ctm)
873 {
874 cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
875
876 *ctm = surface->ctm;
877 }
878
879
880 cairo_region_t *
_cairo_analysis_surface_get_supported(cairo_surface_t * abstract_surface)881 _cairo_analysis_surface_get_supported (cairo_surface_t *abstract_surface)
882 {
883 cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
884
885 return &surface->supported_region;
886 }
887
888 cairo_region_t *
_cairo_analysis_surface_get_unsupported(cairo_surface_t * abstract_surface)889 _cairo_analysis_surface_get_unsupported (cairo_surface_t *abstract_surface)
890 {
891 cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
892
893 return &surface->fallback_region;
894 }
895
896 cairo_bool_t
_cairo_analysis_surface_has_supported(cairo_surface_t * abstract_surface)897 _cairo_analysis_surface_has_supported (cairo_surface_t *abstract_surface)
898 {
899 cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
900
901 return surface->has_supported;
902 }
903
904 cairo_bool_t
_cairo_analysis_surface_has_unsupported(cairo_surface_t * abstract_surface)905 _cairo_analysis_surface_has_unsupported (cairo_surface_t *abstract_surface)
906 {
907 cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
908
909 return surface->has_unsupported;
910 }
911
912 void
_cairo_analysis_surface_get_bounding_box(cairo_surface_t * abstract_surface,cairo_box_t * bbox)913 _cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface,
914 cairo_box_t *bbox)
915 {
916 cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
917
918 *bbox = surface->page_bbox;
919 }
920
921 /* null surface type: a surface that does nothing (has no side effects, yay!) */
922
923 static cairo_int_status_t
_paint_return_success(void * surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_clip_t * clip)924 _paint_return_success (void *surface,
925 cairo_operator_t op,
926 const cairo_pattern_t *source,
927 const cairo_clip_t *clip)
928 {
929 return CAIRO_INT_STATUS_SUCCESS;
930 }
931
932 static cairo_int_status_t
_mask_return_success(void * surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_pattern_t * mask,const cairo_clip_t * clip)933 _mask_return_success (void *surface,
934 cairo_operator_t op,
935 const cairo_pattern_t *source,
936 const cairo_pattern_t *mask,
937 const cairo_clip_t *clip)
938 {
939 return CAIRO_INT_STATUS_SUCCESS;
940 }
941
942 static cairo_int_status_t
_stroke_return_success(void * surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_path_fixed_t * path,const cairo_stroke_style_t * style,const cairo_matrix_t * ctm,const cairo_matrix_t * ctm_inverse,double tolerance,cairo_antialias_t antialias,const cairo_clip_t * clip)943 _stroke_return_success (void *surface,
944 cairo_operator_t op,
945 const cairo_pattern_t *source,
946 const cairo_path_fixed_t *path,
947 const cairo_stroke_style_t *style,
948 const cairo_matrix_t *ctm,
949 const cairo_matrix_t *ctm_inverse,
950 double tolerance,
951 cairo_antialias_t antialias,
952 const cairo_clip_t *clip)
953 {
954 return CAIRO_INT_STATUS_SUCCESS;
955 }
956
957 static cairo_int_status_t
_fill_return_success(void * surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_path_fixed_t * path,cairo_fill_rule_t fill_rule,double tolerance,cairo_antialias_t antialias,const cairo_clip_t * clip)958 _fill_return_success (void *surface,
959 cairo_operator_t op,
960 const cairo_pattern_t *source,
961 const cairo_path_fixed_t *path,
962 cairo_fill_rule_t fill_rule,
963 double tolerance,
964 cairo_antialias_t antialias,
965 const cairo_clip_t *clip)
966 {
967 return CAIRO_INT_STATUS_SUCCESS;
968 }
969
970 static cairo_int_status_t
_show_glyphs_return_success(void * surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_glyph_t * glyphs,int num_glyphs,cairo_scaled_font_t * scaled_font,const cairo_clip_t * clip)971 _show_glyphs_return_success (void *surface,
972 cairo_operator_t op,
973 const cairo_pattern_t *source,
974 cairo_glyph_t *glyphs,
975 int num_glyphs,
976 cairo_scaled_font_t *scaled_font,
977 const cairo_clip_t *clip)
978 {
979 return CAIRO_INT_STATUS_SUCCESS;
980 }
981
982 static const cairo_surface_backend_t cairo_null_surface_backend = {
983 CAIRO_INTERNAL_SURFACE_TYPE_NULL,
984 NULL, /* finish */
985
986 NULL, /* only accessed through the surface functions */
987
988 NULL, /* create_similar */
989 NULL, /* create similar image */
990 NULL, /* map to image */
991 NULL, /* unmap image*/
992
993 NULL, /* source */
994 NULL, /* acquire_source_image */
995 NULL, /* release_source_image */
996 NULL, /* snapshot */
997
998 NULL, /* copy_page */
999 NULL, /* show_page */
1000
1001 NULL, /* get_extents */
1002 NULL, /* get_font_options */
1003
1004 NULL, /* flush */
1005 NULL, /* mark_dirty_rectangle */
1006
1007 _paint_return_success, /* paint */
1008 _mask_return_success, /* mask */
1009 _stroke_return_success, /* stroke */
1010 _fill_return_success, /* fill */
1011 NULL, /* fill_stroke */
1012 _show_glyphs_return_success, /* show_glyphs */
1013 NULL, /* has_show_text_glyphs */
1014 NULL /* show_text_glyphs */
1015 };
1016
1017 cairo_surface_t *
_cairo_null_surface_create(cairo_content_t content)1018 _cairo_null_surface_create (cairo_content_t content)
1019 {
1020 cairo_surface_t *surface;
1021
1022 surface = _cairo_malloc (sizeof (cairo_surface_t));
1023 if (unlikely (surface == NULL)) {
1024 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1025 }
1026
1027 _cairo_surface_init (surface,
1028 &cairo_null_surface_backend,
1029 NULL, /* device */
1030 content,
1031 TRUE); /* is_vector */
1032
1033 return surface;
1034 }
1035