1 /* cairo - a vector graphics library with display and print output
3 * Copyright © 2011 Intel Corporation
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.
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
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/
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.
28 * The Original Code is the cairo graphics library.
30 * The Initial Developer of the Original Code is Intel Corporation.
33 * Chris Wilson <chris@chris-wilson.co.uk>
38 #include "cairo-surface-observer-private.h"
39 #include "cairo-surface-observer-inline.h"
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"
53 #if CAIRO_HAS_SCRIPT_SURFACE
54 #include "cairo-script-private.h"
57 static const cairo_surface_backend_t _cairo_surface_observer_backend;
59 /* observation/stats */
61 static void init_stats (struct stat *s)
67 static void init_extents (struct extents *e)
69 init_stats (&e->area);
72 static void init_pattern (struct pattern *p)
76 static void init_path (struct path *p)
80 static void init_clip (struct clip *c)
84 static void init_paint (struct paint *p)
86 init_extents (&p->extents);
87 init_pattern (&p->source);
91 static void init_mask (struct mask *m)
93 init_extents (&m->extents);
94 init_pattern (&m->source);
95 init_pattern (&m->mask);
99 static void init_fill (struct fill *f)
101 init_extents (&f->extents);
102 init_pattern (&f->source);
103 init_path (&f->path);
104 init_clip (&f->clip);
107 static void init_stroke (struct stroke *s)
109 init_extents (&s->extents);
110 init_pattern (&s->source);
111 init_path (&s->path);
112 init_clip (&s->clip);
115 static void init_glyphs (struct glyphs *g)
117 init_extents (&g->extents);
118 init_pattern (&g->source);
119 init_clip (&g->clip);
122 static cairo_status_t
123 log_init (cairo_observation_t *log,
126 memset (log, 0, sizeof(*log));
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);
134 _cairo_array_init (&log->timings, sizeof (cairo_observation_record_t));
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;
142 log->record->optimize_clears = FALSE;
145 return CAIRO_STATUS_SUCCESS;
149 log_fini (cairo_observation_t *log)
151 _cairo_array_fini (&log->timings);
152 cairo_surface_destroy (&log->record->base);
155 static cairo_surface_t*
156 get_pattern_surface (const cairo_pattern_t *pattern)
158 return ((cairo_surface_pattern_t *)pattern)->surface;
162 classify_pattern (const cairo_pattern_t *pattern,
163 const cairo_surface_t *target)
167 switch (pattern->type) {
168 case CAIRO_PATTERN_TYPE_SURFACE:
169 if (get_pattern_surface (pattern)->type == target->type)
171 else if (get_pattern_surface (pattern)->type == CAIRO_SURFACE_TYPE_RECORDING)
177 case CAIRO_PATTERN_TYPE_SOLID:
180 case CAIRO_PATTERN_TYPE_LINEAR:
183 case CAIRO_PATTERN_TYPE_RADIAL:
186 case CAIRO_PATTERN_TYPE_MESH:
189 case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
197 add_pattern (struct pattern *stats,
198 const cairo_pattern_t *pattern,
199 const cairo_surface_t *target)
201 stats->type[classify_pattern(pattern, target)]++;
205 classify_path (const cairo_path_fixed_t *path,
206 cairo_bool_t is_fill)
210 /* XXX improve for stroke */
213 if (path->fill_is_empty)
215 else if (_cairo_path_fixed_fill_is_rectilinear (path))
216 classify = path->fill_maybe_region ? 1 : 2;
218 if (_cairo_path_fixed_stroke_is_rectilinear (path))
222 classify = 3 + (path->has_curve_to != 0);
228 add_path (struct path *stats,
229 const cairo_path_fixed_t *path,
230 cairo_bool_t is_fill)
232 stats->type[classify_path(path, is_fill)]++;
236 classify_clip (const cairo_clip_t *clip)
242 else if (_cairo_clip_is_region (clip))
244 else if (clip->path == NULL)
246 else if (clip->path->prev == NULL)
248 else if (_cairo_clip_is_polygon (clip))
257 add_clip (struct clip *stats,
258 const cairo_clip_t *clip)
260 stats->type[classify_clip (clip)]++;
264 stats_add (struct stat *s, double v)
276 add_extents (struct extents *stats,
277 const cairo_composite_rectangles_t *extents)
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;
285 /* device interface */
288 _cairo_device_observer_lock (void *_device)
290 cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
291 cairo_status_t ignored;
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);
299 _cairo_device_observer_unlock (void *_device)
301 cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
302 cairo_device_release (device->target);
305 static cairo_status_t
306 _cairo_device_observer_flush (void *_device)
308 cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
310 if (device->target == NULL)
311 return CAIRO_STATUS_SUCCESS;
313 cairo_device_flush (device->target);
314 return device->target->status;
318 _cairo_device_observer_finish (void *_device)
320 cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
321 log_fini (&device->log);
322 cairo_device_finish (device->target);
326 _cairo_device_observer_destroy (void *_device)
328 cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
329 cairo_device_destroy (device->target);
333 static const cairo_device_backend_t _cairo_device_observer_backend = {
334 CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER,
336 _cairo_device_observer_lock,
337 _cairo_device_observer_unlock,
339 _cairo_device_observer_flush,
340 _cairo_device_observer_finish,
341 _cairo_device_observer_destroy,
344 static cairo_device_t *
345 _cairo_device_create_observer_internal (cairo_device_t *target,
348 cairo_device_observer_t *device;
349 cairo_status_t status;
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));
355 _cairo_device_init (&device->base, &_cairo_device_observer_backend);
356 status = log_init (&device->log, record);
357 if (unlikely (status)) {
359 return _cairo_device_create_in_error (status);
362 device->target = cairo_device_reference (target);
364 return &device->base;
367 /* surface interface */
369 static cairo_device_observer_t *
370 to_device (cairo_surface_observer_t *suface)
372 return (cairo_device_observer_t *)suface->base.device;
375 static cairo_surface_t *
376 _cairo_surface_create_observer_internal (cairo_device_t *device,
377 cairo_surface_t *target)
379 cairo_surface_observer_t *surface;
380 cairo_status_t status;
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));
386 _cairo_surface_init (&surface->base,
387 &_cairo_surface_observer_backend, device,
390 status = log_init (&surface->log,
391 ((cairo_device_observer_t *)device)->log.record != NULL);
392 if (unlikely (status)) {
394 return _cairo_surface_create_in_error (status);
397 surface->target = cairo_surface_reference (target);
398 surface->base.type = surface->target->type;
399 surface->base.is_clear = surface->target->is_clear;
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);
407 cairo_list_init (&surface->flush_callbacks);
408 cairo_list_init (&surface->finish_callbacks);
410 surface->log.num_surfaces++;
411 to_device (surface)->log.num_surfaces++;
413 return &surface->base;
417 do_callbacks (cairo_surface_observer_t *surface, cairo_list_t *head)
419 struct callback_list *cb;
421 cairo_list_foreach_entry (cb, struct callback_list, head, link)
422 cb->func (&surface->base, surface->target, cb->data);
426 static cairo_status_t
427 _cairo_surface_observer_finish (void *abstract_surface)
429 cairo_surface_observer_t *surface = abstract_surface;
431 do_callbacks (surface, &surface->finish_callbacks);
433 cairo_surface_destroy (surface->target);
434 log_fini (&surface->log);
436 return CAIRO_STATUS_SUCCESS;
439 static cairo_surface_t *
440 _cairo_surface_observer_create_similar (void *abstract_other,
441 cairo_content_t content,
442 int width, int height)
444 cairo_surface_observer_t *other = abstract_other;
445 cairo_surface_t *target, *surface;
448 if (other->target->backend->create_similar)
449 target = other->target->backend->create_similar (other->target, content,
452 target = _cairo_image_surface_create_with_content (content,
455 surface = _cairo_surface_create_observer_internal (other->base.device,
457 cairo_surface_destroy (target);
462 static cairo_surface_t *
463 _cairo_surface_observer_create_similar_image (void *other,
464 cairo_format_t format,
465 int width, int height)
467 cairo_surface_observer_t *surface = other;
469 if (surface->target->backend->create_similar_image)
470 return surface->target->backend->create_similar_image (surface->target,
477 static cairo_surface_t *
478 _cairo_surface_observer_map_to_image (void *abstract_surface,
479 const cairo_rectangle_int_t *extents)
481 cairo_surface_observer_t *surface = abstract_surface;
483 if (surface->target->backend->map_to_image == NULL)
486 return surface->target->backend->map_to_image (surface->target, extents);
489 static cairo_int_status_t
490 _cairo_surface_observer_unmap_image (void *abstract_surface,
491 cairo_image_surface_t *image)
493 cairo_surface_observer_t *surface = abstract_surface;
495 if (surface->target->backend->unmap_image == NULL)
496 return CAIRO_INT_STATUS_UNSUPPORTED;
498 return surface->target->backend->unmap_image (surface->target, image);
502 record_target (cairo_observation_record_t *r,
503 cairo_surface_t *target)
505 cairo_rectangle_int_t extents;
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;
512 r->target_width = -1;
513 r->target_height = -1;
517 static cairo_observation_record_t *
518 record_paint (cairo_observation_record_t *r,
519 cairo_surface_t *target,
521 const cairo_pattern_t *source,
522 const cairo_clip_t *clip,
523 cairo_time_t elapsed)
525 record_target (r, target);
528 r->source = classify_pattern (source, target);
535 r->clip = classify_clip (clip);
536 r->elapsed = elapsed;
541 static cairo_observation_record_t *
542 record_mask (cairo_observation_record_t *r,
543 cairo_surface_t *target,
545 const cairo_pattern_t *source,
546 const cairo_pattern_t *mask,
547 const cairo_clip_t *clip,
548 cairo_time_t elapsed)
550 record_target (r, target);
553 r->source = classify_pattern (source, target);
554 r->mask = classify_pattern (mask, target);
560 r->clip = classify_clip (clip);
561 r->elapsed = elapsed;
566 static cairo_observation_record_t *
567 record_fill (cairo_observation_record_t *r,
568 cairo_surface_t *target,
570 const cairo_pattern_t *source,
571 const cairo_path_fixed_t *path,
572 cairo_fill_rule_t fill_rule,
574 cairo_antialias_t antialias,
575 const cairo_clip_t *clip,
576 cairo_time_t elapsed)
578 record_target (r, target);
581 r->source = classify_pattern (source, target);
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;
594 static cairo_observation_record_t *
595 record_stroke (cairo_observation_record_t *r,
596 cairo_surface_t *target,
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,
604 cairo_antialias_t antialias,
605 const cairo_clip_t *clip,
606 cairo_time_t elapsed)
608 record_target (r, target);
611 r->source = classify_pattern (source, target);
614 r->path = classify_path (path, FALSE);
616 r->tolerance = tolerance;
617 r->antialias = antialias;
618 r->clip = classify_clip (clip);
619 r->elapsed = elapsed;
624 static cairo_observation_record_t *
625 record_glyphs (cairo_observation_record_t *r,
626 cairo_surface_t *target,
628 const cairo_pattern_t *source,
629 cairo_glyph_t *glyphs,
631 cairo_scaled_font_t *scaled_font,
632 const cairo_clip_t *clip,
633 cairo_time_t elapsed)
635 record_target (r, target);
638 r->source = classify_pattern (source, target);
641 r->num_glyphs = num_glyphs;
645 r->clip = classify_clip (clip);
646 r->elapsed = elapsed;
652 add_record (cairo_observation_t *log,
653 cairo_observation_record_t *r)
655 cairo_int_status_t status;
657 r->index = log->record ? log->record->commands.num_elements : 0;
659 status = _cairo_array_append (&log->timings, r);
660 assert (status == CAIRO_INT_STATUS_SUCCESS);
664 sync (cairo_surface_t *target, int x, int y)
666 cairo_rectangle_int_t extents;
673 cairo_surface_unmap_image (target,
674 cairo_surface_map_to_image (target,
679 midpt (const cairo_composite_rectangles_t *extents, int *x, int *y)
681 *x = extents->bounded.x + extents->bounded.width / 2;
682 *y = extents->bounded.y + extents->bounded.height / 2;
686 add_record_paint (cairo_observation_t *log,
687 cairo_surface_t *target,
689 const cairo_pattern_t *source,
690 const cairo_clip_t *clip,
691 cairo_time_t elapsed)
693 cairo_observation_record_t record;
694 cairo_int_status_t status;
697 record_paint (&record, target, op, source, clip, elapsed));
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.
704 status = log->record->base.backend->paint (&log->record->base,
706 assert (status == CAIRO_INT_STATUS_SUCCESS);
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);
714 static cairo_int_status_t
715 _cairo_surface_observer_paint (void *abstract_surface,
717 const cairo_pattern_t *source,
718 const cairo_clip_t *clip)
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;
727 /* XXX device locking */
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);
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);
739 status = _cairo_composite_rectangles_init_for_paint (&composite,
743 if (unlikely (status)) {
744 surface->log.paint.noop++;
745 device->log.paint.noop++;
749 midpt (&composite, &x, &y);
751 add_extents (&surface->log.paint.extents, &composite);
752 add_extents (&device->log.paint.extents, &composite);
753 _cairo_composite_rectangles_fini (&composite);
755 t = _cairo_time_get ();
756 status = _cairo_surface_paint (surface->target,
759 if (unlikely (status))
762 sync (surface->target, x, y);
763 t = _cairo_time_get_delta (t);
765 add_record_paint (&surface->log, surface->target, op, source, clip, t);
766 add_record_paint (&device->log, surface->target, op, source, clip, t);
768 do_callbacks (surface, &surface->paint_callbacks);
770 return CAIRO_STATUS_SUCCESS;
774 add_record_mask (cairo_observation_t *log,
775 cairo_surface_t *target,
777 const cairo_pattern_t *source,
778 const cairo_pattern_t *mask,
779 const cairo_clip_t *clip,
780 cairo_time_t elapsed)
782 cairo_observation_record_t record;
783 cairo_int_status_t status;
786 record_mask (&record, target, op, source, mask, clip, elapsed));
789 status = log->record->base.backend->mask (&log->record->base,
790 op, source, mask, clip);
791 assert (status == CAIRO_INT_STATUS_SUCCESS);
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);
799 static cairo_int_status_t
800 _cairo_surface_observer_mask (void *abstract_surface,
802 const cairo_pattern_t *source,
803 const cairo_pattern_t *mask,
804 const cairo_clip_t *clip)
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;
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);
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);
825 status = _cairo_composite_rectangles_init_for_mask (&composite,
829 if (unlikely (status)) {
830 surface->log.mask.noop++;
831 device->log.mask.noop++;
835 midpt (&composite, &x, &y);
837 add_extents (&surface->log.mask.extents, &composite);
838 add_extents (&device->log.mask.extents, &composite);
839 _cairo_composite_rectangles_fini (&composite);
841 t = _cairo_time_get ();
842 status = _cairo_surface_mask (surface->target,
845 if (unlikely (status))
848 sync (surface->target, x, y);
849 t = _cairo_time_get_delta (t);
851 add_record_mask (&surface->log,
852 surface->target, op, source, mask, clip,
854 add_record_mask (&device->log,
855 surface->target, op, source, mask, clip,
858 do_callbacks (surface, &surface->mask_callbacks);
860 return CAIRO_STATUS_SUCCESS;
864 add_record_fill (cairo_observation_t *log,
865 cairo_surface_t *target,
867 const cairo_pattern_t *source,
868 const cairo_path_fixed_t *path,
869 cairo_fill_rule_t fill_rule,
871 cairo_antialias_t antialias,
872 const cairo_clip_t *clip,
873 cairo_time_t elapsed)
875 cairo_observation_record_t record;
876 cairo_int_status_t status;
879 record_fill (&record,
881 path, fill_rule, tolerance, antialias,
885 status = log->record->base.backend->fill (&log->record->base,
888 tolerance, antialias,
890 assert (status == CAIRO_INT_STATUS_SUCCESS);
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);
898 static cairo_int_status_t
899 _cairo_surface_observer_fill (void *abstract_surface,
901 const cairo_pattern_t *source,
902 const cairo_path_fixed_t *path,
903 cairo_fill_rule_t fill_rule,
905 cairo_antialias_t antialias,
906 const cairo_clip_t *clip)
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;
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);
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);
931 status = _cairo_composite_rectangles_init_for_fill (&composite,
935 if (unlikely (status)) {
936 surface->log.fill.noop++;
937 device->log.fill.noop++;
941 midpt (&composite, &x, &y);
943 add_extents (&surface->log.fill.extents, &composite);
944 add_extents (&device->log.fill.extents, &composite);
945 _cairo_composite_rectangles_fini (&composite);
947 t = _cairo_time_get ();
948 status = _cairo_surface_fill (surface->target,
950 fill_rule, tolerance, antialias,
952 if (unlikely (status))
955 sync (surface->target, x, y);
956 t = _cairo_time_get_delta (t);
958 add_record_fill (&surface->log,
959 surface->target, op, source, path,
960 fill_rule, tolerance, antialias,
963 add_record_fill (&device->log,
964 surface->target, op, source, path,
965 fill_rule, tolerance, antialias,
968 do_callbacks (surface, &surface->fill_callbacks);
970 return CAIRO_STATUS_SUCCESS;
974 add_record_stroke (cairo_observation_t *log,
975 cairo_surface_t *target,
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,
983 cairo_antialias_t antialias,
984 const cairo_clip_t *clip,
985 cairo_time_t elapsed)
987 cairo_observation_record_t record;
988 cairo_int_status_t status;
991 record_stroke (&record,
993 path, style, ctm,ctm_inverse,
994 tolerance, antialias,
998 status = log->record->base.backend->stroke (&log->record->base,
1000 path, style, ctm,ctm_inverse,
1001 tolerance, antialias,
1003 assert (status == CAIRO_INT_STATUS_SUCCESS);
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);
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,
1020 cairo_antialias_t antialias,
1021 const cairo_clip_t *clip)
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;
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);
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);
1048 status = _cairo_composite_rectangles_init_for_stroke (&composite,
1053 if (unlikely (status)) {
1054 surface->log.stroke.noop++;
1055 device->log.stroke.noop++;
1059 midpt (&composite, &x, &y);
1061 add_extents (&surface->log.stroke.extents, &composite);
1062 add_extents (&device->log.stroke.extents, &composite);
1063 _cairo_composite_rectangles_fini (&composite);
1065 t = _cairo_time_get ();
1066 status = _cairo_surface_stroke (surface->target,
1068 style, ctm, ctm_inverse,
1069 tolerance, antialias,
1071 if (unlikely (status))
1074 sync (surface->target, x, y);
1075 t = _cairo_time_get_delta (t);
1077 add_record_stroke (&surface->log,
1078 surface->target, op, source, path,
1079 style, ctm,ctm_inverse,
1080 tolerance, antialias,
1083 add_record_stroke (&device->log,
1084 surface->target, op, source, path,
1085 style, ctm,ctm_inverse,
1086 tolerance, antialias,
1089 do_callbacks (surface, &surface->stroke_callbacks);
1091 return CAIRO_STATUS_SUCCESS;
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,
1101 cairo_scaled_font_t *scaled_font,
1102 const cairo_clip_t *clip,
1103 cairo_time_t elapsed)
1105 cairo_observation_record_t record;
1106 cairo_int_status_t status;
1109 record_glyphs (&record,
1111 glyphs, num_glyphs, scaled_font,
1115 status = log->record->base.backend->show_text_glyphs (&log->record->base,
1122 assert (status == CAIRO_INT_STATUS_SUCCESS);
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);
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,
1136 cairo_scaled_font_t *scaled_font,
1137 const cairo_clip_t *clip)
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;
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);
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);
1157 status = _cairo_composite_rectangles_init_for_glyphs (&composite,
1164 if (unlikely (status)) {
1165 surface->log.glyphs.noop++;
1166 device->log.glyphs.noop++;
1170 midpt (&composite, &x, &y);
1172 add_extents (&surface->log.glyphs.extents, &composite);
1173 add_extents (&device->log.glyphs.extents, &composite);
1174 _cairo_composite_rectangles_fini (&composite);
1176 /* XXX We have to copy the glyphs, because the backend is allowed to
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));
1183 t = _cairo_time_get ();
1184 status = _cairo_surface_show_text_glyphs (surface->target, op, source,
1186 dev_glyphs, num_glyphs,
1191 if (unlikely (status))
1194 sync (surface->target, x, y);
1195 t = _cairo_time_get_delta (t);
1197 add_record_glyphs (&surface->log,
1198 surface->target, op, source,
1199 glyphs, num_glyphs, scaled_font,
1202 add_record_glyphs (&device->log,
1203 surface->target, op, source,
1204 glyphs, num_glyphs, scaled_font,
1207 do_callbacks (surface, &surface->glyphs_callbacks);
1209 return CAIRO_STATUS_SUCCESS;
1212 static cairo_status_t
1213 _cairo_surface_observer_flush (void *abstract_surface)
1215 cairo_surface_observer_t *surface = abstract_surface;
1217 do_callbacks (surface, &surface->flush_callbacks);
1219 cairo_surface_flush (surface->target);
1220 return surface->target->status;
1223 static cairo_status_t
1224 _cairo_surface_observer_mark_dirty (void *abstract_surface,
1226 int width, int height)
1228 cairo_surface_observer_t *surface = abstract_surface;
1229 cairo_status_t status;
1231 printf ("mark-dirty (%d, %d) x (%d, %d)\n", x, y, width, height);
1233 status = CAIRO_STATUS_SUCCESS;
1234 if (surface->target->backend->mark_dirty_rectangle)
1235 status = surface->target->backend->mark_dirty_rectangle (surface->target,
1241 static cairo_int_status_t
1242 _cairo_surface_observer_copy_page (void *abstract_surface)
1244 cairo_surface_observer_t *surface = abstract_surface;
1245 cairo_status_t status;
1247 status = CAIRO_STATUS_SUCCESS;
1248 if (surface->target->backend->copy_page)
1249 status = surface->target->backend->copy_page (surface->target);
1254 static cairo_int_status_t
1255 _cairo_surface_observer_show_page (void *abstract_surface)
1257 cairo_surface_observer_t *surface = abstract_surface;
1258 cairo_status_t status;
1260 status = CAIRO_STATUS_SUCCESS;
1261 if (surface->target->backend->show_page)
1262 status = surface->target->backend->show_page (surface->target);
1268 _cairo_surface_observer_get_extents (void *abstract_surface,
1269 cairo_rectangle_int_t *extents)
1271 cairo_surface_observer_t *surface = abstract_surface;
1272 return _cairo_surface_get_extents (surface->target, extents);
1276 _cairo_surface_observer_get_font_options (void *abstract_surface,
1277 cairo_font_options_t *options)
1279 cairo_surface_observer_t *surface = abstract_surface;
1281 if (surface->target->backend->get_font_options != NULL)
1282 surface->target->backend->get_font_options (surface->target, options);
1285 static cairo_surface_t *
1286 _cairo_surface_observer_source (void *abstract_surface,
1287 cairo_rectangle_int_t *extents)
1289 cairo_surface_observer_t *surface = abstract_surface;
1290 return _cairo_surface_get_source (surface->target, extents);
1293 static cairo_status_t
1294 _cairo_surface_observer_acquire_source_image (void *abstract_surface,
1295 cairo_image_surface_t **image_out,
1298 cairo_surface_observer_t *surface = abstract_surface;
1300 surface->log.num_sources_acquired++;
1301 to_device (surface)->log.num_sources_acquired++;
1303 return _cairo_surface_acquire_source_image (surface->target,
1304 image_out, image_extra);
1308 _cairo_surface_observer_release_source_image (void *abstract_surface,
1309 cairo_image_surface_t *image,
1312 cairo_surface_observer_t *surface = abstract_surface;
1314 _cairo_surface_release_source_image (surface->target, image, image_extra);
1317 static cairo_surface_t *
1318 _cairo_surface_observer_snapshot (void *abstract_surface)
1320 cairo_surface_observer_t *surface = abstract_surface;
1322 /* XXX hook onto the snapshot so that we measure number of reads */
1324 if (surface->target->backend->snapshot)
1325 return surface->target->backend->snapshot (surface->target);
1331 _cairo_surface_observer_create_context(void *target)
1333 cairo_surface_observer_t *surface = target;
1335 if (_cairo_surface_is_subsurface (&surface->base))
1336 surface = (cairo_surface_observer_t *)
1337 _cairo_surface_subsurface_get_target (&surface->base);
1339 surface->log.num_contexts++;
1340 to_device (surface)->log.num_contexts++;
1342 return surface->target->backend->create_context (target);
1345 static const cairo_surface_backend_t _cairo_surface_observer_backend = {
1346 CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER,
1347 _cairo_surface_observer_finish,
1349 _cairo_surface_observer_create_context,
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,
1356 _cairo_surface_observer_source,
1357 _cairo_surface_observer_acquire_source_image,
1358 _cairo_surface_observer_release_source_image,
1359 _cairo_surface_observer_snapshot,
1361 _cairo_surface_observer_copy_page,
1362 _cairo_surface_observer_show_page,
1364 _cairo_surface_observer_get_extents,
1365 _cairo_surface_observer_get_font_options,
1367 _cairo_surface_observer_flush,
1368 _cairo_surface_observer_mark_dirty,
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,
1379 * cairo_surface_create_observer:
1380 * @target: an existing surface for which the observer will watch
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.
1386 * Return value: a pointer to the newly allocated surface. The caller
1387 * owns the surface and should call cairo_surface_destroy() when done
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.
1397 cairo_surface_create_observer (cairo_surface_t *target,
1398 cairo_surface_observer_mode_t mode)
1400 cairo_device_t *device;
1401 cairo_surface_t *surface;
1402 cairo_bool_t record;
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));
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);
1414 surface = _cairo_surface_create_observer_internal (device, target);
1415 cairo_device_destroy (device);
1420 static cairo_status_t
1421 _cairo_surface_observer_add_callback (cairo_list_t *head,
1422 cairo_surface_observer_callback_t func,
1425 struct callback_list *cb;
1427 cb = malloc (sizeof (*cb));
1428 if (unlikely (cb == NULL))
1429 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1431 cairo_list_add (&cb->link, head);
1435 return CAIRO_STATUS_SUCCESS;
1439 cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface,
1440 cairo_surface_observer_callback_t func,
1443 cairo_surface_observer_t *surface;
1445 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1446 return abstract_surface->status;
1448 if (! _cairo_surface_is_observer (abstract_surface))
1449 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1451 surface = (cairo_surface_observer_t *)abstract_surface;
1452 return _cairo_surface_observer_add_callback (&surface->paint_callbacks,
1457 cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface,
1458 cairo_surface_observer_callback_t func,
1461 cairo_surface_observer_t *surface;
1463 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1464 return abstract_surface->status;
1466 if (! _cairo_surface_is_observer (abstract_surface))
1467 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1469 surface = (cairo_surface_observer_t *)abstract_surface;
1470 return _cairo_surface_observer_add_callback (&surface->mask_callbacks,
1475 cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface,
1476 cairo_surface_observer_callback_t func,
1479 cairo_surface_observer_t *surface;
1481 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1482 return abstract_surface->status;
1484 if (! _cairo_surface_is_observer (abstract_surface))
1485 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1487 surface = (cairo_surface_observer_t *)abstract_surface;
1488 return _cairo_surface_observer_add_callback (&surface->fill_callbacks,
1493 cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface,
1494 cairo_surface_observer_callback_t func,
1497 cairo_surface_observer_t *surface;
1499 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1500 return abstract_surface->status;
1502 if (! _cairo_surface_is_observer (abstract_surface))
1503 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1505 surface = (cairo_surface_observer_t *)abstract_surface;
1506 return _cairo_surface_observer_add_callback (&surface->stroke_callbacks,
1511 cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface,
1512 cairo_surface_observer_callback_t func,
1515 cairo_surface_observer_t *surface;
1517 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1518 return abstract_surface->status;
1520 if (! _cairo_surface_is_observer (abstract_surface))
1521 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1523 surface = (cairo_surface_observer_t *)abstract_surface;
1524 return _cairo_surface_observer_add_callback (&surface->glyphs_callbacks,
1529 cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface,
1530 cairo_surface_observer_callback_t func,
1533 cairo_surface_observer_t *surface;
1535 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1536 return abstract_surface->status;
1538 if (! _cairo_surface_is_observer (abstract_surface))
1539 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1541 surface = (cairo_surface_observer_t *)abstract_surface;
1542 return _cairo_surface_observer_add_callback (&surface->flush_callbacks,
1547 cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface,
1548 cairo_surface_observer_callback_t func,
1551 cairo_surface_observer_t *surface;
1553 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1554 return abstract_surface->status;
1556 if (! _cairo_surface_is_observer (abstract_surface))
1557 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1559 surface = (cairo_surface_observer_t *)abstract_surface;
1560 return _cairo_surface_observer_add_callback (&surface->finish_callbacks,
1565 print_extents (cairo_output_stream_t *stream, const struct extents *e)
1567 _cairo_output_stream_printf (stream,
1568 " extents: total %g, avg %g [unbounded %d]\n",
1570 e->area.sum / e->area.count,
1574 static inline int ordercmp (int a, int b, const unsigned int *array)
1577 return array[b] - array[a];
1579 CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_order, int, ordercmp)
1582 print_array (cairo_output_stream_t *stream,
1583 const unsigned int *array,
1590 assert (count < ARRAY_LENGTH (order));
1591 for (i = j = 0; i < count; i++) {
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 ? "," : "");
1603 static const char *operator_names[] = {
1604 "CLEAR", /* CAIRO_OPERATOR_CLEAR */
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 */
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 */
1618 "XOR", /* CAIRO_OPERATOR_XOR */
1619 "ADD", /* CAIRO_OPERATOR_ADD */
1620 "SATURATE", /* CAIRO_OPERATOR_SATURATE */
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 */
1639 print_operators (cairo_output_stream_t *stream, unsigned int *array)
1641 _cairo_output_stream_printf (stream, " op:");
1642 print_array (stream, array, operator_names, NUM_OPERATORS);
1643 _cairo_output_stream_printf (stream, "\n");
1646 static const char *fill_rule_names[] = {
1651 print_fill_rule (cairo_output_stream_t *stream, unsigned int *array)
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");
1658 static const char *cap_names[] = {
1659 "butt", /* CAIRO_LINE_CAP_BUTT */
1660 "round", /* CAIRO_LINE_CAP_ROUND */
1661 "square" /* CAIRO_LINE_CAP_SQUARE */
1664 print_line_caps (cairo_output_stream_t *stream, unsigned int *array)
1666 _cairo_output_stream_printf (stream, " caps:");
1667 print_array (stream, array, cap_names, NUM_CAPS);
1668 _cairo_output_stream_printf (stream, "\n");
1671 static const char *join_names[] = {
1672 "miter", /* CAIRO_LINE_JOIN_MITER */
1673 "round", /* CAIRO_LINE_JOIN_ROUND */
1674 "bevel", /* CAIRO_LINE_JOIN_BEVEL */
1677 print_line_joins (cairo_output_stream_t *stream, unsigned int *array)
1679 _cairo_output_stream_printf (stream, " joins:");
1680 print_array (stream, array, join_names, NUM_JOINS);
1681 _cairo_output_stream_printf (stream, "\n");
1684 static const char *antialias_names[] = {
1694 print_antialias (cairo_output_stream_t *stream, unsigned int *array)
1696 _cairo_output_stream_printf (stream, " antialias:");
1697 print_array (stream, array, antialias_names, NUM_ANTIALIAS);
1698 _cairo_output_stream_printf (stream, "\n");
1701 static const char *pattern_names[] = {
1712 print_pattern (cairo_output_stream_t *stream,
1714 const struct pattern *p)
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");
1721 static const char *path_names[] = {
1729 print_path (cairo_output_stream_t *stream,
1730 const struct path *p)
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");
1737 static const char *clip_names[] = {
1746 print_clip (cairo_output_stream_t *stream, const struct clip *c)
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");
1754 print_record (cairo_output_stream_t *stream,
1755 cairo_observation_record_t *r)
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]);
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",
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));
1780 static double percent (cairo_time_t a, cairo_time_t b)
1783 return _cairo_round (_cairo_time_to_s (a) * 1000 /
1784 _cairo_time_to_s (b)) / 10;
1788 replay_record (cairo_observation_t *log,
1789 cairo_observation_record_t *r,
1790 cairo_device_t *script)
1792 #if CAIRO_HAS_SCRIPT_SURFACE
1793 cairo_surface_t *surface;
1794 cairo_int_status_t status;
1796 if (log->record == NULL || script == NULL)
1799 surface = cairo_script_surface_create (script,
1804 _cairo_recording_surface_replay_one (log->record, r->index, surface);
1805 cairo_surface_destroy (surface);
1807 assert (status == CAIRO_INT_STATUS_SUCCESS);
1816 _cairo_observation_total_elapsed (cairo_observation_t *log)
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);
1830 _cairo_observation_print (cairo_output_stream_t *stream,
1831 cairo_observation_t *log)
1833 cairo_device_t *script;
1836 #if CAIRO_HAS_SCRIPT_SURFACE
1837 script = _cairo_script_context_create_internal (stream);
1838 _cairo_script_context_attach_snapshots (script, FALSE);
1843 total = _cairo_observation_total_elapsed (log);
1845 _cairo_output_stream_printf (stream, "elapsed: %f\n",
1846 _cairo_time_to_ns (total));
1847 _cairo_output_stream_printf (stream, "surfaces: %d\n",
1849 _cairo_output_stream_printf (stream, "contexts: %d\n",
1851 _cairo_output_stream_printf (stream, "sources acquired: %d\n",
1852 log->num_sources_acquired);
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);
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);
1870 _cairo_output_stream_printf (stream, "\n");
1871 if (replay_record (log, &log->paint.slowest, script))
1872 _cairo_output_stream_printf (stream, "\n\n");
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);
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);
1891 _cairo_output_stream_printf (stream, "\n");
1892 if (replay_record (log, &log->mask.slowest, script))
1893 _cairo_output_stream_printf (stream, "\n\n");
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);
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);
1914 _cairo_output_stream_printf (stream, "\n");
1915 if (replay_record (log, &log->fill.slowest, script))
1916 _cairo_output_stream_printf (stream, "\n\n");
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);
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);
1938 _cairo_output_stream_printf (stream, "\n");
1939 if (replay_record (log, &log->stroke.slowest, script))
1940 _cairo_output_stream_printf (stream, "\n\n");
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);
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);
1958 _cairo_output_stream_printf (stream, "\n");
1959 if (replay_record (log, &log->glyphs.slowest, script))
1960 _cairo_output_stream_printf (stream, "\n\n");
1963 cairo_device_destroy (script);
1967 cairo_surface_observer_print (cairo_surface_t *abstract_surface,
1968 cairo_write_func_t write_func,
1971 cairo_output_stream_t *stream;
1972 cairo_surface_observer_t *surface;
1974 if (unlikely (abstract_surface->status))
1975 return abstract_surface->status;
1977 if (unlikely (! _cairo_surface_is_observer (abstract_surface)))
1978 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1980 surface = (cairo_surface_observer_t *) abstract_surface;
1982 stream = _cairo_output_stream_create (write_func, NULL, closure);
1983 _cairo_observation_print (stream, &surface->log);
1984 return _cairo_output_stream_destroy (stream);
1988 cairo_surface_observer_elapsed (cairo_surface_t *abstract_surface)
1990 cairo_surface_observer_t *surface;
1992 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1995 if (! _cairo_surface_is_observer (abstract_surface))
1998 surface = (cairo_surface_observer_t *) abstract_surface;
1999 return _cairo_time_to_ns (_cairo_observation_total_elapsed (&surface->log));
2003 cairo_device_observer_print (cairo_device_t *abstract_device,
2004 cairo_write_func_t write_func,
2007 cairo_output_stream_t *stream;
2008 cairo_device_observer_t *device;
2010 if (unlikely (abstract_device->status))
2011 return abstract_device->status;
2013 if (unlikely (! _cairo_device_is_observer (abstract_device)))
2014 return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
2016 device = (cairo_device_observer_t *) abstract_device;
2018 stream = _cairo_output_stream_create (write_func, NULL, closure);
2019 _cairo_observation_print (stream, &device->log);
2020 return _cairo_output_stream_destroy (stream);
2024 cairo_device_observer_elapsed (cairo_device_t *abstract_device)
2026 cairo_device_observer_t *device;
2028 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2031 if (! _cairo_device_is_observer (abstract_device))
2034 device = (cairo_device_observer_t *) abstract_device;
2035 return _cairo_time_to_ns (_cairo_observation_total_elapsed (&device->log));
2039 cairo_device_observer_paint_elapsed (cairo_device_t *abstract_device)
2041 cairo_device_observer_t *device;
2043 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2046 if (! _cairo_device_is_observer (abstract_device))
2049 device = (cairo_device_observer_t *) abstract_device;
2050 return _cairo_time_to_ns (device->log.paint.elapsed);
2054 cairo_device_observer_mask_elapsed (cairo_device_t *abstract_device)
2056 cairo_device_observer_t *device;
2058 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2061 if (! _cairo_device_is_observer (abstract_device))
2064 device = (cairo_device_observer_t *) abstract_device;
2065 return _cairo_time_to_ns (device->log.mask.elapsed);
2069 cairo_device_observer_fill_elapsed (cairo_device_t *abstract_device)
2071 cairo_device_observer_t *device;
2073 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2076 if (! _cairo_device_is_observer (abstract_device))
2079 device = (cairo_device_observer_t *) abstract_device;
2080 return _cairo_time_to_ns (device->log.fill.elapsed);
2084 cairo_device_observer_stroke_elapsed (cairo_device_t *abstract_device)
2086 cairo_device_observer_t *device;
2088 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2091 if (! _cairo_device_is_observer (abstract_device))
2094 device = (cairo_device_observer_t *) abstract_device;
2095 return _cairo_time_to_ns (device->log.stroke.elapsed);
2099 cairo_device_observer_glyphs_elapsed (cairo_device_t *abstract_device)
2101 cairo_device_observer_t *device;
2103 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2106 if (! _cairo_device_is_observer (abstract_device))
2109 device = (cairo_device_observer_t *) abstract_device;
2110 return _cairo_time_to_ns (device->log.glyphs.elapsed);