Line data Source code
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-error-private.h"
41 : #include "cairo-paginated-private.h"
42 : #include "cairo-recording-surface-private.h"
43 : #include "cairo-surface-subsurface-private.h"
44 : #include "cairo-region-private.h"
45 :
46 : typedef struct {
47 : cairo_surface_t base;
48 :
49 : cairo_surface_t *target;
50 :
51 : cairo_bool_t first_op;
52 : cairo_bool_t has_supported;
53 : cairo_bool_t has_unsupported;
54 :
55 : cairo_region_t supported_region;
56 : cairo_region_t fallback_region;
57 : cairo_box_t page_bbox;
58 :
59 : cairo_bool_t has_ctm;
60 : cairo_matrix_t ctm;
61 :
62 : } cairo_analysis_surface_t;
63 :
64 : cairo_int_status_t
65 0 : _cairo_analysis_surface_merge_status (cairo_int_status_t status_a,
66 : cairo_int_status_t status_b)
67 : {
68 : /* fatal errors should be checked and propagated at source */
69 0 : assert (! _cairo_status_is_error (status_a));
70 0 : assert (! _cairo_status_is_error (status_b));
71 :
72 : /* return the most important status */
73 0 : if (status_a == CAIRO_INT_STATUS_UNSUPPORTED ||
74 : status_b == CAIRO_INT_STATUS_UNSUPPORTED)
75 0 : return CAIRO_INT_STATUS_UNSUPPORTED;
76 :
77 0 : if (status_a == CAIRO_INT_STATUS_IMAGE_FALLBACK ||
78 : status_b == CAIRO_INT_STATUS_IMAGE_FALLBACK)
79 0 : return CAIRO_INT_STATUS_IMAGE_FALLBACK;
80 :
81 0 : if (status_a == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN ||
82 : status_b == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
83 0 : return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
84 :
85 0 : if (status_a == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
86 : status_b == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
87 0 : return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
88 :
89 : /* at this point we have checked all the valid internal codes, so... */
90 0 : assert (status_a == CAIRO_STATUS_SUCCESS &&
91 : status_b == CAIRO_STATUS_SUCCESS);
92 :
93 0 : return CAIRO_STATUS_SUCCESS;
94 : }
95 :
96 : static cairo_int_status_t
97 0 : _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
98 : const cairo_pattern_t *pattern)
99 : {
100 : const cairo_surface_pattern_t *surface_pattern;
101 : cairo_bool_t old_has_ctm;
102 : cairo_matrix_t old_ctm, p2d;
103 : cairo_status_t status;
104 : cairo_surface_t *source;
105 :
106 0 : assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
107 0 : surface_pattern = (const cairo_surface_pattern_t *) pattern;
108 0 : assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING);
109 :
110 0 : old_ctm = surface->ctm;
111 0 : old_has_ctm = surface->has_ctm;
112 :
113 0 : p2d = pattern->matrix;
114 0 : status = cairo_matrix_invert (&p2d);
115 0 : assert (status == CAIRO_STATUS_SUCCESS);
116 :
117 0 : cairo_matrix_multiply (&surface->ctm, &p2d, &surface->ctm);
118 0 : surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm);
119 :
120 0 : source = surface_pattern->surface;
121 0 : if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
122 0 : cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
123 0 : source = sub->target;
124 : }
125 :
126 0 : status = _cairo_recording_surface_replay_and_create_regions (source, &surface->base);
127 :
128 0 : surface->ctm = old_ctm;
129 0 : surface->has_ctm = old_has_ctm;
130 :
131 0 : return status;
132 : }
133 :
134 : static cairo_int_status_t
135 0 : _add_operation (cairo_analysis_surface_t *surface,
136 : cairo_rectangle_int_t *rect,
137 : cairo_int_status_t backend_status)
138 : {
139 : cairo_int_status_t status;
140 : cairo_box_t bbox;
141 :
142 0 : if (rect->width == 0 || rect->height == 0) {
143 : /* Even though the operation is not visible we must be careful
144 : * to not allow unsupported operations to be replayed to the
145 : * backend during CAIRO_PAGINATED_MODE_RENDER */
146 0 : if (backend_status == CAIRO_STATUS_SUCCESS ||
147 : backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
148 : {
149 0 : return CAIRO_STATUS_SUCCESS;
150 : }
151 : else
152 : {
153 0 : return CAIRO_INT_STATUS_IMAGE_FALLBACK;
154 : }
155 : }
156 :
157 0 : _cairo_box_from_rectangle (&bbox, rect);
158 :
159 0 : if (surface->has_ctm) {
160 : int tx, ty;
161 :
162 0 : if (_cairo_matrix_is_integer_translation (&surface->ctm, &tx, &ty)) {
163 0 : rect->x += tx;
164 0 : rect->y += ty;
165 : } else {
166 0 : _cairo_matrix_transform_bounding_box_fixed (&surface->ctm,
167 : &bbox, NULL);
168 :
169 0 : if (bbox.p1.x == bbox.p2.x || bbox.p1.y == bbox.p2.y) {
170 : /* Even though the operation is not visible we must be
171 : * careful to not allow unsupported operations to be
172 : * replayed to the backend during
173 : * CAIRO_PAGINATED_MODE_RENDER */
174 0 : if (backend_status == CAIRO_STATUS_SUCCESS ||
175 : backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
176 : {
177 0 : return CAIRO_STATUS_SUCCESS;
178 : }
179 : else
180 : {
181 0 : return CAIRO_INT_STATUS_IMAGE_FALLBACK;
182 : }
183 : }
184 :
185 0 : _cairo_box_round_to_rectangle (&bbox, rect);
186 : }
187 : }
188 :
189 0 : if (surface->first_op) {
190 0 : surface->first_op = FALSE;
191 0 : surface->page_bbox = bbox;
192 : } else {
193 0 : if (bbox.p1.x < surface->page_bbox.p1.x)
194 0 : surface->page_bbox.p1.x = bbox.p1.x;
195 0 : if (bbox.p1.y < surface->page_bbox.p1.y)
196 0 : surface->page_bbox.p1.y = bbox.p1.y;
197 0 : if (bbox.p2.x > surface->page_bbox.p2.x)
198 0 : surface->page_bbox.p2.x = bbox.p2.x;
199 0 : if (bbox.p2.y > surface->page_bbox.p2.y)
200 0 : surface->page_bbox.p2.y = bbox.p2.y;
201 : }
202 :
203 : /* If the operation is completely enclosed within the fallback
204 : * region there is no benefit in emitting a native operation as
205 : * the fallback image will be painted on top.
206 : */
207 0 : if (cairo_region_contains_rectangle (&surface->fallback_region, rect) == CAIRO_REGION_OVERLAP_IN)
208 0 : return CAIRO_INT_STATUS_IMAGE_FALLBACK;
209 :
210 0 : if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) {
211 : /* A status of CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY indicates
212 : * that the backend only supports this operation if the
213 : * transparency removed. If the extents of this operation does
214 : * not intersect any other native operation, the operation is
215 : * natively supported and the backend will blend the
216 : * transparency into the white background.
217 : */
218 0 : if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT)
219 0 : backend_status = CAIRO_STATUS_SUCCESS;
220 : }
221 :
222 0 : if (backend_status == CAIRO_STATUS_SUCCESS) {
223 : /* Add the operation to the supported region. Operations in
224 : * this region will be emitted as native operations.
225 : */
226 0 : surface->has_supported = TRUE;
227 0 : return cairo_region_union_rectangle (&surface->supported_region, rect);
228 : }
229 :
230 : /* Add the operation to the unsupported region. This region will
231 : * be painted as an image after all native operations have been
232 : * emitted.
233 : */
234 0 : surface->has_unsupported = TRUE;
235 0 : status = cairo_region_union_rectangle (&surface->fallback_region, rect);
236 :
237 : /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate
238 : * unsupported operations to the recording surface as using
239 : * CAIRO_INT_STATUS_UNSUPPORTED would cause cairo-surface to
240 : * invoke the cairo-surface-fallback path then return
241 : * CAIRO_STATUS_SUCCESS.
242 : */
243 0 : if (status == CAIRO_STATUS_SUCCESS)
244 0 : return CAIRO_INT_STATUS_IMAGE_FALLBACK;
245 : else
246 0 : return status;
247 : }
248 :
249 : static cairo_status_t
250 0 : _cairo_analysis_surface_finish (void *abstract_surface)
251 : {
252 0 : cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
253 :
254 0 : _cairo_region_fini (&surface->supported_region);
255 0 : _cairo_region_fini (&surface->fallback_region);
256 :
257 0 : cairo_surface_destroy (surface->target);
258 :
259 0 : return CAIRO_STATUS_SUCCESS;
260 : }
261 :
262 : static cairo_bool_t
263 0 : _cairo_analysis_surface_get_extents (void *abstract_surface,
264 : cairo_rectangle_int_t *rectangle)
265 : {
266 0 : cairo_analysis_surface_t *surface = abstract_surface;
267 :
268 0 : return _cairo_surface_get_extents (surface->target, rectangle);
269 : }
270 :
271 : static void
272 0 : _rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip)
273 : {
274 : const cairo_rectangle_int_t *clip_extents;
275 : cairo_bool_t is_empty;
276 :
277 0 : clip_extents = NULL;
278 0 : if (clip != NULL)
279 0 : clip_extents = _cairo_clip_get_extents (clip);
280 :
281 0 : if (clip_extents != NULL)
282 0 : is_empty = _cairo_rectangle_intersect (extents, clip_extents);
283 0 : }
284 :
285 : static void
286 0 : _cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface,
287 : cairo_operator_t op,
288 : const cairo_pattern_t *source,
289 : cairo_clip_t *clip,
290 : cairo_rectangle_int_t *extents)
291 : {
292 : cairo_bool_t is_empty;
293 :
294 0 : is_empty = _cairo_surface_get_extents (&surface->base, extents);
295 :
296 0 : if (_cairo_operator_bounded_by_source (op)) {
297 : cairo_rectangle_int_t source_extents;
298 :
299 0 : _cairo_pattern_get_extents (source, &source_extents);
300 0 : is_empty = _cairo_rectangle_intersect (extents, &source_extents);
301 : }
302 :
303 0 : _rectangle_intersect_clip (extents, clip);
304 0 : }
305 :
306 : static cairo_int_status_t
307 0 : _cairo_analysis_surface_paint (void *abstract_surface,
308 : cairo_operator_t op,
309 : const cairo_pattern_t *source,
310 : cairo_clip_t *clip)
311 : {
312 0 : cairo_analysis_surface_t *surface = abstract_surface;
313 : cairo_status_t backend_status;
314 : cairo_rectangle_int_t extents;
315 :
316 0 : if (surface->target->backend->paint == NULL) {
317 0 : backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
318 : } else {
319 0 : backend_status =
320 0 : surface->target->backend->paint (surface->target,
321 : op, source, clip);
322 0 : if (_cairo_status_is_error (backend_status))
323 0 : return backend_status;
324 : }
325 :
326 0 : if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
327 0 : backend_status = _analyze_recording_surface_pattern (surface, source);
328 :
329 0 : _cairo_analysis_surface_operation_extents (surface,
330 : op, source, clip,
331 : &extents);
332 :
333 0 : return _add_operation (surface, &extents, backend_status);
334 : }
335 :
336 : static cairo_int_status_t
337 0 : _cairo_analysis_surface_mask (void *abstract_surface,
338 : cairo_operator_t op,
339 : const cairo_pattern_t *source,
340 : const cairo_pattern_t *mask,
341 : cairo_clip_t *clip)
342 : {
343 0 : cairo_analysis_surface_t *surface = abstract_surface;
344 : cairo_int_status_t backend_status;
345 : cairo_rectangle_int_t extents;
346 : cairo_bool_t is_empty;
347 :
348 0 : if (surface->target->backend->mask == NULL) {
349 0 : backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
350 : } else {
351 0 : backend_status =
352 0 : surface->target->backend->mask (surface->target,
353 : op, source, mask, clip);
354 0 : if (_cairo_status_is_error (backend_status))
355 0 : return backend_status;
356 : }
357 :
358 0 : if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
359 0 : cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS;
360 0 : cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS;
361 :
362 0 : if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
363 0 : const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) source;
364 0 : if (_cairo_surface_is_recording (surface_pattern->surface)) {
365 0 : backend_source_status =
366 : _analyze_recording_surface_pattern (surface, source);
367 0 : if (_cairo_status_is_error (backend_source_status))
368 0 : return backend_source_status;
369 : }
370 : }
371 :
372 0 : if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
373 0 : cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask;
374 0 : if (_cairo_surface_is_recording (surface_pattern->surface)) {
375 0 : backend_mask_status =
376 : _analyze_recording_surface_pattern (surface, mask);
377 0 : if (_cairo_status_is_error (backend_mask_status))
378 0 : return backend_mask_status;
379 : }
380 : }
381 :
382 0 : backend_status =
383 : _cairo_analysis_surface_merge_status (backend_source_status,
384 : backend_mask_status);
385 : }
386 :
387 0 : _cairo_analysis_surface_operation_extents (surface,
388 : op, source, clip,
389 : &extents);
390 :
391 0 : if (_cairo_operator_bounded_by_mask (op)) {
392 : cairo_rectangle_int_t mask_extents;
393 :
394 0 : _cairo_pattern_get_extents (mask, &mask_extents);
395 0 : is_empty = _cairo_rectangle_intersect (&extents, &mask_extents);
396 :
397 : }
398 :
399 0 : return _add_operation (surface, &extents, backend_status);
400 : }
401 :
402 : static cairo_int_status_t
403 0 : _cairo_analysis_surface_stroke (void *abstract_surface,
404 : cairo_operator_t op,
405 : const cairo_pattern_t *source,
406 : cairo_path_fixed_t *path,
407 : const cairo_stroke_style_t *style,
408 : const cairo_matrix_t *ctm,
409 : const cairo_matrix_t *ctm_inverse,
410 : double tolerance,
411 : cairo_antialias_t antialias,
412 : cairo_clip_t *clip)
413 : {
414 0 : cairo_analysis_surface_t *surface = abstract_surface;
415 : cairo_status_t backend_status;
416 : cairo_rectangle_int_t extents;
417 : cairo_bool_t is_empty;
418 :
419 0 : if (surface->target->backend->stroke == NULL) {
420 0 : backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
421 : } else {
422 0 : backend_status =
423 0 : surface->target->backend->stroke (surface->target, op,
424 : source, path, style,
425 : ctm, ctm_inverse,
426 : tolerance, antialias,
427 : clip);
428 0 : if (_cairo_status_is_error (backend_status))
429 0 : return backend_status;
430 : }
431 :
432 0 : if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
433 0 : backend_status = _analyze_recording_surface_pattern (surface, source);
434 :
435 0 : _cairo_analysis_surface_operation_extents (surface,
436 : op, source, clip,
437 : &extents);
438 :
439 0 : if (_cairo_operator_bounded_by_mask (op)) {
440 : cairo_rectangle_int_t mask_extents;
441 :
442 : /* If the backend can handle the stroke, then mark the approximate
443 : * extents of the operation. However, if we need to fallback in order
444 : * to draw the stroke, then ensure that the fallback is as tight as
445 : * possible -- both to minimise output file size and to ensure good
446 : * quality printed output for neighbouring regions.
447 : */
448 0 : if (backend_status == CAIRO_STATUS_SUCCESS) {
449 0 : _cairo_path_fixed_approximate_stroke_extents (path,
450 : style, ctm,
451 : &mask_extents);
452 : } else {
453 : cairo_status_t status;
454 :
455 0 : status = _cairo_path_fixed_stroke_extents (path, style,
456 : ctm, ctm_inverse,
457 : tolerance,
458 : &mask_extents);
459 0 : if (unlikely (status))
460 0 : return status;
461 : }
462 :
463 0 : is_empty = _cairo_rectangle_intersect (&extents, &mask_extents);
464 : }
465 :
466 0 : return _add_operation (surface, &extents, backend_status);
467 : }
468 :
469 : static cairo_int_status_t
470 0 : _cairo_analysis_surface_fill (void *abstract_surface,
471 : cairo_operator_t op,
472 : const cairo_pattern_t *source,
473 : cairo_path_fixed_t *path,
474 : cairo_fill_rule_t fill_rule,
475 : double tolerance,
476 : cairo_antialias_t antialias,
477 : cairo_clip_t *clip)
478 : {
479 0 : cairo_analysis_surface_t *surface = abstract_surface;
480 : cairo_status_t backend_status;
481 : cairo_rectangle_int_t extents;
482 : cairo_bool_t is_empty;
483 :
484 0 : if (surface->target->backend->fill == NULL) {
485 0 : backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
486 : } else {
487 0 : backend_status =
488 0 : surface->target->backend->fill (surface->target, op,
489 : source, path, fill_rule,
490 : tolerance, antialias,
491 : clip);
492 0 : if (_cairo_status_is_error (backend_status))
493 0 : return backend_status;
494 : }
495 :
496 0 : if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
497 0 : backend_status = _analyze_recording_surface_pattern (surface, source);
498 :
499 0 : _cairo_analysis_surface_operation_extents (surface,
500 : op, source, clip,
501 : &extents);
502 :
503 0 : if (_cairo_operator_bounded_by_mask (op)) {
504 : cairo_rectangle_int_t mask_extents;
505 :
506 : /* We want speed for the likely case where the operation can be
507 : * performed natively, but accuracy if we have to resort to
508 : * using images.
509 : */
510 0 : if (backend_status == CAIRO_STATUS_SUCCESS) {
511 0 : _cairo_path_fixed_approximate_fill_extents (path, &mask_extents);
512 : } else {
513 0 : _cairo_path_fixed_fill_extents (path, fill_rule, tolerance,
514 : &mask_extents);
515 : }
516 0 : is_empty = _cairo_rectangle_intersect (&extents, &mask_extents);
517 : }
518 :
519 0 : return _add_operation (surface, &extents, backend_status);
520 : }
521 :
522 : static cairo_int_status_t
523 0 : _cairo_analysis_surface_show_glyphs (void *abstract_surface,
524 : cairo_operator_t op,
525 : const cairo_pattern_t *source,
526 : cairo_glyph_t *glyphs,
527 : int num_glyphs,
528 : cairo_scaled_font_t *scaled_font,
529 : cairo_clip_t *clip,
530 : int *remaining_glyphs)
531 : {
532 0 : cairo_analysis_surface_t *surface = abstract_surface;
533 : cairo_status_t status, backend_status;
534 : cairo_rectangle_int_t extents, glyph_extents;
535 : cairo_bool_t is_empty;
536 :
537 : /* Adapted from _cairo_surface_show_glyphs */
538 0 : if (surface->target->backend->show_glyphs != NULL) {
539 0 : backend_status =
540 0 : surface->target->backend->show_glyphs (surface->target, op,
541 : source,
542 : glyphs, num_glyphs,
543 : scaled_font,
544 : clip,
545 : remaining_glyphs);
546 0 : if (_cairo_status_is_error (backend_status))
547 0 : return backend_status;
548 : }
549 0 : else if (surface->target->backend->show_text_glyphs != NULL)
550 : {
551 0 : backend_status =
552 0 : surface->target->backend->show_text_glyphs (surface->target, op,
553 : source,
554 : NULL, 0,
555 : glyphs, num_glyphs,
556 : NULL, 0,
557 : FALSE,
558 : scaled_font,
559 : clip);
560 0 : if (_cairo_status_is_error (backend_status))
561 0 : return backend_status;
562 : }
563 : else
564 : {
565 0 : backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
566 : }
567 :
568 0 : if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
569 0 : backend_status = _analyze_recording_surface_pattern (surface, source);
570 :
571 0 : _cairo_analysis_surface_operation_extents (surface,
572 : op, source, clip,
573 : &extents);
574 :
575 0 : if (_cairo_operator_bounded_by_mask (op)) {
576 0 : status = _cairo_scaled_font_glyph_device_extents (scaled_font,
577 : glyphs,
578 : num_glyphs,
579 : &glyph_extents,
580 : NULL);
581 0 : if (unlikely (status))
582 0 : return status;
583 :
584 0 : is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents);
585 : }
586 :
587 0 : return _add_operation (surface, &extents, backend_status);
588 : }
589 :
590 : static cairo_bool_t
591 0 : _cairo_analysis_surface_has_show_text_glyphs (void *abstract_surface)
592 : {
593 0 : cairo_analysis_surface_t *surface = abstract_surface;
594 :
595 0 : return cairo_surface_has_show_text_glyphs (surface->target);
596 : }
597 :
598 : static cairo_int_status_t
599 0 : _cairo_analysis_surface_show_text_glyphs (void *abstract_surface,
600 : cairo_operator_t op,
601 : const cairo_pattern_t *source,
602 : const char *utf8,
603 : int utf8_len,
604 : cairo_glyph_t *glyphs,
605 : int num_glyphs,
606 : const cairo_text_cluster_t *clusters,
607 : int num_clusters,
608 : cairo_text_cluster_flags_t cluster_flags,
609 : cairo_scaled_font_t *scaled_font,
610 : cairo_clip_t *clip)
611 : {
612 0 : cairo_analysis_surface_t *surface = abstract_surface;
613 : cairo_status_t status, backend_status;
614 : cairo_rectangle_int_t extents, glyph_extents;
615 : cairo_bool_t is_empty;
616 :
617 : /* Adapted from _cairo_surface_show_glyphs */
618 0 : backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
619 0 : if (surface->target->backend->show_text_glyphs != NULL) {
620 0 : backend_status =
621 0 : surface->target->backend->show_text_glyphs (surface->target, op,
622 : source,
623 : utf8, utf8_len,
624 : glyphs, num_glyphs,
625 : clusters, num_clusters,
626 : cluster_flags,
627 : scaled_font,
628 : clip);
629 0 : if (_cairo_status_is_error (backend_status))
630 0 : return backend_status;
631 : }
632 0 : if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED &&
633 0 : surface->target->backend->show_glyphs != NULL)
634 : {
635 0 : int remaining_glyphs = num_glyphs;
636 0 : backend_status =
637 0 : surface->target->backend->show_glyphs (surface->target, op,
638 : source,
639 : glyphs, num_glyphs,
640 : scaled_font,
641 : clip,
642 : &remaining_glyphs);
643 0 : if (_cairo_status_is_error (backend_status))
644 0 : return backend_status;
645 :
646 0 : glyphs += num_glyphs - remaining_glyphs;
647 0 : num_glyphs = remaining_glyphs;
648 0 : if (remaining_glyphs == 0)
649 0 : backend_status = CAIRO_STATUS_SUCCESS;
650 : }
651 :
652 0 : if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
653 0 : backend_status = _analyze_recording_surface_pattern (surface, source);
654 :
655 0 : _cairo_analysis_surface_operation_extents (surface,
656 : op, source, clip,
657 : &extents);
658 :
659 0 : if (_cairo_operator_bounded_by_mask (op)) {
660 0 : status = _cairo_scaled_font_glyph_device_extents (scaled_font,
661 : glyphs,
662 : num_glyphs,
663 : &glyph_extents,
664 : NULL);
665 0 : if (unlikely (status))
666 0 : return status;
667 :
668 0 : is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents);
669 : }
670 :
671 0 : return _add_operation (surface, &extents, backend_status);
672 : }
673 :
674 : static const cairo_surface_backend_t cairo_analysis_surface_backend = {
675 : CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
676 : NULL, /* create_similar */
677 : _cairo_analysis_surface_finish,
678 : NULL, /* acquire_source_image */
679 : NULL, /* release_source_image */
680 : NULL, /* acquire_dest_image */
681 : NULL, /* release_dest_image */
682 : NULL, /* clone_similar */
683 : NULL, /* composite */
684 : NULL, /* fill_rectangles */
685 : NULL, /* composite_trapezoids */
686 : NULL, /* create_span_renderer */
687 : NULL, /* check_span_renderer */
688 : NULL, /* copy_page */
689 : NULL, /* show_page */
690 : _cairo_analysis_surface_get_extents,
691 : NULL, /* old_show_glyphs */
692 : NULL, /* get_font_options */
693 : NULL, /* flush */
694 : NULL, /* mark_dirty_rectangle */
695 : NULL, /* scaled_font_fini */
696 : NULL, /* scaled_glyph_fini */
697 : _cairo_analysis_surface_paint,
698 : _cairo_analysis_surface_mask,
699 : _cairo_analysis_surface_stroke,
700 : _cairo_analysis_surface_fill,
701 : _cairo_analysis_surface_show_glyphs,
702 : NULL, /* snapshot */
703 : NULL, /* is_similar */
704 : NULL, /* fill_stroke */
705 : NULL, /* create_solid_pattern_surface */
706 : NULL, /* can_repaint_solid_pattern_surface */
707 : _cairo_analysis_surface_has_show_text_glyphs,
708 : _cairo_analysis_surface_show_text_glyphs
709 : };
710 :
711 : cairo_surface_t *
712 0 : _cairo_analysis_surface_create (cairo_surface_t *target)
713 : {
714 : cairo_analysis_surface_t *surface;
715 : cairo_status_t status;
716 :
717 0 : status = target->status;
718 0 : if (unlikely (status))
719 0 : return _cairo_surface_create_in_error (status);
720 :
721 0 : surface = malloc (sizeof (cairo_analysis_surface_t));
722 0 : if (unlikely (surface == NULL))
723 0 : return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
724 :
725 : /* I believe the content type here is truly arbitrary. I'm quite
726 : * sure nothing will ever use this value. */
727 0 : _cairo_surface_init (&surface->base,
728 : &cairo_analysis_surface_backend,
729 : NULL, /* device */
730 : CAIRO_CONTENT_COLOR_ALPHA);
731 :
732 0 : cairo_matrix_init_identity (&surface->ctm);
733 0 : surface->has_ctm = FALSE;
734 :
735 0 : surface->target = cairo_surface_reference (target);
736 0 : surface->first_op = TRUE;
737 0 : surface->has_supported = FALSE;
738 0 : surface->has_unsupported = FALSE;
739 :
740 0 : _cairo_region_init (&surface->supported_region);
741 0 : _cairo_region_init (&surface->fallback_region);
742 :
743 0 : surface->page_bbox.p1.x = 0;
744 0 : surface->page_bbox.p1.y = 0;
745 0 : surface->page_bbox.p2.x = 0;
746 0 : surface->page_bbox.p2.y = 0;
747 :
748 0 : return &surface->base;
749 : }
750 :
751 : void
752 0 : _cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface,
753 : const cairo_matrix_t *ctm)
754 : {
755 : cairo_analysis_surface_t *surface;
756 :
757 0 : if (abstract_surface->status)
758 0 : return;
759 :
760 0 : surface = (cairo_analysis_surface_t *) abstract_surface;
761 :
762 0 : surface->ctm = *ctm;
763 0 : surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm);
764 : }
765 :
766 : void
767 0 : _cairo_analysis_surface_get_ctm (cairo_surface_t *abstract_surface,
768 : cairo_matrix_t *ctm)
769 : {
770 0 : cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
771 :
772 0 : *ctm = surface->ctm;
773 0 : }
774 :
775 :
776 : cairo_region_t *
777 0 : _cairo_analysis_surface_get_supported (cairo_surface_t *abstract_surface)
778 : {
779 0 : cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
780 :
781 0 : return &surface->supported_region;
782 : }
783 :
784 : cairo_region_t *
785 0 : _cairo_analysis_surface_get_unsupported (cairo_surface_t *abstract_surface)
786 : {
787 0 : cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
788 :
789 0 : return &surface->fallback_region;
790 : }
791 :
792 : cairo_bool_t
793 0 : _cairo_analysis_surface_has_supported (cairo_surface_t *abstract_surface)
794 : {
795 0 : cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
796 :
797 0 : return surface->has_supported;
798 : }
799 :
800 : cairo_bool_t
801 0 : _cairo_analysis_surface_has_unsupported (cairo_surface_t *abstract_surface)
802 : {
803 0 : cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
804 :
805 0 : return surface->has_unsupported;
806 : }
807 :
808 : void
809 0 : _cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface,
810 : cairo_box_t *bbox)
811 : {
812 0 : cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface;
813 :
814 0 : *bbox = surface->page_bbox;
815 0 : }
816 :
817 : /* null surface type: a surface that does nothing (has no side effects, yay!) */
818 :
819 : static cairo_int_status_t
820 0 : _return_success (void)
821 : {
822 0 : return CAIRO_STATUS_SUCCESS;
823 : }
824 :
825 : /* These typedefs are just to silence the compiler... */
826 : typedef cairo_int_status_t
827 : (*_paint_func) (void *surface,
828 : cairo_operator_t op,
829 : const cairo_pattern_t *source,
830 : cairo_clip_t *clip);
831 :
832 : typedef cairo_int_status_t
833 : (*_mask_func) (void *surface,
834 : cairo_operator_t op,
835 : const cairo_pattern_t *source,
836 : const cairo_pattern_t *mask,
837 : cairo_clip_t *clip);
838 :
839 : typedef cairo_int_status_t
840 : (*_stroke_func) (void *surface,
841 : cairo_operator_t op,
842 : const cairo_pattern_t *source,
843 : cairo_path_fixed_t *path,
844 : const cairo_stroke_style_t *style,
845 : const cairo_matrix_t *ctm,
846 : const cairo_matrix_t *ctm_inverse,
847 : double tolerance,
848 : cairo_antialias_t antialias,
849 : cairo_clip_t *clip);
850 :
851 : typedef cairo_int_status_t
852 : (*_fill_func) (void *surface,
853 : cairo_operator_t op,
854 : const cairo_pattern_t *source,
855 : cairo_path_fixed_t *path,
856 : cairo_fill_rule_t fill_rule,
857 : double tolerance,
858 : cairo_antialias_t antialias,
859 : cairo_clip_t *clip);
860 :
861 : typedef cairo_int_status_t
862 : (*_show_glyphs_func) (void *surface,
863 : cairo_operator_t op,
864 : const cairo_pattern_t *source,
865 : cairo_glyph_t *glyphs,
866 : int num_glyphs,
867 : cairo_scaled_font_t *scaled_font,
868 : cairo_clip_t *clip,
869 : int *remaining_glyphs);
870 :
871 : static const cairo_surface_backend_t cairo_null_surface_backend = {
872 : CAIRO_INTERNAL_SURFACE_TYPE_NULL,
873 :
874 : NULL, /* create_similar */
875 : NULL, /* finish */
876 : NULL, /* acquire_source_image */
877 : NULL, /* release_source_image */
878 : NULL, /* acquire_dest_image */
879 : NULL, /* release_dest_image */
880 : NULL, /* clone_similar */
881 : NULL, /* composite */
882 : NULL, /* fill_rectangles */
883 : NULL, /* composite_trapezoids */
884 : NULL, /* create_span_renderer */
885 : NULL, /* check_span_renderer */
886 : NULL, /* copy_page */
887 : NULL, /* show_page */
888 : NULL, /* get_extents */
889 : NULL, /* old_show_glyphs */
890 : NULL, /* get_font_options */
891 : NULL, /* flush */
892 : NULL, /* mark_dirty_rectangle */
893 : NULL, /* scaled_font_fini */
894 : NULL, /* scaled_glyph_fini */
895 : (_paint_func) _return_success, /* paint */
896 : (_mask_func) _return_success, /* mask */
897 : (_stroke_func) _return_success, /* stroke */
898 : (_fill_func) _return_success, /* fill */
899 : (_show_glyphs_func) _return_success, /* show_glyphs */
900 : NULL, /* snapshot */
901 : NULL, /* is_similar */
902 : NULL, /* fill_stroke */
903 : NULL, /* create_solid_pattern_surface */
904 : NULL, /* can_repaint_solid_pattern_surface */
905 : NULL, /* has_show_text_glyphs */
906 : NULL /* show_text_glyphs */
907 : };
908 :
909 : cairo_surface_t *
910 0 : cairo_null_surface_create (cairo_content_t content)
911 : {
912 : cairo_surface_t *surface;
913 :
914 0 : surface = malloc (sizeof (cairo_surface_t));
915 0 : if (unlikely (surface == NULL)) {
916 0 : return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
917 : }
918 :
919 0 : _cairo_surface_init (surface,
920 : &cairo_null_surface_backend,
921 : NULL, /* device */
922 : content);
923 :
924 0 : return surface;
925 : }
|