tizen 2.3.1 release
[framework/graphics/cairo.git] / src / cairo-surface-observer.c
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 <chris@chris-wilson.co.uk>
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         cairo_status_t status = device->status;
1403         cairo_device_destroy (device);
1404         return _cairo_surface_create_in_error (status);
1405     }
1406
1407     surface = _cairo_surface_create_observer_internal (device, target);
1408     cairo_device_destroy (device);
1409
1410     return surface;
1411 }
1412
1413 static cairo_status_t
1414 _cairo_surface_observer_add_callback (cairo_list_t *head,
1415                                       cairo_surface_observer_callback_t func,
1416                                       void *data)
1417 {
1418     struct callback_list *cb;
1419
1420     cb = malloc (sizeof (*cb));
1421     if (unlikely (cb == NULL))
1422         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1423
1424     cairo_list_add (&cb->link, head);
1425     cb->func = func;
1426     cb->data = data;
1427
1428     return CAIRO_STATUS_SUCCESS;
1429 }
1430
1431 cairo_status_t
1432 cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface,
1433                                             cairo_surface_observer_callback_t func,
1434                                             void *data)
1435 {
1436     cairo_surface_observer_t *surface;
1437
1438     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1439         return abstract_surface->status;
1440
1441     if (! _cairo_surface_is_observer (abstract_surface))
1442         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1443
1444     surface = (cairo_surface_observer_t *)abstract_surface;
1445     return _cairo_surface_observer_add_callback (&surface->paint_callbacks,
1446                                                  func, data);
1447 }
1448
1449 cairo_status_t
1450 cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface,
1451                                           cairo_surface_observer_callback_t func,
1452                                           void *data)
1453 {
1454     cairo_surface_observer_t *surface;
1455
1456     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1457         return abstract_surface->status;
1458
1459     if (! _cairo_surface_is_observer (abstract_surface))
1460         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1461
1462     surface = (cairo_surface_observer_t *)abstract_surface;
1463     return _cairo_surface_observer_add_callback (&surface->mask_callbacks,
1464                                                  func, data);
1465 }
1466
1467 cairo_status_t
1468 cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface,
1469                                           cairo_surface_observer_callback_t func,
1470                                           void *data)
1471 {
1472     cairo_surface_observer_t *surface;
1473
1474     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1475         return abstract_surface->status;
1476
1477     if (! _cairo_surface_is_observer (abstract_surface))
1478         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1479
1480     surface = (cairo_surface_observer_t *)abstract_surface;
1481     return _cairo_surface_observer_add_callback (&surface->fill_callbacks,
1482                                                  func, data);
1483 }
1484
1485 cairo_status_t
1486 cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface,
1487                                             cairo_surface_observer_callback_t func,
1488                                             void *data)
1489 {
1490     cairo_surface_observer_t *surface;
1491
1492     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1493         return abstract_surface->status;
1494
1495     if (! _cairo_surface_is_observer (abstract_surface))
1496         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1497
1498     surface = (cairo_surface_observer_t *)abstract_surface;
1499     return _cairo_surface_observer_add_callback (&surface->stroke_callbacks,
1500                                                  func, data);
1501 }
1502
1503 cairo_status_t
1504 cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface,
1505                                             cairo_surface_observer_callback_t func,
1506                                             void *data)
1507 {
1508     cairo_surface_observer_t *surface;
1509
1510     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1511         return abstract_surface->status;
1512
1513     if (! _cairo_surface_is_observer (abstract_surface))
1514         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1515
1516     surface = (cairo_surface_observer_t *)abstract_surface;
1517     return _cairo_surface_observer_add_callback (&surface->glyphs_callbacks,
1518                                                  func, data);
1519 }
1520
1521 cairo_status_t
1522 cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface,
1523                                            cairo_surface_observer_callback_t func,
1524                                            void *data)
1525 {
1526     cairo_surface_observer_t *surface;
1527
1528     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1529         return abstract_surface->status;
1530
1531     if (! _cairo_surface_is_observer (abstract_surface))
1532         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1533
1534     surface = (cairo_surface_observer_t *)abstract_surface;
1535     return _cairo_surface_observer_add_callback (&surface->flush_callbacks,
1536                                                  func, data);
1537 }
1538
1539 cairo_status_t
1540 cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface,
1541                                             cairo_surface_observer_callback_t func,
1542                                             void *data)
1543 {
1544     cairo_surface_observer_t *surface;
1545
1546     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1547         return abstract_surface->status;
1548
1549     if (! _cairo_surface_is_observer (abstract_surface))
1550         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1551
1552     surface = (cairo_surface_observer_t *)abstract_surface;
1553     return _cairo_surface_observer_add_callback (&surface->finish_callbacks,
1554                                                  func, data);
1555 }
1556
1557 static void
1558 print_extents (cairo_output_stream_t *stream, const struct extents *e)
1559 {
1560     _cairo_output_stream_printf (stream,
1561                                  "  extents: total %g, avg %g [unbounded %d]\n",
1562                                  e->area.sum,
1563                                  e->area.sum / e->area.count,
1564                                  e->unbounded);
1565 }
1566
1567 static inline int ordercmp (int a, int b, const unsigned int *array)
1568 {
1569     /* high to low */
1570     return array[b] - array[a];
1571 }
1572 CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_order, int, ordercmp)
1573
1574 static void
1575 print_array (cairo_output_stream_t *stream,
1576              const unsigned int *array,
1577              const char **names,
1578              int count)
1579 {
1580     int order[64] = {0,};
1581     int i, j;
1582
1583     assert (count < ARRAY_LENGTH (order));
1584     for (i = j = 0; i < count; i++) {
1585         if (array[i] != 0)
1586             order[j++] = i;
1587     }
1588
1589     sort_order (order, j, (void *)array);
1590     for (i = 0; i < j; i++)
1591         _cairo_output_stream_printf (stream, " %d %s%s",
1592                                      array[order[i]], names[order[i]],
1593                                      i < j -1 ? "," : "");
1594 }
1595
1596 static const char *operator_names[] = {
1597     "CLEAR",    /* CAIRO_OPERATOR_CLEAR */
1598
1599     "SOURCE",   /* CAIRO_OPERATOR_SOURCE */
1600     "OVER",             /* CAIRO_OPERATOR_OVER */
1601     "IN",               /* CAIRO_OPERATOR_IN */
1602     "OUT",              /* CAIRO_OPERATOR_OUT */
1603     "ATOP",             /* CAIRO_OPERATOR_ATOP */
1604
1605     "DEST",             /* CAIRO_OPERATOR_DEST */
1606     "DEST_OVER",        /* CAIRO_OPERATOR_DEST_OVER */
1607     "DEST_IN",  /* CAIRO_OPERATOR_DEST_IN */
1608     "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */
1609     "DEST_ATOP",        /* CAIRO_OPERATOR_DEST_ATOP */
1610
1611     "XOR",              /* CAIRO_OPERATOR_XOR */
1612     "ADD",              /* CAIRO_OPERATOR_ADD */
1613     "SATURATE", /* CAIRO_OPERATOR_SATURATE */
1614
1615     "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */
1616     "SCREEN",   /* CAIRO_OPERATOR_SCREEN */
1617     "OVERLAY",  /* CAIRO_OPERATOR_OVERLAY */
1618     "DARKEN",   /* CAIRO_OPERATOR_DARKEN */
1619     "LIGHTEN",  /* CAIRO_OPERATOR_LIGHTEN */
1620     "DODGE",    /* CAIRO_OPERATOR_COLOR_DODGE */
1621     "BURN",             /* CAIRO_OPERATOR_COLOR_BURN */
1622     "HARD_LIGHT",       /* CAIRO_OPERATOR_HARD_LIGHT */
1623     "SOFT_LIGHT",       /* CAIRO_OPERATOR_SOFT_LIGHT */
1624     "DIFFERENCE",       /* CAIRO_OPERATOR_DIFFERENCE */
1625     "EXCLUSION",        /* CAIRO_OPERATOR_EXCLUSION */
1626     "HSL_HUE",  /* CAIRO_OPERATOR_HSL_HUE */
1627     "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
1628     "HSL_COLOR",        /* CAIRO_OPERATOR_HSL_COLOR */
1629     "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
1630 };
1631 static void
1632 print_operators (cairo_output_stream_t *stream, unsigned int *array)
1633 {
1634     _cairo_output_stream_printf (stream, "  op:");
1635     print_array (stream, array, operator_names, NUM_OPERATORS);
1636     _cairo_output_stream_printf (stream, "\n");
1637 }
1638
1639 static const char *fill_rule_names[] = {
1640     "non-zero",
1641     "even-odd",
1642 };
1643 static void
1644 print_fill_rule (cairo_output_stream_t *stream, unsigned int *array)
1645 {
1646     _cairo_output_stream_printf (stream, "  fill rule:");
1647     print_array (stream, array, fill_rule_names, ARRAY_LENGTH(fill_rule_names));
1648     _cairo_output_stream_printf (stream, "\n");
1649 }
1650
1651 static const char *cap_names[] = {
1652     "butt",             /* CAIRO_LINE_CAP_BUTT */
1653     "round",    /* CAIRO_LINE_CAP_ROUND */
1654     "square"    /* CAIRO_LINE_CAP_SQUARE */
1655 };
1656 static void
1657 print_line_caps (cairo_output_stream_t *stream, unsigned int *array)
1658 {
1659     _cairo_output_stream_printf (stream, "  caps:");
1660     print_array (stream, array, cap_names, NUM_CAPS);
1661     _cairo_output_stream_printf (stream, "\n");
1662 }
1663
1664 static const char *join_names[] = {
1665     "miter",    /* CAIRO_LINE_JOIN_MITER */
1666     "round",    /* CAIRO_LINE_JOIN_ROUND */
1667     "bevel",    /* CAIRO_LINE_JOIN_BEVEL */
1668 };
1669 static void
1670 print_line_joins (cairo_output_stream_t *stream, unsigned int *array)
1671 {
1672     _cairo_output_stream_printf (stream, "  joins:");
1673     print_array (stream, array, join_names, NUM_JOINS);
1674     _cairo_output_stream_printf (stream, "\n");
1675 }
1676
1677 static const char *antialias_names[] = {
1678     "default",
1679     "none",
1680     "gray",
1681     "subpixel",
1682     "fast",
1683     "good",
1684     "best"
1685 };
1686 static void
1687 print_antialias (cairo_output_stream_t *stream, unsigned int *array)
1688 {
1689     _cairo_output_stream_printf (stream, "  antialias:");
1690     print_array (stream, array, antialias_names, NUM_ANTIALIAS);
1691     _cairo_output_stream_printf (stream, "\n");
1692 }
1693
1694 static const char *pattern_names[] = {
1695     "native",
1696     "record",
1697     "other surface",
1698     "solid",
1699     "linear",
1700     "radial",
1701     "mesh",
1702     "raster"
1703 };
1704 static void
1705 print_pattern (cairo_output_stream_t *stream,
1706                const char *name,
1707                const struct pattern *p)
1708 {
1709     _cairo_output_stream_printf (stream, "  %s:", name);
1710     print_array (stream, p->type, pattern_names, ARRAY_LENGTH (pattern_names));
1711     _cairo_output_stream_printf (stream, "\n");
1712 }
1713
1714 static const char *path_names[] = {
1715     "empty",
1716     "pixel-aligned",
1717     "rectliinear",
1718     "straight",
1719     "curved",
1720 };
1721 static void
1722 print_path (cairo_output_stream_t *stream,
1723             const struct path *p)
1724 {
1725     _cairo_output_stream_printf (stream, "  path:");
1726     print_array (stream, p->type, path_names, ARRAY_LENGTH (path_names));
1727     _cairo_output_stream_printf (stream, "\n");
1728 }
1729
1730 static const char *clip_names[] = {
1731     "none",
1732     "region",
1733     "boxes",
1734     "single path",
1735     "polygon",
1736     "general",
1737 };
1738 static void
1739 print_clip (cairo_output_stream_t *stream, const struct clip *c)
1740 {
1741     _cairo_output_stream_printf (stream, "  clip:");
1742     print_array (stream, c->type, clip_names, ARRAY_LENGTH (clip_names));
1743     _cairo_output_stream_printf (stream, "\n");
1744 }
1745
1746 static void
1747 print_record (cairo_output_stream_t *stream,
1748               cairo_observation_record_t *r)
1749 {
1750     _cairo_output_stream_printf (stream, "  op: %s\n", operator_names[r->op]);
1751     _cairo_output_stream_printf (stream, "  source: %s\n",
1752                                  pattern_names[r->source]);
1753     if (r->mask != -1)
1754         _cairo_output_stream_printf (stream, "  mask: %s\n",
1755                                      pattern_names[r->mask]);
1756     if (r->num_glyphs != -1)
1757         _cairo_output_stream_printf (stream, "  num_glyphs: %d\n",
1758                                      r->num_glyphs);
1759     if (r->path != -1)
1760         _cairo_output_stream_printf (stream, "  path: %s\n",
1761                                     path_names[r->path]);
1762     if (r->fill_rule != -1)
1763         _cairo_output_stream_printf (stream, "  fill rule: %s\n",
1764                                      fill_rule_names[r->fill_rule]);
1765     if (r->antialias != -1)
1766         _cairo_output_stream_printf (stream, "  antialias: %s\n",
1767                                      antialias_names[r->antialias]);
1768     _cairo_output_stream_printf (stream, "  clip: %s\n", clip_names[r->clip]);
1769     _cairo_output_stream_printf (stream, "  elapsed: %f ns\n",
1770                                  _cairo_time_to_ns (r->elapsed));
1771 }
1772
1773 static double percent (cairo_time_t a, cairo_time_t b)
1774 {
1775     /* Fake %.1f */
1776     return _cairo_round (_cairo_time_to_s (a) * 1000 /
1777                          _cairo_time_to_s (b)) / 10;
1778 }
1779
1780 static cairo_bool_t
1781 replay_record (cairo_observation_t *log,
1782                cairo_observation_record_t *r,
1783                cairo_device_t *script)
1784 {
1785 #if CAIRO_HAS_SCRIPT_SURFACE
1786     cairo_surface_t *surface;
1787     cairo_int_status_t status;
1788
1789     if (log->record == NULL || script == NULL)
1790         return FALSE;
1791
1792     surface = cairo_script_surface_create (script,
1793                                            r->target_content,
1794                                            r->target_width,
1795                                            r->target_height);
1796     status =
1797         _cairo_recording_surface_replay_one (log->record, r->index, surface);
1798     cairo_surface_destroy (surface);
1799
1800     assert (status == CAIRO_INT_STATUS_SUCCESS);
1801
1802     return TRUE;
1803 #else
1804     return FALSE;
1805 #endif
1806 }
1807
1808 static cairo_time_t
1809 _cairo_observation_total_elapsed (cairo_observation_t *log)
1810 {
1811     cairo_time_t total;
1812
1813     total = log->paint.elapsed;
1814     total = _cairo_time_add (total, log->mask.elapsed);
1815     total = _cairo_time_add (total, log->fill.elapsed);
1816     total = _cairo_time_add (total, log->stroke.elapsed);
1817     total = _cairo_time_add (total, log->glyphs.elapsed);
1818
1819     return total;
1820 }
1821
1822 static void
1823 _cairo_observation_print (cairo_output_stream_t *stream,
1824                           cairo_observation_t *log)
1825 {
1826     cairo_device_t *script;
1827     cairo_time_t total;
1828
1829 #if CAIRO_HAS_SCRIPT_SURFACE
1830     script = _cairo_script_context_create_internal (stream);
1831     _cairo_script_context_attach_snapshots (script, FALSE);
1832 #else
1833     script = NULL;
1834 #endif
1835
1836     total = _cairo_observation_total_elapsed (log);
1837
1838     _cairo_output_stream_printf (stream, "elapsed: %f\n",
1839                                  _cairo_time_to_ns (total));
1840     _cairo_output_stream_printf (stream, "surfaces: %d\n",
1841                                  log->num_surfaces);
1842     _cairo_output_stream_printf (stream, "contexts: %d\n",
1843                                  log->num_contexts);
1844     _cairo_output_stream_printf (stream, "sources acquired: %d\n",
1845                                  log->num_sources_acquired);
1846
1847
1848     _cairo_output_stream_printf (stream, "paint: count %d [no-op %d], elapsed %f [%f%%]\n",
1849                                  log->paint.count, log->paint.noop,
1850                                  _cairo_time_to_ns (log->paint.elapsed),
1851                                  percent (log->paint.elapsed, total));
1852     if (log->paint.count) {
1853         print_extents (stream, &log->paint.extents);
1854         print_operators (stream, log->paint.operators);
1855         print_pattern (stream, "source", &log->paint.source);
1856         print_clip (stream, &log->paint.clip);
1857
1858         _cairo_output_stream_printf (stream, "slowest paint: %f%%\n",
1859                                      percent (log->paint.slowest.elapsed,
1860                                               log->paint.elapsed));
1861         print_record (stream, &log->paint.slowest);
1862
1863         _cairo_output_stream_printf (stream, "\n");
1864         if (replay_record (log, &log->paint.slowest, script))
1865             _cairo_output_stream_printf (stream, "\n\n");
1866     }
1867
1868     _cairo_output_stream_printf (stream, "mask: count %d [no-op %d], elapsed %f [%f%%]\n",
1869                                  log->mask.count, log->mask.noop,
1870                                  _cairo_time_to_ns (log->mask.elapsed),
1871                                  percent (log->mask.elapsed, total));
1872     if (log->mask.count) {
1873         print_extents (stream, &log->mask.extents);
1874         print_operators (stream, log->mask.operators);
1875         print_pattern (stream, "source", &log->mask.source);
1876         print_pattern (stream, "mask", &log->mask.mask);
1877         print_clip (stream, &log->mask.clip);
1878
1879         _cairo_output_stream_printf (stream, "slowest mask: %f%%\n",
1880                                      percent (log->mask.slowest.elapsed,
1881                                               log->mask.elapsed));
1882         print_record (stream, &log->mask.slowest);
1883
1884         _cairo_output_stream_printf (stream, "\n");
1885         if (replay_record (log, &log->mask.slowest, script))
1886             _cairo_output_stream_printf (stream, "\n\n");
1887     }
1888
1889     _cairo_output_stream_printf (stream, "fill: count %d [no-op %d], elaspsed %f [%f%%]\n",
1890                                  log->fill.count, log->fill.noop,
1891                                  _cairo_time_to_ns (log->fill.elapsed),
1892                                  percent (log->fill.elapsed, total));
1893     if (log->fill.count) {
1894         print_extents (stream, &log->fill.extents);
1895         print_operators (stream, log->fill.operators);
1896         print_pattern (stream, "source", &log->fill.source);
1897         print_path (stream, &log->fill.path);
1898         print_fill_rule (stream, log->fill.fill_rule);
1899         print_antialias (stream, log->fill.antialias);
1900         print_clip (stream, &log->fill.clip);
1901
1902         _cairo_output_stream_printf (stream, "slowest fill: %f%%\n",
1903                                      percent (log->fill.slowest.elapsed,
1904                                               log->fill.elapsed));
1905         print_record (stream, &log->fill.slowest);
1906
1907         _cairo_output_stream_printf (stream, "\n");
1908         if (replay_record (log, &log->fill.slowest, script))
1909             _cairo_output_stream_printf (stream, "\n\n");
1910     }
1911
1912     _cairo_output_stream_printf (stream, "stroke: count %d [no-op %d], elapsed %f [%f%%]\n",
1913                                  log->stroke.count, log->stroke.noop,
1914                                  _cairo_time_to_ns (log->stroke.elapsed),
1915                                  percent (log->stroke.elapsed, total));
1916     if (log->stroke.count) {
1917         print_extents (stream, &log->stroke.extents);
1918         print_operators (stream, log->stroke.operators);
1919         print_pattern (stream, "source", &log->stroke.source);
1920         print_path (stream, &log->stroke.path);
1921         print_antialias (stream, log->stroke.antialias);
1922         print_line_caps (stream, log->stroke.caps);
1923         print_line_joins (stream, log->stroke.joins);
1924         print_clip (stream, &log->stroke.clip);
1925
1926         _cairo_output_stream_printf (stream, "slowest stroke: %f%%\n",
1927                                      percent (log->stroke.slowest.elapsed,
1928                                               log->stroke.elapsed));
1929         print_record (stream, &log->stroke.slowest);
1930
1931         _cairo_output_stream_printf (stream, "\n");
1932         if (replay_record (log, &log->stroke.slowest, script))
1933             _cairo_output_stream_printf (stream, "\n\n");
1934     }
1935
1936     _cairo_output_stream_printf (stream, "glyphs: count %d [no-op %d], elasped %f [%f%%]\n",
1937                                  log->glyphs.count, log->glyphs.noop,
1938                                  _cairo_time_to_ns (log->glyphs.elapsed),
1939                                  percent (log->glyphs.elapsed, total));
1940     if (log->glyphs.count) {
1941         print_extents (stream, &log->glyphs.extents);
1942         print_operators (stream, log->glyphs.operators);
1943         print_pattern (stream, "source", &log->glyphs.source);
1944         print_clip (stream, &log->glyphs.clip);
1945
1946         _cairo_output_stream_printf (stream, "slowest glyphs: %f%%\n",
1947                                      percent (log->glyphs.slowest.elapsed,
1948                                               log->glyphs.elapsed));
1949         print_record (stream, &log->glyphs.slowest);
1950
1951         _cairo_output_stream_printf (stream, "\n");
1952         if (replay_record (log, &log->glyphs.slowest, script))
1953             _cairo_output_stream_printf (stream, "\n\n");
1954     }
1955
1956     cairo_device_destroy (script);
1957 }
1958
1959 cairo_status_t
1960 cairo_surface_observer_print (cairo_surface_t *abstract_surface,
1961                               cairo_write_func_t write_func,
1962                               void *closure)
1963 {
1964     cairo_output_stream_t *stream;
1965     cairo_surface_observer_t *surface;
1966
1967     if (unlikely (abstract_surface->status))
1968         return abstract_surface->status;
1969
1970     if (unlikely (! _cairo_surface_is_observer (abstract_surface)))
1971         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1972
1973     surface = (cairo_surface_observer_t *) abstract_surface;
1974
1975     stream = _cairo_output_stream_create (write_func, NULL, closure);
1976     _cairo_observation_print (stream, &surface->log);
1977     return _cairo_output_stream_destroy (stream);
1978 }
1979
1980 double
1981 cairo_surface_observer_elapsed (cairo_surface_t *abstract_surface)
1982 {
1983     cairo_surface_observer_t *surface;
1984
1985     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1986         return -1;
1987
1988     if (! _cairo_surface_is_observer (abstract_surface))
1989         return -1;
1990
1991     surface = (cairo_surface_observer_t *) abstract_surface;
1992     return _cairo_time_to_ns (_cairo_observation_total_elapsed (&surface->log));
1993 }
1994
1995 cairo_status_t
1996 cairo_device_observer_print (cairo_device_t *abstract_device,
1997                              cairo_write_func_t write_func,
1998                              void *closure)
1999 {
2000     cairo_output_stream_t *stream;
2001     cairo_device_observer_t *device;
2002
2003     if (unlikely (abstract_device->status))
2004         return abstract_device->status;
2005
2006     if (unlikely (! _cairo_device_is_observer (abstract_device)))
2007         return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
2008
2009     device = (cairo_device_observer_t *) abstract_device;
2010
2011     stream = _cairo_output_stream_create (write_func, NULL, closure);
2012     _cairo_observation_print (stream, &device->log);
2013     return _cairo_output_stream_destroy (stream);
2014 }
2015
2016 double
2017 cairo_device_observer_elapsed (cairo_device_t *abstract_device)
2018 {
2019     cairo_device_observer_t *device;
2020
2021     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2022         return -1;
2023
2024     if (! _cairo_device_is_observer (abstract_device))
2025         return -1;
2026
2027     device = (cairo_device_observer_t *) abstract_device;
2028     return _cairo_time_to_ns (_cairo_observation_total_elapsed (&device->log));
2029 }
2030
2031 double
2032 cairo_device_observer_paint_elapsed (cairo_device_t *abstract_device)
2033 {
2034     cairo_device_observer_t *device;
2035
2036     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2037         return -1;
2038
2039     if (! _cairo_device_is_observer (abstract_device))
2040         return -1;
2041
2042     device = (cairo_device_observer_t *) abstract_device;
2043     return _cairo_time_to_ns (device->log.paint.elapsed);
2044 }
2045
2046 double
2047 cairo_device_observer_mask_elapsed (cairo_device_t *abstract_device)
2048 {
2049     cairo_device_observer_t *device;
2050
2051     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2052         return -1;
2053
2054     if (! _cairo_device_is_observer (abstract_device))
2055         return -1;
2056
2057     device = (cairo_device_observer_t *) abstract_device;
2058     return _cairo_time_to_ns (device->log.mask.elapsed);
2059 }
2060
2061 double
2062 cairo_device_observer_fill_elapsed (cairo_device_t *abstract_device)
2063 {
2064     cairo_device_observer_t *device;
2065
2066     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2067         return -1;
2068
2069     if (! _cairo_device_is_observer (abstract_device))
2070         return -1;
2071
2072     device = (cairo_device_observer_t *) abstract_device;
2073     return _cairo_time_to_ns (device->log.fill.elapsed);
2074 }
2075
2076 double
2077 cairo_device_observer_stroke_elapsed (cairo_device_t *abstract_device)
2078 {
2079     cairo_device_observer_t *device;
2080
2081     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2082         return -1;
2083
2084     if (! _cairo_device_is_observer (abstract_device))
2085         return -1;
2086
2087     device = (cairo_device_observer_t *) abstract_device;
2088     return _cairo_time_to_ns (device->log.stroke.elapsed);
2089 }
2090
2091 double
2092 cairo_device_observer_glyphs_elapsed (cairo_device_t *abstract_device)
2093 {
2094     cairo_device_observer_t *device;
2095
2096     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2097         return -1;
2098
2099     if (! _cairo_device_is_observer (abstract_device))
2100         return -1;
2101
2102     device = (cairo_device_observer_t *) abstract_device;
2103     return _cairo_time_to_ns (device->log.glyphs.elapsed);
2104 }