Subversion Repositories Scribus

Rev

Rev 14952 | Details | Compare with Previous | Last modification | View Log | RSS feed

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