Coverage Report

Created: 2026-03-31 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cairo/src/cairo-recording-surface.c
Line
Count
Source
1
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
2
/* cairo - a vector graphics library with display and print output
3
 *
4
 * Copyright © 2005 Red Hat, Inc
5
 * Copyright © 2007 Adrian Johnson
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it either under the terms of the GNU Lesser General Public
9
 * License version 2.1 as published by the Free Software Foundation
10
 * (the "LGPL") or, at your option, under the terms of the Mozilla
11
 * Public License Version 1.1 (the "MPL"). If you do not alter this
12
 * notice, a recipient may use your version of this file under either
13
 * the MPL or the LGPL.
14
 *
15
 * You should have received a copy of the LGPL along with this library
16
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
18
 * You should have received a copy of the MPL along with this library
19
 * in the file COPYING-MPL-1.1
20
 *
21
 * The contents of this file are subject to the Mozilla Public License
22
 * Version 1.1 (the "License"); you may not use this file except in
23
 * compliance with the License. You may obtain a copy of the License at
24
 * http://www.mozilla.org/MPL/
25
 *
26
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28
 * the specific language governing rights and limitations.
29
 *
30
 * The Original Code is the cairo graphics library.
31
 *
32
 * The Initial Developer of the Original Code is Red Hat, Inc.
33
 *
34
 * Contributor(s):
35
 *  Kristian Høgsberg <krh@redhat.com>
36
 *  Carl Worth <cworth@cworth.org>
37
 *  Adrian Johnson <ajohnson@redneon.com>
38
 */
39
40
/**
41
 * SECTION:cairo-recording
42
 * @Title: Recording Surfaces
43
 * @Short_Description: Records all drawing operations
44
 * @See_Also: #cairo_surface_t
45
 *
46
 * A recording surface is a surface that records all drawing operations at
47
 * the highest level of the surface backend interface, (that is, the
48
 * level of paint, mask, stroke, fill, and show_text_glyphs). The recording
49
 * surface can then be "replayed" against any target surface by using it
50
 * as a source surface.
51
 *
52
 * If you want to replay a surface so that the results in target will be
53
 * identical to the results that would have been obtained if the original
54
 * operations applied to the recording surface had instead been applied to the
55
 * target surface, you can use code like this:
56
 * <informalexample><programlisting>
57
 * cairo_t *cr;
58
 *
59
 * cr = cairo_create (target);
60
 * cairo_set_source_surface (cr, recording_surface, 0.0, 0.0);
61
 * cairo_paint (cr);
62
 * cairo_destroy (cr);
63
 * </programlisting></informalexample>
64
 *
65
 * A recording surface is logically unbounded, i.e. it has no implicit constraint
66
 * on the size of the drawing surface. However, in practice this is rarely
67
 * useful as you wish to replay against a particular target surface with
68
 * known bounds. For this case, it is more efficient to specify the target
69
 * extents to the recording surface upon creation.
70
 *
71
 * The recording phase of the recording surface is careful to snapshot all
72
 * necessary objects (paths, patterns, etc.), in order to achieve
73
 * accurate replay. The efficiency of the recording surface could be
74
 * improved by improving the implementation of snapshot for the
75
 * various objects. For example, it would be nice to have a
76
 * copy-on-write implementation for _cairo_surface_snapshot.
77
 **/
78
79
#include "cairoint.h"
80
81
#include "cairo-array-private.h"
82
#include "cairo-analysis-surface-private.h"
83
#include "cairo-clip-private.h"
84
#include "cairo-combsort-inline.h"
85
#include "cairo-composite-rectangles-private.h"
86
#include "cairo-default-context-private.h"
87
#include "cairo-error-private.h"
88
#include "cairo-image-surface-private.h"
89
#include "cairo-list-inline.h"
90
#include "cairo-recording-surface-inline.h"
91
#include "cairo-surface-snapshot-inline.h"
92
#include "cairo-surface-wrapper-private.h"
93
#include "cairo-traps-private.h"
94
95
typedef struct _cairo_recording_surface_replay_params {
96
    const cairo_rectangle_int_t *surface_extents;
97
    const cairo_matrix_t *surface_transform;
98
    cairo_surface_t      *target;
99
    const cairo_clip_t *target_clip;
100
    cairo_bool_t surface_is_unbounded;
101
    cairo_recording_replay_type_t type;
102
    cairo_recording_region_type_t region;
103
    unsigned int regions_id;
104
    const cairo_color_t *foreground_color;
105
    cairo_bool_t foreground_used;
106
    cairo_bool_t replay_all;
107
} cairo_recording_surface_replay_params_t;
108
109
static const cairo_surface_backend_t cairo_recording_surface_backend;
110
111
/**
112
 * CAIRO_HAS_RECORDING_SURFACE:
113
 *
114
 * Defined if the recording surface backend is available.
115
 * The recording surface backend is always built in.
116
 * This macro was added for completeness in cairo 1.10.
117
 *
118
 * Since: 1.10
119
 **/
120
121
/* Currently all recording surfaces do have a size which should be passed
122
 * in as the maximum size of any target surface against which the
123
 * recording-surface will ever be replayed.
124
 *
125
 * XXX: The naming of "pixels" in the size here is a misnomer. It's
126
 * actually a size in whatever device-space units are desired (again,
127
 * according to the intended replay target).
128
 */
129
130
static int bbtree_left_or_right (struct bbtree *bbt,
131
         const cairo_box_t *box)
132
0
{
133
0
    int left, right;
134
135
0
    if (bbt->left) {
136
0
  cairo_box_t *e = &bbt->left->extents;
137
0
  cairo_box_t b;
138
139
0
  b.p1.x = MIN (e->p1.x, box->p1.x);
140
0
  b.p1.y = MIN (e->p1.y, box->p1.y);
141
0
  b.p2.x = MAX (e->p2.x, box->p2.x);
142
0
  b.p2.y = MAX (e->p2.y, box->p2.y);
143
144
0
  left = _cairo_fixed_integer_part (b.p2.x - b.p1.x) * _cairo_fixed_integer_part (b.p2.y - b.p1.y);
145
0
  left -= _cairo_fixed_integer_part (e->p2.x - e->p1.x) * _cairo_fixed_integer_part (e->p2.y - e->p1.y);
146
0
    } else
147
0
  left = 0;
148
149
0
    if (bbt->right) {
150
0
  cairo_box_t *e = &bbt->right->extents;
151
0
  cairo_box_t b;
152
153
0
  b.p1.x = MIN (e->p1.x, box->p1.x);
154
0
  b.p1.y = MIN (e->p1.y, box->p1.y);
155
0
  b.p2.x = MAX (e->p2.x, box->p2.x);
156
0
  b.p2.y = MAX (e->p2.y, box->p2.y);
157
158
0
  right = _cairo_fixed_integer_part (b.p2.x - b.p1.x) * _cairo_fixed_integer_part (b.p2.y - b.p1.y);
159
0
  right -= _cairo_fixed_integer_part (e->p2.x - e->p1.x) * _cairo_fixed_integer_part (e->p2.y - e->p1.y);
160
0
    } else
161
0
  right = 0;
162
163
0
    return left <= right;
164
0
}
165
166
76.2k
#define INVALID_CHAIN ((cairo_command_header_t *)-1)
167
168
static struct bbtree *
169
bbtree_new (const cairo_box_t *box, cairo_command_header_t *chain)
170
0
{
171
0
    struct bbtree *bbt = _cairo_calloc (sizeof (*bbt));
172
0
    if (bbt == NULL)
173
0
  return NULL;
174
0
    bbt->extents = *box;
175
0
    bbt->left = bbt->right = NULL;
176
0
    bbt->chain = chain;
177
0
    return bbt;
178
0
}
179
180
static void
181
bbtree_init (struct bbtree *bbt, cairo_command_header_t *header)
182
454
{
183
454
    _cairo_box_from_rectangle (&bbt->extents, &header->extents);
184
454
    bbt->chain = header;
185
454
}
186
187
static cairo_status_t
188
bbtree_add (struct bbtree *bbt,
189
      cairo_command_header_t *header,
190
      const cairo_box_t *box)
191
0
{
192
0
    if (box->p1.x < bbt->extents.p1.x || box->p1.y < bbt->extents.p1.y ||
193
0
  box->p2.x > bbt->extents.p2.x || box->p2.y > bbt->extents.p2.y)
194
0
    {
195
0
  if (bbt->chain) {
196
0
      if (bbtree_left_or_right (bbt, &bbt->extents)) {
197
0
    if (bbt->left == NULL) {
198
0
        bbt->left = bbtree_new (&bbt->extents, bbt->chain);
199
0
        if (unlikely (bbt->left == NULL))
200
0
      return _cairo_error (CAIRO_STATUS_NO_MEMORY);
201
0
    } else
202
0
        bbtree_add (bbt->left, bbt->chain, &bbt->extents);
203
0
      } else {
204
0
    if (bbt->right == NULL) {
205
0
        bbt->right = bbtree_new (&bbt->extents, bbt->chain);
206
0
        if (unlikely (bbt->right == NULL))
207
0
      return _cairo_error (CAIRO_STATUS_NO_MEMORY);
208
0
    } else
209
0
        bbtree_add (bbt->right, bbt->chain, &bbt->extents);
210
0
      }
211
212
0
      bbt->chain = NULL;
213
0
  }
214
215
0
  bbt->extents.p1.x = MIN (bbt->extents.p1.x, box->p1.x);
216
0
  bbt->extents.p1.y = MIN (bbt->extents.p1.y, box->p1.y);
217
0
  bbt->extents.p2.x = MAX (bbt->extents.p2.x, box->p2.x);
218
0
  bbt->extents.p2.y = MAX (bbt->extents.p2.y, box->p2.y);
219
0
    }
220
221
0
    if (box->p1.x == bbt->extents.p1.x && box->p1.y == bbt->extents.p1.y &&
222
0
  box->p2.x == bbt->extents.p2.x && box->p2.y == bbt->extents.p2.y)
223
0
    {
224
0
  cairo_command_header_t *last = header;
225
0
  while (last->chain) /* expected to be infrequent */
226
0
      last = last->chain;
227
0
  last->chain = bbt->chain;
228
0
  bbt->chain = header;
229
0
  return CAIRO_STATUS_SUCCESS;
230
0
    }
231
232
0
    if (bbtree_left_or_right (bbt, box)) {
233
0
  if (bbt->left == NULL) {
234
0
      bbt->left = bbtree_new (box, header);
235
0
      if (unlikely (bbt->left == NULL))
236
0
    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
237
0
  } else
238
0
      return bbtree_add (bbt->left, header, box);
239
0
    } else {
240
0
  if (bbt->right == NULL) {
241
0
      bbt->right = bbtree_new (box, header);
242
0
      if (unlikely (bbt->right == NULL))
243
0
    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
244
0
  } else
245
0
      return bbtree_add (bbt->right, header, box);
246
0
    }
247
248
0
    return CAIRO_STATUS_SUCCESS;
249
0
}
250
251
static void bbtree_del (struct bbtree *bbt)
252
0
{
253
0
    if (bbt->left)
254
0
  bbtree_del (bbt->left);
255
0
    if (bbt->right)
256
0
  bbtree_del (bbt->right);
257
258
0
    free (bbt);
259
0
}
260
261
static cairo_bool_t box_outside (const cairo_box_t *a, const cairo_box_t *b)
262
0
{
263
0
    return
264
0
  a->p1.x >= b->p2.x || a->p1.y >= b->p2.y ||
265
0
  a->p2.x <= b->p1.x || a->p2.y <= b->p1.y;
266
0
}
267
268
static void
269
bbtree_foreach_mark_visible (struct bbtree *bbt,
270
           const cairo_box_t *box,
271
           unsigned int **indices)
272
454
{
273
454
    cairo_command_header_t *chain;
274
275
908
    for (chain = bbt->chain; chain; chain = chain->chain)
276
454
  *(*indices)++ = chain->index;
277
278
454
    if (bbt->left && ! box_outside (box, &bbt->left->extents))
279
0
  bbtree_foreach_mark_visible (bbt->left, box, indices);
280
454
    if (bbt->right && ! box_outside (box, &bbt->right->extents))
281
0
  bbtree_foreach_mark_visible (bbt->right, box, indices);
282
454
}
283
284
static inline int intcmp (const unsigned int a, const unsigned int b)
285
0
{
286
0
    return a - b;
287
0
}
288
CAIRO_COMBSORT_DECLARE (sort_indices, unsigned int, intcmp)
289
290
static inline int sizecmp (unsigned int a, unsigned int b, cairo_command_header_t **elements)
291
0
{
292
0
    const cairo_rectangle_int_t *r;
293
294
0
    r = &elements[a]->extents;
295
0
    a = r->width * r->height;
296
297
0
    r = &elements[b]->extents;
298
0
    b = r->width * r->height;
299
300
0
    return b - a;
301
0
}
302
CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_commands, unsigned int, sizecmp)
303
304
static void
305
_cairo_recording_surface_destroy_bbtree (cairo_recording_surface_t *surface)
306
60.5k
{
307
60.5k
    cairo_command_t **elements;
308
60.5k
    int i, num_elements;
309
310
60.5k
    if (surface->bbtree.chain == INVALID_CHAIN)
311
60.5k
  return;
312
313
0
    if (surface->bbtree.left) {
314
0
  bbtree_del (surface->bbtree.left);
315
0
  surface->bbtree.left = NULL;
316
0
    }
317
0
    if (surface->bbtree.right) {
318
0
  bbtree_del (surface->bbtree.right);
319
0
  surface->bbtree.right = NULL;
320
0
    }
321
322
0
    elements = _cairo_array_index (&surface->commands, 0);
323
0
    num_elements = surface->commands.num_elements;
324
0
    for (i = 0; i < num_elements; i++)
325
0
  elements[i]->header.chain = NULL;
326
327
0
    surface->bbtree.chain = INVALID_CHAIN;
328
0
}
329
330
static cairo_status_t
331
_cairo_recording_surface_create_bbtree (cairo_recording_surface_t *surface)
332
454
{
333
454
    cairo_command_t **elements = _cairo_array_index (&surface->commands, 0);
334
454
    unsigned int *indices;
335
454
    cairo_status_t status;
336
454
    unsigned int i, count;
337
338
454
    count = surface->commands.num_elements;
339
454
    if (count > surface->num_indices) {
340
454
  free (surface->indices);
341
454
  surface->indices = _cairo_malloc_ab (count, sizeof (int));
342
454
  if (unlikely (surface->indices == NULL))
343
0
      return _cairo_error (CAIRO_STATUS_NO_MEMORY);
344
345
454
  surface->num_indices = count;
346
454
    }
347
348
454
    indices = surface->indices;
349
908
    for (i = 0; i < count; i++)
350
454
  indices[i] = i;
351
352
454
    sort_commands (indices, count, elements);
353
354
454
    bbtree_init (&surface->bbtree, &elements[indices[0]]->header);
355
454
    for (i = 1; i < count; i++) {
356
0
  cairo_command_header_t *header = &elements[indices[i]]->header;
357
0
  cairo_box_t box;
358
359
0
  _cairo_box_from_rectangle (&box, &header->extents);
360
0
  status = bbtree_add (&surface->bbtree, header, &box);
361
0
  if (unlikely (status))
362
0
      goto cleanup;
363
0
    }
364
365
454
    return CAIRO_STATUS_SUCCESS;
366
367
0
cleanup:
368
0
    if (surface->bbtree.left)
369
0
  bbtree_del (surface->bbtree.left);
370
0
    if (surface->bbtree.right)
371
0
  bbtree_del (surface->bbtree.right);
372
0
    return status;
373
454
}
374
375
/**
376
 * cairo_recording_surface_create:
377
 * @content: the content of the recording surface
378
 * @extents: the extents to record in pixels, can be %NULL to record
379
 *           unbounded operations.
380
 *
381
 * Creates a recording-surface which can be used to record all drawing operations
382
 * at the highest level (that is, the level of paint, mask, stroke, fill
383
 * and show_text_glyphs). The recording surface can then be "replayed" against
384
 * any target surface by using it as a source to drawing operations.
385
 *
386
 * The recording phase of the recording surface is careful to snapshot all
387
 * necessary objects (paths, patterns, etc.), in order to achieve
388
 * accurate replay.
389
 *
390
 * Return value: a pointer to the newly created surface. The caller
391
 * owns the surface and should call cairo_surface_destroy() when done
392
 * with it.
393
 *
394
 * Since: 1.10
395
 **/
396
cairo_surface_t *
397
cairo_recording_surface_create (cairo_content_t    content,
398
        const cairo_rectangle_t *extents)
399
6.60k
{
400
6.60k
    cairo_recording_surface_t *surface;
401
402
6.60k
    surface = _cairo_calloc (sizeof (cairo_recording_surface_t));
403
6.60k
    if (unlikely (surface == NULL))
404
0
  return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
405
406
6.60k
    _cairo_surface_init (&surface->base,
407
6.60k
       &cairo_recording_surface_backend,
408
6.60k
       NULL, /* device */
409
6.60k
       content,
410
6.60k
       TRUE); /* is_vector */
411
412
413
6.60k
    surface->unbounded = TRUE;
414
415
    /* unbounded -> 'infinite' extents */
416
6.60k
    if (extents != NULL) {
417
5.13k
  surface->extents_pixels = *extents;
418
419
  /* XXX check for overflow */
420
5.13k
  surface->extents.x = floor (extents->x);
421
5.13k
  surface->extents.y = floor (extents->y);
422
5.13k
  surface->extents.width = ceil (extents->x + extents->width) - surface->extents.x;
423
5.13k
  surface->extents.height = ceil (extents->y + extents->height) - surface->extents.y;
424
425
5.13k
  surface->unbounded = FALSE;
426
5.13k
    }
427
428
6.60k
    _cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
429
430
6.60k
    surface->base.is_clear = TRUE;
431
432
6.60k
    surface->bbtree.left = surface->bbtree.right = NULL;
433
6.60k
    surface->bbtree.chain = INVALID_CHAIN;
434
435
6.60k
    surface->indices = NULL;
436
6.60k
    surface->num_indices = 0;
437
6.60k
    surface->optimize_clears = TRUE;
438
6.60k
    surface->has_bilevel_alpha = FALSE;
439
6.60k
    surface->has_only_op_over = FALSE;
440
6.60k
    surface->has_tags = FALSE;
441
442
6.60k
    CAIRO_MUTEX_INIT (surface->mutex);
443
444
6.60k
    cairo_list_init (&surface->region_array_list);
445
446
6.60k
    return &surface->base;
447
6.60k
}
448
449
static cairo_surface_t *
450
_cairo_recording_surface_create_similar (void          *abstract_surface,
451
           cairo_content_t  content,
452
           int      width,
453
           int      height)
454
73
{
455
73
    cairo_rectangle_t extents;
456
73
    extents.x = extents.y = 0;
457
73
    extents.width = width;
458
73
    extents.height = height;
459
73
    return cairo_recording_surface_create (content, &extents);
460
73
}
461
462
static void
463
destroy_pattern_region_array (const cairo_pattern_t *pattern,
464
            unsigned int region_id)
465
797k
{
466
797k
    if (region_id != 0) {
467
1.57k
  if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
468
1.04k
      cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
469
1.04k
      if (_cairo_surface_is_recording (surface_pattern->surface))
470
0
    _cairo_recording_surface_region_array_remove (surface_pattern->surface, region_id);
471
1.04k
  }
472
1.57k
    }
473
797k
}
474
475
static void
476
_cairo_recording_surface_region_array_destroy (cairo_recording_surface_t       *surface,
477
                 cairo_recording_regions_array_t *region_array)
478
2.66k
{
479
2.66k
    cairo_command_t **elements;
480
2.66k
    cairo_recording_region_element_t *region_elements;
481
2.66k
    int i, num_elements;
482
483
2.66k
    num_elements = MIN(surface->commands.num_elements, _cairo_array_num_elements(&region_array->regions));
484
2.66k
    elements = _cairo_array_index (&surface->commands, 0);
485
2.66k
    region_elements = _cairo_array_index (&region_array->regions, 0);
486
798k
    for (i = 0; i < num_elements; i++) {
487
795k
  cairo_command_t *command = elements[i];
488
795k
  cairo_recording_region_element_t *region_element = &region_elements[i];
489
490
795k
  switch (command->header.type) {
491
1.82k
      case CAIRO_COMMAND_PAINT:
492
1.82k
    destroy_pattern_region_array (&command->paint.source.base, region_element->source_id);
493
1.82k
    break;
494
495
1.73k
      case CAIRO_COMMAND_MASK:
496
1.73k
    destroy_pattern_region_array (&command->mask.source.base, region_element->source_id);
497
1.73k
    destroy_pattern_region_array (&command->mask.mask.base, region_element->mask_id);
498
1.73k
    break;
499
500
35.9k
      case CAIRO_COMMAND_STROKE:
501
35.9k
    destroy_pattern_region_array (&command->stroke.source.base, region_element->source_id);
502
35.9k
    break;
503
504
19.0k
      case CAIRO_COMMAND_FILL:
505
19.0k
    destroy_pattern_region_array (&command->fill.source.base, region_element->source_id);
506
19.0k
    break;
507
508
736k
      case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
509
736k
    destroy_pattern_region_array (&command->show_text_glyphs.source.base, region_element->source_id);
510
736k
    break;
511
512
0
      case CAIRO_COMMAND_TAG:
513
0
    break;
514
515
0
      default:
516
0
    ASSERT_NOT_REACHED;
517
795k
  }
518
795k
    }
519
520
2.66k
    _cairo_array_fini (&region_array->regions);
521
2.66k
    free (region_array);
522
2.66k
}
523
524
static void
525
_cairo_recording_surface_reset (cairo_recording_surface_t *surface)
526
7.64k
{
527
7.64k
    cairo_command_t **elements;
528
7.64k
    int i, num_elements;
529
7.64k
    cairo_recording_regions_array_t *region_array, *region_next;
530
531
    /* Normally backend surfaces hold a reference to the surface as
532
     * well as the region and free the region before the surface. So
533
     * the regions should already be freed at this point but just in
534
     * case we ensure the regions are freed before destroying the
535
     * surface. */
536
7.64k
    cairo_list_foreach_entry_safe (region_array, region_next,
537
7.64k
           cairo_recording_regions_array_t,
538
7.64k
           &surface->region_array_list, link)
539
1.04k
    {
540
1.04k
  cairo_list_del (&region_array->link);
541
1.04k
  _cairo_recording_surface_region_array_destroy (surface, region_array);
542
1.04k
    }
543
544
7.64k
    num_elements = surface->commands.num_elements;
545
7.64k
    elements = _cairo_array_index (&surface->commands, 0);
546
807k
    for (i = 0; i < num_elements; i++) {
547
799k
  cairo_command_t *command = elements[i];
548
549
799k
  switch (command->header.type) {
550
1.88k
  case CAIRO_COMMAND_PAINT:
551
1.88k
      _cairo_pattern_fini (&command->paint.source.base);
552
1.88k
      break;
553
554
3.50k
  case CAIRO_COMMAND_MASK:
555
3.50k
      _cairo_pattern_fini (&command->mask.source.base);
556
3.50k
      _cairo_pattern_fini (&command->mask.mask.base);
557
3.50k
      break;
558
559
36.6k
  case CAIRO_COMMAND_STROKE:
560
36.6k
      _cairo_pattern_fini (&command->stroke.source.base);
561
36.6k
      _cairo_path_fixed_fini (&command->stroke.path);
562
36.6k
      _cairo_stroke_style_fini (&command->stroke.style);
563
36.6k
      break;
564
565
20.6k
  case CAIRO_COMMAND_FILL:
566
20.6k
      _cairo_pattern_fini (&command->fill.source.base);
567
20.6k
      _cairo_path_fixed_fini (&command->fill.path);
568
20.6k
      break;
569
570
737k
  case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
571
737k
      _cairo_pattern_fini (&command->show_text_glyphs.source.base);
572
737k
      free (command->show_text_glyphs.utf8);
573
737k
      free (command->show_text_glyphs.glyphs);
574
737k
      free (command->show_text_glyphs.clusters);
575
737k
      cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font);
576
737k
      break;
577
578
0
  case CAIRO_COMMAND_TAG:
579
0
      free (command->tag.tag_name);
580
0
      if (command->tag.begin) {
581
0
    free (command->tag.attributes);
582
0
      }
583
0
      break;
584
585
0
      default:
586
0
      ASSERT_NOT_REACHED;
587
799k
  }
588
589
799k
  _cairo_clip_destroy (command->header.clip);
590
799k
  free (command);
591
799k
    }
592
593
7.64k
    _cairo_array_fini (&surface->commands);
594
7.64k
    _cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
595
596
7.64k
    if (surface->bbtree.left)
597
0
  bbtree_del (surface->bbtree.left);
598
7.64k
    if (surface->bbtree.right)
599
0
  bbtree_del (surface->bbtree.right);
600
7.64k
    surface->bbtree.left = surface->bbtree.right = NULL;
601
7.64k
    surface->bbtree.chain = INVALID_CHAIN;
602
603
7.64k
    free (surface->indices);
604
7.64k
    surface->indices = NULL;
605
7.64k
    surface->num_indices = 0;
606
7.64k
}
607
608
static cairo_status_t
609
_cairo_recording_surface_finish (void *abstract_surface)
610
7.64k
{
611
7.64k
    cairo_recording_surface_t *surface = abstract_surface;
612
613
7.64k
    _cairo_recording_surface_reset (surface);
614
7.64k
    CAIRO_MUTEX_FINI (surface->mutex);
615
616
7.64k
    return CAIRO_STATUS_SUCCESS;
617
7.64k
}
618
619
struct proxy {
620
    cairo_surface_t base;
621
    cairo_surface_t *image;
622
};
623
624
static cairo_status_t
625
proxy_acquire_source_image (void       *abstract_surface,
626
          cairo_image_surface_t **image_out,
627
          void      **image_extra)
628
0
{
629
0
    struct proxy *proxy = abstract_surface;
630
0
    return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra);
631
0
}
632
633
static void
634
proxy_release_source_image (void      *abstract_surface,
635
          cairo_image_surface_t *image,
636
          void      *image_extra)
637
0
{
638
0
    struct proxy *proxy = abstract_surface;
639
0
    _cairo_surface_release_source_image (proxy->image, image, image_extra);
640
0
}
641
642
static cairo_status_t
643
proxy_finish (void *abstract_surface)
644
0
{
645
0
    return CAIRO_STATUS_SUCCESS;
646
0
}
647
648
static const cairo_surface_backend_t proxy_backend  = {
649
    CAIRO_INTERNAL_SURFACE_TYPE_NULL,
650
    proxy_finish,
651
    NULL,
652
653
    NULL, /* create similar */
654
    NULL, /* create similar image */
655
    NULL, /* map to image */
656
    NULL, /* unmap image */
657
658
    _cairo_surface_default_source,
659
    proxy_acquire_source_image,
660
    proxy_release_source_image,
661
};
662
663
static cairo_surface_t *
664
attach_proxy (cairo_surface_t *source,
665
        cairo_surface_t *image)
666
0
{
667
0
    struct proxy *proxy;
668
669
0
    proxy = _cairo_calloc (sizeof (*proxy));
670
0
    if (unlikely (proxy == NULL))
671
0
  return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
672
673
0
    _cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content, FALSE);
674
675
0
    proxy->image = image;
676
0
    _cairo_surface_attach_snapshot (source, &proxy->base, NULL);
677
678
0
    return &proxy->base;
679
0
}
680
681
static void
682
detach_proxy (cairo_surface_t *source,
683
        cairo_surface_t *proxy)
684
0
{
685
0
    cairo_surface_finish (proxy);
686
0
    cairo_surface_destroy (proxy);
687
0
}
688
689
static cairo_surface_t *
690
get_proxy (cairo_surface_t *proxy)
691
0
{
692
0
    return ((struct proxy *)proxy)->image;
693
0
}
694
695
static cairo_status_t
696
_cairo_recording_surface_acquire_source_image (void      *abstract_surface,
697
                 cairo_image_surface_t  **image_out,
698
                 void     **image_extra)
699
0
{
700
0
    cairo_recording_surface_t *surface = abstract_surface;
701
0
    cairo_surface_t *image, *proxy;
702
0
    cairo_status_t status;
703
704
0
    proxy = _cairo_surface_has_snapshot (abstract_surface, &proxy_backend);
705
0
    if (proxy != NULL) {
706
0
  *image_out = (cairo_image_surface_t *)
707
0
      cairo_surface_reference (get_proxy (proxy));
708
0
  *image_extra = NULL;
709
0
  return CAIRO_STATUS_SUCCESS;
710
0
    }
711
712
0
    if (surface->unbounded)
713
0
  return CAIRO_INT_STATUS_UNSUPPORTED;
714
0
    image = _cairo_image_surface_create_with_content (surface->base.content,
715
0
                  surface->extents.width,
716
0
                  surface->extents.height);
717
0
    cairo_surface_set_device_offset (image, -surface->extents.x, -surface->extents.y);
718
0
    if (unlikely (image->status))
719
0
  return image->status;
720
721
0
    cairo_surface_set_device_offset(image, -surface->extents.x, -surface->extents.y);
722
723
    /* Handle recursion by returning future reads from the current image */
724
0
    proxy = attach_proxy (abstract_surface, image);
725
0
    status = _cairo_recording_surface_replay (&surface->base, image);
726
0
    detach_proxy (abstract_surface, proxy);
727
728
0
    if (unlikely (status)) {
729
0
  cairo_surface_destroy (image);
730
0
  return status;
731
0
    }
732
733
0
    *image_out = (cairo_image_surface_t *) image;
734
0
    *image_extra = NULL;
735
0
    return CAIRO_STATUS_SUCCESS;
736
0
}
737
738
static void
739
_cairo_recording_surface_release_source_image (void     *abstract_surface,
740
                 cairo_image_surface_t  *image,
741
                 void     *image_extra)
742
0
{
743
0
    cairo_surface_destroy (&image->base);
744
0
}
745
746
static cairo_status_t
747
_command_init (cairo_recording_surface_t *surface,
748
         cairo_command_header_t *command,
749
         cairo_command_type_t type,
750
         cairo_operator_t op,
751
         cairo_composite_rectangles_t *composite)
752
797k
{
753
797k
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
754
755
797k
    command->type = type;
756
797k
    command->op = op;
757
758
797k
    command->extents = composite ? composite->unbounded : _cairo_empty_rectangle;
759
797k
    command->chain = NULL;
760
797k
    command->index = surface->commands.num_elements;
761
762
    /* steal the clip */
763
797k
    command->clip = NULL;
764
797k
    if (composite && ! _cairo_composite_rectangles_can_reduce_clip (composite,
765
797k
                    composite->clip))
766
7.53k
    {
767
7.53k
  command->clip = composite->clip;
768
7.53k
  composite->clip = NULL;
769
7.53k
    }
770
771
797k
    return status;
772
797k
}
773
774
static void
775
_cairo_recording_surface_break_self_copy_loop (cairo_recording_surface_t *surface)
776
799k
{
777
799k
    cairo_surface_flush (&surface->base);
778
799k
}
779
780
static cairo_status_t
781
_cairo_recording_surface_commit (cairo_recording_surface_t *surface,
782
         cairo_command_header_t *command)
783
799k
{
784
799k
    _cairo_recording_surface_break_self_copy_loop (surface);
785
799k
    return _cairo_array_append (&surface->commands, &command);
786
799k
}
787
788
static cairo_int_status_t
789
_cairo_recording_surface_paint (void        *abstract_surface,
790
        cairo_operator_t     op,
791
        const cairo_pattern_t   *source,
792
        const cairo_clip_t    *clip)
793
1.87k
{
794
1.87k
    cairo_status_t status;
795
1.87k
    cairo_recording_surface_t *surface = abstract_surface;
796
1.87k
    cairo_command_paint_t *command;
797
1.87k
    cairo_composite_rectangles_t composite;
798
799
1.87k
    TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
800
801
1.87k
    if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) {
802
0
  if (surface->optimize_clears) {
803
0
      _cairo_recording_surface_reset (surface);
804
0
      return CAIRO_STATUS_SUCCESS;
805
0
  }
806
0
    }
807
808
1.87k
    if (clip == NULL && surface->optimize_clears &&
809
0
  (op == CAIRO_OPERATOR_SOURCE ||
810
0
   (op == CAIRO_OPERATOR_OVER &&
811
0
    (surface->base.is_clear || _cairo_pattern_is_opaque_solid (source)))))
812
0
    {
813
0
  _cairo_recording_surface_reset (surface);
814
0
    }
815
816
1.87k
    status = _cairo_composite_rectangles_init_for_paint (&composite,
817
1.87k
               &surface->base,
818
1.87k
               op, source,
819
1.87k
               clip);
820
1.87k
    if (unlikely (status))
821
7
  return status;
822
823
1.86k
    command = _cairo_calloc (sizeof (cairo_command_paint_t));
824
1.86k
    if (unlikely (command == NULL)) {
825
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
826
0
  goto CLEANUP_COMPOSITE;
827
0
    }
828
829
1.86k
    status = _command_init (surface,
830
1.86k
          &command->header, CAIRO_COMMAND_PAINT, op,
831
1.86k
          &composite);
832
1.86k
    if (unlikely (status))
833
0
  goto CLEANUP_COMMAND;
834
835
1.86k
    status = _cairo_pattern_init_snapshot (&command->source.base, source);
836
1.86k
    if (unlikely (status))
837
0
  goto CLEANUP_COMMAND;
838
839
1.86k
    status = _cairo_recording_surface_commit (surface, &command->header);
840
1.86k
    if (unlikely (status))
841
0
  goto CLEANUP_SOURCE;
842
843
1.86k
    _cairo_recording_surface_destroy_bbtree (surface);
844
845
1.86k
    _cairo_composite_rectangles_fini (&composite);
846
1.86k
    return CAIRO_STATUS_SUCCESS;
847
848
0
  CLEANUP_SOURCE:
849
0
    _cairo_pattern_fini (&command->source.base);
850
0
  CLEANUP_COMMAND:
851
0
    _cairo_clip_destroy (command->header.clip);
852
0
    free (command);
853
0
CLEANUP_COMPOSITE:
854
0
    _cairo_composite_rectangles_fini (&composite);
855
0
    return status;
856
0
}
857
858
static cairo_int_status_t
859
_cairo_recording_surface_mask (void     *abstract_surface,
860
             cairo_operator_t    op,
861
             const cairo_pattern_t  *source,
862
             const cairo_pattern_t  *mask,
863
             const cairo_clip_t *clip)
864
3.09k
{
865
3.09k
    cairo_status_t status;
866
3.09k
    cairo_recording_surface_t *surface = abstract_surface;
867
3.09k
    cairo_command_mask_t *command;
868
3.09k
    cairo_composite_rectangles_t composite;
869
870
3.09k
    TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
871
872
3.09k
    status = _cairo_composite_rectangles_init_for_mask (&composite,
873
3.09k
              &surface->base,
874
3.09k
              op, source, mask,
875
3.09k
              clip);
876
3.09k
    if (unlikely (status))
877
0
  return status;
878
879
3.09k
    command = _cairo_calloc (sizeof (cairo_command_mask_t));
880
3.09k
    if (unlikely (command == NULL)) {
881
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
882
0
  goto CLEANUP_COMPOSITE;
883
0
    }
884
885
3.09k
    status = _command_init (surface,
886
3.09k
          &command->header, CAIRO_COMMAND_MASK, op,
887
3.09k
          &composite);
888
3.09k
    if (unlikely (status))
889
0
  goto CLEANUP_COMMAND;
890
891
3.09k
    status = _cairo_pattern_init_snapshot (&command->source.base, source);
892
3.09k
    if (unlikely (status))
893
0
  goto CLEANUP_COMMAND;
894
895
3.09k
    status = _cairo_pattern_init_snapshot (&command->mask.base, mask);
896
3.09k
    if (unlikely (status))
897
0
  goto CLEANUP_SOURCE;
898
899
3.09k
    status = _cairo_recording_surface_commit (surface, &command->header);
900
3.09k
    if (unlikely (status))
901
0
  goto CLEANUP_MASK;
902
903
3.09k
    _cairo_recording_surface_destroy_bbtree (surface);
904
905
3.09k
    _cairo_composite_rectangles_fini (&composite);
906
3.09k
    return CAIRO_STATUS_SUCCESS;
907
908
0
  CLEANUP_MASK:
909
0
    _cairo_pattern_fini (&command->mask.base);
910
0
  CLEANUP_SOURCE:
911
0
    _cairo_pattern_fini (&command->source.base);
912
0
  CLEANUP_COMMAND:
913
0
    _cairo_clip_destroy (command->header.clip);
914
0
    free (command);
915
0
CLEANUP_COMPOSITE:
916
0
    _cairo_composite_rectangles_fini (&composite);
917
0
    return status;
918
0
}
919
920
static cairo_int_status_t
921
_cairo_recording_surface_stroke (void     *abstract_surface,
922
         cairo_operator_t  op,
923
         const cairo_pattern_t  *source,
924
         const cairo_path_fixed_t *path,
925
         const cairo_stroke_style_t *style,
926
         const cairo_matrix_t   *ctm,
927
         const cairo_matrix_t   *ctm_inverse,
928
         double      tolerance,
929
         cairo_antialias_t   antialias,
930
         const cairo_clip_t *clip)
931
36.9k
{
932
36.9k
    cairo_status_t status;
933
36.9k
    cairo_recording_surface_t *surface = abstract_surface;
934
36.9k
    cairo_command_stroke_t *command;
935
36.9k
    cairo_composite_rectangles_t composite;
936
937
36.9k
    TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
938
939
36.9k
    status = _cairo_composite_rectangles_init_for_stroke (&composite,
940
36.9k
                &surface->base,
941
36.9k
                op, source,
942
36.9k
                path, style, ctm,
943
36.9k
                clip);
944
36.9k
    if (unlikely (status))
945
993
  return status;
946
947
35.9k
    command = _cairo_calloc (sizeof (cairo_command_stroke_t));
948
35.9k
    if (unlikely (command == NULL)) {
949
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
950
0
  goto CLEANUP_COMPOSITE;
951
0
    }
952
953
35.9k
    status = _command_init (surface,
954
35.9k
          &command->header, CAIRO_COMMAND_STROKE, op,
955
35.9k
          &composite);
956
35.9k
    if (unlikely (status))
957
0
  goto CLEANUP_COMMAND;
958
959
35.9k
    status = _cairo_pattern_init_snapshot (&command->source.base, source);
960
35.9k
    if (unlikely (status))
961
0
  goto CLEANUP_COMMAND;
962
963
35.9k
    status = _cairo_path_fixed_init_copy (&command->path, path);
964
35.9k
    if (unlikely (status))
965
0
  goto CLEANUP_SOURCE;
966
967
35.9k
    status = _cairo_stroke_style_init_copy (&command->style, style);
968
35.9k
    if (unlikely (status))
969
0
  goto CLEANUP_PATH;
970
971
35.9k
    command->ctm = *ctm;
972
35.9k
    command->ctm_inverse = *ctm_inverse;
973
35.9k
    command->tolerance = tolerance;
974
35.9k
    command->antialias = antialias;
975
976
35.9k
    status = _cairo_recording_surface_commit (surface, &command->header);
977
35.9k
    if (unlikely (status))
978
0
  goto CLEANUP_STYLE;
979
980
35.9k
    _cairo_recording_surface_destroy_bbtree (surface);
981
982
35.9k
    _cairo_composite_rectangles_fini (&composite);
983
35.9k
    return CAIRO_STATUS_SUCCESS;
984
985
0
  CLEANUP_STYLE:
986
0
    _cairo_stroke_style_fini (&command->style);
987
0
  CLEANUP_PATH:
988
0
    _cairo_path_fixed_fini (&command->path);
989
0
  CLEANUP_SOURCE:
990
0
    _cairo_pattern_fini (&command->source.base);
991
0
  CLEANUP_COMMAND:
992
0
    _cairo_clip_destroy (command->header.clip);
993
0
    free (command);
994
0
CLEANUP_COMPOSITE:
995
0
    _cairo_composite_rectangles_fini (&composite);
996
0
    return status;
997
0
}
998
999
static cairo_int_status_t
1000
_cairo_recording_surface_fill (void     *abstract_surface,
1001
             cairo_operator_t    op,
1002
             const cairo_pattern_t  *source,
1003
             const cairo_path_fixed_t *path,
1004
             cairo_fill_rule_t   fill_rule,
1005
             double      tolerance,
1006
             cairo_antialias_t   antialias,
1007
             const cairo_clip_t *clip)
1008
21.1k
{
1009
21.1k
    cairo_status_t status;
1010
21.1k
    cairo_recording_surface_t *surface = abstract_surface;
1011
21.1k
    cairo_command_fill_t *command;
1012
21.1k
    cairo_composite_rectangles_t composite;
1013
1014
21.1k
    TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
1015
1016
21.1k
    status = _cairo_composite_rectangles_init_for_fill (&composite,
1017
21.1k
              &surface->base,
1018
21.1k
              op, source, path,
1019
21.1k
              clip);
1020
21.1k
    if (unlikely (status))
1021
1.56k
  return status;
1022
1023
19.5k
    command = _cairo_calloc (sizeof (cairo_command_fill_t));
1024
19.5k
    if (unlikely (command == NULL)) {
1025
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1026
0
  goto CLEANUP_COMPOSITE;
1027
0
    }
1028
1029
19.5k
    status =_command_init (surface,
1030
19.5k
         &command->header, CAIRO_COMMAND_FILL, op,
1031
19.5k
         &composite);
1032
19.5k
    if (unlikely (status))
1033
0
  goto CLEANUP_COMMAND;
1034
1035
19.5k
    status = _cairo_pattern_init_snapshot (&command->source.base, source);
1036
19.5k
    if (unlikely (status))
1037
0
  goto CLEANUP_COMMAND;
1038
1039
19.5k
    status = _cairo_path_fixed_init_copy (&command->path, path);
1040
19.5k
    if (unlikely (status))
1041
0
  goto CLEANUP_SOURCE;
1042
1043
19.5k
    command->fill_rule = fill_rule;
1044
19.5k
    command->tolerance = tolerance;
1045
19.5k
    command->antialias = antialias;
1046
1047
19.5k
    status = _cairo_recording_surface_commit (surface, &command->header);
1048
19.5k
    if (unlikely (status))
1049
0
  goto CLEANUP_PATH;
1050
1051
19.5k
    _cairo_recording_surface_destroy_bbtree (surface);
1052
1053
19.5k
    _cairo_composite_rectangles_fini (&composite);
1054
19.5k
    return CAIRO_STATUS_SUCCESS;
1055
1056
0
  CLEANUP_PATH:
1057
0
    _cairo_path_fixed_fini (&command->path);
1058
0
  CLEANUP_SOURCE:
1059
0
    _cairo_pattern_fini (&command->source.base);
1060
0
  CLEANUP_COMMAND:
1061
0
    _cairo_clip_destroy (command->header.clip);
1062
0
    free (command);
1063
0
CLEANUP_COMPOSITE:
1064
0
    _cairo_composite_rectangles_fini (&composite);
1065
0
    return status;
1066
0
}
1067
1068
static cairo_bool_t
1069
_cairo_recording_surface_has_show_text_glyphs (void *abstract_surface)
1070
549
{
1071
549
    return TRUE;
1072
549
}
1073
1074
static cairo_int_status_t
1075
_cairo_recording_surface_show_text_glyphs (void       *abstract_surface,
1076
             cairo_operator_t    op,
1077
             const cairo_pattern_t  *source,
1078
             const char     *utf8,
1079
             int         utf8_len,
1080
             cairo_glyph_t    *glyphs,
1081
             int         num_glyphs,
1082
             const cairo_text_cluster_t *clusters,
1083
             int         num_clusters,
1084
             cairo_text_cluster_flags_t  cluster_flags,
1085
             cairo_scaled_font_t    *scaled_font,
1086
             const cairo_clip_t   *clip)
1087
738k
{
1088
738k
    cairo_status_t status;
1089
738k
    cairo_recording_surface_t *surface = abstract_surface;
1090
738k
    cairo_command_show_text_glyphs_t *command;
1091
738k
    cairo_composite_rectangles_t composite;
1092
1093
738k
    TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
1094
1095
738k
    status = _cairo_composite_rectangles_init_for_glyphs (&composite,
1096
738k
                &surface->base,
1097
738k
                op, source,
1098
738k
                scaled_font,
1099
738k
                glyphs, num_glyphs,
1100
738k
                clip,
1101
738k
                NULL);
1102
738k
    if (unlikely (status))
1103
2.18k
  return status;
1104
1105
736k
    command = _cairo_calloc (sizeof (cairo_command_show_text_glyphs_t));
1106
736k
    if (unlikely (command == NULL)) {
1107
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1108
0
  goto CLEANUP_COMPOSITE;
1109
0
    }
1110
1111
736k
    status = _command_init (surface,
1112
736k
          &command->header, CAIRO_COMMAND_SHOW_TEXT_GLYPHS,
1113
736k
          op, &composite);
1114
736k
    if (unlikely (status))
1115
0
  goto CLEANUP_COMMAND;
1116
1117
736k
    status = _cairo_pattern_init_snapshot (&command->source.base, source);
1118
736k
    if (unlikely (status))
1119
0
  goto CLEANUP_COMMAND;
1120
1121
736k
    command->utf8 = NULL;
1122
736k
    command->utf8_len = utf8_len;
1123
736k
    command->glyphs = NULL;
1124
736k
    command->num_glyphs = num_glyphs;
1125
736k
    command->clusters = NULL;
1126
736k
    command->num_clusters = num_clusters;
1127
1128
736k
    if (utf8_len) {
1129
176k
  command->utf8 = _cairo_malloc (utf8_len);
1130
176k
  if (unlikely (command->utf8 == NULL)) {
1131
0
      status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1132
0
      goto CLEANUP_ARRAYS;
1133
0
  }
1134
176k
  memcpy (command->utf8, utf8, utf8_len);
1135
176k
    }
1136
736k
    if (num_glyphs) {
1137
736k
  command->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (glyphs[0]));
1138
736k
  if (unlikely (command->glyphs == NULL)) {
1139
0
      status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1140
0
      goto CLEANUP_ARRAYS;
1141
0
  }
1142
736k
  memcpy (command->glyphs, glyphs, sizeof (glyphs[0]) * num_glyphs);
1143
736k
    }
1144
736k
    if (num_clusters) {
1145
176k
  command->clusters = _cairo_malloc_ab (num_clusters, sizeof (clusters[0]));
1146
176k
  if (unlikely (command->clusters == NULL)) {
1147
0
      status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1148
0
      goto CLEANUP_ARRAYS;
1149
0
  }
1150
176k
  memcpy (command->clusters, clusters, sizeof (clusters[0]) * num_clusters);
1151
176k
    }
1152
1153
736k
    command->cluster_flags = cluster_flags;
1154
1155
736k
    status = scaled_font->status;
1156
736k
    if (unlikely (status))
1157
0
  goto CLEANUP_ARRAYS;
1158
1159
736k
    command->scaled_font = cairo_scaled_font_reference (scaled_font);
1160
1161
736k
    status = _cairo_recording_surface_commit (surface, &command->header);
1162
736k
    if (unlikely (status))
1163
0
  goto CLEANUP_SCALED_FONT;
1164
1165
736k
    _cairo_composite_rectangles_fini (&composite);
1166
736k
    return CAIRO_STATUS_SUCCESS;
1167
1168
0
  CLEANUP_SCALED_FONT:
1169
0
    cairo_scaled_font_destroy (command->scaled_font);
1170
0
  CLEANUP_ARRAYS:
1171
0
    free (command->utf8);
1172
0
    free (command->glyphs);
1173
0
    free (command->clusters);
1174
1175
0
    _cairo_pattern_fini (&command->source.base);
1176
0
  CLEANUP_COMMAND:
1177
0
    _cairo_clip_destroy (command->header.clip);
1178
0
    free (command);
1179
0
CLEANUP_COMPOSITE:
1180
0
    _cairo_composite_rectangles_fini (&composite);
1181
0
    return status;
1182
0
}
1183
1184
static cairo_int_status_t
1185
_cairo_recording_surface_tag (void       *abstract_surface,
1186
            cairo_bool_t                begin,
1187
            const char                 *tag_name,
1188
            const char                 *attributes)
1189
0
{
1190
0
    cairo_status_t status;
1191
0
    cairo_recording_surface_t *surface = abstract_surface;
1192
0
    cairo_command_tag_t *command;
1193
1194
0
    TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
1195
1196
0
    surface->has_tags = TRUE;
1197
1198
0
    command = _cairo_calloc (sizeof (cairo_command_tag_t));
1199
0
    if (unlikely (command == NULL)) {
1200
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1201
0
    }
1202
1203
0
    status = _command_init (surface,
1204
0
          &command->header, CAIRO_COMMAND_TAG, CAIRO_OPERATOR_SOURCE,
1205
0
          NULL);
1206
0
    if (unlikely (status))
1207
0
  goto CLEANUP_COMMAND;
1208
1209
0
    command->begin = begin;
1210
0
    command->tag_name = strdup (tag_name);
1211
0
    if (unlikely (command->tag_name == NULL)) {
1212
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1213
0
  goto CLEANUP_COMMAND;
1214
0
    }
1215
0
    if (begin) {
1216
0
  if (attributes) {
1217
0
      command->attributes = strdup (attributes);
1218
0
      if (unlikely (command->attributes == NULL)) {
1219
0
    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1220
0
    goto CLEANUP_STRINGS;
1221
0
      }
1222
0
  }
1223
0
    }
1224
1225
0
    status = _cairo_recording_surface_commit (surface, &command->header);
1226
0
    if (unlikely (status))
1227
0
  goto CLEANUP_STRINGS;
1228
1229
0
    _cairo_recording_surface_destroy_bbtree (surface);
1230
1231
0
    return CAIRO_STATUS_SUCCESS;
1232
1233
0
  CLEANUP_STRINGS:
1234
0
    free (command->tag_name);
1235
0
    free (command->attributes);
1236
0
  CLEANUP_COMMAND:
1237
0
    _cairo_clip_destroy (command->header.clip);
1238
0
    free (command);
1239
0
    return status;
1240
0
}
1241
1242
static cairo_bool_t
1243
_cairo_recording_surface_supports_color_glyph (void                 *abstract_surface,
1244
                                               cairo_scaled_font_t  *scaled_font,
1245
                                               unsigned long         glyph_index)
1246
0
{
1247
0
    return TRUE;
1248
0
}
1249
1250
static void
1251
_command_init_copy (cairo_recording_surface_t *surface,
1252
        cairo_command_header_t *dst,
1253
        const cairo_command_header_t *src)
1254
2.68k
{
1255
2.68k
    dst->type = src->type;
1256
2.68k
    dst->op = src->op;
1257
1258
2.68k
    dst->extents = src->extents;
1259
2.68k
    dst->chain = NULL;
1260
2.68k
    dst->index = surface->commands.num_elements;
1261
1262
2.68k
    dst->clip = _cairo_clip_copy (src->clip);
1263
2.68k
}
1264
1265
static cairo_status_t
1266
_cairo_recording_surface_copy__paint (cairo_recording_surface_t *surface,
1267
              const cairo_command_t *src)
1268
18
{
1269
18
    cairo_command_paint_t *command;
1270
18
    cairo_status_t status;
1271
1272
18
    command = _cairo_calloc (sizeof (*command));
1273
18
    if (unlikely (command == NULL)) {
1274
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1275
0
  goto err;
1276
0
    }
1277
1278
18
    _command_init_copy (surface, &command->header, &src->header);
1279
1280
18
    status = _cairo_pattern_init_copy (&command->source.base,
1281
18
               &src->paint.source.base);
1282
18
    if (unlikely (status))
1283
0
  goto err_command;
1284
1285
18
    status = _cairo_recording_surface_commit (surface, &command->header);
1286
18
    if (unlikely (status))
1287
0
  goto err_source;
1288
1289
18
    return CAIRO_STATUS_SUCCESS;
1290
1291
0
err_source:
1292
0
    _cairo_pattern_fini (&command->source.base);
1293
0
err_command:
1294
0
    free(command);
1295
0
err:
1296
0
    return status;
1297
0
}
1298
1299
static cairo_status_t
1300
_cairo_recording_surface_copy__mask (cairo_recording_surface_t *surface,
1301
             const cairo_command_t *src)
1302
407
{
1303
407
    cairo_command_mask_t *command;
1304
407
    cairo_status_t status;
1305
1306
407
    command = _cairo_calloc (sizeof (*command));
1307
407
    if (unlikely (command == NULL)) {
1308
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1309
0
  goto err;
1310
0
    }
1311
1312
407
    _command_init_copy (surface, &command->header, &src->header);
1313
1314
407
    status = _cairo_pattern_init_copy (&command->source.base,
1315
407
               &src->mask.source.base);
1316
407
    if (unlikely (status))
1317
0
  goto err_command;
1318
1319
407
    status = _cairo_pattern_init_copy (&command->mask.base,
1320
407
               &src->mask.mask.base);
1321
407
    if (unlikely (status))
1322
0
  goto err_source;
1323
1324
407
    status = _cairo_recording_surface_commit (surface, &command->header);
1325
407
    if (unlikely (status))
1326
0
  goto err_mask;
1327
1328
407
    return CAIRO_STATUS_SUCCESS;
1329
1330
0
err_mask:
1331
0
    _cairo_pattern_fini (&command->mask.base);
1332
0
err_source:
1333
0
    _cairo_pattern_fini (&command->source.base);
1334
0
err_command:
1335
0
    free(command);
1336
0
err:
1337
0
    return status;
1338
0
}
1339
1340
static cairo_status_t
1341
_cairo_recording_surface_copy__stroke (cairo_recording_surface_t *surface,
1342
             const cairo_command_t *src)
1343
646
{
1344
646
    cairo_command_stroke_t *command;
1345
646
    cairo_status_t status;
1346
1347
646
    command = _cairo_calloc (sizeof (*command));
1348
646
    if (unlikely (command == NULL)) {
1349
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1350
0
  goto err;
1351
0
    }
1352
1353
646
    _command_init_copy (surface, &command->header, &src->header);
1354
1355
646
    status = _cairo_pattern_init_copy (&command->source.base,
1356
646
               &src->stroke.source.base);
1357
646
    if (unlikely (status))
1358
0
  goto err_command;
1359
1360
646
    status = _cairo_path_fixed_init_copy (&command->path, &src->stroke.path);
1361
646
    if (unlikely (status))
1362
0
  goto err_source;
1363
1364
646
    status = _cairo_stroke_style_init_copy (&command->style,
1365
646
              &src->stroke.style);
1366
646
    if (unlikely (status))
1367
0
  goto err_path;
1368
1369
646
    command->ctm = src->stroke.ctm;
1370
646
    command->ctm_inverse = src->stroke.ctm_inverse;
1371
646
    command->tolerance = src->stroke.tolerance;
1372
646
    command->antialias = src->stroke.antialias;
1373
1374
646
    status = _cairo_recording_surface_commit (surface, &command->header);
1375
646
    if (unlikely (status))
1376
0
  goto err_style;
1377
1378
646
    return CAIRO_STATUS_SUCCESS;
1379
1380
0
err_style:
1381
0
    _cairo_stroke_style_fini (&command->style);
1382
0
err_path:
1383
0
    _cairo_path_fixed_fini (&command->path);
1384
0
err_source:
1385
0
    _cairo_pattern_fini (&command->source.base);
1386
0
err_command:
1387
0
    free(command);
1388
0
err:
1389
0
    return status;
1390
0
}
1391
1392
static cairo_status_t
1393
_cairo_recording_surface_copy__fill (cairo_recording_surface_t *surface,
1394
             const cairo_command_t *src)
1395
1.06k
{
1396
1.06k
    cairo_command_fill_t *command;
1397
1.06k
    cairo_status_t status;
1398
1399
1.06k
    command = _cairo_calloc (sizeof (*command));
1400
1.06k
    if (unlikely (command == NULL)) {
1401
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1402
0
  goto err;
1403
0
    }
1404
1405
1.06k
    _command_init_copy (surface, &command->header, &src->header);
1406
1407
1.06k
    status = _cairo_pattern_init_copy (&command->source.base,
1408
1.06k
               &src->fill.source.base);
1409
1.06k
    if (unlikely (status))
1410
0
  goto err_command;
1411
1412
1.06k
    status = _cairo_path_fixed_init_copy (&command->path, &src->fill.path);
1413
1.06k
    if (unlikely (status))
1414
0
  goto err_source;
1415
1416
1.06k
    command->fill_rule = src->fill.fill_rule;
1417
1.06k
    command->tolerance = src->fill.tolerance;
1418
1.06k
    command->antialias = src->fill.antialias;
1419
1420
1.06k
    status = _cairo_recording_surface_commit (surface, &command->header);
1421
1.06k
    if (unlikely (status))
1422
0
  goto err_path;
1423
1424
1.06k
    return CAIRO_STATUS_SUCCESS;
1425
1426
0
err_path:
1427
0
    _cairo_path_fixed_fini (&command->path);
1428
0
err_source:
1429
0
    _cairo_pattern_fini (&command->source.base);
1430
0
err_command:
1431
0
    free(command);
1432
0
err:
1433
0
    return status;
1434
0
}
1435
1436
static cairo_status_t
1437
_cairo_recording_surface_copy__glyphs (cairo_recording_surface_t *surface,
1438
               const cairo_command_t *src)
1439
546
{
1440
546
    cairo_command_show_text_glyphs_t *command;
1441
546
    cairo_status_t status;
1442
1443
546
    command = _cairo_calloc (sizeof (*command));
1444
546
    if (unlikely (command == NULL)) {
1445
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1446
0
  goto err;
1447
0
    }
1448
1449
546
    _command_init_copy (surface, &command->header, &src->header);
1450
1451
546
    status = _cairo_pattern_init_copy (&command->source.base,
1452
546
               &src->show_text_glyphs.source.base);
1453
546
    if (unlikely (status))
1454
0
  goto err_command;
1455
1456
546
    command->utf8 = NULL;
1457
546
    command->utf8_len = src->show_text_glyphs.utf8_len;
1458
546
    command->glyphs = NULL;
1459
546
    command->num_glyphs = src->show_text_glyphs.num_glyphs;
1460
546
    command->clusters = NULL;
1461
546
    command->num_clusters = src->show_text_glyphs.num_clusters;
1462
1463
546
    if (command->utf8_len) {
1464
545
  command->utf8 = _cairo_malloc (command->utf8_len);
1465
545
  if (unlikely (command->utf8 == NULL)) {
1466
0
      status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1467
0
      goto err_arrays;
1468
0
  }
1469
545
  memcpy (command->utf8, src->show_text_glyphs.utf8, command->utf8_len);
1470
545
    }
1471
546
    if (command->num_glyphs) {
1472
546
  command->glyphs = _cairo_malloc_ab (command->num_glyphs,
1473
546
              sizeof (command->glyphs[0]));
1474
546
  if (unlikely (command->glyphs == NULL)) {
1475
0
      status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1476
0
      goto err_arrays;
1477
0
  }
1478
546
  memcpy (command->glyphs, src->show_text_glyphs.glyphs,
1479
546
    sizeof (command->glyphs[0]) * command->num_glyphs);
1480
546
    }
1481
546
    if (command->num_clusters) {
1482
545
  command->clusters = _cairo_malloc_ab (command->num_clusters,
1483
545
                sizeof (command->clusters[0]));
1484
545
  if (unlikely (command->clusters == NULL)) {
1485
0
      status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1486
0
      goto err_arrays;
1487
0
  }
1488
545
  memcpy (command->clusters, src->show_text_glyphs.clusters,
1489
545
    sizeof (command->clusters[0]) * command->num_clusters);
1490
545
    }
1491
1492
546
    command->cluster_flags = src->show_text_glyphs.cluster_flags;
1493
1494
546
    command->scaled_font =
1495
546
  cairo_scaled_font_reference (src->show_text_glyphs.scaled_font);
1496
1497
546
    status = _cairo_recording_surface_commit (surface, &command->header);
1498
546
    if (unlikely (status))
1499
0
  goto err_arrays;
1500
1501
546
    return CAIRO_STATUS_SUCCESS;
1502
1503
0
err_arrays:
1504
0
    free (command->utf8);
1505
0
    free (command->glyphs);
1506
0
    free (command->clusters);
1507
0
    _cairo_pattern_fini (&command->source.base);
1508
0
err_command:
1509
0
    free(command);
1510
0
err:
1511
0
    return status;
1512
0
}
1513
1514
static cairo_status_t
1515
_cairo_recording_surface_copy__tag (cairo_recording_surface_t *surface,
1516
            const cairo_command_t *src)
1517
0
{
1518
0
    cairo_command_tag_t *command;
1519
0
    cairo_status_t status;
1520
1521
0
    command = _cairo_calloc (sizeof (*command));
1522
0
    if (unlikely (command == NULL)) {
1523
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1524
0
  goto err;
1525
0
    }
1526
1527
0
    _command_init_copy (surface, &command->header, &src->header);
1528
1529
0
    command->begin = src->tag.begin;
1530
0
    command->tag_name = strdup (src->tag.tag_name);
1531
0
    if (unlikely (command->tag_name == NULL)) {
1532
0
  status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1533
0
  goto err_command;
1534
0
    }
1535
0
    if (src->tag.begin) {
1536
0
  if (src->tag.attributes) {
1537
0
      command->attributes = strdup (src->tag.attributes);
1538
0
      if (unlikely (command->attributes == NULL)) {
1539
0
    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1540
0
    goto err_command;
1541
0
      }
1542
0
  }
1543
0
    }
1544
1545
0
    status = _cairo_recording_surface_commit (surface, &command->header);
1546
0
    if (unlikely (status))
1547
0
  goto err_command;
1548
1549
0
    return CAIRO_STATUS_SUCCESS;
1550
1551
0
err_command:
1552
0
    free(command->tag_name);
1553
0
    free(command->attributes);
1554
0
    free(command);
1555
0
err:
1556
0
    return status;
1557
0
}
1558
1559
static cairo_status_t
1560
_cairo_recording_surface_copy (cairo_recording_surface_t *dst,
1561
             cairo_recording_surface_t *src)
1562
1.03k
{
1563
1.03k
    cairo_command_t **elements;
1564
1.03k
    int i, num_elements;
1565
1.03k
    cairo_status_t status;
1566
1567
1.03k
    elements = _cairo_array_index (&src->commands, 0);
1568
1.03k
    num_elements = src->commands.num_elements;
1569
3.72k
    for (i = 0; i < num_elements; i++) {
1570
2.68k
  const cairo_command_t *command = elements[i];
1571
1572
2.68k
  switch (command->header.type) {
1573
18
  case CAIRO_COMMAND_PAINT:
1574
18
      status = _cairo_recording_surface_copy__paint (dst, command);
1575
18
      break;
1576
1577
407
  case CAIRO_COMMAND_MASK:
1578
407
      status = _cairo_recording_surface_copy__mask (dst, command);
1579
407
      break;
1580
1581
646
  case CAIRO_COMMAND_STROKE:
1582
646
      status = _cairo_recording_surface_copy__stroke (dst, command);
1583
646
      break;
1584
1585
1.06k
  case CAIRO_COMMAND_FILL:
1586
1.06k
      status = _cairo_recording_surface_copy__fill (dst, command);
1587
1.06k
      break;
1588
1589
546
  case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
1590
546
      status = _cairo_recording_surface_copy__glyphs (dst, command);
1591
546
      break;
1592
1593
0
  case CAIRO_COMMAND_TAG:
1594
0
      status = _cairo_recording_surface_copy__tag (dst, command);
1595
0
      break;
1596
1597
0
  default:
1598
0
      ASSERT_NOT_REACHED;
1599
2.68k
  }
1600
1601
2.68k
  if (unlikely (status))
1602
0
      return status;
1603
2.68k
    }
1604
1605
1.03k
    return CAIRO_STATUS_SUCCESS;
1606
1.03k
}
1607
1608
/**
1609
 * _cairo_recording_surface_snapshot:
1610
 * @surface: a #cairo_surface_t which must be a recording surface
1611
 *
1612
 * Make an immutable copy of @surface. It is an error to call a
1613
 * surface-modifying function on the result of this function.
1614
 *
1615
 * The caller owns the return value and should call
1616
 * cairo_surface_destroy() when finished with it. This function will not
1617
 * return %NULL, but will return a nil surface instead.
1618
 *
1619
 * Return value: The snapshot surface.
1620
 **/
1621
static cairo_surface_t *
1622
_cairo_recording_surface_snapshot (void *abstract_other)
1623
1.03k
{
1624
1.03k
    cairo_recording_surface_t *other = abstract_other;
1625
1.03k
    cairo_recording_surface_t *surface;
1626
1.03k
    cairo_status_t status;
1627
1628
1.03k
    surface = _cairo_calloc (sizeof (cairo_recording_surface_t));
1629
1.03k
    if (unlikely (surface == NULL))
1630
0
  return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1631
1632
1.03k
    _cairo_surface_init (&surface->base,
1633
1.03k
       &cairo_recording_surface_backend,
1634
1.03k
       NULL, /* device */
1635
1.03k
       other->base.content,
1636
1.03k
       other->base.is_vector);
1637
1638
1.03k
    surface->extents_pixels = other->extents_pixels;
1639
1.03k
    surface->extents = other->extents;
1640
1.03k
    surface->unbounded = other->unbounded;
1641
1.03k
    surface->has_bilevel_alpha = other->has_bilevel_alpha;
1642
1.03k
    surface->has_only_op_over = other->has_only_op_over;
1643
1.03k
    surface->has_tags = other->has_tags;
1644
1645
1.03k
    surface->base.is_clear = other->base.is_clear;
1646
1647
1.03k
    surface->bbtree.left = surface->bbtree.right = NULL;
1648
1.03k
    surface->bbtree.chain = INVALID_CHAIN;
1649
1650
1.03k
    surface->indices = NULL;
1651
1.03k
    surface->num_indices = 0;
1652
1.03k
    surface->optimize_clears = TRUE;
1653
1654
1.03k
    CAIRO_MUTEX_INIT (surface->mutex);
1655
1656
1.03k
    cairo_list_init (&surface->region_array_list);
1657
1658
1.03k
    _cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
1659
1.03k
    status = _cairo_recording_surface_copy (surface, other);
1660
1.03k
    if (unlikely (status)) {
1661
0
  cairo_surface_destroy (&surface->base);
1662
0
  return _cairo_surface_create_in_error (status);
1663
0
    }
1664
1665
1.03k
    return &surface->base;
1666
1.03k
}
1667
1668
static cairo_bool_t
1669
_cairo_recording_surface_get_extents (void        *abstract_surface,
1670
              cairo_rectangle_int_t *rectangle)
1671
815k
{
1672
815k
    cairo_recording_surface_t *surface = abstract_surface;
1673
1674
815k
    if (surface->unbounded)
1675
1.36k
  return FALSE;
1676
1677
813k
    *rectangle = surface->extents;
1678
813k
    return TRUE;
1679
815k
}
1680
1681
static const cairo_surface_backend_t cairo_recording_surface_backend = {
1682
    CAIRO_SURFACE_TYPE_RECORDING,
1683
    _cairo_recording_surface_finish,
1684
1685
    _cairo_default_context_create,
1686
1687
    _cairo_recording_surface_create_similar,
1688
    NULL, /* create similar image */
1689
    NULL, /* map to image */
1690
    NULL, /* unmap image */
1691
1692
    _cairo_surface_default_source,
1693
    _cairo_recording_surface_acquire_source_image,
1694
    _cairo_recording_surface_release_source_image,
1695
    _cairo_recording_surface_snapshot,
1696
1697
    NULL, /* copy_page */
1698
    NULL, /* show_page */
1699
1700
    _cairo_recording_surface_get_extents,
1701
    NULL, /* get_font_options */
1702
1703
    NULL, /* flush */
1704
    NULL, /* mark_dirty_rectangle */
1705
1706
    /* Here are the 5 basic drawing operations, (which are in some
1707
     * sense the only things that cairo_recording_surface should need to
1708
     * implement).  However, we implement the more generic show_text_glyphs
1709
     * instead of show_glyphs.  One or the other is eough. */
1710
1711
    _cairo_recording_surface_paint,
1712
    _cairo_recording_surface_mask,
1713
    _cairo_recording_surface_stroke,
1714
    _cairo_recording_surface_fill,
1715
    NULL, /* fill-stroke */
1716
    NULL,
1717
    _cairo_recording_surface_has_show_text_glyphs,
1718
    _cairo_recording_surface_show_text_glyphs,
1719
    NULL, /* get_supported_mime_types */
1720
    _cairo_recording_surface_tag,
1721
    _cairo_recording_surface_supports_color_glyph,
1722
    NULL, /* analyze_recording_surface */
1723
    NULL, /* command_id */
1724
};
1725
1726
static unsigned int
1727
_cairo_recording_surface_regions_allocate_unique_id (void)
1728
2.66k
{
1729
2.66k
    static cairo_atomic_int_t unique_id;
1730
1731
#if CAIRO_NO_MUTEX
1732
    if (++unique_id == 0)
1733
  unique_id = 1;
1734
    return unique_id;
1735
#else
1736
2.66k
    int old, id;
1737
1738
2.66k
    do {
1739
2.66k
  old = _cairo_atomic_uint_get (&unique_id);
1740
2.66k
  id = old + 1;
1741
2.66k
  if (id == 0)
1742
0
      id = 1;
1743
2.66k
    } while (! _cairo_atomic_uint_cmpxchg (&unique_id, old, id));
1744
1745
2.66k
    return id;
1746
2.66k
#endif
1747
2.66k
}
1748
1749
static cairo_recording_regions_array_t *
1750
_cairo_recording_surface_region_array_find (cairo_recording_surface_t *surface,
1751
              unsigned int               id)
1752
5.85k
{
1753
5.85k
    cairo_recording_regions_array_t *regions;
1754
1755
5.85k
    cairo_list_foreach_entry (regions, cairo_recording_regions_array_t,
1756
5.85k
            &surface->region_array_list, link)
1757
5.85k
    {
1758
5.85k
  if (regions->id == id)
1759
5.85k
      return regions;
1760
5.85k
    }
1761
1762
0
    return NULL;
1763
5.85k
}
1764
1765
/* Create and initialize a new #cairo_recording_regions_array_t. Attach
1766
 * it to the recording surface and return its id
1767
 */
1768
cairo_status_t
1769
_cairo_recording_surface_region_array_attach (cairo_surface_t *abstract_surface,
1770
                                              unsigned int    *id)
1771
2.66k
{
1772
2.66k
    cairo_recording_regions_array_t *region_array;
1773
2.66k
    cairo_recording_surface_t *surface = (cairo_recording_surface_t *) abstract_surface;
1774
1775
2.66k
    assert (_cairo_surface_is_recording (abstract_surface));
1776
1777
2.66k
    region_array = _cairo_calloc (sizeof (cairo_recording_regions_array_t));
1778
2.66k
    if (region_array == NULL) {
1779
0
  *id = 0;
1780
0
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1781
0
    }
1782
1783
2.66k
    region_array->id = _cairo_recording_surface_regions_allocate_unique_id ();
1784
1785
2.66k
    CAIRO_REFERENCE_COUNT_INIT (&region_array->ref_count, 1);
1786
1787
2.66k
    _cairo_array_init (&region_array->regions, sizeof (cairo_recording_region_element_t));
1788
1789
2.66k
    CAIRO_MUTEX_LOCK (surface->mutex);
1790
2.66k
    cairo_list_add (&region_array->link, &surface->region_array_list);
1791
2.66k
    CAIRO_MUTEX_UNLOCK (surface->mutex);
1792
1793
2.66k
    *id = region_array->id;
1794
1795
2.66k
    return CAIRO_STATUS_SUCCESS;
1796
2.66k
}
1797
1798
void
1799
_cairo_recording_surface_region_array_remove (cairo_surface_t *abstract_surface,
1800
                                              unsigned int     id)
1801
1.61k
{
1802
1.61k
    cairo_recording_regions_array_t *region_array;
1803
1.61k
    cairo_recording_surface_t *surface = (cairo_recording_surface_t *) abstract_surface;
1804
1805
1.61k
    if (id == 0)
1806
0
  return;
1807
1808
1.61k
    assert (_cairo_surface_is_recording (abstract_surface));
1809
1810
1.61k
    CAIRO_MUTEX_LOCK (surface->mutex);
1811
1.61k
    region_array = _cairo_recording_surface_region_array_find (surface, id);
1812
1.61k
    if (region_array) {
1813
1.61k
  if (_cairo_reference_count_dec_and_test (&region_array->ref_count))
1814
1.61k
      cairo_list_del (&region_array->link);
1815
0
  else
1816
0
      region_array = NULL;
1817
1.61k
    }
1818
1819
1.61k
    CAIRO_MUTEX_UNLOCK (surface->mutex);
1820
1821
1.61k
    if (region_array)
1822
1.61k
  _cairo_recording_surface_region_array_destroy (surface, region_array);
1823
1.61k
}
1824
1825
void
1826
_cairo_recording_surface_region_array_reference (cairo_surface_t *abstract_surface,
1827
             unsigned int     id)
1828
0
{
1829
0
    cairo_recording_regions_array_t *region_array;
1830
0
    cairo_recording_surface_t *surface = (cairo_recording_surface_t *) abstract_surface;
1831
1832
0
    assert (_cairo_surface_is_recording (abstract_surface));
1833
1834
0
    CAIRO_MUTEX_LOCK (surface->mutex);
1835
0
    region_array = _cairo_recording_surface_region_array_find (surface, id);
1836
0
    if (region_array) {
1837
0
  _cairo_reference_count_inc (&region_array->ref_count);
1838
0
    }
1839
1840
0
    CAIRO_MUTEX_UNLOCK (surface->mutex);
1841
0
}
1842
1843
cairo_int_status_t
1844
_cairo_recording_surface_get_path (cairo_surface_t    *abstract_surface,
1845
           cairo_path_fixed_t *path)
1846
239
{
1847
239
    cairo_recording_surface_t *surface;
1848
239
    cairo_command_t **elements;
1849
239
    int i, num_elements;
1850
239
    cairo_int_status_t status;
1851
1852
239
    if (unlikely (abstract_surface->status))
1853
0
  return abstract_surface->status;
1854
1855
239
    surface = (cairo_recording_surface_t *) abstract_surface;
1856
239
    status = CAIRO_STATUS_SUCCESS;
1857
1858
239
    num_elements = surface->commands.num_elements;
1859
239
    elements = _cairo_array_index (&surface->commands, 0);
1860
239
    for (i = 0; i < num_elements; i++) {
1861
227
  cairo_command_t *command = elements[i];
1862
1863
227
  switch (command->header.type) {
1864
0
  case CAIRO_COMMAND_PAINT:
1865
227
  case CAIRO_COMMAND_MASK:
1866
227
      status = CAIRO_INT_STATUS_UNSUPPORTED;
1867
227
      break;
1868
1869
0
  case CAIRO_COMMAND_STROKE:
1870
0
  {
1871
0
      cairo_traps_t traps;
1872
1873
0
      _cairo_traps_init (&traps);
1874
1875
      /* XXX call cairo_stroke_to_path() when that is implemented */
1876
0
      status = _cairo_path_fixed_stroke_polygon_to_traps (&command->stroke.path,
1877
0
                &command->stroke.style,
1878
0
                &command->stroke.ctm,
1879
0
                &command->stroke.ctm_inverse,
1880
0
                command->stroke.tolerance,
1881
0
                &traps);
1882
1883
0
      if (status == CAIRO_INT_STATUS_SUCCESS)
1884
0
    status = _cairo_traps_path (&traps, path);
1885
1886
0
      _cairo_traps_fini (&traps);
1887
0
      break;
1888
0
  }
1889
0
  case CAIRO_COMMAND_FILL:
1890
0
  {
1891
0
      status = _cairo_path_fixed_append (path,
1892
0
                 &command->fill.path,
1893
0
                 0, 0);
1894
0
      break;
1895
0
  }
1896
0
  case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
1897
0
  {
1898
0
      status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font,
1899
0
                command->show_text_glyphs.glyphs,
1900
0
                command->show_text_glyphs.num_glyphs,
1901
0
                path);
1902
0
      break;
1903
0
  }
1904
1905
0
  case CAIRO_COMMAND_TAG:
1906
0
      break;
1907
1908
0
  default:
1909
0
      ASSERT_NOT_REACHED;
1910
227
  }
1911
1912
227
  if (unlikely (status))
1913
227
      break;
1914
227
    }
1915
1916
239
    return status;
1917
239
}
1918
1919
static int
1920
_cairo_recording_surface_get_visible_commands (cairo_recording_surface_t *surface,
1921
                 const cairo_rectangle_int_t *extents)
1922
454
{
1923
454
    unsigned int num_visible, *indices;
1924
454
    cairo_box_t box;
1925
1926
454
    if (surface->commands.num_elements == 0)
1927
0
  return 0;
1928
1929
454
    _cairo_box_from_rectangle (&box, extents);
1930
1931
454
    if (surface->bbtree.chain == INVALID_CHAIN)
1932
454
  _cairo_recording_surface_create_bbtree (surface);
1933
1934
454
    indices = surface->indices;
1935
454
    bbtree_foreach_mark_visible (&surface->bbtree, &box, &indices);
1936
454
    num_visible = indices - surface->indices;
1937
454
    if (num_visible > 1)
1938
0
  sort_indices (surface->indices, num_visible);
1939
1940
454
    return num_visible;
1941
454
}
1942
1943
static void
1944
_cairo_recording_surface_merge_source_attributes (cairo_recording_surface_t  *surface,
1945
              cairo_operator_t            op,
1946
              const cairo_pattern_t      *source)
1947
797k
{
1948
797k
    if (op != CAIRO_OPERATOR_OVER)
1949
1.33k
  surface->has_only_op_over = FALSE;
1950
1951
797k
    if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
1952
4.66k
  cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) source;
1953
4.66k
  cairo_surface_t *surf = surf_pat->surface;
1954
4.66k
  cairo_surface_t *free_me = NULL;
1955
1956
4.66k
  if (_cairo_surface_is_snapshot (surf))
1957
4.66k
      free_me = surf = _cairo_surface_snapshot_get_target (surf);
1958
1959
4.66k
  if (unlikely (surf->status))
1960
      // There was some kind of error and the surface could be a nil error
1961
      // surface with various "problems" (e.g. ->backend == NULL).
1962
0
      return;
1963
1964
4.66k
  if (surf->type == CAIRO_SURFACE_TYPE_RECORDING) {
1965
1.04k
      cairo_recording_surface_t *rec_surf = (cairo_recording_surface_t *) surf;
1966
1967
1.04k
      if (! _cairo_recording_surface_has_only_bilevel_alpha (rec_surf))
1968
21
    surface->has_bilevel_alpha = FALSE;
1969
1970
1.04k
      if (! _cairo_recording_surface_has_only_op_over (rec_surf))
1971
13
    surface->has_only_op_over = FALSE;
1972
1973
3.61k
  } else if (surf->type == CAIRO_SURFACE_TYPE_IMAGE) {
1974
3.61k
      cairo_image_surface_t *img_surf = (cairo_image_surface_t *) surf;
1975
1976
3.61k
      if (_cairo_image_analyze_transparency (img_surf) == CAIRO_IMAGE_HAS_ALPHA)
1977
523
    surface->has_bilevel_alpha = FALSE;
1978
1979
3.61k
  } else {
1980
0
      if (!_cairo_pattern_is_clear (source) && !_cairo_pattern_is_opaque (source, NULL))
1981
0
    surface->has_bilevel_alpha = FALSE;
1982
0
  }
1983
1984
4.66k
  cairo_surface_destroy (free_me);
1985
4.66k
  return;
1986
1987
792k
    } else if (source->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
1988
0
  cairo_surface_t *image;
1989
0
  cairo_surface_t *raster;
1990
1991
0
  image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
1992
0
  raster = _cairo_raster_source_pattern_acquire (source, image, NULL);
1993
0
  cairo_surface_destroy (image);
1994
0
  if (raster) {
1995
0
      if (raster->type == CAIRO_SURFACE_TYPE_IMAGE) {
1996
0
    if (_cairo_image_analyze_transparency ((cairo_image_surface_t *)raster) == CAIRO_IMAGE_HAS_ALPHA)
1997
0
        surface->has_bilevel_alpha = FALSE;
1998
0
      }
1999
2000
0
      _cairo_raster_source_pattern_release (source, raster);
2001
0
      if (raster->type == CAIRO_SURFACE_TYPE_IMAGE)
2002
0
    return;
2003
0
  }
2004
0
    }
2005
2006
792k
    if (!_cairo_pattern_is_clear (source) && !_cairo_pattern_is_opaque (source, NULL))
2007
2.26k
  surface->has_bilevel_alpha = FALSE;
2008
792k
}
2009
2010
static cairo_status_t
2011
_cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
2012
            cairo_recording_surface_replay_params_t *params)
2013
6.03k
{
2014
6.03k
    cairo_surface_wrapper_t wrapper;
2015
6.03k
    cairo_command_t **elements;
2016
6.03k
    cairo_recording_regions_array_t *regions_array = NULL;
2017
6.03k
    cairo_recording_region_element_t *region_elements = NULL;
2018
6.03k
    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
2019
6.03k
    cairo_rectangle_int_t extents;
2020
6.03k
    cairo_bool_t use_indices = FALSE;
2021
6.03k
    const cairo_rectangle_int_t *r;
2022
6.03k
    unsigned int i, num_elements;
2023
2024
6.03k
    if (unlikely (surface->base.status))
2025
0
  return surface->base.status;
2026
2027
6.03k
    if (unlikely (params->target->status))
2028
0
  return params->target->status;
2029
2030
6.03k
    if (unlikely (surface->base.finished))
2031
0
  return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
2032
2033
6.03k
    if (surface->base.is_clear)
2034
433
  return CAIRO_STATUS_SUCCESS;
2035
2036
6.03k
    assert (_cairo_surface_is_recording (&surface->base));
2037
2038
5.59k
    if (params->regions_id != 0) {
2039
4.23k
  regions_array = _cairo_recording_surface_region_array_find (surface, params->regions_id);
2040
4.23k
  assert (regions_array != NULL);
2041
4.23k
    }
2042
2043
5.59k
    _cairo_surface_wrapper_init (&wrapper, params->target);
2044
5.59k
    if (params->surface_extents)
2045
0
  _cairo_surface_wrapper_intersect_extents (&wrapper, params->surface_extents);
2046
5.59k
    r = &_cairo_unbounded_rectangle;
2047
5.59k
    if (! surface->unbounded && !params->surface_is_unbounded) {
2048
4.57k
  _cairo_surface_wrapper_intersect_extents (&wrapper, &surface->extents);
2049
4.57k
  r = &surface->extents;
2050
4.57k
    }
2051
5.59k
    _cairo_surface_wrapper_set_inverse_transform (&wrapper, params->surface_transform);
2052
5.59k
    _cairo_surface_wrapper_set_clip (&wrapper, params->target_clip);
2053
2054
5.59k
    if (params->foreground_color) {
2055
0
  params->target->foreground_source = _cairo_pattern_create_solid (params->foreground_color);
2056
0
  params->target->foreground_used = FALSE;
2057
0
    }
2058
2059
    /* Compute the extents of the target clip in recorded device space */
2060
5.59k
    if (! _cairo_surface_wrapper_get_target_extents (&wrapper, params->surface_is_unbounded, &extents))
2061
0
  goto done;
2062
2063
5.59k
    surface->has_bilevel_alpha = TRUE;
2064
5.59k
    surface->has_only_op_over = TRUE;
2065
2066
5.59k
    num_elements = surface->commands.num_elements;
2067
5.59k
    if (regions_array) {
2068
4.23k
  if (params->type == CAIRO_RECORDING_CREATE_REGIONS) {
2069
      /* Re-running create regions with the same region id is not supported. */
2070
2.27k
      assert (_cairo_array_num_elements (&regions_array->regions) == 0);
2071
2.27k
      void *array_elems;
2072
2.27k
      status = _cairo_array_allocate (&regions_array->regions, num_elements, &array_elems);
2073
2.27k
      if (unlikely (status))
2074
0
    return status;
2075
2076
      /* Set regions to CAIRO_RECORDING_REGION_ALL and ids to 0 */
2077
2.27k
      memset (array_elems, 0, num_elements * sizeof (cairo_recording_region_element_t));
2078
2.27k
  } else {
2079
1.96k
      assert (_cairo_array_num_elements (&regions_array->regions) == num_elements);
2080
1.96k
  }
2081
4.23k
    }
2082
2083
5.59k
    elements = _cairo_array_index (&surface->commands, 0);
2084
5.59k
    if (regions_array)
2085
4.23k
  region_elements = _cairo_array_index (&regions_array->regions, 0);
2086
2087
5.59k
    if (!params->replay_all && (extents.width < r->width || extents.height < r->height)) {
2088
454
  num_elements =
2089
454
      _cairo_recording_surface_get_visible_commands (surface, &extents);
2090
454
  use_indices = num_elements != surface->commands.num_elements;
2091
454
    }
2092
2093
5.59k
    cairo_bool_t target_is_analysis = _cairo_surface_is_analysis (params->target);
2094
2095
1.59M
    for (i = 0; i < num_elements; i++) {
2096
1.58M
  cairo_command_t *command = elements[use_indices ? surface->indices[i] : i];
2097
1.58M
  cairo_recording_region_element_t *region_element = NULL;
2098
1.58M
  unsigned int source_region_id = 0;
2099
1.58M
  unsigned int mask_region_id = 0;
2100
2101
1.58M
  if (region_elements)
2102
1.58M
      region_element = &region_elements[use_indices ? surface->indices[i] : i];
2103
2104
1.58M
  if (region_element && params->type == CAIRO_RECORDING_REPLAY_REGION &&
2105
789k
            region_element->region != params->region)
2106
0
  {
2107
0
      continue;
2108
0
  }
2109
2110
1.58M
  if (! _cairo_rectangle_intersects (&extents, &command->header.extents)) {
2111
0
      if (command->header.type != CAIRO_COMMAND_TAG)
2112
0
    continue;
2113
0
  }
2114
2115
2116
1.58M
  if (params->target->backend->command_id) {
2117
1.58M
      status = params->target->backend->command_id (params->target, params->regions_id, i);
2118
1.58M
      if (unlikely (status))
2119
0
    return status;
2120
1.58M
  }
2121
2122
1.58M
  switch (command->header.type) {
2123
3.69k
  case CAIRO_COMMAND_PAINT:
2124
3.69k
      if (region_element)
2125
3.64k
    source_region_id = region_element->source_id;
2126
2127
3.69k
      status = _cairo_surface_wrapper_paint (&wrapper,
2128
3.69k
               command->header.op,
2129
3.69k
               &command->paint.source.base,
2130
3.69k
               source_region_id,
2131
3.69k
               command->header.clip);
2132
3.69k
      if (params->type == CAIRO_RECORDING_CREATE_REGIONS) {
2133
1.82k
    _cairo_recording_surface_merge_source_attributes (surface,
2134
1.82k
                  command->header.op,
2135
1.82k
                  &command->paint.source.base);
2136
1.82k
    if (region_element && target_is_analysis)
2137
1.82k
        region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target);
2138
1.82k
      }
2139
3.69k
      break;
2140
2141
4.21k
  case CAIRO_COMMAND_MASK:
2142
4.21k
      if (region_element) {
2143
3.44k
    source_region_id = region_element->source_id;
2144
3.44k
    mask_region_id = region_element->mask_id;
2145
3.44k
      }
2146
2147
4.21k
      status = _cairo_surface_wrapper_mask (&wrapper,
2148
4.21k
              command->header.op,
2149
4.21k
              &command->mask.source.base,
2150
4.21k
              source_region_id,
2151
4.21k
              &command->mask.mask.base,
2152
4.21k
              mask_region_id,
2153
4.21k
              command->header.clip);
2154
4.21k
      if (params->type == CAIRO_RECORDING_CREATE_REGIONS) {
2155
1.73k
    _cairo_recording_surface_merge_source_attributes (surface,
2156
1.73k
                  command->header.op,
2157
1.73k
                  &command->mask.source.base);
2158
1.73k
    _cairo_recording_surface_merge_source_attributes (surface,
2159
1.73k
                  command->header.op,
2160
1.73k
                  &command->mask.mask.base);
2161
1.73k
    if (region_element && target_is_analysis) {
2162
1.73k
        region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target);
2163
1.73k
        region_element->mask_id = _cairo_analysis_surface_get_mask_region_id (params->target);
2164
1.73k
    }
2165
1.73k
      }
2166
4.21k
      break;
2167
2168
68.9k
  case CAIRO_COMMAND_STROKE:
2169
68.9k
      if (region_element)
2170
68.3k
    source_region_id = region_element->source_id;
2171
2172
68.9k
      status = _cairo_surface_wrapper_stroke (&wrapper,
2173
68.9k
                command->header.op,
2174
68.9k
                &command->stroke.source.base,
2175
68.9k
                source_region_id,
2176
68.9k
                &command->stroke.path,
2177
68.9k
                &command->stroke.style,
2178
68.9k
                &command->stroke.ctm,
2179
68.9k
                &command->stroke.ctm_inverse,
2180
68.9k
                command->stroke.tolerance,
2181
68.9k
                command->stroke.antialias,
2182
68.9k
                command->header.clip);
2183
68.9k
      if (params->type == CAIRO_RECORDING_CREATE_REGIONS) {
2184
35.9k
    _cairo_recording_surface_merge_source_attributes (surface,
2185
35.9k
                  command->header.op,
2186
35.9k
                  &command->stroke.source.base);
2187
35.9k
    if (region_element && target_is_analysis)
2188
35.9k
        region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target);
2189
35.9k
      }
2190
68.9k
      break;
2191
2192
38.7k
  case CAIRO_COMMAND_FILL:
2193
38.7k
      status = CAIRO_INT_STATUS_UNSUPPORTED;
2194
38.7k
      if (region_element)
2195
38.0k
    source_region_id = region_element->source_id;
2196
2197
38.7k
      if (_cairo_surface_wrapper_has_fill_stroke (&wrapper)) {
2198
19.0k
    cairo_command_t *stroke_command = NULL;
2199
19.0k
    cairo_recording_region_element_t *stroke_region_element = NULL;
2200
19.0k
    unsigned stroke_region_id = 0;
2201
2202
    /* The analysis surface does not implement
2203
     * fill_stroke.  When creating regions the fill and
2204
     * stroke commands are tested separately.
2205
     */
2206
19.0k
    if (params->type != CAIRO_RECORDING_CREATE_REGIONS && i < num_elements - 1) {
2207
18.8k
        stroke_command = elements[i + 1];
2208
18.8k
        if (region_elements)
2209
18.8k
      stroke_region_element = &region_elements[i + 1];
2210
18.8k
    }
2211
2212
19.0k
    if (stroke_region_element)
2213
18.8k
        stroke_region_id = stroke_region_element->source_id;
2214
2215
19.0k
    if (stroke_command && stroke_region_element &&
2216
18.8k
        params->type == CAIRO_RECORDING_REPLAY_REGION &&
2217
18.8k
        params->region != CAIRO_RECORDING_REGION_ALL)
2218
18.8k
    {
2219
18.8k
        if (stroke_region_element->region != params->region)
2220
0
      stroke_command = NULL;
2221
18.8k
    }
2222
2223
19.0k
    if (stroke_command != NULL &&
2224
18.8k
        stroke_command->header.type == CAIRO_COMMAND_STROKE &&
2225
6.76k
        _cairo_path_fixed_equal (&command->fill.path,
2226
6.76k
               &stroke_command->stroke.path) &&
2227
3.61k
        _cairo_clip_equal (command->header.clip,
2228
3.61k
               stroke_command->header.clip))
2229
3.09k
    {
2230
3.09k
        status = _cairo_surface_wrapper_fill_stroke (&wrapper,
2231
3.09k
                 command->header.op,
2232
3.09k
                 &command->fill.source.base,
2233
3.09k
                 source_region_id,
2234
3.09k
                 command->fill.fill_rule,
2235
3.09k
                 command->fill.tolerance,
2236
3.09k
                 command->fill.antialias,
2237
3.09k
                 &command->fill.path,
2238
3.09k
                 stroke_command->header.op,
2239
3.09k
                 &stroke_command->stroke.source.base,
2240
3.09k
                 stroke_region_id,
2241
3.09k
                 &stroke_command->stroke.style,
2242
3.09k
                 &stroke_command->stroke.ctm,
2243
3.09k
                 &stroke_command->stroke.ctm_inverse,
2244
3.09k
                 stroke_command->stroke.tolerance,
2245
3.09k
                 stroke_command->stroke.antialias,
2246
3.09k
                 command->header.clip);
2247
3.09k
        if (params->type == CAIRO_RECORDING_CREATE_REGIONS) {
2248
0
      _cairo_recording_surface_merge_source_attributes (surface,
2249
0
                    command->header.op,
2250
0
                    &command->fill.source.base);
2251
0
      _cairo_recording_surface_merge_source_attributes (surface,
2252
0
                    command->header.op,
2253
0
                    &command->stroke.source.base);
2254
0
        }
2255
3.09k
        i++;
2256
3.09k
    }
2257
19.0k
      }
2258
38.7k
      if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
2259
35.6k
    status = _cairo_surface_wrapper_fill (&wrapper,
2260
35.6k
                  command->header.op,
2261
35.6k
                  &command->fill.source.base,
2262
35.6k
                  source_region_id,
2263
35.6k
                  &command->fill.path,
2264
35.6k
                  command->fill.fill_rule,
2265
35.6k
                  command->fill.tolerance,
2266
35.6k
                  command->fill.antialias,
2267
35.6k
                  command->header.clip);
2268
35.6k
    if (params->type == CAIRO_RECORDING_CREATE_REGIONS) {
2269
19.0k
        _cairo_recording_surface_merge_source_attributes (surface,
2270
19.0k
                      command->header.op,
2271
19.0k
                      &command->fill.source.base);
2272
19.0k
    if (region_element && target_is_analysis)
2273
19.0k
        region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target);
2274
19.0k
    }
2275
35.6k
      }
2276
38.7k
      break;
2277
2278
1.47M
  case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
2279
1.47M
      if (region_element)
2280
1.47M
    source_region_id = region_element->source_id;
2281
2282
1.47M
      status = _cairo_surface_wrapper_show_text_glyphs (&wrapper,
2283
1.47M
                    command->header.op,
2284
1.47M
                    &command->show_text_glyphs.source.base,
2285
1.47M
                    source_region_id,
2286
1.47M
                    command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
2287
1.47M
                    command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs,
2288
1.47M
                    command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
2289
1.47M
                    command->show_text_glyphs.cluster_flags,
2290
1.47M
                    command->show_text_glyphs.scaled_font,
2291
1.47M
                    command->header.clip);
2292
1.47M
      if (params->type == CAIRO_RECORDING_CREATE_REGIONS) {
2293
736k
    _cairo_recording_surface_merge_source_attributes (surface,
2294
736k
                  command->header.op,
2295
736k
                  &command->show_text_glyphs.source.base);
2296
736k
    if (region_element && target_is_analysis)
2297
736k
        region_element->source_id = _cairo_analysis_surface_get_source_region_id (params->target);
2298
2299
736k
      }
2300
1.47M
      break;
2301
2302
0
  case CAIRO_COMMAND_TAG:
2303
0
      status = _cairo_surface_wrapper_tag (&wrapper,
2304
0
             command->tag.begin,
2305
0
             command->tag.tag_name,
2306
0
             command->tag.attributes);
2307
0
      break;
2308
2309
0
  default:
2310
0
      ASSERT_NOT_REACHED;
2311
1.58M
  }
2312
2313
  /* It's possible that a degenerate clip caused the command to end up doing nothing when replayed. */
2314
1.58M
  if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
2315
0
      status = CAIRO_INT_STATUS_SUCCESS;
2316
2317
1.58M
  if (params->type == CAIRO_RECORDING_CREATE_REGIONS && region_element) {
2318
795k
      if (status == CAIRO_INT_STATUS_SUCCESS) {
2319
795k
    region_element->region = CAIRO_RECORDING_REGION_NATIVE;
2320
795k
      } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) {
2321
0
    region_element->region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK;
2322
0
    status = CAIRO_INT_STATUS_SUCCESS;
2323
0
      } else {
2324
0
    assert (_cairo_int_status_is_error (status));
2325
0
      }
2326
795k
  }
2327
2328
1.58M
  if (unlikely (status))
2329
5
      break;
2330
1.58M
    }
2331
2332
5.59k
done:
2333
5.59k
    if (params->foreground_color) {
2334
0
  cairo_pattern_destroy (params->target->foreground_source);
2335
0
  params->target->foreground_source = NULL;
2336
0
  params->foreground_used = params->target->foreground_used;
2337
0
    }
2338
2339
5.59k
    _cairo_surface_wrapper_fini (&wrapper);
2340
5.59k
    return _cairo_surface_set_error (&surface->base, status);
2341
5.59k
}
2342
2343
cairo_status_t
2344
_cairo_recording_surface_replay_one (cairo_recording_surface_t  *surface,
2345
             long unsigned index,
2346
             cairo_surface_t       *target)
2347
0
{
2348
0
    cairo_surface_wrapper_t wrapper;
2349
0
    cairo_command_t **elements, *command;
2350
0
    cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
2351
2352
0
    if (unlikely (surface->base.status))
2353
0
  return surface->base.status;
2354
2355
0
    if (unlikely (target->status))
2356
0
  return target->status;
2357
2358
0
    if (unlikely (surface->base.finished))
2359
0
  return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
2360
2361
0
    assert (_cairo_surface_is_recording (&surface->base));
2362
2363
    /* XXX
2364
     * Use a surface wrapper because we may want to do transformed
2365
     * replay in the future.
2366
     */
2367
0
    _cairo_surface_wrapper_init (&wrapper, target);
2368
2369
0
    if (index > surface->commands.num_elements)
2370
0
  return _cairo_error (CAIRO_STATUS_READ_ERROR);
2371
2372
0
    elements = _cairo_array_index (&surface->commands, 0);
2373
0
    command = elements[index];
2374
0
    switch (command->header.type) {
2375
0
    case CAIRO_COMMAND_PAINT:
2376
0
  status = _cairo_surface_wrapper_paint (&wrapper,
2377
0
                 command->header.op,
2378
0
                 &command->paint.source.base,
2379
0
                 0,
2380
0
                 command->header.clip);
2381
0
  break;
2382
2383
0
    case CAIRO_COMMAND_MASK:
2384
0
  status = _cairo_surface_wrapper_mask (&wrapper,
2385
0
                command->header.op,
2386
0
                &command->mask.source.base,
2387
0
                0,
2388
0
                &command->mask.mask.base,
2389
0
                0,
2390
0
                command->header.clip);
2391
0
  break;
2392
2393
0
    case CAIRO_COMMAND_STROKE:
2394
0
  status = _cairo_surface_wrapper_stroke (&wrapper,
2395
0
            command->header.op,
2396
0
            &command->stroke.source.base,
2397
0
            0,
2398
0
            &command->stroke.path,
2399
0
            &command->stroke.style,
2400
0
            &command->stroke.ctm,
2401
0
            &command->stroke.ctm_inverse,
2402
0
            command->stroke.tolerance,
2403
0
            command->stroke.antialias,
2404
0
            command->header.clip);
2405
0
  break;
2406
2407
0
    case CAIRO_COMMAND_FILL:
2408
0
  status = _cairo_surface_wrapper_fill (&wrapper,
2409
0
                command->header.op,
2410
0
                &command->fill.source.base,
2411
0
                0,
2412
0
                &command->fill.path,
2413
0
                command->fill.fill_rule,
2414
0
                command->fill.tolerance,
2415
0
                command->fill.antialias,
2416
0
                command->header.clip);
2417
0
  break;
2418
2419
0
    case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
2420
0
  status = _cairo_surface_wrapper_show_text_glyphs (&wrapper,
2421
0
                command->header.op,
2422
0
                &command->show_text_glyphs.source.base,
2423
0
                0,
2424
0
                command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
2425
0
                command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs,
2426
0
                command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
2427
0
                command->show_text_glyphs.cluster_flags,
2428
0
                command->show_text_glyphs.scaled_font,
2429
0
                command->header.clip);
2430
0
  break;
2431
2432
0
    case CAIRO_COMMAND_TAG:
2433
0
  status = _cairo_surface_wrapper_tag (&wrapper,
2434
0
               command->tag.begin,
2435
0
               command->tag.tag_name,
2436
0
               command->tag.attributes);
2437
0
  break;
2438
2439
0
    default:
2440
0
  ASSERT_NOT_REACHED;
2441
0
    }
2442
2443
0
    _cairo_surface_wrapper_fini (&wrapper);
2444
0
    return _cairo_surface_set_error (&surface->base, status);
2445
0
}
2446
/**
2447
 * _cairo_recording_surface_replay:
2448
 * @surface: the #cairo_recording_surface_t
2449
 * @target: a target #cairo_surface_t onto which to replay the operations
2450
 * @width_pixels: width of the surface, in pixels
2451
 * @height_pixels: height of the surface, in pixels
2452
 *
2453
 * A recording surface can be "replayed" against any target surface,
2454
 * after which the results in target will be identical to the results
2455
 * that would have been obtained if the original operations applied to
2456
 * the recording surface had instead been applied to the target surface.
2457
 **/
2458
cairo_status_t
2459
_cairo_recording_surface_replay (cairo_surface_t *surface,
2460
         cairo_surface_t *target)
2461
1.29k
{
2462
1.29k
    cairo_recording_surface_replay_params_t params;
2463
2464
1.29k
    params.surface_extents = NULL;
2465
1.29k
    params.surface_transform = NULL;
2466
1.29k
    params.target = target;
2467
1.29k
    params.target_clip = NULL;
2468
1.29k
    params.surface_is_unbounded = FALSE;
2469
1.29k
    params.type = CAIRO_RECORDING_REPLAY;
2470
1.29k
    params.region = CAIRO_RECORDING_REGION_ALL;
2471
1.29k
    params.regions_id = 0;
2472
1.29k
    params.foreground_color = NULL;
2473
1.29k
    params.replay_all = FALSE;
2474
2475
1.29k
    return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, &params);
2476
1.29k
}
2477
2478
cairo_status_t
2479
_cairo_recording_surface_replay_with_foreground_color (cairo_surface_t     *surface,
2480
                                                       cairo_surface_t     *target,
2481
                   const cairo_color_t *foreground_color,
2482
                   cairo_bool_t        *foreground_used)
2483
3
{
2484
3
    cairo_recording_surface_replay_params_t params;
2485
3
    cairo_status_t status;
2486
2487
3
    params.surface_extents = NULL;
2488
3
    params.surface_transform = NULL;
2489
3
    params.target = target;
2490
3
    params.target_clip = NULL;
2491
3
    params.surface_is_unbounded = FALSE;
2492
3
    params.type = CAIRO_RECORDING_REPLAY;
2493
3
    params.region = CAIRO_RECORDING_REGION_ALL;
2494
3
    params.regions_id = 0;
2495
3
    params.foreground_color = foreground_color;
2496
3
    params.foreground_used = FALSE;
2497
3
    params.replay_all = FALSE;
2498
2499
3
    status = _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, &params);
2500
3
    *foreground_used = params.foreground_used;
2501
2502
3
    return status;
2503
3
}
2504
2505
cairo_status_t
2506
_cairo_recording_surface_replay_with_transform (cairo_surface_t *surface,
2507
            const cairo_matrix_t *surface_transform,
2508
            cairo_surface_t *target,
2509
            cairo_bool_t surface_is_unbounded,
2510
            cairo_bool_t replay_all)
2511
25
{
2512
25
    cairo_recording_surface_replay_params_t params;
2513
2514
25
    params.surface_extents = NULL;
2515
25
    params.surface_transform = surface_transform;
2516
25
    params.target = target;
2517
25
    params.target_clip = NULL;
2518
25
    params.surface_is_unbounded = surface_is_unbounded;
2519
25
    params.type = CAIRO_RECORDING_REPLAY;
2520
25
    params.region = CAIRO_RECORDING_REGION_ALL;
2521
25
    params.regions_id = 0;
2522
25
    params.foreground_color = NULL;
2523
25
    params.replay_all = replay_all;
2524
2525
25
    return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, &params);
2526
25
}
2527
2528
cairo_status_t
2529
_cairo_recording_surface_replay_with_clip (cairo_surface_t *surface,
2530
             const cairo_matrix_t *surface_transform,
2531
             cairo_surface_t *target,
2532
             const cairo_clip_t *target_clip)
2533
80
{
2534
80
    cairo_recording_surface_replay_params_t params;
2535
2536
80
    params.surface_extents = NULL;
2537
80
    params.surface_transform = surface_transform;
2538
80
    params.target = target;
2539
80
    params.target_clip = target_clip;
2540
80
    params.surface_is_unbounded = FALSE;
2541
80
    params.type = CAIRO_RECORDING_REPLAY;
2542
80
    params.region = CAIRO_RECORDING_REGION_ALL;
2543
80
    params.regions_id = 0;
2544
80
    params.foreground_color = NULL;
2545
80
    params.replay_all = FALSE;
2546
2547
80
    return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, &params);
2548
80
}
2549
2550
/* Replay recording to surface. When the return status of each operation is
2551
 * one of %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED, or
2552
 * %CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY the status of each operation
2553
 * will be stored in the recording surface. Any other status will abort the
2554
 * replay and return the status.
2555
 */
2556
cairo_status_t
2557
_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface,
2558
                unsigned int regions_id,
2559
                const cairo_matrix_t *surface_transform,
2560
                cairo_surface_t *target,
2561
                cairo_bool_t surface_is_unbounded,
2562
                cairo_bool_t replay_all)
2563
2.66k
{
2564
2.66k
    cairo_recording_surface_replay_params_t params;
2565
2566
2.66k
    params.surface_extents = NULL;
2567
2.66k
    params.surface_transform = surface_transform;
2568
2.66k
    params.target = target;
2569
2.66k
    params.target_clip = NULL;
2570
2.66k
    params.surface_is_unbounded = surface_is_unbounded;
2571
2.66k
    params.type = CAIRO_RECORDING_CREATE_REGIONS;
2572
2.66k
    params.region = CAIRO_RECORDING_REGION_ALL;
2573
2.66k
    params.regions_id = regions_id;
2574
2.66k
    params.foreground_color = NULL;
2575
2.66k
    params.replay_all = replay_all;
2576
2577
2.66k
    return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, &params);
2578
2.66k
}
2579
2580
cairo_status_t
2581
_cairo_recording_surface_replay_region (cairo_surface_t          *surface,
2582
          unsigned int regions_id,
2583
          const cairo_rectangle_int_t *surface_extents,
2584
          cairo_surface_t          *target,
2585
          cairo_recording_region_type_t  region)
2586
1.96k
{
2587
1.96k
    cairo_recording_surface_replay_params_t params;
2588
2589
1.96k
    params.surface_extents = surface_extents;
2590
1.96k
    params.surface_transform = NULL;
2591
1.96k
    params.target = target;
2592
1.96k
    params.target_clip = NULL;
2593
1.96k
    params.surface_is_unbounded = FALSE;
2594
1.96k
    params.type = CAIRO_RECORDING_REPLAY_REGION;
2595
1.96k
    params.region = region;
2596
1.96k
    params.regions_id = regions_id;
2597
1.96k
    params.foreground_color = NULL;
2598
1.96k
    params.replay_all = FALSE;
2599
2600
1.96k
    return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, &params);
2601
1.96k
}
2602
2603
static cairo_status_t
2604
_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface,
2605
         cairo_box_t *bbox,
2606
         const cairo_matrix_t *transform)
2607
579
{
2608
579
    cairo_surface_t *null_surface;
2609
579
    cairo_surface_t *analysis_surface;
2610
579
    cairo_status_t status;
2611
2612
579
    null_surface = _cairo_null_surface_create (surface->base.content);
2613
579
    analysis_surface = _cairo_analysis_surface_create (null_surface, FALSE);
2614
579
    cairo_surface_destroy (null_surface);
2615
2616
579
    status = analysis_surface->status;
2617
579
    if (unlikely (status))
2618
0
  return status;
2619
2620
579
    if (transform != NULL)
2621
579
  _cairo_analysis_surface_set_ctm (analysis_surface, transform);
2622
2623
579
    status = _cairo_recording_surface_replay (&surface->base, analysis_surface);
2624
579
    _cairo_analysis_surface_get_bounding_box (analysis_surface, bbox);
2625
579
    cairo_surface_destroy (analysis_surface);
2626
2627
579
    return status;
2628
579
}
2629
2630
/**
2631
 * cairo_recording_surface_ink_extents:
2632
 * @surface: a #cairo_recording_surface_t
2633
 * @x0: the x-coordinate of the top-left of the ink bounding box
2634
 * @y0: the y-coordinate of the top-left of the ink bounding box
2635
 * @width: the width of the ink bounding box
2636
 * @height: the height of the ink bounding box
2637
 *
2638
 * Measures the extents of the operations stored within the recording-surface.
2639
 * This is useful to compute the required size of an image surface (or
2640
 * equivalent) into which to replay the full sequence of drawing operations.
2641
 *
2642
 * Since: 1.10
2643
 **/
2644
void
2645
cairo_recording_surface_ink_extents (cairo_surface_t *surface,
2646
             double *x0,
2647
             double *y0,
2648
             double *width,
2649
             double *height)
2650
0
{
2651
0
    cairo_status_t status;
2652
0
    cairo_box_t bbox;
2653
2654
0
    memset (&bbox, 0, sizeof (bbox));
2655
2656
0
    if (surface->status || ! _cairo_surface_is_recording (surface)) {
2657
0
  _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
2658
0
  goto DONE;
2659
0
    }
2660
2661
0
    status = _recording_surface_get_ink_bbox ((cairo_recording_surface_t *) surface,
2662
0
           &bbox,
2663
0
           NULL);
2664
0
    if (unlikely (status))
2665
0
  status = _cairo_surface_set_error (surface, status);
2666
2667
0
DONE:
2668
0
    if (x0)
2669
0
  *x0 = _cairo_fixed_to_double (bbox.p1.x);
2670
0
    if (y0)
2671
0
  *y0 = _cairo_fixed_to_double (bbox.p1.y);
2672
0
    if (width)
2673
0
  *width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x);
2674
0
    if (height)
2675
0
  *height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y);
2676
0
}
2677
2678
cairo_status_t
2679
_cairo_recording_surface_get_bbox (cairo_recording_surface_t *surface,
2680
           cairo_box_t *bbox,
2681
           const cairo_matrix_t *transform)
2682
3
{
2683
3
    if (! surface->unbounded) {
2684
0
  _cairo_box_from_rectangle (bbox, &surface->extents);
2685
0
  if (transform != NULL)
2686
0
      _cairo_matrix_transform_bounding_box_fixed (transform, bbox, NULL);
2687
2688
0
  return CAIRO_STATUS_SUCCESS;
2689
0
    }
2690
2691
3
    return _recording_surface_get_ink_bbox (surface, bbox, transform);
2692
3
}
2693
2694
cairo_status_t
2695
_cairo_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface,
2696
               cairo_box_t *bbox,
2697
               const cairo_matrix_t *transform)
2698
576
{
2699
576
    return _recording_surface_get_ink_bbox (surface, bbox, transform);
2700
576
}
2701
2702
/**
2703
 * cairo_recording_surface_get_extents:
2704
 * @surface: a #cairo_recording_surface_t
2705
 * @extents: the #cairo_rectangle_t to be assigned the extents
2706
 *
2707
 * Get the extents of the recording-surface.
2708
 *
2709
 * Return value: %TRUE if the surface is bounded, of recording type, and
2710
 * not in an error state, otherwise %FALSE
2711
 *
2712
 * Since: 1.12
2713
 **/
2714
cairo_bool_t
2715
cairo_recording_surface_get_extents (cairo_surface_t *surface,
2716
             cairo_rectangle_t *extents)
2717
0
{
2718
0
    cairo_recording_surface_t *record;
2719
2720
0
    if (surface->status || ! _cairo_surface_is_recording (surface)) {
2721
0
  _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
2722
0
  return FALSE;
2723
0
    }
2724
2725
0
    record = (cairo_recording_surface_t *)surface;
2726
0
    if (record->unbounded)
2727
0
  return FALSE;
2728
2729
0
    *extents = record->extents_pixels;
2730
0
    return TRUE;
2731
0
}
2732
2733
cairo_bool_t
2734
_cairo_recording_surface_has_only_bilevel_alpha (cairo_recording_surface_t *surface)
2735
1.52k
{
2736
1.52k
    return surface->has_bilevel_alpha;
2737
1.52k
}
2738
2739
cairo_bool_t
2740
_cairo_recording_surface_has_only_op_over (cairo_recording_surface_t *surface)
2741
1.52k
{
2742
1.52k
    return surface->has_only_op_over;
2743
1.52k
}
2744
2745
cairo_bool_t
2746
_cairo_recording_surface_has_tags (cairo_surface_t *surface)
2747
2.72k
{
2748
2.72k
    cairo_recording_surface_t *record;
2749
2750
2.72k
    if (surface->status || ! _cairo_surface_is_recording (surface)) {
2751
0
  _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
2752
0
  return FALSE;
2753
0
    }
2754
2755
2.72k
    record = (cairo_recording_surface_t *)surface;
2756
2757
2.72k
    return record->has_tags;
2758
2.72k
}
2759
2760
static void
2761
print_indent (FILE *file, int indent)
2762
0
{
2763
0
    fprintf (file, "%*s", indent * 2, "");
2764
0
}
2765
2766
static void
2767
print_pattern (FILE                  *file,
2768
         const cairo_pattern_t *pattern,
2769
         unsigned int           region_id,
2770
         int                    indent,
2771
         cairo_bool_t           recurse)
2772
0
{
2773
0
    switch (pattern->type) {
2774
0
  case CAIRO_PATTERN_TYPE_SOLID: {
2775
0
      cairo_solid_pattern_t *p = (cairo_solid_pattern_t *) pattern;
2776
0
      if (pattern->is_foreground_marker) {
2777
0
    fprintf (file, "solid foreground\n");
2778
0
      } else {
2779
0
    fprintf (file, "solid rgba: %f %f %f %f\n",
2780
0
       p->color.red,
2781
0
       p->color.green,
2782
0
       p->color.blue,
2783
0
       p->color.alpha);
2784
0
      }
2785
0
  } break;
2786
0
  case CAIRO_PATTERN_TYPE_SURFACE: {
2787
0
      cairo_surface_pattern_t *p = (cairo_surface_pattern_t *) pattern;
2788
0
      fprintf (file, "surface ");
2789
0
      if (p->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
2790
0
    fprintf (file, "recording id: %d\n", p->surface->unique_id);
2791
0
    if (recurse) {
2792
0
        _cairo_debug_print_recording_surface (file, p->surface,
2793
0
                region_id,
2794
0
                indent + 1, recurse);
2795
0
    }
2796
0
      } else if (p->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
2797
0
    cairo_image_surface_t *image = (cairo_image_surface_t *)p->surface;
2798
0
    fprintf (file, "image format: ");
2799
0
    switch (image->format) {
2800
0
        case CAIRO_FORMAT_INVALID: fputs ("INVALID", file); break;
2801
0
        case CAIRO_FORMAT_ARGB32: fputs ("ARGB32", file); break;
2802
0
        case CAIRO_FORMAT_RGB24: fputs ("RGB24", file); break;
2803
0
        case CAIRO_FORMAT_A8: fputs ("A8", file); break;
2804
0
        case CAIRO_FORMAT_A1: fputs ("A1", file); break;
2805
0
        case CAIRO_FORMAT_RGB16_565: fputs ("RGB16_565", file); break;
2806
0
        case CAIRO_FORMAT_RGB30: fputs ("RGB30", file); break;
2807
0
        case CAIRO_FORMAT_RGB96F: fputs ("RGB96F", file); break;
2808
0
        case CAIRO_FORMAT_RGBA128F: fputs ("RGBA128F", file); break;
2809
0
    }
2810
0
    fprintf (file, " width: %d height: %d\n", image->width, image->height);
2811
0
      } else {
2812
0
    fprintf (file, "type %d\n", p->surface->type);
2813
0
      }
2814
0
  } break;
2815
0
  case CAIRO_PATTERN_TYPE_LINEAR:
2816
0
      fprintf (file, "linear\n");
2817
0
      break;
2818
0
  case CAIRO_PATTERN_TYPE_RADIAL:
2819
0
      fprintf (file, "radial\n");
2820
0
      break;
2821
0
  case CAIRO_PATTERN_TYPE_MESH:
2822
0
      fprintf (file, "mesh\n");
2823
0
      break;
2824
0
  case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
2825
0
      fprintf (file, "raster\n");
2826
0
      break;
2827
0
    }
2828
0
}
2829
2830
void
2831
_cairo_debug_print_recording_surface (FILE            *file,
2832
              cairo_surface_t *surface,
2833
                                      unsigned int     regions_id,
2834
              int              indent,
2835
              cairo_bool_t     recurse)
2836
0
{
2837
0
    cairo_command_t **elements;
2838
0
    cairo_recording_region_element_t *region_elements = NULL;
2839
0
    unsigned int i, num_elements;
2840
0
    cairo_recording_surface_t *recording_surface;
2841
0
    cairo_surface_t *free_me = NULL;
2842
0
    char common[100];
2843
2844
0
    if (_cairo_surface_is_snapshot (surface))
2845
0
  free_me = surface = _cairo_surface_snapshot_get_target (surface);
2846
2847
0
    assert (_cairo_surface_is_recording (surface));
2848
0
    recording_surface = (cairo_recording_surface_t *)surface;
2849
2850
0
    print_indent (file, indent);
2851
0
    indent++;
2852
0
    fprintf(file, "recording surface id: %d   regions id: %d\n", recording_surface->base.unique_id, regions_id);
2853
0
    num_elements = recording_surface->commands.num_elements;
2854
0
    elements = _cairo_array_index (&recording_surface->commands, 0);
2855
2856
0
    if (regions_id != 0) {
2857
0
  cairo_recording_regions_array_t *regions_array;
2858
0
  regions_array = _cairo_recording_surface_region_array_find (recording_surface, regions_id);
2859
0
  assert (regions_array != NULL);
2860
0
  assert (_cairo_array_num_elements (&regions_array->regions) == num_elements);
2861
0
  region_elements = _cairo_array_index (&regions_array->regions, 0);
2862
0
    }
2863
2864
0
    for (i = 0; i < num_elements; i++) {
2865
0
  cairo_command_t *command = elements[i];
2866
0
  unsigned int source_region_id = 0;
2867
0
  unsigned int mask_region_id = 0;
2868
2869
0
  common[0] = 0;
2870
0
  if (region_elements) {
2871
0
      cairo_recording_region_element_t *region_element = &region_elements[i];
2872
0
      strcpy (common, "region: ");
2873
0
      switch (region_element->region) {
2874
0
    case CAIRO_RECORDING_REGION_ALL: strcat (common, "all"); break;
2875
0
    case CAIRO_RECORDING_REGION_NATIVE: strcat (common, "native"); break;
2876
0
    case CAIRO_RECORDING_REGION_IMAGE_FALLBACK: strcat (common, "fallback"); break;
2877
0
      }
2878
0
      source_region_id  = region_element->source_id;
2879
0
      mask_region_id = region_element->mask_id;
2880
0
  }
2881
0
  sprintf (common + strlen(common), " op: %s", _cairo_debug_operator_to_string (command->header.op));
2882
2883
0
  switch (command->header.type) {
2884
0
      case CAIRO_COMMAND_PAINT:
2885
0
    print_indent (file, indent);
2886
0
    fprintf(file, "%d PAINT %s source: ", i, common);
2887
0
    print_pattern (file, &command->paint.source.base, source_region_id, indent + 1, recurse);
2888
0
    break;
2889
2890
0
      case CAIRO_COMMAND_MASK:
2891
0
    print_indent (file, indent);
2892
0
    fprintf(file, "%d MASK %s\n", i, common);
2893
0
    print_indent (file, indent + 1);
2894
0
    fprintf(file, "source: ");
2895
0
    print_pattern (file, &command->mask.source.base, source_region_id, indent + 1, recurse);
2896
0
    print_indent (file, indent + 1);
2897
0
    fprintf(file, "mask: ");
2898
0
    print_pattern (file, &command->mask.mask.base, mask_region_id, indent + 1, recurse);
2899
0
    break;
2900
2901
0
      case CAIRO_COMMAND_STROKE:
2902
0
    print_indent (file, indent);
2903
0
    fprintf(file, "%d STROKE %s source:", i, common);
2904
0
    print_pattern (file, &command->stroke.source.base, source_region_id, indent + 1, recurse);
2905
0
    break;
2906
2907
0
      case CAIRO_COMMAND_FILL:
2908
0
    print_indent (file, indent);
2909
0
    fprintf(file, "%d FILL %s source: ", i, common);
2910
0
    print_pattern (file, &command->fill.source.base, source_region_id, indent + 1, recurse);
2911
0
    break;
2912
2913
0
      case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
2914
0
    print_indent (file, indent);
2915
0
    fprintf(file, "%d SHOW_TEXT_GLYPHS %s font_type: ", i, common);
2916
0
    switch (command->show_text_glyphs.scaled_font->backend->type) {
2917
0
        case CAIRO_FONT_TYPE_TOY: fputs ("toy", file); break;
2918
0
        case CAIRO_FONT_TYPE_FT: fputs ("ft", file); break;
2919
0
        case CAIRO_FONT_TYPE_WIN32: fputs ("win32", file); break;
2920
0
        case CAIRO_FONT_TYPE_QUARTZ: fputs ("quartz", file); break;
2921
0
        case CAIRO_FONT_TYPE_USER: fputs ("user", file); break;
2922
0
        case CAIRO_FONT_TYPE_DWRITE: fputs ("dwrite", file); break;
2923
0
    }
2924
0
    fprintf (file, " glyphs:");
2925
0
    for (unsigned j = 0; j < command->show_text_glyphs.num_glyphs; j++)
2926
0
        fprintf (file, " %ld", command->show_text_glyphs.glyphs[j].index);
2927
0
    fprintf (file, " source:");
2928
0
    print_pattern (file, &command->show_text_glyphs.source.base, source_region_id, indent + 1, recurse);
2929
0
    break;
2930
2931
0
      case CAIRO_COMMAND_TAG:
2932
0
    print_indent (file, indent);
2933
0
    fprintf(file, "%d %s %s '%s'\n",
2934
0
      i,
2935
0
      command->tag.begin ? "BEGIN TAG" : "END TAG",
2936
0
      command->tag.tag_name, command->tag.attributes);
2937
0
    break;
2938
2939
0
      default:
2940
0
    ASSERT_NOT_REACHED;
2941
0
  }
2942
0
    }
2943
0
    cairo_surface_destroy (free_me);
2944
0
}