9247bc4dad6a2933105506f22d5c2a9b1523b4fb
[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_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
483     if (surface->target->backend->map_to_image == NULL)
484         return NULL;
485
486     return surface->target->backend->map_to_image (surface->target, extents);
487 }
488
489 static cairo_int_status_t
490 _cairo_surface_observer_unmap_image (void *abstract_surface,
491                                      cairo_image_surface_t *image)
492 {
493     cairo_surface_observer_t *surface = abstract_surface;
494
495     if (surface->target->backend->unmap_image == NULL)
496         return CAIRO_INT_STATUS_UNSUPPORTED;
497
498     return surface->target->backend->unmap_image (surface->target, image);
499 }
500
501 static void
502 record_target (cairo_observation_record_t *r,
503                cairo_surface_t *target)
504 {
505     cairo_rectangle_int_t extents;
506
507     r->target_content = target->content;
508     if (_cairo_surface_get_extents (target, &extents)) {
509         r->target_width = extents.width;
510         r->target_height = extents.height;
511     } else {
512         r->target_width = -1;
513         r->target_height = -1;
514     }
515 }
516
517 static cairo_observation_record_t *
518 record_paint (cairo_observation_record_t *r,
519               cairo_surface_t *target,
520               cairo_operator_t op,
521               const cairo_pattern_t *source,
522               const cairo_clip_t *clip,
523               cairo_time_t elapsed)
524 {
525     record_target (r, target);
526
527     r->op = op;
528     r->source = classify_pattern (source, target);
529     r->mask = -1;
530     r->num_glyphs = -1;
531     r->path = -1;
532     r->fill_rule = -1;
533     r->tolerance = -1;
534     r->antialias = -1;
535     r->clip = classify_clip (clip);
536     r->elapsed = elapsed;
537
538     return r;
539 }
540
541 static cairo_observation_record_t *
542 record_mask (cairo_observation_record_t *r,
543              cairo_surface_t *target,
544              cairo_operator_t op,
545              const cairo_pattern_t *source,
546              const cairo_pattern_t *mask,
547              const cairo_clip_t *clip,
548              cairo_time_t elapsed)
549 {
550     record_target (r, target);
551
552     r->op = op;
553     r->source = classify_pattern (source, target);
554     r->mask = classify_pattern (mask, target);
555     r->num_glyphs = -1;
556     r->path = -1;
557     r->fill_rule = -1;
558     r->tolerance = -1;
559     r->antialias = -1;
560     r->clip = classify_clip (clip);
561     r->elapsed = elapsed;
562
563     return r;
564 }
565
566 static cairo_observation_record_t *
567 record_fill (cairo_observation_record_t *r,
568              cairo_surface_t            *target,
569              cairo_operator_t           op,
570              const cairo_pattern_t      *source,
571              const cairo_path_fixed_t   *path,
572              cairo_fill_rule_t           fill_rule,
573              double                      tolerance,
574              cairo_antialias_t           antialias,
575              const cairo_clip_t         *clip,
576              cairo_time_t elapsed)
577 {
578     record_target (r, target);
579
580     r->op = op;
581     r->source = classify_pattern (source, target);
582     r->mask = -1;
583     r->num_glyphs = -1;
584     r->path = classify_path (path, TRUE);
585     r->fill_rule = fill_rule;
586     r->tolerance = tolerance;
587     r->antialias = antialias;
588     r->clip = classify_clip (clip);
589     r->elapsed = elapsed;
590
591     return r;
592 }
593
594 static cairo_observation_record_t *
595 record_stroke (cairo_observation_record_t *r,
596                cairo_surface_t          *target,
597                cairo_operator_t         op,
598                const cairo_pattern_t    *source,
599                const cairo_path_fixed_t *path,
600                const cairo_stroke_style_t       *style,
601                const cairo_matrix_t     *ctm,
602                const cairo_matrix_t     *ctm_inverse,
603                double                    tolerance,
604                cairo_antialias_t         antialias,
605                const cairo_clip_t       *clip,
606                cairo_time_t              elapsed)
607 {
608     record_target (r, target);
609
610     r->op = op;
611     r->source = classify_pattern (source, target);
612     r->mask = -1;
613     r->num_glyphs = -1;
614     r->path = classify_path (path, FALSE);
615     r->fill_rule = -1;
616     r->tolerance = tolerance;
617     r->antialias = antialias;
618     r->clip = classify_clip (clip);
619     r->elapsed = elapsed;
620
621     return r;
622 }
623
624 static cairo_observation_record_t *
625 record_glyphs (cairo_observation_record_t *r,
626                cairo_surface_t          *target,
627                cairo_operator_t         op,
628                const cairo_pattern_t    *source,
629                cairo_glyph_t            *glyphs,
630                int                       num_glyphs,
631                cairo_scaled_font_t      *scaled_font,
632                const cairo_clip_t       *clip,
633                cairo_time_t              elapsed)
634 {
635     record_target (r, target);
636
637     r->op = op;
638     r->source = classify_pattern (source, target);
639     r->mask = -1;
640     r->path = -1;
641     r->num_glyphs = num_glyphs;
642     r->fill_rule = -1;
643     r->tolerance = -1;
644     r->antialias = -1;
645     r->clip = classify_clip (clip);
646     r->elapsed = elapsed;
647
648     return r;
649 }
650
651 static void
652 add_record (cairo_observation_t *log,
653             cairo_observation_record_t *r)
654 {
655     cairo_int_status_t status;
656
657     r->index = log->record ? log->record->commands.num_elements : 0;
658
659     status = _cairo_array_append (&log->timings, r);
660     assert (status == CAIRO_INT_STATUS_SUCCESS);
661 }
662
663 static void
664 sync (cairo_surface_t *target, int x, int y)
665 {
666     cairo_rectangle_int_t extents;
667
668     extents.x = x;
669     extents.y = y;
670     extents.width  = 1;
671     extents.height = 1;
672
673     cairo_surface_unmap_image (target,
674                                cairo_surface_map_to_image (target,
675                                                            &extents));
676 }
677
678 static void
679 midpt (const cairo_composite_rectangles_t *extents, int *x, int *y)
680 {
681     *x = extents->bounded.x + extents->bounded.width / 2;
682     *y = extents->bounded.y + extents->bounded.height / 2;
683 }
684
685 static void
686 add_record_paint (cairo_observation_t *log,
687                  cairo_surface_t *target,
688                  cairo_operator_t op,
689                  const cairo_pattern_t *source,
690                  const cairo_clip_t *clip,
691                  cairo_time_t elapsed)
692 {
693     cairo_observation_record_t record;
694     cairo_int_status_t status;
695
696     add_record (log,
697                 record_paint (&record, target, op, source, clip, elapsed));
698
699     /* We have to bypass the high-level surface layer in case it tries to be
700      * too smart and discard operations; we need to record exactly what just
701      * happened on the target.
702      */
703     if (log->record) {
704         status = log->record->base.backend->paint (&log->record->base,
705                                                    op, source, clip);
706         assert (status == CAIRO_INT_STATUS_SUCCESS);
707     }
708
709     if (_cairo_time_gt (elapsed, log->paint.slowest.elapsed))
710         log->paint.slowest = record;
711     log->paint.elapsed = _cairo_time_add (log->paint.elapsed, elapsed);
712 }
713
714 static cairo_int_status_t
715 _cairo_surface_observer_paint (void *abstract_surface,
716                                cairo_operator_t op,
717                                const cairo_pattern_t *source,
718                                const cairo_clip_t *clip)
719 {
720     cairo_surface_observer_t *surface = abstract_surface;
721     cairo_device_observer_t *device = to_device (surface);
722     cairo_composite_rectangles_t composite;
723     cairo_int_status_t status;
724     cairo_time_t t;
725     int x, y;
726
727     /* XXX device locking */
728
729     surface->log.paint.count++;
730     surface->log.paint.operators[op]++;
731     add_pattern (&surface->log.paint.source, source, surface->target);
732     add_clip (&surface->log.paint.clip, clip);
733
734     device->log.paint.count++;
735     device->log.paint.operators[op]++;
736     add_pattern (&device->log.paint.source, source, surface->target);
737     add_clip (&device->log.paint.clip, clip);
738
739     status = _cairo_composite_rectangles_init_for_paint (&composite,
740                                                          surface->target,
741                                                          op, source,
742                                                          clip);
743     if (unlikely (status)) {
744         surface->log.paint.noop++;
745         device->log.paint.noop++;
746         return status;
747     }
748
749     midpt (&composite, &x, &y);
750
751     add_extents (&surface->log.paint.extents, &composite);
752     add_extents (&device->log.paint.extents, &composite);
753     _cairo_composite_rectangles_fini (&composite);
754
755     t = _cairo_time_get ();
756     status = _cairo_surface_paint (surface->target,
757                                    op, source,
758                                    clip);
759     if (unlikely (status))
760         return status;
761
762     sync (surface->target, x, y);
763     t = _cairo_time_get_delta (t);
764
765     add_record_paint (&surface->log, surface->target, op, source, clip, t);
766     add_record_paint (&device->log, surface->target, op, source, clip, t);
767
768     do_callbacks (surface, &surface->paint_callbacks);
769
770     return CAIRO_STATUS_SUCCESS;
771 }
772
773 static void
774 add_record_mask (cairo_observation_t *log,
775                  cairo_surface_t *target,
776                  cairo_operator_t op,
777                  const cairo_pattern_t *source,
778                  const cairo_pattern_t *mask,
779                  const cairo_clip_t *clip,
780                  cairo_time_t elapsed)
781 {
782     cairo_observation_record_t record;
783     cairo_int_status_t status;
784
785     add_record (log,
786                 record_mask (&record, target, op, source, mask, clip, elapsed));
787
788     if (log->record) {
789         status = log->record->base.backend->mask (&log->record->base,
790                                                   op, source, mask, clip);
791         assert (status == CAIRO_INT_STATUS_SUCCESS);
792     }
793
794     if (_cairo_time_gt (elapsed, log->mask.slowest.elapsed))
795         log->mask.slowest = record;
796     log->mask.elapsed = _cairo_time_add (log->mask.elapsed, elapsed);
797 }
798
799 static cairo_int_status_t
800 _cairo_surface_observer_mask (void *abstract_surface,
801                               cairo_operator_t op,
802                               const cairo_pattern_t *source,
803                               const cairo_pattern_t *mask,
804                               const cairo_clip_t *clip)
805 {
806     cairo_surface_observer_t *surface = abstract_surface;
807     cairo_device_observer_t *device = to_device (surface);
808     cairo_composite_rectangles_t composite;
809     cairo_int_status_t status;
810     cairo_time_t t;
811     int x, y;
812
813     surface->log.mask.count++;
814     surface->log.mask.operators[op]++;
815     add_pattern (&surface->log.mask.source, source, surface->target);
816     add_pattern (&surface->log.mask.mask, mask, surface->target);
817     add_clip (&surface->log.mask.clip, clip);
818
819     device->log.mask.count++;
820     device->log.mask.operators[op]++;
821     add_pattern (&device->log.mask.source, source, surface->target);
822     add_pattern (&device->log.mask.mask, mask, surface->target);
823     add_clip (&device->log.mask.clip, clip);
824
825     status = _cairo_composite_rectangles_init_for_mask (&composite,
826                                                         surface->target,
827                                                         op, source, mask,
828                                                         clip);
829     if (unlikely (status)) {
830         surface->log.mask.noop++;
831         device->log.mask.noop++;
832         return status;
833     }
834
835     midpt (&composite, &x, &y);
836
837     add_extents (&surface->log.mask.extents, &composite);
838     add_extents (&device->log.mask.extents, &composite);
839     _cairo_composite_rectangles_fini (&composite);
840
841     t = _cairo_time_get ();
842     status =  _cairo_surface_mask (surface->target,
843                                    op, source, mask,
844                                    clip);
845     if (unlikely (status))
846         return status;
847
848     sync (surface->target, x, y);
849     t = _cairo_time_get_delta (t);
850
851     add_record_mask (&surface->log,
852                      surface->target, op, source, mask, clip,
853                      t);
854     add_record_mask (&device->log,
855                      surface->target, op, source, mask, clip,
856                      t);
857
858     do_callbacks (surface, &surface->mask_callbacks);
859
860     return CAIRO_STATUS_SUCCESS;
861 }
862
863 static void
864 add_record_fill (cairo_observation_t *log,
865                  cairo_surface_t *target,
866                  cairo_operator_t               op,
867                  const cairo_pattern_t          *source,
868                  const cairo_path_fixed_t       *path,
869                  cairo_fill_rule_t               fill_rule,
870                  double                          tolerance,
871                  cairo_antialias_t               antialias,
872                  const cairo_clip_t              *clip,
873                  cairo_time_t elapsed)
874 {
875     cairo_observation_record_t record;
876     cairo_int_status_t status;
877
878     add_record (log,
879                 record_fill (&record,
880                              target, op, source,
881                              path, fill_rule, tolerance, antialias,
882                              clip, elapsed));
883
884     if (log->record) {
885         status = log->record->base.backend->fill (&log->record->base,
886                                                   op, source,
887                                                   path, fill_rule,
888                                                   tolerance, antialias,
889                                                   clip);
890         assert (status == CAIRO_INT_STATUS_SUCCESS);
891     }
892
893     if (_cairo_time_gt (elapsed, log->fill.slowest.elapsed))
894         log->fill.slowest = record;
895     log->fill.elapsed = _cairo_time_add (log->fill.elapsed, elapsed);
896 }
897
898 static cairo_int_status_t
899 _cairo_surface_observer_fill (void                      *abstract_surface,
900                               cairo_operator_t          op,
901                               const cairo_pattern_t     *source,
902                               const cairo_path_fixed_t  *path,
903                               cairo_fill_rule_t         fill_rule,
904                               double                     tolerance,
905                               cairo_antialias_t         antialias,
906                               const cairo_clip_t        *clip)
907 {
908     cairo_surface_observer_t *surface = abstract_surface;
909     cairo_device_observer_t *device = to_device (surface);
910     cairo_composite_rectangles_t composite;
911     cairo_int_status_t status;
912     cairo_time_t t;
913     int x, y;
914
915     surface->log.fill.count++;
916     surface->log.fill.operators[op]++;
917     surface->log.fill.fill_rule[fill_rule]++;
918     surface->log.fill.antialias[antialias]++;
919     add_pattern (&surface->log.fill.source, source, surface->target);
920     add_path (&surface->log.fill.path, path, TRUE);
921     add_clip (&surface->log.fill.clip, clip);
922
923     device->log.fill.count++;
924     device->log.fill.operators[op]++;
925     device->log.fill.fill_rule[fill_rule]++;
926     device->log.fill.antialias[antialias]++;
927     add_pattern (&device->log.fill.source, source, surface->target);
928     add_path (&device->log.fill.path, path, TRUE);
929     add_clip (&device->log.fill.clip, clip);
930
931     status = _cairo_composite_rectangles_init_for_fill (&composite,
932                                                         surface->target,
933                                                         op, source, path,
934                                                         clip);
935     if (unlikely (status)) {
936         surface->log.fill.noop++;
937         device->log.fill.noop++;
938         return status;
939     }
940
941     midpt (&composite, &x, &y);
942
943     add_extents (&surface->log.fill.extents, &composite);
944     add_extents (&device->log.fill.extents, &composite);
945     _cairo_composite_rectangles_fini (&composite);
946
947     t = _cairo_time_get ();
948     status = _cairo_surface_fill (surface->target,
949                                   op, source, path,
950                                   fill_rule, tolerance, antialias,
951                                   clip);
952     if (unlikely (status))
953         return status;
954
955     sync (surface->target, x, y);
956     t = _cairo_time_get_delta (t);
957
958     add_record_fill (&surface->log,
959                      surface->target, op, source, path,
960                      fill_rule, tolerance, antialias,
961                      clip, t);
962
963     add_record_fill (&device->log,
964                      surface->target, op, source, path,
965                      fill_rule, tolerance, antialias,
966                      clip, t);
967
968     do_callbacks (surface, &surface->fill_callbacks);
969
970     return CAIRO_STATUS_SUCCESS;
971 }
972
973 static void
974 add_record_stroke (cairo_observation_t *log,
975                  cairo_surface_t *target,
976                  cairo_operator_t                op,
977                  const cairo_pattern_t          *source,
978                  const cairo_path_fixed_t       *path,
979                  const cairo_stroke_style_t     *style,
980                  const cairo_matrix_t           *ctm,
981                  const cairo_matrix_t           *ctm_inverse,
982                  double                          tolerance,
983                  cairo_antialias_t               antialias,
984                  const cairo_clip_t             *clip,
985                  cairo_time_t elapsed)
986 {
987     cairo_observation_record_t record;
988     cairo_int_status_t status;
989
990     add_record (log,
991                 record_stroke (&record,
992                                target, op, source,
993                                path, style, ctm,ctm_inverse,
994                                tolerance, antialias,
995                                clip, elapsed));
996
997     if (log->record) {
998         status = log->record->base.backend->stroke (&log->record->base,
999                                                     op, source,
1000                                                     path, style, ctm,ctm_inverse,
1001                                                     tolerance, antialias,
1002                                                     clip);
1003         assert (status == CAIRO_INT_STATUS_SUCCESS);
1004     }
1005
1006     if (_cairo_time_gt (elapsed, log->stroke.slowest.elapsed))
1007         log->stroke.slowest = record;
1008     log->stroke.elapsed = _cairo_time_add (log->stroke.elapsed, elapsed);
1009 }
1010
1011 static cairo_int_status_t
1012 _cairo_surface_observer_stroke (void                            *abstract_surface,
1013                                 cairo_operator_t                 op,
1014                                 const cairo_pattern_t           *source,
1015                                 const cairo_path_fixed_t        *path,
1016                                 const cairo_stroke_style_t      *style,
1017                                 const cairo_matrix_t            *ctm,
1018                                 const cairo_matrix_t            *ctm_inverse,
1019                                 double                           tolerance,
1020                                 cairo_antialias_t                antialias,
1021                                 const cairo_clip_t              *clip)
1022 {
1023     cairo_surface_observer_t *surface = abstract_surface;
1024     cairo_device_observer_t *device = to_device (surface);
1025     cairo_composite_rectangles_t composite;
1026     cairo_int_status_t status;
1027     cairo_time_t t;
1028     int x, y;
1029
1030     surface->log.stroke.count++;
1031     surface->log.stroke.operators[op]++;
1032     surface->log.stroke.antialias[antialias]++;
1033     surface->log.stroke.caps[style->line_cap]++;
1034     surface->log.stroke.joins[style->line_join]++;
1035     add_pattern (&surface->log.stroke.source, source, surface->target);
1036     add_path (&surface->log.stroke.path, path, FALSE);
1037     add_clip (&surface->log.stroke.clip, clip);
1038
1039     device->log.stroke.count++;
1040     device->log.stroke.operators[op]++;
1041     device->log.stroke.antialias[antialias]++;
1042     device->log.stroke.caps[style->line_cap]++;
1043     device->log.stroke.joins[style->line_join]++;
1044     add_pattern (&device->log.stroke.source, source, surface->target);
1045     add_path (&device->log.stroke.path, path, FALSE);
1046     add_clip (&device->log.stroke.clip, clip);
1047
1048     status = _cairo_composite_rectangles_init_for_stroke (&composite,
1049                                                           surface->target,
1050                                                           op, source,
1051                                                           path, style, ctm,
1052                                                           clip);
1053     if (unlikely (status)) {
1054         surface->log.stroke.noop++;
1055         device->log.stroke.noop++;
1056         return status;
1057     }
1058
1059     midpt (&composite, &x, &y);
1060
1061     add_extents (&surface->log.stroke.extents, &composite);
1062     add_extents (&device->log.stroke.extents, &composite);
1063     _cairo_composite_rectangles_fini (&composite);
1064
1065     t = _cairo_time_get ();
1066     status = _cairo_surface_stroke (surface->target,
1067                                   op, source, path,
1068                                   style, ctm, ctm_inverse,
1069                                   tolerance, antialias,
1070                                   clip);
1071     if (unlikely (status))
1072         return status;
1073
1074     sync (surface->target, x, y);
1075     t = _cairo_time_get_delta (t);
1076
1077     add_record_stroke (&surface->log,
1078                        surface->target, op, source, path,
1079                        style, ctm,ctm_inverse,
1080                        tolerance, antialias,
1081                        clip, t);
1082
1083     add_record_stroke (&device->log,
1084                        surface->target, op, source, path,
1085                        style, ctm,ctm_inverse,
1086                        tolerance, antialias,
1087                        clip, t);
1088
1089     do_callbacks (surface, &surface->stroke_callbacks);
1090
1091     return CAIRO_STATUS_SUCCESS;
1092 }
1093
1094 static void
1095 add_record_glyphs (cairo_observation_t  *log,
1096                    cairo_surface_t      *target,
1097                    cairo_operator_t      op,
1098                    const cairo_pattern_t*source,
1099                    cairo_glyph_t        *glyphs,
1100                    int                   num_glyphs,
1101                    cairo_scaled_font_t  *scaled_font,
1102                    const cairo_clip_t   *clip,
1103                    cairo_time_t elapsed)
1104 {
1105     cairo_observation_record_t record;
1106     cairo_int_status_t status;
1107
1108     add_record (log,
1109                 record_glyphs (&record,
1110                                target, op, source,
1111                                glyphs, num_glyphs, scaled_font,
1112                                clip, elapsed));
1113
1114     if (log->record) {
1115         status = log->record->base.backend->show_text_glyphs (&log->record->base,
1116                                                               op, source,
1117                                                               NULL, 0,
1118                                                               glyphs, num_glyphs,
1119                                                               NULL, 0, 0,
1120                                                               scaled_font,
1121                                                               clip);
1122         assert (status == CAIRO_INT_STATUS_SUCCESS);
1123     }
1124
1125     if (_cairo_time_gt (elapsed, log->glyphs.slowest.elapsed))
1126         log->glyphs.slowest = record;
1127     log->glyphs.elapsed = _cairo_time_add (log->glyphs.elapsed, elapsed);
1128 }
1129
1130 static cairo_int_status_t
1131 _cairo_surface_observer_glyphs (void                    *abstract_surface,
1132                                 cairo_operator_t         op,
1133                                 const cairo_pattern_t   *source,
1134                                 cairo_glyph_t           *glyphs,
1135                                 int                      num_glyphs,
1136                                 cairo_scaled_font_t     *scaled_font,
1137                                 const cairo_clip_t              *clip)
1138 {
1139     cairo_surface_observer_t *surface = abstract_surface;
1140     cairo_device_observer_t *device = to_device (surface);
1141     cairo_composite_rectangles_t composite;
1142     cairo_int_status_t status;
1143     cairo_glyph_t *dev_glyphs;
1144     cairo_time_t t;
1145     int x, y;
1146
1147     surface->log.glyphs.count++;
1148     surface->log.glyphs.operators[op]++;
1149     add_pattern (&surface->log.glyphs.source, source, surface->target);
1150     add_clip (&surface->log.glyphs.clip, clip);
1151
1152     device->log.glyphs.count++;
1153     device->log.glyphs.operators[op]++;
1154     add_pattern (&device->log.glyphs.source, source, surface->target);
1155     add_clip (&device->log.glyphs.clip, clip);
1156
1157     status = _cairo_composite_rectangles_init_for_glyphs (&composite,
1158                                                           surface->target,
1159                                                           op, source,
1160                                                           scaled_font,
1161                                                           glyphs, num_glyphs,
1162                                                           clip,
1163                                                           NULL);
1164     if (unlikely (status)) {
1165         surface->log.glyphs.noop++;
1166         device->log.glyphs.noop++;
1167         return status;
1168     }
1169
1170     midpt (&composite, &x, &y);
1171
1172     add_extents (&surface->log.glyphs.extents, &composite);
1173     add_extents (&device->log.glyphs.extents, &composite);
1174     _cairo_composite_rectangles_fini (&composite);
1175
1176     /* XXX We have to copy the glyphs, because the backend is allowed to
1177      * modify! */
1178     dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
1179     if (unlikely (dev_glyphs == NULL))
1180         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1181     memcpy (dev_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t));
1182
1183     t = _cairo_time_get ();
1184     status = _cairo_surface_show_text_glyphs (surface->target, op, source,
1185                                               NULL, 0,
1186                                               dev_glyphs, num_glyphs,
1187                                               NULL, 0, 0,
1188                                               scaled_font,
1189                                               clip);
1190     free (dev_glyphs);
1191     if (unlikely (status))
1192         return status;
1193
1194     sync (surface->target, x, y);
1195     t = _cairo_time_get_delta (t);
1196
1197     add_record_glyphs (&surface->log,
1198                        surface->target, op, source,
1199                        glyphs, num_glyphs, scaled_font,
1200                        clip, t);
1201
1202     add_record_glyphs (&device->log,
1203                        surface->target, op, source,
1204                        glyphs, num_glyphs, scaled_font,
1205                        clip, t);
1206
1207     do_callbacks (surface, &surface->glyphs_callbacks);
1208
1209     return CAIRO_STATUS_SUCCESS;
1210 }
1211
1212 static cairo_status_t
1213 _cairo_surface_observer_flush (void *abstract_surface)
1214 {
1215     cairo_surface_observer_t *surface = abstract_surface;
1216
1217     do_callbacks (surface, &surface->flush_callbacks);
1218
1219     cairo_surface_flush (surface->target);
1220     return surface->target->status;
1221 }
1222
1223 static cairo_status_t
1224 _cairo_surface_observer_mark_dirty (void *abstract_surface,
1225                                       int x, int y,
1226                                       int width, int height)
1227 {
1228     cairo_surface_observer_t *surface = abstract_surface;
1229     cairo_status_t status;
1230
1231     printf ("mark-dirty (%d, %d) x (%d, %d)\n", x, y, width, height);
1232
1233     status = CAIRO_STATUS_SUCCESS;
1234     if (surface->target->backend->mark_dirty_rectangle)
1235         status = surface->target->backend->mark_dirty_rectangle (surface->target,
1236                                                        x,y, width,height);
1237
1238     return status;
1239 }
1240
1241 static cairo_int_status_t
1242 _cairo_surface_observer_copy_page (void *abstract_surface)
1243 {
1244     cairo_surface_observer_t *surface = abstract_surface;
1245     cairo_status_t status;
1246
1247     status = CAIRO_STATUS_SUCCESS;
1248     if (surface->target->backend->copy_page)
1249         status = surface->target->backend->copy_page (surface->target);
1250
1251     return status;
1252 }
1253
1254 static cairo_int_status_t
1255 _cairo_surface_observer_show_page (void *abstract_surface)
1256 {
1257     cairo_surface_observer_t *surface = abstract_surface;
1258     cairo_status_t status;
1259
1260     status = CAIRO_STATUS_SUCCESS;
1261     if (surface->target->backend->show_page)
1262         status = surface->target->backend->show_page (surface->target);
1263
1264     return status;
1265 }
1266
1267 static cairo_bool_t
1268 _cairo_surface_observer_get_extents (void *abstract_surface,
1269                                      cairo_rectangle_int_t *extents)
1270 {
1271     cairo_surface_observer_t *surface = abstract_surface;
1272     return _cairo_surface_get_extents (surface->target, extents);
1273 }
1274
1275 static void
1276 _cairo_surface_observer_get_font_options (void *abstract_surface,
1277                                           cairo_font_options_t *options)
1278 {
1279     cairo_surface_observer_t *surface = abstract_surface;
1280
1281     if (surface->target->backend->get_font_options != NULL)
1282         surface->target->backend->get_font_options (surface->target, options);
1283 }
1284
1285 static cairo_surface_t *
1286 _cairo_surface_observer_source (void                    *abstract_surface,
1287                                 cairo_rectangle_int_t   *extents)
1288 {
1289     cairo_surface_observer_t *surface = abstract_surface;
1290     return _cairo_surface_get_source (surface->target, extents);
1291 }
1292
1293 static cairo_status_t
1294 _cairo_surface_observer_acquire_source_image (void                    *abstract_surface,
1295                                                 cairo_image_surface_t  **image_out,
1296                                                 void                   **image_extra)
1297 {
1298     cairo_surface_observer_t *surface = abstract_surface;
1299
1300     surface->log.num_sources_acquired++;
1301     to_device (surface)->log.num_sources_acquired++;
1302
1303     return _cairo_surface_acquire_source_image (surface->target,
1304                                                 image_out, image_extra);
1305 }
1306
1307 static void
1308 _cairo_surface_observer_release_source_image (void                   *abstract_surface,
1309                                                 cairo_image_surface_t  *image,
1310                                                 void                   *image_extra)
1311 {
1312     cairo_surface_observer_t *surface = abstract_surface;
1313
1314     _cairo_surface_release_source_image (surface->target, image, image_extra);
1315 }
1316
1317 static cairo_surface_t *
1318 _cairo_surface_observer_snapshot (void *abstract_surface)
1319 {
1320     cairo_surface_observer_t *surface = abstract_surface;
1321
1322     /* XXX hook onto the snapshot so that we measure number of reads */
1323
1324     if (surface->target->backend->snapshot)
1325         return surface->target->backend->snapshot (surface->target);
1326
1327     return NULL;
1328 }
1329
1330 static cairo_t *
1331 _cairo_surface_observer_create_context(void *target)
1332 {
1333     cairo_surface_observer_t *surface = target;
1334
1335     if (_cairo_surface_is_subsurface (&surface->base))
1336         surface = (cairo_surface_observer_t *)
1337             _cairo_surface_subsurface_get_target (&surface->base);
1338
1339     surface->log.num_contexts++;
1340     to_device (surface)->log.num_contexts++;
1341
1342     return surface->target->backend->create_context (target);
1343 }
1344
1345 static const cairo_surface_backend_t _cairo_surface_observer_backend = {
1346     CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER,
1347     _cairo_surface_observer_finish,
1348
1349     _cairo_surface_observer_create_context,
1350
1351     _cairo_surface_observer_create_similar,
1352     _cairo_surface_observer_create_similar_image,
1353     _cairo_surface_observer_map_to_image,
1354     _cairo_surface_observer_unmap_image,
1355
1356     _cairo_surface_observer_source,
1357     _cairo_surface_observer_acquire_source_image,
1358     _cairo_surface_observer_release_source_image,
1359     _cairo_surface_observer_snapshot,
1360
1361     _cairo_surface_observer_copy_page,
1362     _cairo_surface_observer_show_page,
1363
1364     _cairo_surface_observer_get_extents,
1365     _cairo_surface_observer_get_font_options,
1366
1367     _cairo_surface_observer_flush,
1368     _cairo_surface_observer_mark_dirty,
1369
1370     _cairo_surface_observer_paint,
1371     _cairo_surface_observer_mask,
1372     _cairo_surface_observer_stroke,
1373     _cairo_surface_observer_fill,
1374     NULL, /* fill-stroke */
1375     _cairo_surface_observer_glyphs,
1376 };
1377
1378 /**
1379  * cairo_surface_create_observer:
1380  * @target: an existing surface for which the observer will watch
1381  *
1382  * Create a new surface that exists solely to watch another is doing. In
1383  * the process it will log operations and times, which are fast, which are
1384  * slow, which are frequent, etc.
1385  *
1386  * Return value: a pointer to the newly allocated surface. The caller
1387  * owns the surface and should call cairo_surface_destroy() when done
1388  * with it.
1389  *
1390  * This function always returns a valid pointer, but it will return a
1391  * pointer to a "nil" surface if @other is already in an error state
1392  * or any other error occurs.
1393  *
1394  * Since: 1.12
1395  **/
1396 cairo_surface_t *
1397 cairo_surface_create_observer (cairo_surface_t *target,
1398                                cairo_surface_observer_mode_t mode)
1399 {
1400     cairo_device_t *device;
1401     cairo_surface_t *surface;
1402     cairo_bool_t record;
1403
1404     if (unlikely (target->status))
1405         return _cairo_surface_create_in_error (target->status);
1406     if (unlikely (target->finished))
1407         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
1408
1409     record = mode & CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS;
1410     device = _cairo_device_create_observer_internal (target->device, record);
1411     if (unlikely (device->status))
1412         return _cairo_surface_create_in_error (device->status);
1413
1414     surface = _cairo_surface_create_observer_internal (device, target);
1415     cairo_device_destroy (device);
1416
1417     return surface;
1418 }
1419
1420 static cairo_status_t
1421 _cairo_surface_observer_add_callback (cairo_list_t *head,
1422                                       cairo_surface_observer_callback_t func,
1423                                       void *data)
1424 {
1425     struct callback_list *cb;
1426
1427     cb = malloc (sizeof (*cb));
1428     if (unlikely (cb == NULL))
1429         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1430
1431     cairo_list_add (&cb->link, head);
1432     cb->func = func;
1433     cb->data = data;
1434
1435     return CAIRO_STATUS_SUCCESS;
1436 }
1437
1438 cairo_status_t
1439 cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface,
1440                                             cairo_surface_observer_callback_t func,
1441                                             void *data)
1442 {
1443     cairo_surface_observer_t *surface;
1444
1445     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1446         return abstract_surface->status;
1447
1448     if (! _cairo_surface_is_observer (abstract_surface))
1449         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1450
1451     surface = (cairo_surface_observer_t *)abstract_surface;
1452     return _cairo_surface_observer_add_callback (&surface->paint_callbacks,
1453                                                  func, data);
1454 }
1455
1456 cairo_status_t
1457 cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface,
1458                                           cairo_surface_observer_callback_t func,
1459                                           void *data)
1460 {
1461     cairo_surface_observer_t *surface;
1462
1463     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1464         return abstract_surface->status;
1465
1466     if (! _cairo_surface_is_observer (abstract_surface))
1467         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1468
1469     surface = (cairo_surface_observer_t *)abstract_surface;
1470     return _cairo_surface_observer_add_callback (&surface->mask_callbacks,
1471                                                  func, data);
1472 }
1473
1474 cairo_status_t
1475 cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface,
1476                                           cairo_surface_observer_callback_t func,
1477                                           void *data)
1478 {
1479     cairo_surface_observer_t *surface;
1480
1481     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1482         return abstract_surface->status;
1483
1484     if (! _cairo_surface_is_observer (abstract_surface))
1485         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1486
1487     surface = (cairo_surface_observer_t *)abstract_surface;
1488     return _cairo_surface_observer_add_callback (&surface->fill_callbacks,
1489                                                  func, data);
1490 }
1491
1492 cairo_status_t
1493 cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface,
1494                                             cairo_surface_observer_callback_t func,
1495                                             void *data)
1496 {
1497     cairo_surface_observer_t *surface;
1498
1499     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1500         return abstract_surface->status;
1501
1502     if (! _cairo_surface_is_observer (abstract_surface))
1503         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1504
1505     surface = (cairo_surface_observer_t *)abstract_surface;
1506     return _cairo_surface_observer_add_callback (&surface->stroke_callbacks,
1507                                                  func, data);
1508 }
1509
1510 cairo_status_t
1511 cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface,
1512                                             cairo_surface_observer_callback_t func,
1513                                             void *data)
1514 {
1515     cairo_surface_observer_t *surface;
1516
1517     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1518         return abstract_surface->status;
1519
1520     if (! _cairo_surface_is_observer (abstract_surface))
1521         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1522
1523     surface = (cairo_surface_observer_t *)abstract_surface;
1524     return _cairo_surface_observer_add_callback (&surface->glyphs_callbacks,
1525                                                  func, data);
1526 }
1527
1528 cairo_status_t
1529 cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface,
1530                                            cairo_surface_observer_callback_t func,
1531                                            void *data)
1532 {
1533     cairo_surface_observer_t *surface;
1534
1535     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1536         return abstract_surface->status;
1537
1538     if (! _cairo_surface_is_observer (abstract_surface))
1539         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1540
1541     surface = (cairo_surface_observer_t *)abstract_surface;
1542     return _cairo_surface_observer_add_callback (&surface->flush_callbacks,
1543                                                  func, data);
1544 }
1545
1546 cairo_status_t
1547 cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface,
1548                                             cairo_surface_observer_callback_t func,
1549                                             void *data)
1550 {
1551     cairo_surface_observer_t *surface;
1552
1553     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1554         return abstract_surface->status;
1555
1556     if (! _cairo_surface_is_observer (abstract_surface))
1557         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1558
1559     surface = (cairo_surface_observer_t *)abstract_surface;
1560     return _cairo_surface_observer_add_callback (&surface->finish_callbacks,
1561                                                  func, data);
1562 }
1563
1564 static void
1565 print_extents (cairo_output_stream_t *stream, const struct extents *e)
1566 {
1567     _cairo_output_stream_printf (stream,
1568                                  "  extents: total %g, avg %g [unbounded %d]\n",
1569                                  e->area.sum,
1570                                  e->area.sum / e->area.count,
1571                                  e->unbounded);
1572 }
1573
1574 static inline int ordercmp (int a, int b, const unsigned int *array)
1575 {
1576     /* high to low */
1577     return array[b] - array[a];
1578 }
1579 CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_order, int, ordercmp)
1580
1581 static void
1582 print_array (cairo_output_stream_t *stream,
1583              const unsigned int *array,
1584              const char **names,
1585              int count)
1586 {
1587     int order[64];
1588     int i, j;
1589
1590     assert (count < ARRAY_LENGTH (order));
1591     for (i = j = 0; i < count; i++) {
1592         if (array[i] != 0)
1593             order[j++] = i;
1594     }
1595
1596     sort_order (order, j, (void *)array);
1597     for (i = 0; i < j; i++)
1598         _cairo_output_stream_printf (stream, " %d %s%s",
1599                                      array[order[i]], names[order[i]],
1600                                      i < j -1 ? "," : "");
1601 }
1602
1603 static const char *operator_names[] = {
1604     "CLEAR",    /* CAIRO_OPERATOR_CLEAR */
1605
1606     "SOURCE",   /* CAIRO_OPERATOR_SOURCE */
1607     "OVER",             /* CAIRO_OPERATOR_OVER */
1608     "IN",               /* CAIRO_OPERATOR_IN */
1609     "OUT",              /* CAIRO_OPERATOR_OUT */
1610     "ATOP",             /* CAIRO_OPERATOR_ATOP */
1611
1612     "DEST",             /* CAIRO_OPERATOR_DEST */
1613     "DEST_OVER",        /* CAIRO_OPERATOR_DEST_OVER */
1614     "DEST_IN",  /* CAIRO_OPERATOR_DEST_IN */
1615     "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */
1616     "DEST_ATOP",        /* CAIRO_OPERATOR_DEST_ATOP */
1617
1618     "XOR",              /* CAIRO_OPERATOR_XOR */
1619     "ADD",              /* CAIRO_OPERATOR_ADD */
1620     "SATURATE", /* CAIRO_OPERATOR_SATURATE */
1621
1622     "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */
1623     "SCREEN",   /* CAIRO_OPERATOR_SCREEN */
1624     "OVERLAY",  /* CAIRO_OPERATOR_OVERLAY */
1625     "DARKEN",   /* CAIRO_OPERATOR_DARKEN */
1626     "LIGHTEN",  /* CAIRO_OPERATOR_LIGHTEN */
1627     "DODGE",    /* CAIRO_OPERATOR_COLOR_DODGE */
1628     "BURN",             /* CAIRO_OPERATOR_COLOR_BURN */
1629     "HARD_LIGHT",       /* CAIRO_OPERATOR_HARD_LIGHT */
1630     "SOFT_LIGHT",       /* CAIRO_OPERATOR_SOFT_LIGHT */
1631     "DIFFERENCE",       /* CAIRO_OPERATOR_DIFFERENCE */
1632     "EXCLUSION",        /* CAIRO_OPERATOR_EXCLUSION */
1633     "HSL_HUE",  /* CAIRO_OPERATOR_HSL_HUE */
1634     "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
1635     "HSL_COLOR",        /* CAIRO_OPERATOR_HSL_COLOR */
1636     "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
1637 };
1638 static void
1639 print_operators (cairo_output_stream_t *stream, unsigned int *array)
1640 {
1641     _cairo_output_stream_printf (stream, "  op:");
1642     print_array (stream, array, operator_names, NUM_OPERATORS);
1643     _cairo_output_stream_printf (stream, "\n");
1644 }
1645
1646 static const char *fill_rule_names[] = {
1647     "non-zero",
1648     "even-odd",
1649 };
1650 static void
1651 print_fill_rule (cairo_output_stream_t *stream, unsigned int *array)
1652 {
1653     _cairo_output_stream_printf (stream, "  fill rule:");
1654     print_array (stream, array, fill_rule_names, ARRAY_LENGTH(fill_rule_names));
1655     _cairo_output_stream_printf (stream, "\n");
1656 }
1657
1658 static const char *cap_names[] = {
1659     "butt",             /* CAIRO_LINE_CAP_BUTT */
1660     "round",    /* CAIRO_LINE_CAP_ROUND */
1661     "square"    /* CAIRO_LINE_CAP_SQUARE */
1662 };
1663 static void
1664 print_line_caps (cairo_output_stream_t *stream, unsigned int *array)
1665 {
1666     _cairo_output_stream_printf (stream, "  caps:");
1667     print_array (stream, array, cap_names, NUM_CAPS);
1668     _cairo_output_stream_printf (stream, "\n");
1669 }
1670
1671 static const char *join_names[] = {
1672     "miter",    /* CAIRO_LINE_JOIN_MITER */
1673     "round",    /* CAIRO_LINE_JOIN_ROUND */
1674     "bevel",    /* CAIRO_LINE_JOIN_BEVEL */
1675 };
1676 static void
1677 print_line_joins (cairo_output_stream_t *stream, unsigned int *array)
1678 {
1679     _cairo_output_stream_printf (stream, "  joins:");
1680     print_array (stream, array, join_names, NUM_JOINS);
1681     _cairo_output_stream_printf (stream, "\n");
1682 }
1683
1684 static const char *antialias_names[] = {
1685     "default",
1686     "none",
1687     "gray",
1688     "subpixel",
1689     "fast",
1690     "good",
1691     "best"
1692 };
1693 static void
1694 print_antialias (cairo_output_stream_t *stream, unsigned int *array)
1695 {
1696     _cairo_output_stream_printf (stream, "  antialias:");
1697     print_array (stream, array, antialias_names, NUM_ANTIALIAS);
1698     _cairo_output_stream_printf (stream, "\n");
1699 }
1700
1701 static const char *pattern_names[] = {
1702     "native",
1703     "record",
1704     "other surface",
1705     "solid",
1706     "linear",
1707     "radial",
1708     "mesh",
1709     "raster"
1710 };
1711 static void
1712 print_pattern (cairo_output_stream_t *stream,
1713                const char *name,
1714                const struct pattern *p)
1715 {
1716     _cairo_output_stream_printf (stream, "  %s:", name);
1717     print_array (stream, p->type, pattern_names, ARRAY_LENGTH (pattern_names));
1718     _cairo_output_stream_printf (stream, "\n");
1719 }
1720
1721 static const char *path_names[] = {
1722     "empty",
1723     "pixel-aligned",
1724     "rectliinear",
1725     "straight",
1726     "curved",
1727 };
1728 static void
1729 print_path (cairo_output_stream_t *stream,
1730             const struct path *p)
1731 {
1732     _cairo_output_stream_printf (stream, "  path:");
1733     print_array (stream, p->type, path_names, ARRAY_LENGTH (path_names));
1734     _cairo_output_stream_printf (stream, "\n");
1735 }
1736
1737 static const char *clip_names[] = {
1738     "none",
1739     "region",
1740     "boxes",
1741     "single path",
1742     "polygon",
1743     "general",
1744 };
1745 static void
1746 print_clip (cairo_output_stream_t *stream, const struct clip *c)
1747 {
1748     _cairo_output_stream_printf (stream, "  clip:");
1749     print_array (stream, c->type, clip_names, ARRAY_LENGTH (clip_names));
1750     _cairo_output_stream_printf (stream, "\n");
1751 }
1752
1753 static void
1754 print_record (cairo_output_stream_t *stream,
1755               cairo_observation_record_t *r)
1756 {
1757     _cairo_output_stream_printf (stream, "  op: %s\n", operator_names[r->op]);
1758     _cairo_output_stream_printf (stream, "  source: %s\n",
1759                                  pattern_names[r->source]);
1760     if (r->mask != -1)
1761         _cairo_output_stream_printf (stream, "  mask: %s\n",
1762                                      pattern_names[r->mask]);
1763     if (r->num_glyphs != -1)
1764         _cairo_output_stream_printf (stream, "  num_glyphs: %d\n",
1765                                      r->num_glyphs);
1766     if (r->path != -1)
1767         _cairo_output_stream_printf (stream, "  path: %s\n",
1768                                     path_names[r->path]);
1769     if (r->fill_rule != -1)
1770         _cairo_output_stream_printf (stream, "  fill rule: %s\n",
1771                                      fill_rule_names[r->fill_rule]);
1772     if (r->antialias != -1)
1773         _cairo_output_stream_printf (stream, "  antialias: %s\n",
1774                                      antialias_names[r->antialias]);
1775     _cairo_output_stream_printf (stream, "  clip: %s\n", clip_names[r->clip]);
1776     _cairo_output_stream_printf (stream, "  elapsed: %f ns\n",
1777                                  _cairo_time_to_ns (r->elapsed));
1778 }
1779
1780 static double percent (cairo_time_t a, cairo_time_t b)
1781 {
1782     /* Fake %.1f */
1783     return _cairo_round (_cairo_time_to_s (a) * 1000 /
1784                          _cairo_time_to_s (b)) / 10;
1785 }
1786
1787 static cairo_bool_t
1788 replay_record (cairo_observation_t *log,
1789                cairo_observation_record_t *r,
1790                cairo_device_t *script)
1791 {
1792 #if CAIRO_HAS_SCRIPT_SURFACE
1793     cairo_surface_t *surface;
1794     cairo_int_status_t status;
1795
1796     if (log->record == NULL || script == NULL)
1797         return FALSE;
1798
1799     surface = cairo_script_surface_create (script,
1800                                            r->target_content,
1801                                            r->target_width,
1802                                            r->target_height);
1803     status =
1804         _cairo_recording_surface_replay_one (log->record, r->index, surface);
1805     cairo_surface_destroy (surface);
1806
1807     assert (status == CAIRO_INT_STATUS_SUCCESS);
1808
1809     return TRUE;
1810 #else
1811     return FALSE;
1812 #endif
1813 }
1814
1815 static cairo_time_t
1816 _cairo_observation_total_elapsed (cairo_observation_t *log)
1817 {
1818     cairo_time_t total;
1819
1820     total = log->paint.elapsed;
1821     total = _cairo_time_add (total, log->mask.elapsed);
1822     total = _cairo_time_add (total, log->fill.elapsed);
1823     total = _cairo_time_add (total, log->stroke.elapsed);
1824     total = _cairo_time_add (total, log->glyphs.elapsed);
1825
1826     return total;
1827 }
1828
1829 static void
1830 _cairo_observation_print (cairo_output_stream_t *stream,
1831                           cairo_observation_t *log)
1832 {
1833     cairo_device_t *script;
1834     cairo_time_t total;
1835
1836 #if CAIRO_HAS_SCRIPT_SURFACE
1837     script = _cairo_script_context_create_internal (stream);
1838     _cairo_script_context_attach_snapshots (script, FALSE);
1839 #else
1840     script = NULL;
1841 #endif
1842
1843     total = _cairo_observation_total_elapsed (log);
1844
1845     _cairo_output_stream_printf (stream, "elapsed: %f\n",
1846                                  _cairo_time_to_ns (total));
1847     _cairo_output_stream_printf (stream, "surfaces: %d\n",
1848                                  log->num_surfaces);
1849     _cairo_output_stream_printf (stream, "contexts: %d\n",
1850                                  log->num_contexts);
1851     _cairo_output_stream_printf (stream, "sources acquired: %d\n",
1852                                  log->num_sources_acquired);
1853
1854
1855     _cairo_output_stream_printf (stream, "paint: count %d [no-op %d], elapsed %f [%f%%]\n",
1856                                  log->paint.count, log->paint.noop,
1857                                  _cairo_time_to_ns (log->paint.elapsed),
1858                                  percent (log->paint.elapsed, total));
1859     if (log->paint.count) {
1860         print_extents (stream, &log->paint.extents);
1861         print_operators (stream, log->paint.operators);
1862         print_pattern (stream, "source", &log->paint.source);
1863         print_clip (stream, &log->paint.clip);
1864
1865         _cairo_output_stream_printf (stream, "slowest paint: %f%%\n",
1866                                      percent (log->paint.slowest.elapsed,
1867                                               log->paint.elapsed));
1868         print_record (stream, &log->paint.slowest);
1869
1870         _cairo_output_stream_printf (stream, "\n");
1871         if (replay_record (log, &log->paint.slowest, script))
1872             _cairo_output_stream_printf (stream, "\n\n");
1873     }
1874
1875     _cairo_output_stream_printf (stream, "mask: count %d [no-op %d], elapsed %f [%f%%]\n",
1876                                  log->mask.count, log->mask.noop,
1877                                  _cairo_time_to_ns (log->mask.elapsed),
1878                                  percent (log->mask.elapsed, total));
1879     if (log->mask.count) {
1880         print_extents (stream, &log->mask.extents);
1881         print_operators (stream, log->mask.operators);
1882         print_pattern (stream, "source", &log->mask.source);
1883         print_pattern (stream, "mask", &log->mask.mask);
1884         print_clip (stream, &log->mask.clip);
1885
1886         _cairo_output_stream_printf (stream, "slowest mask: %f%%\n",
1887                                      percent (log->mask.slowest.elapsed,
1888                                               log->mask.elapsed));
1889         print_record (stream, &log->mask.slowest);
1890
1891         _cairo_output_stream_printf (stream, "\n");
1892         if (replay_record (log, &log->mask.slowest, script))
1893             _cairo_output_stream_printf (stream, "\n\n");
1894     }
1895
1896     _cairo_output_stream_printf (stream, "fill: count %d [no-op %d], elaspsed %f [%f%%]\n",
1897                                  log->fill.count, log->fill.noop,
1898                                  _cairo_time_to_ns (log->fill.elapsed),
1899                                  percent (log->fill.elapsed, total));
1900     if (log->fill.count) {
1901         print_extents (stream, &log->fill.extents);
1902         print_operators (stream, log->fill.operators);
1903         print_pattern (stream, "source", &log->fill.source);
1904         print_path (stream, &log->fill.path);
1905         print_fill_rule (stream, log->fill.fill_rule);
1906         print_antialias (stream, log->fill.antialias);
1907         print_clip (stream, &log->fill.clip);
1908
1909         _cairo_output_stream_printf (stream, "slowest fill: %f%%\n",
1910                                      percent (log->fill.slowest.elapsed,
1911                                               log->fill.elapsed));
1912         print_record (stream, &log->fill.slowest);
1913
1914         _cairo_output_stream_printf (stream, "\n");
1915         if (replay_record (log, &log->fill.slowest, script))
1916             _cairo_output_stream_printf (stream, "\n\n");
1917     }
1918
1919     _cairo_output_stream_printf (stream, "stroke: count %d [no-op %d], elapsed %f [%f%%]\n",
1920                                  log->stroke.count, log->stroke.noop,
1921                                  _cairo_time_to_ns (log->stroke.elapsed),
1922                                  percent (log->stroke.elapsed, total));
1923     if (log->stroke.count) {
1924         print_extents (stream, &log->stroke.extents);
1925         print_operators (stream, log->stroke.operators);
1926         print_pattern (stream, "source", &log->stroke.source);
1927         print_path (stream, &log->stroke.path);
1928         print_antialias (stream, log->stroke.antialias);
1929         print_line_caps (stream, log->stroke.caps);
1930         print_line_joins (stream, log->stroke.joins);
1931         print_clip (stream, &log->stroke.clip);
1932
1933         _cairo_output_stream_printf (stream, "slowest stroke: %f%%\n",
1934                                      percent (log->stroke.slowest.elapsed,
1935                                               log->stroke.elapsed));
1936         print_record (stream, &log->stroke.slowest);
1937
1938         _cairo_output_stream_printf (stream, "\n");
1939         if (replay_record (log, &log->stroke.slowest, script))
1940             _cairo_output_stream_printf (stream, "\n\n");
1941     }
1942
1943     _cairo_output_stream_printf (stream, "glyphs: count %d [no-op %d], elasped %f [%f%%]\n",
1944                                  log->glyphs.count, log->glyphs.noop,
1945                                  _cairo_time_to_ns (log->glyphs.elapsed),
1946                                  percent (log->glyphs.elapsed, total));
1947     if (log->glyphs.count) {
1948         print_extents (stream, &log->glyphs.extents);
1949         print_operators (stream, log->glyphs.operators);
1950         print_pattern (stream, "source", &log->glyphs.source);
1951         print_clip (stream, &log->glyphs.clip);
1952
1953         _cairo_output_stream_printf (stream, "slowest glyphs: %f%%\n",
1954                                      percent (log->glyphs.slowest.elapsed,
1955                                               log->glyphs.elapsed));
1956         print_record (stream, &log->glyphs.slowest);
1957
1958         _cairo_output_stream_printf (stream, "\n");
1959         if (replay_record (log, &log->glyphs.slowest, script))
1960             _cairo_output_stream_printf (stream, "\n\n");
1961     }
1962
1963     cairo_device_destroy (script);
1964 }
1965
1966 cairo_status_t
1967 cairo_surface_observer_print (cairo_surface_t *abstract_surface,
1968                               cairo_write_func_t write_func,
1969                               void *closure)
1970 {
1971     cairo_output_stream_t *stream;
1972     cairo_surface_observer_t *surface;
1973
1974     if (unlikely (abstract_surface->status))
1975         return abstract_surface->status;
1976
1977     if (unlikely (! _cairo_surface_is_observer (abstract_surface)))
1978         return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1979
1980     surface = (cairo_surface_observer_t *) abstract_surface;
1981
1982     stream = _cairo_output_stream_create (write_func, NULL, closure);
1983     _cairo_observation_print (stream, &surface->log);
1984     return _cairo_output_stream_destroy (stream);
1985 }
1986
1987 double
1988 cairo_surface_observer_elapsed (cairo_surface_t *abstract_surface)
1989 {
1990     cairo_surface_observer_t *surface;
1991
1992     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1993         return -1;
1994
1995     if (! _cairo_surface_is_observer (abstract_surface))
1996         return -1;
1997
1998     surface = (cairo_surface_observer_t *) abstract_surface;
1999     return _cairo_time_to_ns (_cairo_observation_total_elapsed (&surface->log));
2000 }
2001
2002 cairo_status_t
2003 cairo_device_observer_print (cairo_device_t *abstract_device,
2004                              cairo_write_func_t write_func,
2005                              void *closure)
2006 {
2007     cairo_output_stream_t *stream;
2008     cairo_device_observer_t *device;
2009
2010     if (unlikely (abstract_device->status))
2011         return abstract_device->status;
2012
2013     if (unlikely (! _cairo_device_is_observer (abstract_device)))
2014         return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
2015
2016     device = (cairo_device_observer_t *) abstract_device;
2017
2018     stream = _cairo_output_stream_create (write_func, NULL, closure);
2019     _cairo_observation_print (stream, &device->log);
2020     return _cairo_output_stream_destroy (stream);
2021 }
2022
2023 double
2024 cairo_device_observer_elapsed (cairo_device_t *abstract_device)
2025 {
2026     cairo_device_observer_t *device;
2027
2028     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2029         return -1;
2030
2031     if (! _cairo_device_is_observer (abstract_device))
2032         return -1;
2033
2034     device = (cairo_device_observer_t *) abstract_device;
2035     return _cairo_time_to_ns (_cairo_observation_total_elapsed (&device->log));
2036 }
2037
2038 double
2039 cairo_device_observer_paint_elapsed (cairo_device_t *abstract_device)
2040 {
2041     cairo_device_observer_t *device;
2042
2043     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2044         return -1;
2045
2046     if (! _cairo_device_is_observer (abstract_device))
2047         return -1;
2048
2049     device = (cairo_device_observer_t *) abstract_device;
2050     return _cairo_time_to_ns (device->log.paint.elapsed);
2051 }
2052
2053 double
2054 cairo_device_observer_mask_elapsed (cairo_device_t *abstract_device)
2055 {
2056     cairo_device_observer_t *device;
2057
2058     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2059         return -1;
2060
2061     if (! _cairo_device_is_observer (abstract_device))
2062         return -1;
2063
2064     device = (cairo_device_observer_t *) abstract_device;
2065     return _cairo_time_to_ns (device->log.mask.elapsed);
2066 }
2067
2068 double
2069 cairo_device_observer_fill_elapsed (cairo_device_t *abstract_device)
2070 {
2071     cairo_device_observer_t *device;
2072
2073     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2074         return -1;
2075
2076     if (! _cairo_device_is_observer (abstract_device))
2077         return -1;
2078
2079     device = (cairo_device_observer_t *) abstract_device;
2080     return _cairo_time_to_ns (device->log.fill.elapsed);
2081 }
2082
2083 double
2084 cairo_device_observer_stroke_elapsed (cairo_device_t *abstract_device)
2085 {
2086     cairo_device_observer_t *device;
2087
2088     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2089         return -1;
2090
2091     if (! _cairo_device_is_observer (abstract_device))
2092         return -1;
2093
2094     device = (cairo_device_observer_t *) abstract_device;
2095     return _cairo_time_to_ns (device->log.stroke.elapsed);
2096 }
2097
2098 double
2099 cairo_device_observer_glyphs_elapsed (cairo_device_t *abstract_device)
2100 {
2101     cairo_device_observer_t *device;
2102
2103     if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2104         return -1;
2105
2106     if (! _cairo_device_is_observer (abstract_device))
2107         return -1;
2108
2109     device = (cairo_device_observer_t *) abstract_device;
2110     return _cairo_time_to_ns (device->log.glyphs.elapsed);
2111 }