Coverage Report

Created: 2025-07-07 10:01

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