Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3959 Serge 1
/* cairo - a vector graphics library with display and print output
2
 *
3
 * Copyright © 2011 Intel Corporation
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it either under the terms of the GNU Lesser General Public
7
 * License version 2.1 as published by the Free Software Foundation
8
 * (the "LGPL") or, at your option, under the terms of the Mozilla
9
 * Public License Version 1.1 (the "MPL"). If you do not alter this
10
 * notice, a recipient may use your version of this file under either
11
 * the MPL or the LGPL.
12
 *
13
 * You should have received a copy of the LGPL along with this library
14
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16
 * You should have received a copy of the MPL along with this library
17
 * in the file COPYING-MPL-1.1
18
 *
19
 * The contents of this file are subject to the Mozilla Public License
20
 * Version 1.1 (the "License"); you may not use this file except in
21
 * compliance with the License. You may obtain a copy of the License at
22
 * http://www.mozilla.org/MPL/
23
 *
24
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26
 * the specific language governing rights and limitations.
27
 *
28
 * The Original Code is the cairo graphics library.
29
 *
30
 * The Initial Developer of the Original Code is Intel Corporation.
31
 *
32
 * Contributor(s):
33
 *      Chris Wilson 
34
 */
35
 
36
#include "cairoint.h"
37
 
38
#include "cairo-surface-observer-private.h"
39
#include "cairo-surface-observer-inline.h"
40
 
41
#include "cairo-array-private.h"
42
#include "cairo-combsort-inline.h"
43
#include "cairo-composite-rectangles-private.h"
44
#include "cairo-error-private.h"
45
#include "cairo-image-surface-private.h"
46
#include "cairo-list-inline.h"
47
#include "cairo-pattern-private.h"
48
#include "cairo-output-stream-private.h"
49
#include "cairo-recording-surface-private.h"
50
#include "cairo-surface-subsurface-inline.h"
51
#include "cairo-reference-count-private.h"
52
 
53
#if CAIRO_HAS_SCRIPT_SURFACE
54
#include "cairo-script-private.h"
55
#endif
56
 
57
static const cairo_surface_backend_t _cairo_surface_observer_backend;
58
 
59
/* observation/stats */
60
 
61
static void init_stats (struct stat *s)
62
{
63
    s->min = HUGE_VAL;
64
    s->max = -HUGE_VAL;
65
}
66
 
67
static void init_extents (struct extents *e)
68
{
69
    init_stats (&e->area);
70
}
71
 
72
static void init_pattern (struct pattern *p)
73
{
74
}
75
 
76
static void init_path (struct path *p)
77
{
78
}
79
 
80
static void init_clip (struct clip *c)
81
{
82
}
83
 
84
static void init_paint (struct paint *p)
85
{
86
    init_extents (&p->extents);
87
    init_pattern (&p->source);
88
    init_clip (&p->clip);
89
}
90
 
91
static void init_mask (struct mask *m)
92
{
93
    init_extents (&m->extents);
94
    init_pattern (&m->source);
95
    init_pattern (&m->mask);
96
    init_clip (&m->clip);
97
}
98
 
99
static void init_fill (struct fill *f)
100
{
101
    init_extents (&f->extents);
102
    init_pattern (&f->source);
103
    init_path (&f->path);
104
    init_clip (&f->clip);
105
}
106
 
107
static void init_stroke (struct stroke *s)
108
{
109
    init_extents (&s->extents);
110
    init_pattern (&s->source);
111
    init_path (&s->path);
112
    init_clip (&s->clip);
113
}
114
 
115
static void init_glyphs (struct glyphs *g)
116
{
117
    init_extents (&g->extents);
118
    init_pattern (&g->source);
119
    init_clip (&g->clip);
120
}
121
 
122
static cairo_status_t
123
log_init (cairo_observation_t *log,
124
	  cairo_bool_t record)
125
{
126
    memset (log, 0, sizeof(*log));
127
 
128
    init_paint (&log->paint);
129
    init_mask (&log->mask);
130
    init_fill (&log->fill);
131
    init_stroke (&log->stroke);
132
    init_glyphs (&log->glyphs);
133
 
134
    _cairo_array_init (&log->timings, sizeof (cairo_observation_record_t));
135
 
136
    if (record) {
137
	log->record = (cairo_recording_surface_t *)
138
	    cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
139
	if (unlikely (log->record->base.status))
140
	    return log->record->base.status;
141
 
142
	log->record->optimize_clears = FALSE;
143
    }
144
 
145
    return CAIRO_STATUS_SUCCESS;
146
}
147
 
148
static void
149
log_fini (cairo_observation_t *log)
150
{
151
    _cairo_array_fini (&log->timings);
152
    cairo_surface_destroy (&log->record->base);
153
}
154
 
155
static cairo_surface_t*
156
get_pattern_surface (const cairo_pattern_t *pattern)
157
{
158
    return ((cairo_surface_pattern_t *)pattern)->surface;
159
}
160
 
161
static int
162
classify_pattern (const cairo_pattern_t *pattern,
163
		  const cairo_surface_t *target)
164
{
165
    int classify;
166
 
167
    switch (pattern->type) {
168
    case CAIRO_PATTERN_TYPE_SURFACE:
169
	if (get_pattern_surface (pattern)->type == target->type)
170
	    classify = 0;
171
	else if (get_pattern_surface (pattern)->type == CAIRO_SURFACE_TYPE_RECORDING)
172
	    classify = 1;
173
	else
174
	    classify = 2;
175
	break;
176
    default:
177
    case CAIRO_PATTERN_TYPE_SOLID:
178
	classify = 3;
179
	break;
180
    case CAIRO_PATTERN_TYPE_LINEAR:
181
	classify = 4;
182
	break;
183
    case CAIRO_PATTERN_TYPE_RADIAL:
184
	classify = 5;
185
	break;
186
    case CAIRO_PATTERN_TYPE_MESH:
187
	classify = 6;
188
	break;
189
    case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
190
	classify = 7;
191
	break;
192
    }
193
    return classify;
194
}
195
 
196
static void
197
add_pattern (struct pattern *stats,
198
	     const cairo_pattern_t *pattern,
199
	     const cairo_surface_t *target)
200
{
201
    stats->type[classify_pattern(pattern, target)]++;
202
}
203
 
204
static int
205
classify_path (const cairo_path_fixed_t *path,
206
	       cairo_bool_t is_fill)
207
{
208
    int classify;
209
 
210
    /* XXX improve for stroke */
211
    classify = -1;
212
    if (is_fill) {
213
	if (path->fill_is_empty)
214
	    classify = 0;
215
	else if (_cairo_path_fixed_fill_is_rectilinear (path))
216
	    classify = path->fill_maybe_region ? 1 : 2;
217
    } else {
218
	if (_cairo_path_fixed_stroke_is_rectilinear (path))
219
	    classify = 2;
220
    }
221
    if (classify == -1)
222
	classify = 3 + (path->has_curve_to != 0);
223
 
224
    return classify;
225
}
226
 
227
static void
228
add_path (struct path *stats,
229
	  const cairo_path_fixed_t *path,
230
	  cairo_bool_t is_fill)
231
{
232
    stats->type[classify_path(path, is_fill)]++;
233
}
234
 
235
static int
236
classify_clip (const cairo_clip_t *clip)
237
{
238
    int classify;
239
 
240
    if (clip == NULL)
241
	classify = 0;
242
    else if (_cairo_clip_is_region (clip))
243
	classify = 1;
244
    else if (clip->path == NULL)
245
	classify = 2;
246
    else if (clip->path->prev == NULL)
247
	classify = 3;
248
    else if (_cairo_clip_is_polygon (clip))
249
	classify = 4;
250
    else
251
	classify = 5;
252
 
253
    return classify;
254
}
255
 
256
static void
257
add_clip (struct clip *stats,
258
	  const cairo_clip_t *clip)
259
{
260
    stats->type[classify_clip (clip)]++;
261
}
262
 
263
static void
264
stats_add (struct stat *s, double v)
265
{
266
    if (v < s->min)
267
	s->min = v;
268
    if (v > s->max)
269
	s->max = v;
270
    s->sum += v;
271
    s->sum_sq += v*v;
272
    s->count++;
273
}
274
 
275
static void
276
add_extents (struct extents *stats,
277
	     const cairo_composite_rectangles_t *extents)
278
{
279
    const cairo_rectangle_int_t *r = extents->is_bounded ? &extents->bounded :&extents->unbounded;
280
    stats_add (&stats->area, r->width * r->height);
281
    stats->bounded += extents->is_bounded != 0;
282
    stats->unbounded += extents->is_bounded == 0;
283
}
284
 
285
/* device interface */
286
 
287
static void
288
_cairo_device_observer_lock (void *_device)
289
{
290
    cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
291
    cairo_status_t ignored;
292
 
293
    /* cairo_device_acquire() can fail for nil and finished
294
     * devices. We don't care about observing them. */
295
    ignored = cairo_device_acquire (device->target);
296
}
297
 
298
static void
299
_cairo_device_observer_unlock (void *_device)
300
{
301
    cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
302
    cairo_device_release (device->target);
303
}
304
 
305
static cairo_status_t
306
_cairo_device_observer_flush (void *_device)
307
{
308
    cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
309
 
310
    if (device->target == NULL)
311
	return CAIRO_STATUS_SUCCESS;
312
 
313
    cairo_device_flush (device->target);
314
    return device->target->status;
315
}
316
 
317
static void
318
_cairo_device_observer_finish (void *_device)
319
{
320
    cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
321
    log_fini (&device->log);
322
    cairo_device_finish (device->target);
323
}
324
 
325
static void
326
_cairo_device_observer_destroy (void *_device)
327
{
328
    cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
329
    cairo_device_destroy (device->target);
330
    free (device);
331
}
332
 
333
static const cairo_device_backend_t _cairo_device_observer_backend = {
334
    CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER,
335
 
336
    _cairo_device_observer_lock,
337
    _cairo_device_observer_unlock,
338
 
339
    _cairo_device_observer_flush,
340
    _cairo_device_observer_finish,
341
    _cairo_device_observer_destroy,
342
};
343
 
344
static cairo_device_t *
345
_cairo_device_create_observer_internal (cairo_device_t *target,
346
					cairo_bool_t record)
347
{
348
    cairo_device_observer_t *device;
349
    cairo_status_t status;
350
 
351
    device = malloc (sizeof (cairo_device_observer_t));
352
    if (unlikely (device == NULL))
353
	return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
354
 
355
    _cairo_device_init (&device->base, &_cairo_device_observer_backend);
356
    status = log_init (&device->log, record);
357
    if (unlikely (status)) {
358
	free (device);
359
	return _cairo_device_create_in_error (status);
360
    }
361
 
362
    device->target = cairo_device_reference (target);
363
 
364
    return &device->base;
365
}
366
 
367
/* surface interface */
368
 
369
static cairo_device_observer_t *
370
to_device (cairo_surface_observer_t *suface)
371
{
372
    return (cairo_device_observer_t *)suface->base.device;
373
}
374
 
375
static cairo_surface_t *
376
_cairo_surface_create_observer_internal (cairo_device_t *device,
377
					 cairo_surface_t *target)
378
{
379
    cairo_surface_observer_t *surface;
380
    cairo_status_t status;
381
 
382
    surface = malloc (sizeof (cairo_surface_observer_t));
383
    if (unlikely (surface == NULL))
384
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
385
 
386
    _cairo_surface_init (&surface->base,
387
			 &_cairo_surface_observer_backend, device,
388
			 target->content);
389
 
390
    status = log_init (&surface->log,
391
		       ((cairo_device_observer_t *)device)->log.record != NULL);
392
    if (unlikely (status)) {
393
	free (surface);
394
	return _cairo_surface_create_in_error (status);
395
    }
396
 
397
    surface->target = cairo_surface_reference (target);
398
    surface->base.type = surface->target->type;
399
    surface->base.is_clear = surface->target->is_clear;
400
 
401
    cairo_list_init (&surface->paint_callbacks);
402
    cairo_list_init (&surface->mask_callbacks);
403
    cairo_list_init (&surface->fill_callbacks);
404
    cairo_list_init (&surface->stroke_callbacks);
405
    cairo_list_init (&surface->glyphs_callbacks);
406
 
407
    cairo_list_init (&surface->flush_callbacks);
408
    cairo_list_init (&surface->finish_callbacks);
409
 
410
    surface->log.num_surfaces++;
411
    to_device (surface)->log.num_surfaces++;
412
 
413
    return &surface->base;
414
}
415
 
416
static inline void
417
do_callbacks (cairo_surface_observer_t *surface, cairo_list_t *head)
418
{
419
    struct callback_list *cb;
420
 
421
    cairo_list_foreach_entry (cb, struct callback_list, head, link)
422
	cb->func (&surface->base, surface->target, cb->data);
423
}
424
 
425
 
426
static cairo_status_t
427
_cairo_surface_observer_finish (void *abstract_surface)
428
{
429
    cairo_surface_observer_t *surface = abstract_surface;
430
 
431
    do_callbacks (surface, &surface->finish_callbacks);
432
 
433
    cairo_surface_destroy (surface->target);
434
    log_fini (&surface->log);
435
 
436
    return CAIRO_STATUS_SUCCESS;
437
}
438
 
439
static cairo_surface_t *
440
_cairo_surface_observer_create_similar (void *abstract_other,
441
					cairo_content_t content,
442
					int width, int height)
443
{
444
    cairo_surface_observer_t *other = abstract_other;
445
    cairo_surface_t *target, *surface;
446
 
447
    target = NULL;
448
    if (other->target->backend->create_similar)
449
	target = other->target->backend->create_similar (other->target, content,
450
							 width, height);
451
    if (target == NULL)
452
	target = _cairo_image_surface_create_with_content (content,
453
							   width, height);
454
 
455
    surface = _cairo_surface_create_observer_internal (other->base.device,
456
						       target);
457
    cairo_surface_destroy (target);
458
 
459
    return surface;
460
}
461
 
462
static cairo_surface_t *
463
_cairo_surface_observer_create_similar_image (void *other,
464
					      cairo_format_t format,
465
					      int width, int height)
466
{
467
    cairo_surface_observer_t *surface = other;
468
 
469
    if (surface->target->backend->create_similar_image)
470
	return surface->target->backend->create_similar_image (surface->target,
471
							       format,
472
							       width, height);
473
 
474
    return NULL;
475
}
476
 
477
static cairo_image_surface_t *
478
_cairo_surface_observer_map_to_image (void *abstract_surface,
479
				      const cairo_rectangle_int_t *extents)
480
{
481
    cairo_surface_observer_t *surface = abstract_surface;
482
    return _cairo_surface_map_to_image (surface->target, extents);
483
}
484
 
485
static cairo_int_status_t
486
_cairo_surface_observer_unmap_image (void *abstract_surface,
487
				     cairo_image_surface_t *image)
488
{
489
    cairo_surface_observer_t *surface = abstract_surface;
490
    return _cairo_surface_unmap_image (surface->target, image);
491
}
492
 
493
static void
494
record_target (cairo_observation_record_t *r,
495
	       cairo_surface_t *target)
496
{
497
    cairo_rectangle_int_t extents;
498
 
499
    r->target_content = target->content;
500
    if (_cairo_surface_get_extents (target, &extents)) {
501
	r->target_width = extents.width;
502
	r->target_height = extents.height;
503
    } else {
504
	r->target_width = -1;
505
	r->target_height = -1;
506
    }
507
}
508
 
509
static cairo_observation_record_t *
510
record_paint (cairo_observation_record_t *r,
511
	      cairo_surface_t *target,
512
	      cairo_operator_t op,
513
	      const cairo_pattern_t *source,
514
	      const cairo_clip_t *clip,
515
	      cairo_time_t elapsed)
516
{
517
    record_target (r, target);
518
 
519
    r->op = op;
520
    r->source = classify_pattern (source, target);
521
    r->mask = -1;
522
    r->num_glyphs = -1;
523
    r->path = -1;
524
    r->fill_rule = -1;
525
    r->tolerance = -1;
526
    r->antialias = -1;
527
    r->clip = classify_clip (clip);
528
    r->elapsed = elapsed;
529
 
530
    return r;
531
}
532
 
533
static cairo_observation_record_t *
534
record_mask (cairo_observation_record_t *r,
535
	     cairo_surface_t *target,
536
	     cairo_operator_t op,
537
	     const cairo_pattern_t *source,
538
	     const cairo_pattern_t *mask,
539
	     const cairo_clip_t *clip,
540
	     cairo_time_t elapsed)
541
{
542
    record_target (r, target);
543
 
544
    r->op = op;
545
    r->source = classify_pattern (source, target);
546
    r->mask = classify_pattern (mask, target);
547
    r->num_glyphs = -1;
548
    r->path = -1;
549
    r->fill_rule = -1;
550
    r->tolerance = -1;
551
    r->antialias = -1;
552
    r->clip = classify_clip (clip);
553
    r->elapsed = elapsed;
554
 
555
    return r;
556
}
557
 
558
static cairo_observation_record_t *
559
record_fill (cairo_observation_record_t *r,
560
	     cairo_surface_t		*target,
561
	     cairo_operator_t		op,
562
	     const cairo_pattern_t	*source,
563
	     const cairo_path_fixed_t	*path,
564
	     cairo_fill_rule_t		 fill_rule,
565
	     double			 tolerance,
566
	     cairo_antialias_t		 antialias,
567
	     const cairo_clip_t		*clip,
568
	     cairo_time_t elapsed)
569
{
570
    record_target (r, target);
571
 
572
    r->op = op;
573
    r->source = classify_pattern (source, target);
574
    r->mask = -1;
575
    r->num_glyphs = -1;
576
    r->path = classify_path (path, TRUE);
577
    r->fill_rule = fill_rule;
578
    r->tolerance = tolerance;
579
    r->antialias = antialias;
580
    r->clip = classify_clip (clip);
581
    r->elapsed = elapsed;
582
 
583
    return r;
584
}
585
 
586
static cairo_observation_record_t *
587
record_stroke (cairo_observation_record_t *r,
588
	       cairo_surface_t		*target,
589
	       cairo_operator_t		op,
590
	       const cairo_pattern_t	*source,
591
	       const cairo_path_fixed_t	*path,
592
	       const cairo_stroke_style_t	*style,
593
	       const cairo_matrix_t	*ctm,
594
	       const cairo_matrix_t	*ctm_inverse,
595
	       double			 tolerance,
596
	       cairo_antialias_t	 antialias,
597
	       const cairo_clip_t	*clip,
598
	       cairo_time_t		 elapsed)
599
{
600
    record_target (r, target);
601
 
602
    r->op = op;
603
    r->source = classify_pattern (source, target);
604
    r->mask = -1;
605
    r->num_glyphs = -1;
606
    r->path = classify_path (path, FALSE);
607
    r->fill_rule = -1;
608
    r->tolerance = tolerance;
609
    r->antialias = antialias;
610
    r->clip = classify_clip (clip);
611
    r->elapsed = elapsed;
612
 
613
    return r;
614
}
615
 
616
static cairo_observation_record_t *
617
record_glyphs (cairo_observation_record_t *r,
618
	       cairo_surface_t		*target,
619
	       cairo_operator_t		op,
620
	       const cairo_pattern_t	*source,
621
	       cairo_glyph_t		*glyphs,
622
	       int			 num_glyphs,
623
	       cairo_scaled_font_t	*scaled_font,
624
	       const cairo_clip_t	*clip,
625
	       cairo_time_t		 elapsed)
626
{
627
    record_target (r, target);
628
 
629
    r->op = op;
630
    r->source = classify_pattern (source, target);
631
    r->mask = -1;
632
    r->path = -1;
633
    r->num_glyphs = num_glyphs;
634
    r->fill_rule = -1;
635
    r->tolerance = -1;
636
    r->antialias = -1;
637
    r->clip = classify_clip (clip);
638
    r->elapsed = elapsed;
639
 
640
    return r;
641
}
642
 
643
static void
644
add_record (cairo_observation_t *log,
645
	    cairo_observation_record_t *r)
646
{
647
    cairo_int_status_t status;
648
 
649
    r->index = log->record ? log->record->commands.num_elements : 0;
650
 
651
    status = _cairo_array_append (&log->timings, r);
652
    assert (status == CAIRO_INT_STATUS_SUCCESS);
653
}
654
 
655
static void
656
sync (cairo_surface_t *target, int x, int y)
657
{
658
    cairo_rectangle_int_t extents;
659
 
660
    extents.x = x;
661
    extents.y = y;
662
    extents.width  = 1;
663
    extents.height = 1;
664
 
665
    _cairo_surface_unmap_image (target,
666
				_cairo_surface_map_to_image (target,
667
							     &extents));
668
}
669
 
670
static void
671
midpt (const cairo_composite_rectangles_t *extents, int *x, int *y)
672
{
673
    *x = extents->bounded.x + extents->bounded.width / 2;
674
    *y = extents->bounded.y + extents->bounded.height / 2;
675
}
676
 
677
static void
678
add_record_paint (cairo_observation_t *log,
679
		 cairo_surface_t *target,
680
		 cairo_operator_t op,
681
		 const cairo_pattern_t *source,
682
		 const cairo_clip_t *clip,
683
		 cairo_time_t elapsed)
684
{
685
    cairo_observation_record_t record;
686
    cairo_int_status_t status;
687
 
688
    add_record (log,
689
		record_paint (&record, target, op, source, clip, elapsed));
690
 
691
    /* We have to bypass the high-level surface layer in case it tries to be
692
     * too smart and discard operations; we need to record exactly what just
693
     * happened on the target.
694
     */
695
    if (log->record) {
696
	status = log->record->base.backend->paint (&log->record->base,
697
						   op, source, clip);
698
	assert (status == CAIRO_INT_STATUS_SUCCESS);
699
    }
700
 
701
    if (_cairo_time_gt (elapsed, log->paint.slowest.elapsed))
702
	log->paint.slowest = record;
703
    log->paint.elapsed = _cairo_time_add (log->paint.elapsed, elapsed);
704
}
705
 
706
static cairo_int_status_t
707
_cairo_surface_observer_paint (void *abstract_surface,
708
			       cairo_operator_t op,
709
			       const cairo_pattern_t *source,
710
			       const cairo_clip_t *clip)
711
{
712
    cairo_surface_observer_t *surface = abstract_surface;
713
    cairo_device_observer_t *device = to_device (surface);
714
    cairo_composite_rectangles_t composite;
715
    cairo_int_status_t status;
716
    cairo_time_t t;
717
    int x, y;
718
 
719
    /* XXX device locking */
720
 
721
    surface->log.paint.count++;
722
    surface->log.paint.operators[op]++;
723
    add_pattern (&surface->log.paint.source, source, surface->target);
724
    add_clip (&surface->log.paint.clip, clip);
725
 
726
    device->log.paint.count++;
727
    device->log.paint.operators[op]++;
728
    add_pattern (&device->log.paint.source, source, surface->target);
729
    add_clip (&device->log.paint.clip, clip);
730
 
731
    status = _cairo_composite_rectangles_init_for_paint (&composite,
732
							 surface->target,
733
							 op, source,
734
							 clip);
735
    if (unlikely (status)) {
736
	surface->log.paint.noop++;
737
	device->log.paint.noop++;
738
	return status;
739
    }
740
 
741
    midpt (&composite, &x, &y);
742
 
743
    add_extents (&surface->log.paint.extents, &composite);
744
    add_extents (&device->log.paint.extents, &composite);
745
    _cairo_composite_rectangles_fini (&composite);
746
 
747
    t = _cairo_time_get ();
748
    status = _cairo_surface_paint (surface->target,
749
				   op, source,
750
				   clip);
751
    if (unlikely (status))
752
	return status;
753
 
754
    sync (surface->target, x, y);
755
    t = _cairo_time_get_delta (t);
756
 
757
    add_record_paint (&surface->log, surface->target, op, source, clip, t);
758
    add_record_paint (&device->log, surface->target, op, source, clip, t);
759
 
760
    do_callbacks (surface, &surface->paint_callbacks);
761
 
762
    return CAIRO_STATUS_SUCCESS;
763
}
764
 
765
static void
766
add_record_mask (cairo_observation_t *log,
767
		 cairo_surface_t *target,
768
		 cairo_operator_t op,
769
		 const cairo_pattern_t *source,
770
		 const cairo_pattern_t *mask,
771
		 const cairo_clip_t *clip,
772
		 cairo_time_t elapsed)
773
{
774
    cairo_observation_record_t record;
775
    cairo_int_status_t status;
776
 
777
    add_record (log,
778
		record_mask (&record, target, op, source, mask, clip, elapsed));
779
 
780
    if (log->record) {
781
	status = log->record->base.backend->mask (&log->record->base,
782
						  op, source, mask, clip);
783
	assert (status == CAIRO_INT_STATUS_SUCCESS);
784
    }
785
 
786
    if (_cairo_time_gt (elapsed, log->mask.slowest.elapsed))
787
	log->mask.slowest = record;
788
    log->mask.elapsed = _cairo_time_add (log->mask.elapsed, elapsed);
789
}
790
 
791
static cairo_int_status_t
792
_cairo_surface_observer_mask (void *abstract_surface,
793
			      cairo_operator_t op,
794
			      const cairo_pattern_t *source,
795
			      const cairo_pattern_t *mask,
796
			      const cairo_clip_t *clip)
797
{
798
    cairo_surface_observer_t *surface = abstract_surface;
799
    cairo_device_observer_t *device = to_device (surface);
800
    cairo_composite_rectangles_t composite;
801
    cairo_int_status_t status;
802
    cairo_time_t t;
803
    int x, y;
804
 
805
    surface->log.mask.count++;
806
    surface->log.mask.operators[op]++;
807
    add_pattern (&surface->log.mask.source, source, surface->target);
808
    add_pattern (&surface->log.mask.mask, mask, surface->target);
809
    add_clip (&surface->log.mask.clip, clip);
810
 
811
    device->log.mask.count++;
812
    device->log.mask.operators[op]++;
813
    add_pattern (&device->log.mask.source, source, surface->target);
814
    add_pattern (&device->log.mask.mask, mask, surface->target);
815
    add_clip (&device->log.mask.clip, clip);
816
 
817
    status = _cairo_composite_rectangles_init_for_mask (&composite,
818
							surface->target,
819
							op, source, mask,
820
							clip);
821
    if (unlikely (status)) {
822
	surface->log.mask.noop++;
823
	device->log.mask.noop++;
824
	return status;
825
    }
826
 
827
    midpt (&composite, &x, &y);
828
 
829
    add_extents (&surface->log.mask.extents, &composite);
830
    add_extents (&device->log.mask.extents, &composite);
831
    _cairo_composite_rectangles_fini (&composite);
832
 
833
    t = _cairo_time_get ();
834
    status =  _cairo_surface_mask (surface->target,
835
				   op, source, mask,
836
				   clip);
837
    if (unlikely (status))
838
	return status;
839
 
840
    sync (surface->target, x, y);
841
    t = _cairo_time_get_delta (t);
842
 
843
    add_record_mask (&surface->log,
844
		     surface->target, op, source, mask, clip,
845
		     t);
846
    add_record_mask (&device->log,
847
		     surface->target, op, source, mask, clip,
848
		     t);
849
 
850
    do_callbacks (surface, &surface->mask_callbacks);
851
 
852
    return CAIRO_STATUS_SUCCESS;
853
}
854
 
855
static void
856
add_record_fill (cairo_observation_t *log,
857
		 cairo_surface_t *target,
858
		 cairo_operator_t		op,
859
		 const cairo_pattern_t		*source,
860
		 const cairo_path_fixed_t	*path,
861
		 cairo_fill_rule_t		 fill_rule,
862
		 double				 tolerance,
863
		 cairo_antialias_t		 antialias,
864
		 const cairo_clip_t		 *clip,
865
		 cairo_time_t elapsed)
866
{
867
    cairo_observation_record_t record;
868
    cairo_int_status_t status;
869
 
870
    add_record (log,
871
		record_fill (&record,
872
			     target, op, source,
873
			     path, fill_rule, tolerance, antialias,
874
			     clip, elapsed));
875
 
876
    if (log->record) {
877
	status = log->record->base.backend->fill (&log->record->base,
878
						  op, source,
879
						  path, fill_rule,
880
						  tolerance, antialias,
881
						  clip);
882
	assert (status == CAIRO_INT_STATUS_SUCCESS);
883
    }
884
 
885
    if (_cairo_time_gt (elapsed, log->fill.slowest.elapsed))
886
	log->fill.slowest = record;
887
    log->fill.elapsed = _cairo_time_add (log->fill.elapsed, elapsed);
888
}
889
 
890
static cairo_int_status_t
891
_cairo_surface_observer_fill (void			*abstract_surface,
892
			      cairo_operator_t		op,
893
			      const cairo_pattern_t	*source,
894
			      const cairo_path_fixed_t	*path,
895
			      cairo_fill_rule_t		fill_rule,
896
			      double			 tolerance,
897
			      cairo_antialias_t		antialias,
898
			      const cairo_clip_t	*clip)
899
{
900
    cairo_surface_observer_t *surface = abstract_surface;
901
    cairo_device_observer_t *device = to_device (surface);
902
    cairo_composite_rectangles_t composite;
903
    cairo_int_status_t status;
904
    cairo_time_t t;
905
    int x, y;
906
 
907
    surface->log.fill.count++;
908
    surface->log.fill.operators[op]++;
909
    surface->log.fill.fill_rule[fill_rule]++;
910
    surface->log.fill.antialias[antialias]++;
911
    add_pattern (&surface->log.fill.source, source, surface->target);
912
    add_path (&surface->log.fill.path, path, TRUE);
913
    add_clip (&surface->log.fill.clip, clip);
914
 
915
    device->log.fill.count++;
916
    device->log.fill.operators[op]++;
917
    device->log.fill.fill_rule[fill_rule]++;
918
    device->log.fill.antialias[antialias]++;
919
    add_pattern (&device->log.fill.source, source, surface->target);
920
    add_path (&device->log.fill.path, path, TRUE);
921
    add_clip (&device->log.fill.clip, clip);
922
 
923
    status = _cairo_composite_rectangles_init_for_fill (&composite,
924
							surface->target,
925
							op, source, path,
926
							clip);
927
    if (unlikely (status)) {
928
	surface->log.fill.noop++;
929
	device->log.fill.noop++;
930
	return status;
931
    }
932
 
933
    midpt (&composite, &x, &y);
934
 
935
    add_extents (&surface->log.fill.extents, &composite);
936
    add_extents (&device->log.fill.extents, &composite);
937
    _cairo_composite_rectangles_fini (&composite);
938
 
939
    t = _cairo_time_get ();
940
    status = _cairo_surface_fill (surface->target,
941
				  op, source, path,
942
				  fill_rule, tolerance, antialias,
943
				  clip);
944
    if (unlikely (status))
945
	return status;
946
 
947
    sync (surface->target, x, y);
948
    t = _cairo_time_get_delta (t);
949
 
950
    add_record_fill (&surface->log,
951
		     surface->target, op, source, path,
952
		     fill_rule, tolerance, antialias,
953
		     clip, t);
954
 
955
    add_record_fill (&device->log,
956
		     surface->target, op, source, path,
957
		     fill_rule, tolerance, antialias,
958
		     clip, t);
959
 
960
    do_callbacks (surface, &surface->fill_callbacks);
961
 
962
    return CAIRO_STATUS_SUCCESS;
963
}
964
 
965
static void
966
add_record_stroke (cairo_observation_t *log,
967
		 cairo_surface_t *target,
968
		 cairo_operator_t		 op,
969
		 const cairo_pattern_t		*source,
970
		 const cairo_path_fixed_t	*path,
971
		 const cairo_stroke_style_t	*style,
972
		 const cairo_matrix_t		*ctm,
973
		 const cairo_matrix_t		*ctm_inverse,
974
		 double				 tolerance,
975
		 cairo_antialias_t		 antialias,
976
		 const cairo_clip_t		*clip,
977
		 cairo_time_t elapsed)
978
{
979
    cairo_observation_record_t record;
980
    cairo_int_status_t status;
981
 
982
    add_record (log,
983
		record_stroke (&record,
984
			       target, op, source,
985
			       path, style, ctm,ctm_inverse,
986
			       tolerance, antialias,
987
			       clip, elapsed));
988
 
989
    if (log->record) {
990
	status = log->record->base.backend->stroke (&log->record->base,
991
						    op, source,
992
						    path, style, ctm,ctm_inverse,
993
						    tolerance, antialias,
994
						    clip);
995
	assert (status == CAIRO_INT_STATUS_SUCCESS);
996
    }
997
 
998
    if (_cairo_time_gt (elapsed, log->stroke.slowest.elapsed))
999
	log->stroke.slowest = record;
1000
    log->stroke.elapsed = _cairo_time_add (log->stroke.elapsed, elapsed);
1001
}
1002
 
1003
static cairo_int_status_t
1004
_cairo_surface_observer_stroke (void				*abstract_surface,
1005
				cairo_operator_t		 op,
1006
				const cairo_pattern_t		*source,
1007
				const cairo_path_fixed_t	*path,
1008
				const cairo_stroke_style_t	*style,
1009
				const cairo_matrix_t		*ctm,
1010
				const cairo_matrix_t		*ctm_inverse,
1011
				double				 tolerance,
1012
				cairo_antialias_t		 antialias,
1013
				const cairo_clip_t		*clip)
1014
{
1015
    cairo_surface_observer_t *surface = abstract_surface;
1016
    cairo_device_observer_t *device = to_device (surface);
1017
    cairo_composite_rectangles_t composite;
1018
    cairo_int_status_t status;
1019
    cairo_time_t t;
1020
    int x, y;
1021
 
1022
    surface->log.stroke.count++;
1023
    surface->log.stroke.operators[op]++;
1024
    surface->log.stroke.antialias[antialias]++;
1025
    surface->log.stroke.caps[style->line_cap]++;
1026
    surface->log.stroke.joins[style->line_join]++;
1027
    add_pattern (&surface->log.stroke.source, source, surface->target);
1028
    add_path (&surface->log.stroke.path, path, FALSE);
1029
    add_clip (&surface->log.stroke.clip, clip);
1030
 
1031
    device->log.stroke.count++;
1032
    device->log.stroke.operators[op]++;
1033
    device->log.stroke.antialias[antialias]++;
1034
    device->log.stroke.caps[style->line_cap]++;
1035
    device->log.stroke.joins[style->line_join]++;
1036
    add_pattern (&device->log.stroke.source, source, surface->target);
1037
    add_path (&device->log.stroke.path, path, FALSE);
1038
    add_clip (&device->log.stroke.clip, clip);
1039
 
1040
    status = _cairo_composite_rectangles_init_for_stroke (&composite,
1041
							  surface->target,
1042
							  op, source,
1043
							  path, style, ctm,
1044
							  clip);
1045
    if (unlikely (status)) {
1046
	surface->log.stroke.noop++;
1047
	device->log.stroke.noop++;
1048
	return status;
1049
    }
1050
 
1051
    midpt (&composite, &x, &y);
1052
 
1053
    add_extents (&surface->log.stroke.extents, &composite);
1054
    add_extents (&device->log.stroke.extents, &composite);
1055
    _cairo_composite_rectangles_fini (&composite);
1056
 
1057
    t = _cairo_time_get ();
1058
    status = _cairo_surface_stroke (surface->target,
1059
				  op, source, path,
1060
				  style, ctm, ctm_inverse,
1061
				  tolerance, antialias,
1062
				  clip);
1063
    if (unlikely (status))
1064
	return status;
1065
 
1066
    sync (surface->target, x, y);
1067
    t = _cairo_time_get_delta (t);
1068
 
1069
    add_record_stroke (&surface->log,
1070
		       surface->target, op, source, path,
1071
		       style, ctm,ctm_inverse,
1072
		       tolerance, antialias,
1073
		       clip, t);
1074
 
1075
    add_record_stroke (&device->log,
1076
		       surface->target, op, source, path,
1077
		       style, ctm,ctm_inverse,
1078
		       tolerance, antialias,
1079
		       clip, t);
1080
 
1081
    do_callbacks (surface, &surface->stroke_callbacks);
1082
 
1083
    return CAIRO_STATUS_SUCCESS;
1084
}
1085
 
1086
static void
1087
add_record_glyphs (cairo_observation_t	*log,
1088
		   cairo_surface_t	*target,
1089
		   cairo_operator_t	 op,
1090
		   const cairo_pattern_t*source,
1091
		   cairo_glyph_t	*glyphs,
1092
		   int			 num_glyphs,
1093
		   cairo_scaled_font_t	*scaled_font,
1094
		   const cairo_clip_t	*clip,
1095
		   cairo_time_t elapsed)
1096
{
1097
    cairo_observation_record_t record;
1098
    cairo_int_status_t status;
1099
 
1100
    add_record (log,
1101
		record_glyphs (&record,
1102
			       target, op, source,
1103
			       glyphs, num_glyphs, scaled_font,
1104
			       clip, elapsed));
1105
 
1106
    if (log->record) {
1107
	status = log->record->base.backend->show_text_glyphs (&log->record->base,
1108
							      op, source,
1109
							      NULL, 0,
1110
							      glyphs, num_glyphs,
1111
							      NULL, 0, 0,
1112
							      scaled_font,
1113
							      clip);
1114
	assert (status == CAIRO_INT_STATUS_SUCCESS);
1115
    }
1116
 
1117
    if (_cairo_time_gt (elapsed, log->glyphs.slowest.elapsed))
1118
	log->glyphs.slowest = record;
1119
    log->glyphs.elapsed = _cairo_time_add (log->glyphs.elapsed, elapsed);
1120
}
1121
 
1122
static cairo_int_status_t
1123
_cairo_surface_observer_glyphs (void			*abstract_surface,
1124
				cairo_operator_t	 op,
1125
				const cairo_pattern_t	*source,
1126
				cairo_glyph_t		*glyphs,
1127
				int			 num_glyphs,
1128
				cairo_scaled_font_t	*scaled_font,
1129
				const cairo_clip_t		*clip)
1130
{
1131
    cairo_surface_observer_t *surface = abstract_surface;
1132
    cairo_device_observer_t *device = to_device (surface);
1133
    cairo_composite_rectangles_t composite;
1134
    cairo_int_status_t status;
1135
    cairo_glyph_t *dev_glyphs;
1136
    cairo_time_t t;
1137
    int x, y;
1138
 
1139
    surface->log.glyphs.count++;
1140
    surface->log.glyphs.operators[op]++;
1141
    add_pattern (&surface->log.glyphs.source, source, surface->target);
1142
    add_clip (&surface->log.glyphs.clip, clip);
1143
 
1144
    device->log.glyphs.count++;
1145
    device->log.glyphs.operators[op]++;
1146
    add_pattern (&device->log.glyphs.source, source, surface->target);
1147
    add_clip (&device->log.glyphs.clip, clip);
1148
 
1149
    status = _cairo_composite_rectangles_init_for_glyphs (&composite,
1150
							  surface->target,
1151
							  op, source,
1152
							  scaled_font,
1153
							  glyphs, num_glyphs,
1154
							  clip,
1155
							  NULL);
1156
    if (unlikely (status)) {
1157
	surface->log.glyphs.noop++;
1158
	device->log.glyphs.noop++;
1159
	return status;
1160
    }
1161
 
1162
    midpt (&composite, &x, &y);
1163
 
1164
    add_extents (&surface->log.glyphs.extents, &composite);
1165
    add_extents (&device->log.glyphs.extents, &composite);
1166
    _cairo_composite_rectangles_fini (&composite);
1167
 
1168
    /* XXX We have to copy the glyphs, because the backend is allowed to
1169
     * modify! */
1170
    dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
1171
    if (unlikely (dev_glyphs == NULL))
1172
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1173
    memcpy (dev_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t));
1174
 
1175
    t = _cairo_time_get ();
1176
    status = _cairo_surface_show_text_glyphs (surface->target, op, source,
1177
					      NULL, 0,
1178
					      dev_glyphs, num_glyphs,
1179
					      NULL, 0, 0,
1180
					      scaled_font,
1181
					      clip);
1182
    free (dev_glyphs);
1183
    if (unlikely (status))
1184
	return status;
1185
 
1186
    sync (surface->target, x, y);
1187
    t = _cairo_time_get_delta (t);
1188
 
1189
    add_record_glyphs (&surface->log,
1190
		       surface->target, op, source,
1191
		       glyphs, num_glyphs, scaled_font,
1192
		       clip, t);
1193
 
1194
    add_record_glyphs (&device->log,
1195
		       surface->target, op, source,
1196
		       glyphs, num_glyphs, scaled_font,
1197
		       clip, t);
1198
 
1199
    do_callbacks (surface, &surface->glyphs_callbacks);
1200
 
1201
    return CAIRO_STATUS_SUCCESS;
1202
}
1203
 
1204
static cairo_status_t
1205
_cairo_surface_observer_flush (void *abstract_surface, unsigned flags)
1206
{
1207
    cairo_surface_observer_t *surface = abstract_surface;
1208
 
1209
    do_callbacks (surface, &surface->flush_callbacks);
1210
    return _cairo_surface_flush (surface->target, flags);
1211
}
1212
 
1213
static cairo_status_t
1214
_cairo_surface_observer_mark_dirty (void *abstract_surface,
1215
				      int x, int y,
1216
				      int width, int height)
1217
{
1218
    cairo_surface_observer_t *surface = abstract_surface;
1219
    cairo_status_t status;
1220
 
1221
    printf ("mark-dirty (%d, %d) x (%d, %d)\n", x, y, width, height);
1222
 
1223
    status = CAIRO_STATUS_SUCCESS;
1224
    if (surface->target->backend->mark_dirty_rectangle)
1225
	status = surface->target->backend->mark_dirty_rectangle (surface->target,
1226
						       x,y, width,height);
1227
 
1228
    return status;
1229
}
1230
 
1231
static cairo_int_status_t
1232
_cairo_surface_observer_copy_page (void *abstract_surface)
1233
{
1234
    cairo_surface_observer_t *surface = abstract_surface;
1235
    cairo_status_t status;
1236
 
1237
    status = CAIRO_STATUS_SUCCESS;
1238
    if (surface->target->backend->copy_page)
1239
	status = surface->target->backend->copy_page (surface->target);
1240
 
1241
    return status;
1242
}
1243
 
1244
static cairo_int_status_t
1245
_cairo_surface_observer_show_page (void *abstract_surface)
1246
{
1247
    cairo_surface_observer_t *surface = abstract_surface;
1248
    cairo_status_t status;
1249
 
1250
    status = CAIRO_STATUS_SUCCESS;
1251
    if (surface->target->backend->show_page)
1252
	status = surface->target->backend->show_page (surface->target);
1253
 
1254
    return status;
1255
}
1256
 
1257
static cairo_bool_t
1258
_cairo_surface_observer_get_extents (void *abstract_surface,
1259
				     cairo_rectangle_int_t *extents)
1260
{
1261
    cairo_surface_observer_t *surface = abstract_surface;
1262
    return _cairo_surface_get_extents (surface->target, extents);
1263
}
1264
 
1265
static void
1266
_cairo_surface_observer_get_font_options (void *abstract_surface,
1267
					  cairo_font_options_t *options)
1268
{
1269
    cairo_surface_observer_t *surface = abstract_surface;
1270
 
1271
    if (surface->target->backend->get_font_options != NULL)
1272
	surface->target->backend->get_font_options (surface->target, options);
1273
}
1274
 
1275
static cairo_surface_t *
1276
_cairo_surface_observer_source (void                    *abstract_surface,
1277
				cairo_rectangle_int_t	*extents)
1278
{
1279
    cairo_surface_observer_t *surface = abstract_surface;
1280
    return _cairo_surface_get_source (surface->target, extents);
1281
}
1282
 
1283
static cairo_status_t
1284
_cairo_surface_observer_acquire_source_image (void                    *abstract_surface,
1285
						cairo_image_surface_t  **image_out,
1286
						void                   **image_extra)
1287
{
1288
    cairo_surface_observer_t *surface = abstract_surface;
1289
 
1290
    surface->log.num_sources_acquired++;
1291
    to_device (surface)->log.num_sources_acquired++;
1292
 
1293
    return _cairo_surface_acquire_source_image (surface->target,
1294
						image_out, image_extra);
1295
}
1296
 
1297
static void
1298
_cairo_surface_observer_release_source_image (void                   *abstract_surface,
1299
						cairo_image_surface_t  *image,
1300
						void                   *image_extra)
1301
{
1302
    cairo_surface_observer_t *surface = abstract_surface;
1303
 
1304
    _cairo_surface_release_source_image (surface->target, image, image_extra);
1305
}
1306
 
1307
static cairo_surface_t *
1308
_cairo_surface_observer_snapshot (void *abstract_surface)
1309
{
1310
    cairo_surface_observer_t *surface = abstract_surface;
1311
 
1312
    /* XXX hook onto the snapshot so that we measure number of reads */
1313
 
1314
    if (surface->target->backend->snapshot)
1315
	return surface->target->backend->snapshot (surface->target);
1316
 
1317
    return NULL;
1318
}
1319
 
1320
static cairo_t *
1321
_cairo_surface_observer_create_context(void *target)
1322
{
1323
    cairo_surface_observer_t *surface = target;
1324
 
1325
    if (_cairo_surface_is_subsurface (&surface->base))
1326
	surface = (cairo_surface_observer_t *)
1327
	    _cairo_surface_subsurface_get_target (&surface->base);
1328
 
1329
    surface->log.num_contexts++;
1330
    to_device (surface)->log.num_contexts++;
1331
 
1332
    return surface->target->backend->create_context (target);
1333
}
1334
 
1335
static const cairo_surface_backend_t _cairo_surface_observer_backend = {
1336
    CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER,
1337
    _cairo_surface_observer_finish,
1338
 
1339
    _cairo_surface_observer_create_context,
1340
 
1341
    _cairo_surface_observer_create_similar,
1342
    _cairo_surface_observer_create_similar_image,
1343
    _cairo_surface_observer_map_to_image,
1344
    _cairo_surface_observer_unmap_image,
1345
 
1346
    _cairo_surface_observer_source,
1347
    _cairo_surface_observer_acquire_source_image,
1348
    _cairo_surface_observer_release_source_image,
1349
    _cairo_surface_observer_snapshot,
1350
 
1351
    _cairo_surface_observer_copy_page,
1352
    _cairo_surface_observer_show_page,
1353
 
1354
    _cairo_surface_observer_get_extents,
1355
    _cairo_surface_observer_get_font_options,
1356
 
1357
    _cairo_surface_observer_flush,
1358
    _cairo_surface_observer_mark_dirty,
1359
 
1360
    _cairo_surface_observer_paint,
1361
    _cairo_surface_observer_mask,
1362
    _cairo_surface_observer_stroke,
1363
    _cairo_surface_observer_fill,
1364
    NULL, /* fill-stroke */
1365
    _cairo_surface_observer_glyphs,
1366
};
1367
 
1368
/**
1369
 * cairo_surface_create_observer:
1370
 * @target: an existing surface for which the observer will watch
1371
 *
1372
 * Create a new surface that exists solely to watch another is doing. In
1373
 * the process it will log operations and times, which are fast, which are
1374
 * slow, which are frequent, etc.
1375
 *
1376
 * Return value: a pointer to the newly allocated surface. The caller
1377
 * owns the surface and should call cairo_surface_destroy() when done
1378
 * with it.
1379
 *
1380
 * This function always returns a valid pointer, but it will return a
1381
 * pointer to a "nil" surface if @other is already in an error state
1382
 * or any other error occurs.
1383
 *
1384
 * Since: 1.12
1385
 **/
1386
cairo_surface_t *
1387
cairo_surface_create_observer (cairo_surface_t *target,
1388
			       cairo_surface_observer_mode_t mode)
1389
{
1390
    cairo_device_t *device;
1391
    cairo_surface_t *surface;
1392
    cairo_bool_t record;
1393
 
1394
    if (unlikely (target->status))
1395
	return _cairo_surface_create_in_error (target->status);
1396
    if (unlikely (target->finished))
1397
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
1398
 
1399
    record = mode & CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS;
1400
    device = _cairo_device_create_observer_internal (target->device, record);
1401
    if (unlikely (device->status))
1402
	return _cairo_surface_create_in_error (device->status);
1403
 
1404
    surface = _cairo_surface_create_observer_internal (device, target);
1405
    cairo_device_destroy (device);
1406
 
1407
    return surface;
1408
}
1409
 
1410
static cairo_status_t
1411
_cairo_surface_observer_add_callback (cairo_list_t *head,
1412
				      cairo_surface_observer_callback_t func,
1413
				      void *data)
1414
{
1415
    struct callback_list *cb;
1416
 
1417
    cb = malloc (sizeof (*cb));
1418
    if (unlikely (cb == NULL))
1419
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1420
 
1421
    cairo_list_add (&cb->link, head);
1422
    cb->func = func;
1423
    cb->data = data;
1424
 
1425
    return CAIRO_STATUS_SUCCESS;
1426
}
1427
 
1428
cairo_status_t
1429
cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface,
1430
					    cairo_surface_observer_callback_t func,
1431
					    void *data)
1432
{
1433
    cairo_surface_observer_t *surface;
1434
 
1435
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1436
	return abstract_surface->status;
1437
 
1438
    if (! _cairo_surface_is_observer (abstract_surface))
1439
	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1440
 
1441
    surface = (cairo_surface_observer_t *)abstract_surface;
1442
    return _cairo_surface_observer_add_callback (&surface->paint_callbacks,
1443
						 func, data);
1444
}
1445
 
1446
cairo_status_t
1447
cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface,
1448
					  cairo_surface_observer_callback_t func,
1449
					  void *data)
1450
{
1451
    cairo_surface_observer_t *surface;
1452
 
1453
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1454
	return abstract_surface->status;
1455
 
1456
    if (! _cairo_surface_is_observer (abstract_surface))
1457
	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1458
 
1459
    surface = (cairo_surface_observer_t *)abstract_surface;
1460
    return _cairo_surface_observer_add_callback (&surface->mask_callbacks,
1461
						 func, data);
1462
}
1463
 
1464
cairo_status_t
1465
cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface,
1466
					  cairo_surface_observer_callback_t func,
1467
					  void *data)
1468
{
1469
    cairo_surface_observer_t *surface;
1470
 
1471
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1472
	return abstract_surface->status;
1473
 
1474
    if (! _cairo_surface_is_observer (abstract_surface))
1475
	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1476
 
1477
    surface = (cairo_surface_observer_t *)abstract_surface;
1478
    return _cairo_surface_observer_add_callback (&surface->fill_callbacks,
1479
						 func, data);
1480
}
1481
 
1482
cairo_status_t
1483
cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface,
1484
					    cairo_surface_observer_callback_t func,
1485
					    void *data)
1486
{
1487
    cairo_surface_observer_t *surface;
1488
 
1489
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1490
	return abstract_surface->status;
1491
 
1492
    if (! _cairo_surface_is_observer (abstract_surface))
1493
	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1494
 
1495
    surface = (cairo_surface_observer_t *)abstract_surface;
1496
    return _cairo_surface_observer_add_callback (&surface->stroke_callbacks,
1497
						 func, data);
1498
}
1499
 
1500
cairo_status_t
1501
cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface,
1502
					    cairo_surface_observer_callback_t func,
1503
					    void *data)
1504
{
1505
    cairo_surface_observer_t *surface;
1506
 
1507
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1508
	return abstract_surface->status;
1509
 
1510
    if (! _cairo_surface_is_observer (abstract_surface))
1511
	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1512
 
1513
    surface = (cairo_surface_observer_t *)abstract_surface;
1514
    return _cairo_surface_observer_add_callback (&surface->glyphs_callbacks,
1515
						 func, data);
1516
}
1517
 
1518
cairo_status_t
1519
cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface,
1520
					   cairo_surface_observer_callback_t func,
1521
					   void *data)
1522
{
1523
    cairo_surface_observer_t *surface;
1524
 
1525
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1526
	return abstract_surface->status;
1527
 
1528
    if (! _cairo_surface_is_observer (abstract_surface))
1529
	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1530
 
1531
    surface = (cairo_surface_observer_t *)abstract_surface;
1532
    return _cairo_surface_observer_add_callback (&surface->flush_callbacks,
1533
						 func, data);
1534
}
1535
 
1536
cairo_status_t
1537
cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface,
1538
					    cairo_surface_observer_callback_t func,
1539
					    void *data)
1540
{
1541
    cairo_surface_observer_t *surface;
1542
 
1543
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1544
	return abstract_surface->status;
1545
 
1546
    if (! _cairo_surface_is_observer (abstract_surface))
1547
	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1548
 
1549
    surface = (cairo_surface_observer_t *)abstract_surface;
1550
    return _cairo_surface_observer_add_callback (&surface->finish_callbacks,
1551
						 func, data);
1552
}
1553
 
1554
static void
1555
print_extents (cairo_output_stream_t *stream, const struct extents *e)
1556
{
1557
    _cairo_output_stream_printf (stream,
1558
				 "  extents: total %g, avg %g [unbounded %d]\n",
1559
				 e->area.sum,
1560
				 e->area.sum / e->area.count,
1561
				 e->unbounded);
1562
}
1563
 
1564
static inline int ordercmp (int a, int b, const unsigned int *array)
1565
{
1566
    /* high to low */
1567
    return array[b] - array[a];
1568
}
1569
CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_order, int, ordercmp)
1570
 
1571
static void
1572
print_array (cairo_output_stream_t *stream,
1573
	     const unsigned int *array,
1574
	     const char **names,
1575
	     int count)
1576
{
1577
    int order[64];
1578
    int i, j;
1579
 
1580
    assert (count < ARRAY_LENGTH (order));
1581
    for (i = j = 0; i < count; i++) {
1582
	if (array[i] != 0)
1583
	    order[j++] = i;
1584
    }
1585
 
1586
    sort_order (order, j, (void *)array);
1587
    for (i = 0; i < j; i++)
1588
	_cairo_output_stream_printf (stream, " %d %s%s",
1589
				     array[order[i]], names[order[i]],
1590
				     i < j -1 ? "," : "");
1591
}
1592
 
1593
static const char *operator_names[] = {
1594
    "CLEAR",	/* CAIRO_OPERATOR_CLEAR */
1595
 
1596
    "SOURCE",	/* CAIRO_OPERATOR_SOURCE */
1597
    "OVER",		/* CAIRO_OPERATOR_OVER */
1598
    "IN",		/* CAIRO_OPERATOR_IN */
1599
    "OUT",		/* CAIRO_OPERATOR_OUT */
1600
    "ATOP",		/* CAIRO_OPERATOR_ATOP */
1601
 
1602
    "DEST",		/* CAIRO_OPERATOR_DEST */
1603
    "DEST_OVER",	/* CAIRO_OPERATOR_DEST_OVER */
1604
    "DEST_IN",	/* CAIRO_OPERATOR_DEST_IN */
1605
    "DEST_OUT",	/* CAIRO_OPERATOR_DEST_OUT */
1606
    "DEST_ATOP",	/* CAIRO_OPERATOR_DEST_ATOP */
1607
 
1608
    "XOR",		/* CAIRO_OPERATOR_XOR */
1609
    "ADD",		/* CAIRO_OPERATOR_ADD */
1610
    "SATURATE",	/* CAIRO_OPERATOR_SATURATE */
1611
 
1612
    "MULTIPLY",	/* CAIRO_OPERATOR_MULTIPLY */
1613
    "SCREEN",	/* CAIRO_OPERATOR_SCREEN */
1614
    "OVERLAY",	/* CAIRO_OPERATOR_OVERLAY */
1615
    "DARKEN",	/* CAIRO_OPERATOR_DARKEN */
1616
    "LIGHTEN",	/* CAIRO_OPERATOR_LIGHTEN */
1617
    "DODGE",	/* CAIRO_OPERATOR_COLOR_DODGE */
1618
    "BURN",		/* CAIRO_OPERATOR_COLOR_BURN */
1619
    "HARD_LIGHT",	/* CAIRO_OPERATOR_HARD_LIGHT */
1620
    "SOFT_LIGHT",	/* CAIRO_OPERATOR_SOFT_LIGHT */
1621
    "DIFFERENCE",	/* CAIRO_OPERATOR_DIFFERENCE */
1622
    "EXCLUSION",	/* CAIRO_OPERATOR_EXCLUSION */
1623
    "HSL_HUE",	/* CAIRO_OPERATOR_HSL_HUE */
1624
    "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
1625
    "HSL_COLOR",	/* CAIRO_OPERATOR_HSL_COLOR */
1626
    "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
1627
};
1628
static void
1629
print_operators (cairo_output_stream_t *stream, unsigned int *array)
1630
{
1631
    _cairo_output_stream_printf (stream, "  op:");
1632
    print_array (stream, array, operator_names, NUM_OPERATORS);
1633
    _cairo_output_stream_printf (stream, "\n");
1634
}
1635
 
1636
static const char *fill_rule_names[] = {
1637
    "non-zero",
1638
    "even-odd",
1639
};
1640
static void
1641
print_fill_rule (cairo_output_stream_t *stream, unsigned int *array)
1642
{
1643
    _cairo_output_stream_printf (stream, "  fill rule:");
1644
    print_array (stream, array, fill_rule_names, ARRAY_LENGTH(fill_rule_names));
1645
    _cairo_output_stream_printf (stream, "\n");
1646
}
1647
 
1648
static const char *cap_names[] = {
1649
    "butt",		/* CAIRO_LINE_CAP_BUTT */
1650
    "round",	/* CAIRO_LINE_CAP_ROUND */
1651
    "square"	/* CAIRO_LINE_CAP_SQUARE */
1652
};
1653
static void
1654
print_line_caps (cairo_output_stream_t *stream, unsigned int *array)
1655
{
1656
    _cairo_output_stream_printf (stream, "  caps:");
1657
    print_array (stream, array, cap_names, NUM_CAPS);
1658
    _cairo_output_stream_printf (stream, "\n");
1659
}
1660
 
1661
static const char *join_names[] = {
1662
    "miter",	/* CAIRO_LINE_JOIN_MITER */
1663
    "round",	/* CAIRO_LINE_JOIN_ROUND */
1664
    "bevel",	/* CAIRO_LINE_JOIN_BEVEL */
1665
};
1666
static void
1667
print_line_joins (cairo_output_stream_t *stream, unsigned int *array)
1668
{
1669
    _cairo_output_stream_printf (stream, "  joins:");
1670
    print_array (stream, array, join_names, NUM_JOINS);
1671
    _cairo_output_stream_printf (stream, "\n");
1672
}
1673
 
1674
static const char *antialias_names[] = {
1675
    "default",
1676
    "none",
1677
    "gray",
1678
    "subpixel",
1679
    "fast",
1680
    "good",
1681
    "best"
1682
};
1683
static void
1684
print_antialias (cairo_output_stream_t *stream, unsigned int *array)
1685
{
1686
    _cairo_output_stream_printf (stream, "  antialias:");
1687
    print_array (stream, array, antialias_names, NUM_ANTIALIAS);
1688
    _cairo_output_stream_printf (stream, "\n");
1689
}
1690
 
1691
static const char *pattern_names[] = {
1692
    "native",
1693
    "record",
1694
    "other surface",
1695
    "solid",
1696
    "linear",
1697
    "radial",
1698
    "mesh",
1699
    "raster"
1700
};
1701
static void
1702
print_pattern (cairo_output_stream_t *stream,
1703
	       const char *name,
1704
	       const struct pattern *p)
1705
{
1706
    _cairo_output_stream_printf (stream, "  %s:", name);
1707
    print_array (stream, p->type, pattern_names, ARRAY_LENGTH (pattern_names));
1708
    _cairo_output_stream_printf (stream, "\n");
1709
}
1710
 
1711
static const char *path_names[] = {
1712
    "empty",
1713
    "pixel-aligned",
1714
    "rectliinear",
1715
    "straight",
1716
    "curved",
1717
};
1718
static void
1719
print_path (cairo_output_stream_t *stream,
1720
	    const struct path *p)
1721
{
1722
    _cairo_output_stream_printf (stream, "  path:");
1723
    print_array (stream, p->type, path_names, ARRAY_LENGTH (path_names));
1724
    _cairo_output_stream_printf (stream, "\n");
1725
}
1726
 
1727
static const char *clip_names[] = {
1728
    "none",
1729
    "region",
1730
    "boxes",
1731
    "single path",
1732
    "polygon",
1733
    "general",
1734
};
1735
static void
1736
print_clip (cairo_output_stream_t *stream, const struct clip *c)
1737
{
1738
    _cairo_output_stream_printf (stream, "  clip:");
1739
    print_array (stream, c->type, clip_names, ARRAY_LENGTH (clip_names));
1740
    _cairo_output_stream_printf (stream, "\n");
1741
}
1742
 
1743
static void
1744
print_record (cairo_output_stream_t *stream,
1745
	      cairo_observation_record_t *r)
1746
{
1747
    _cairo_output_stream_printf (stream, "  op: %s\n", operator_names[r->op]);
1748
    _cairo_output_stream_printf (stream, "  source: %s\n",
1749
				 pattern_names[r->source]);
1750
    if (r->mask != -1)
1751
	_cairo_output_stream_printf (stream, "  mask: %s\n",
1752
				     pattern_names[r->mask]);
1753
    if (r->num_glyphs != -1)
1754
	_cairo_output_stream_printf (stream, "  num_glyphs: %d\n",
1755
				     r->num_glyphs);
1756
    if (r->path != -1)
1757
	_cairo_output_stream_printf (stream, "  path: %s\n",
1758
				    path_names[r->path]);
1759
    if (r->fill_rule != -1)
1760
	_cairo_output_stream_printf (stream, "  fill rule: %s\n",
1761
				     fill_rule_names[r->fill_rule]);
1762
    if (r->antialias != -1)
1763
	_cairo_output_stream_printf (stream, "  antialias: %s\n",
1764
				     antialias_names[r->antialias]);
1765
    _cairo_output_stream_printf (stream, "  clip: %s\n", clip_names[r->clip]);
1766
    _cairo_output_stream_printf (stream, "  elapsed: %f ns\n",
1767
				 _cairo_time_to_ns (r->elapsed));
1768
}
1769
 
1770
static double percent (cairo_time_t a, cairo_time_t b)
1771
{
1772
    /* Fake %.1f */
1773
    return _cairo_round (_cairo_time_to_s (a) * 1000 /
1774
			 _cairo_time_to_s (b)) / 10;
1775
}
1776
 
1777
static cairo_bool_t
1778
replay_record (cairo_observation_t *log,
1779
	       cairo_observation_record_t *r,
1780
	       cairo_device_t *script)
1781
{
1782
#if CAIRO_HAS_SCRIPT_SURFACE
1783
    cairo_surface_t *surface;
1784
    cairo_int_status_t status;
1785
 
1786
    if (log->record == NULL || script == NULL)
1787
	return FALSE;
1788
 
1789
    surface = cairo_script_surface_create (script,
1790
					   r->target_content,
1791
					   r->target_width,
1792
					   r->target_height);
1793
    status =
1794
	_cairo_recording_surface_replay_one (log->record, r->index, surface);
1795
    cairo_surface_destroy (surface);
1796
 
1797
    assert (status == CAIRO_INT_STATUS_SUCCESS);
1798
 
1799
    return TRUE;
1800
#else
1801
    return FALSE;
1802
#endif
1803
}
1804
 
1805
static cairo_time_t
1806
_cairo_observation_total_elapsed (cairo_observation_t *log)
1807
{
1808
    cairo_time_t total;
1809
 
1810
    total = log->paint.elapsed;
1811
    total = _cairo_time_add (total, log->mask.elapsed);
1812
    total = _cairo_time_add (total, log->fill.elapsed);
1813
    total = _cairo_time_add (total, log->stroke.elapsed);
1814
    total = _cairo_time_add (total, log->glyphs.elapsed);
1815
 
1816
    return total;
1817
}
1818
 
1819
static void
1820
_cairo_observation_print (cairo_output_stream_t *stream,
1821
			  cairo_observation_t *log)
1822
{
1823
    cairo_device_t *script;
1824
    cairo_time_t total;
1825
 
1826
#if CAIRO_HAS_SCRIPT_SURFACE
1827
    script = _cairo_script_context_create_internal (stream);
1828
    _cairo_script_context_attach_snapshots (script, FALSE);
1829
#else
1830
    script = NULL;
1831
#endif
1832
 
1833
    total = _cairo_observation_total_elapsed (log);
1834
 
1835
    _cairo_output_stream_printf (stream, "elapsed: %f\n",
1836
				 _cairo_time_to_ns (total));
1837
    _cairo_output_stream_printf (stream, "surfaces: %d\n",
1838
				 log->num_surfaces);
1839
    _cairo_output_stream_printf (stream, "contexts: %d\n",
1840
				 log->num_contexts);
1841
    _cairo_output_stream_printf (stream, "sources acquired: %d\n",
1842
				 log->num_sources_acquired);
1843
 
1844
 
1845
    _cairo_output_stream_printf (stream, "paint: count %d [no-op %d], elapsed %f [%f%%]\n",
1846
				 log->paint.count, log->paint.noop,
1847
				 _cairo_time_to_ns (log->paint.elapsed),
1848
				 percent (log->paint.elapsed, total));
1849
    if (log->paint.count) {
1850
	print_extents (stream, &log->paint.extents);
1851
	print_operators (stream, log->paint.operators);
1852
	print_pattern (stream, "source", &log->paint.source);
1853
	print_clip (stream, &log->paint.clip);
1854
 
1855
	_cairo_output_stream_printf (stream, "slowest paint: %f%%\n",
1856
				     percent (log->paint.slowest.elapsed,
1857
					      log->paint.elapsed));
1858
	print_record (stream, &log->paint.slowest);
1859
 
1860
	_cairo_output_stream_printf (stream, "\n");
1861
	if (replay_record (log, &log->paint.slowest, script))
1862
	    _cairo_output_stream_printf (stream, "\n\n");
1863
    }
1864
 
1865
    _cairo_output_stream_printf (stream, "mask: count %d [no-op %d], elapsed %f [%f%%]\n",
1866
				 log->mask.count, log->mask.noop,
1867
				 _cairo_time_to_ns (log->mask.elapsed),
1868
				 percent (log->mask.elapsed, total));
1869
    if (log->mask.count) {
1870
	print_extents (stream, &log->mask.extents);
1871
	print_operators (stream, log->mask.operators);
1872
	print_pattern (stream, "source", &log->mask.source);
1873
	print_pattern (stream, "mask", &log->mask.mask);
1874
	print_clip (stream, &log->mask.clip);
1875
 
1876
	_cairo_output_stream_printf (stream, "slowest mask: %f%%\n",
1877
				     percent (log->mask.slowest.elapsed,
1878
					      log->mask.elapsed));
1879
	print_record (stream, &log->mask.slowest);
1880
 
1881
	_cairo_output_stream_printf (stream, "\n");
1882
	if (replay_record (log, &log->mask.slowest, script))
1883
	    _cairo_output_stream_printf (stream, "\n\n");
1884
    }
1885
 
1886
    _cairo_output_stream_printf (stream, "fill: count %d [no-op %d], elaspsed %f [%f%%]\n",
1887
				 log->fill.count, log->fill.noop,
1888
				 _cairo_time_to_ns (log->fill.elapsed),
1889
				 percent (log->fill.elapsed, total));
1890
    if (log->fill.count) {
1891
	print_extents (stream, &log->fill.extents);
1892
	print_operators (stream, log->fill.operators);
1893
	print_pattern (stream, "source", &log->fill.source);
1894
	print_path (stream, &log->fill.path);
1895
	print_fill_rule (stream, log->fill.fill_rule);
1896
	print_antialias (stream, log->fill.antialias);
1897
	print_clip (stream, &log->fill.clip);
1898
 
1899
	_cairo_output_stream_printf (stream, "slowest fill: %f%%\n",
1900
				     percent (log->fill.slowest.elapsed,
1901
					      log->fill.elapsed));
1902
	print_record (stream, &log->fill.slowest);
1903
 
1904
	_cairo_output_stream_printf (stream, "\n");
1905
	if (replay_record (log, &log->fill.slowest, script))
1906
	    _cairo_output_stream_printf (stream, "\n\n");
1907
    }
1908
 
1909
    _cairo_output_stream_printf (stream, "stroke: count %d [no-op %d], elapsed %f [%f%%]\n",
1910
				 log->stroke.count, log->stroke.noop,
1911
				 _cairo_time_to_ns (log->stroke.elapsed),
1912
				 percent (log->stroke.elapsed, total));
1913
    if (log->stroke.count) {
1914
	print_extents (stream, &log->stroke.extents);
1915
	print_operators (stream, log->stroke.operators);
1916
	print_pattern (stream, "source", &log->stroke.source);
1917
	print_path (stream, &log->stroke.path);
1918
	print_antialias (stream, log->stroke.antialias);
1919
	print_line_caps (stream, log->stroke.caps);
1920
	print_line_joins (stream, log->stroke.joins);
1921
	print_clip (stream, &log->stroke.clip);
1922
 
1923
	_cairo_output_stream_printf (stream, "slowest stroke: %f%%\n",
1924
				     percent (log->stroke.slowest.elapsed,
1925
					      log->stroke.elapsed));
1926
	print_record (stream, &log->stroke.slowest);
1927
 
1928
	_cairo_output_stream_printf (stream, "\n");
1929
	if (replay_record (log, &log->stroke.slowest, script))
1930
	    _cairo_output_stream_printf (stream, "\n\n");
1931
    }
1932
 
1933
    _cairo_output_stream_printf (stream, "glyphs: count %d [no-op %d], elasped %f [%f%%]\n",
1934
				 log->glyphs.count, log->glyphs.noop,
1935
				 _cairo_time_to_ns (log->glyphs.elapsed),
1936
				 percent (log->glyphs.elapsed, total));
1937
    if (log->glyphs.count) {
1938
	print_extents (stream, &log->glyphs.extents);
1939
	print_operators (stream, log->glyphs.operators);
1940
	print_pattern (stream, "source", &log->glyphs.source);
1941
	print_clip (stream, &log->glyphs.clip);
1942
 
1943
	_cairo_output_stream_printf (stream, "slowest glyphs: %f%%\n",
1944
				     percent (log->glyphs.slowest.elapsed,
1945
					      log->glyphs.elapsed));
1946
	print_record (stream, &log->glyphs.slowest);
1947
 
1948
	_cairo_output_stream_printf (stream, "\n");
1949
	if (replay_record (log, &log->glyphs.slowest, script))
1950
	    _cairo_output_stream_printf (stream, "\n\n");
1951
    }
1952
 
1953
    cairo_device_destroy (script);
1954
}
1955
 
1956
cairo_status_t
1957
cairo_surface_observer_print (cairo_surface_t *abstract_surface,
1958
			      cairo_write_func_t write_func,
1959
			      void *closure)
1960
{
1961
    cairo_output_stream_t *stream;
1962
    cairo_surface_observer_t *surface;
1963
 
1964
    if (unlikely (abstract_surface->status))
1965
	return abstract_surface->status;
1966
 
1967
    if (unlikely (! _cairo_surface_is_observer (abstract_surface)))
1968
	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1969
 
1970
    surface = (cairo_surface_observer_t *) abstract_surface;
1971
 
1972
    stream = _cairo_output_stream_create (write_func, NULL, closure);
1973
    _cairo_observation_print (stream, &surface->log);
1974
    return _cairo_output_stream_destroy (stream);
1975
}
1976
 
1977
double
1978
cairo_surface_observer_elapsed (cairo_surface_t *abstract_surface)
1979
{
1980
    cairo_surface_observer_t *surface;
1981
 
1982
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1983
	return -1;
1984
 
1985
    if (! _cairo_surface_is_observer (abstract_surface))
1986
	return -1;
1987
 
1988
    surface = (cairo_surface_observer_t *) abstract_surface;
1989
    return _cairo_time_to_ns (_cairo_observation_total_elapsed (&surface->log));
1990
}
1991
 
1992
cairo_status_t
1993
cairo_device_observer_print (cairo_device_t *abstract_device,
1994
			     cairo_write_func_t write_func,
1995
			     void *closure)
1996
{
1997
    cairo_output_stream_t *stream;
1998
    cairo_device_observer_t *device;
1999
 
2000
    if (unlikely (abstract_device->status))
2001
	return abstract_device->status;
2002
 
2003
    if (unlikely (! _cairo_device_is_observer (abstract_device)))
2004
	return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
2005
 
2006
    device = (cairo_device_observer_t *) abstract_device;
2007
 
2008
    stream = _cairo_output_stream_create (write_func, NULL, closure);
2009
    _cairo_observation_print (stream, &device->log);
2010
    return _cairo_output_stream_destroy (stream);
2011
}
2012
 
2013
double
2014
cairo_device_observer_elapsed (cairo_device_t *abstract_device)
2015
{
2016
    cairo_device_observer_t *device;
2017
 
2018
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2019
	return -1;
2020
 
2021
    if (! _cairo_device_is_observer (abstract_device))
2022
	return -1;
2023
 
2024
    device = (cairo_device_observer_t *) abstract_device;
2025
    return _cairo_time_to_ns (_cairo_observation_total_elapsed (&device->log));
2026
}
2027
 
2028
double
2029
cairo_device_observer_paint_elapsed (cairo_device_t *abstract_device)
2030
{
2031
    cairo_device_observer_t *device;
2032
 
2033
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2034
	return -1;
2035
 
2036
    if (! _cairo_device_is_observer (abstract_device))
2037
	return -1;
2038
 
2039
    device = (cairo_device_observer_t *) abstract_device;
2040
    return _cairo_time_to_ns (device->log.paint.elapsed);
2041
}
2042
 
2043
double
2044
cairo_device_observer_mask_elapsed (cairo_device_t *abstract_device)
2045
{
2046
    cairo_device_observer_t *device;
2047
 
2048
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2049
	return -1;
2050
 
2051
    if (! _cairo_device_is_observer (abstract_device))
2052
	return -1;
2053
 
2054
    device = (cairo_device_observer_t *) abstract_device;
2055
    return _cairo_time_to_ns (device->log.mask.elapsed);
2056
}
2057
 
2058
double
2059
cairo_device_observer_fill_elapsed (cairo_device_t *abstract_device)
2060
{
2061
    cairo_device_observer_t *device;
2062
 
2063
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2064
	return -1;
2065
 
2066
    if (! _cairo_device_is_observer (abstract_device))
2067
	return -1;
2068
 
2069
    device = (cairo_device_observer_t *) abstract_device;
2070
    return _cairo_time_to_ns (device->log.fill.elapsed);
2071
}
2072
 
2073
double
2074
cairo_device_observer_stroke_elapsed (cairo_device_t *abstract_device)
2075
{
2076
    cairo_device_observer_t *device;
2077
 
2078
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2079
	return -1;
2080
 
2081
    if (! _cairo_device_is_observer (abstract_device))
2082
	return -1;
2083
 
2084
    device = (cairo_device_observer_t *) abstract_device;
2085
    return _cairo_time_to_ns (device->log.stroke.elapsed);
2086
}
2087
 
2088
double
2089
cairo_device_observer_glyphs_elapsed (cairo_device_t *abstract_device)
2090
{
2091
    cairo_device_observer_t *device;
2092
 
2093
    if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2094
	return -1;
2095
 
2096
    if (! _cairo_device_is_observer (abstract_device))
2097
	return -1;
2098
 
2099
    device = (cairo_device_observer_t *) abstract_device;
2100
    return _cairo_time_to_ns (device->log.glyphs.elapsed);
2101
}