Coverage Report

Created: 2025-11-16 09:57

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