beta-0.89.2
[luatex.git] / source / libs / cairo / cairo-src / src / cairo-surface-observer.c
blob52b013644dbd9a829da7fe864a5c3b192e566985
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.
32 * Contributor(s):
33 * Chris Wilson <chris@chris-wilson.co.uk>
36 #include "cairoint.h"
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"
55 #endif
57 static const cairo_surface_backend_t _cairo_surface_observer_backend;
59 /* observation/stats */
61 static void init_stats (struct stat *s)
63 s->min = HUGE_VAL;
64 s->max = -HUGE_VAL;
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);
88 init_clip (&p->clip);
91 static void init_mask (struct mask *m)
93 init_extents (&m->extents);
94 init_pattern (&m->source);
95 init_pattern (&m->mask);
96 init_clip (&m->clip);
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,
124 cairo_bool_t record)
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));
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;
142 log->record->optimize_clears = FALSE;
145 return CAIRO_STATUS_SUCCESS;
148 static void
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;
161 static int
162 classify_pattern (const cairo_pattern_t *pattern,
163 const cairo_surface_t *target)
165 int classify;
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;
193 return classify;
196 static void
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)]++;
204 static int
205 classify_path (const cairo_path_fixed_t *path,
206 cairo_bool_t is_fill)
208 int classify;
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;
221 if (classify == -1)
222 classify = 3 + (path->has_curve_to != 0);
224 return classify;
227 static void
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)]++;
235 static int
236 classify_clip (const cairo_clip_t *clip)
238 int classify;
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;
253 return classify;
256 static void
257 add_clip (struct clip *stats,
258 const cairo_clip_t *clip)
260 stats->type[classify_clip (clip)]++;
263 static void
264 stats_add (struct stat *s, double v)
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++;
275 static void
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 */
287 static void
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);
298 static void
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;
317 static void
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);
325 static void
326 _cairo_device_observer_destroy (void *_device)
328 cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
329 cairo_device_destroy (device->target);
330 free (device);
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,
346 cairo_bool_t record)
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)) {
358 free (device);
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,
388 target->content);
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);
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;
416 static inline void
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;
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);
455 surface = _cairo_surface_create_observer_internal (other->base.device,
456 target);
457 cairo_surface_destroy (target);
459 return surface;
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,
471 format,
472 width, height);
474 return NULL;
477 static cairo_image_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;
482 return _cairo_surface_map_to_image (surface->target, extents);
485 static cairo_int_status_t
486 _cairo_surface_observer_unmap_image (void *abstract_surface,
487 cairo_image_surface_t *image)
489 cairo_surface_observer_t *surface = abstract_surface;
490 return _cairo_surface_unmap_image (surface->target, image);
493 static void
494 record_target (cairo_observation_record_t *r,
495 cairo_surface_t *target)
497 cairo_rectangle_int_t extents;
499 r->target_content = target->content;
500 if (_cairo_surface_get_extents (target, &extents)) {
501 r->target_width = extents.width;
502 r->target_height = extents.height;
503 } else {
504 r->target_width = -1;
505 r->target_height = -1;
509 static cairo_observation_record_t *
510 record_paint (cairo_observation_record_t *r,
511 cairo_surface_t *target,
512 cairo_operator_t op,
513 const cairo_pattern_t *source,
514 const cairo_clip_t *clip,
515 cairo_time_t elapsed)
517 record_target (r, target);
519 r->op = op;
520 r->source = classify_pattern (source, target);
521 r->mask = -1;
522 r->num_glyphs = -1;
523 r->path = -1;
524 r->fill_rule = -1;
525 r->tolerance = -1;
526 r->antialias = -1;
527 r->clip = classify_clip (clip);
528 r->elapsed = elapsed;
530 return r;
533 static cairo_observation_record_t *
534 record_mask (cairo_observation_record_t *r,
535 cairo_surface_t *target,
536 cairo_operator_t op,
537 const cairo_pattern_t *source,
538 const cairo_pattern_t *mask,
539 const cairo_clip_t *clip,
540 cairo_time_t elapsed)
542 record_target (r, target);
544 r->op = op;
545 r->source = classify_pattern (source, target);
546 r->mask = classify_pattern (mask, target);
547 r->num_glyphs = -1;
548 r->path = -1;
549 r->fill_rule = -1;
550 r->tolerance = -1;
551 r->antialias = -1;
552 r->clip = classify_clip (clip);
553 r->elapsed = elapsed;
555 return r;
558 static cairo_observation_record_t *
559 record_fill (cairo_observation_record_t *r,
560 cairo_surface_t *target,
561 cairo_operator_t op,
562 const cairo_pattern_t *source,
563 const cairo_path_fixed_t *path,
564 cairo_fill_rule_t fill_rule,
565 double tolerance,
566 cairo_antialias_t antialias,
567 const cairo_clip_t *clip,
568 cairo_time_t elapsed)
570 record_target (r, target);
572 r->op = op;
573 r->source = classify_pattern (source, target);
574 r->mask = -1;
575 r->num_glyphs = -1;
576 r->path = classify_path (path, TRUE);
577 r->fill_rule = fill_rule;
578 r->tolerance = tolerance;
579 r->antialias = antialias;
580 r->clip = classify_clip (clip);
581 r->elapsed = elapsed;
583 return r;
586 static cairo_observation_record_t *
587 record_stroke (cairo_observation_record_t *r,
588 cairo_surface_t *target,
589 cairo_operator_t op,
590 const cairo_pattern_t *source,
591 const cairo_path_fixed_t *path,
592 const cairo_stroke_style_t *style,
593 const cairo_matrix_t *ctm,
594 const cairo_matrix_t *ctm_inverse,
595 double tolerance,
596 cairo_antialias_t antialias,
597 const cairo_clip_t *clip,
598 cairo_time_t elapsed)
600 record_target (r, target);
602 r->op = op;
603 r->source = classify_pattern (source, target);
604 r->mask = -1;
605 r->num_glyphs = -1;
606 r->path = classify_path (path, FALSE);
607 r->fill_rule = -1;
608 r->tolerance = tolerance;
609 r->antialias = antialias;
610 r->clip = classify_clip (clip);
611 r->elapsed = elapsed;
613 return r;
616 static cairo_observation_record_t *
617 record_glyphs (cairo_observation_record_t *r,
618 cairo_surface_t *target,
619 cairo_operator_t op,
620 const cairo_pattern_t *source,
621 cairo_glyph_t *glyphs,
622 int num_glyphs,
623 cairo_scaled_font_t *scaled_font,
624 const cairo_clip_t *clip,
625 cairo_time_t elapsed)
627 record_target (r, target);
629 r->op = op;
630 r->source = classify_pattern (source, target);
631 r->mask = -1;
632 r->path = -1;
633 r->num_glyphs = num_glyphs;
634 r->fill_rule = -1;
635 r->tolerance = -1;
636 r->antialias = -1;
637 r->clip = classify_clip (clip);
638 r->elapsed = elapsed;
640 return r;
643 static void
644 add_record (cairo_observation_t *log,
645 cairo_observation_record_t *r)
647 cairo_int_status_t status;
649 r->index = log->record ? log->record->commands.num_elements : 0;
651 status = _cairo_array_append (&log->timings, r);
652 assert (status == CAIRO_INT_STATUS_SUCCESS);
655 static void
656 _cairo_surface_sync (cairo_surface_t *target, int x, int y)
658 cairo_rectangle_int_t extents;
660 extents.x = x;
661 extents.y = y;
662 extents.width = 1;
663 extents.height = 1;
665 _cairo_surface_unmap_image (target,
666 _cairo_surface_map_to_image (target,
667 &extents));
670 static void
671 midpt (const cairo_composite_rectangles_t *extents, int *x, int *y)
673 *x = extents->bounded.x + extents->bounded.width / 2;
674 *y = extents->bounded.y + extents->bounded.height / 2;
677 static void
678 add_record_paint (cairo_observation_t *log,
679 cairo_surface_t *target,
680 cairo_operator_t op,
681 const cairo_pattern_t *source,
682 const cairo_clip_t *clip,
683 cairo_time_t elapsed)
685 cairo_observation_record_t record;
686 cairo_int_status_t status;
688 add_record (log,
689 record_paint (&record, target, op, source, clip, elapsed));
691 /* We have to bypass the high-level surface layer in case it tries to be
692 * too smart and discard operations; we need to record exactly what just
693 * happened on the target.
695 if (log->record) {
696 status = log->record->base.backend->paint (&log->record->base,
697 op, source, clip);
698 assert (status == CAIRO_INT_STATUS_SUCCESS);
701 if (_cairo_time_gt (elapsed, log->paint.slowest.elapsed))
702 log->paint.slowest = record;
703 log->paint.elapsed = _cairo_time_add (log->paint.elapsed, elapsed);
706 static cairo_int_status_t
707 _cairo_surface_observer_paint (void *abstract_surface,
708 cairo_operator_t op,
709 const cairo_pattern_t *source,
710 const cairo_clip_t *clip)
712 cairo_surface_observer_t *surface = abstract_surface;
713 cairo_device_observer_t *device = to_device (surface);
714 cairo_composite_rectangles_t composite;
715 cairo_int_status_t status;
716 cairo_time_t t;
717 int x, y;
719 /* XXX device locking */
721 surface->log.paint.count++;
722 surface->log.paint.operators[op]++;
723 add_pattern (&surface->log.paint.source, source, surface->target);
724 add_clip (&surface->log.paint.clip, clip);
726 device->log.paint.count++;
727 device->log.paint.operators[op]++;
728 add_pattern (&device->log.paint.source, source, surface->target);
729 add_clip (&device->log.paint.clip, clip);
731 status = _cairo_composite_rectangles_init_for_paint (&composite,
732 surface->target,
733 op, source,
734 clip);
735 if (unlikely (status)) {
736 surface->log.paint.noop++;
737 device->log.paint.noop++;
738 return status;
741 midpt (&composite, &x, &y);
743 add_extents (&surface->log.paint.extents, &composite);
744 add_extents (&device->log.paint.extents, &composite);
745 _cairo_composite_rectangles_fini (&composite);
747 t = _cairo_time_get ();
748 status = _cairo_surface_paint (surface->target,
749 op, source,
750 clip);
751 if (unlikely (status))
752 return status;
754 _cairo_surface_sync (surface->target, x, y);
755 t = _cairo_time_get_delta (t);
757 add_record_paint (&surface->log, surface->target, op, source, clip, t);
758 add_record_paint (&device->log, surface->target, op, source, clip, t);
760 do_callbacks (surface, &surface->paint_callbacks);
762 return CAIRO_STATUS_SUCCESS;
765 static void
766 add_record_mask (cairo_observation_t *log,
767 cairo_surface_t *target,
768 cairo_operator_t op,
769 const cairo_pattern_t *source,
770 const cairo_pattern_t *mask,
771 const cairo_clip_t *clip,
772 cairo_time_t elapsed)
774 cairo_observation_record_t record;
775 cairo_int_status_t status;
777 add_record (log,
778 record_mask (&record, target, op, source, mask, clip, elapsed));
780 if (log->record) {
781 status = log->record->base.backend->mask (&log->record->base,
782 op, source, mask, clip);
783 assert (status == CAIRO_INT_STATUS_SUCCESS);
786 if (_cairo_time_gt (elapsed, log->mask.slowest.elapsed))
787 log->mask.slowest = record;
788 log->mask.elapsed = _cairo_time_add (log->mask.elapsed, elapsed);
791 static cairo_int_status_t
792 _cairo_surface_observer_mask (void *abstract_surface,
793 cairo_operator_t op,
794 const cairo_pattern_t *source,
795 const cairo_pattern_t *mask,
796 const cairo_clip_t *clip)
798 cairo_surface_observer_t *surface = abstract_surface;
799 cairo_device_observer_t *device = to_device (surface);
800 cairo_composite_rectangles_t composite;
801 cairo_int_status_t status;
802 cairo_time_t t;
803 int x, y;
805 surface->log.mask.count++;
806 surface->log.mask.operators[op]++;
807 add_pattern (&surface->log.mask.source, source, surface->target);
808 add_pattern (&surface->log.mask.mask, mask, surface->target);
809 add_clip (&surface->log.mask.clip, clip);
811 device->log.mask.count++;
812 device->log.mask.operators[op]++;
813 add_pattern (&device->log.mask.source, source, surface->target);
814 add_pattern (&device->log.mask.mask, mask, surface->target);
815 add_clip (&device->log.mask.clip, clip);
817 status = _cairo_composite_rectangles_init_for_mask (&composite,
818 surface->target,
819 op, source, mask,
820 clip);
821 if (unlikely (status)) {
822 surface->log.mask.noop++;
823 device->log.mask.noop++;
824 return status;
827 midpt (&composite, &x, &y);
829 add_extents (&surface->log.mask.extents, &composite);
830 add_extents (&device->log.mask.extents, &composite);
831 _cairo_composite_rectangles_fini (&composite);
833 t = _cairo_time_get ();
834 status = _cairo_surface_mask (surface->target,
835 op, source, mask,
836 clip);
837 if (unlikely (status))
838 return status;
840 _cairo_surface_sync (surface->target, x, y);
841 t = _cairo_time_get_delta (t);
843 add_record_mask (&surface->log,
844 surface->target, op, source, mask, clip,
846 add_record_mask (&device->log,
847 surface->target, op, source, mask, clip,
850 do_callbacks (surface, &surface->mask_callbacks);
852 return CAIRO_STATUS_SUCCESS;
855 static void
856 add_record_fill (cairo_observation_t *log,
857 cairo_surface_t *target,
858 cairo_operator_t op,
859 const cairo_pattern_t *source,
860 const cairo_path_fixed_t *path,
861 cairo_fill_rule_t fill_rule,
862 double tolerance,
863 cairo_antialias_t antialias,
864 const cairo_clip_t *clip,
865 cairo_time_t elapsed)
867 cairo_observation_record_t record;
868 cairo_int_status_t status;
870 add_record (log,
871 record_fill (&record,
872 target, op, source,
873 path, fill_rule, tolerance, antialias,
874 clip, elapsed));
876 if (log->record) {
877 status = log->record->base.backend->fill (&log->record->base,
878 op, source,
879 path, fill_rule,
880 tolerance, antialias,
881 clip);
882 assert (status == CAIRO_INT_STATUS_SUCCESS);
885 if (_cairo_time_gt (elapsed, log->fill.slowest.elapsed))
886 log->fill.slowest = record;
887 log->fill.elapsed = _cairo_time_add (log->fill.elapsed, elapsed);
890 static cairo_int_status_t
891 _cairo_surface_observer_fill (void *abstract_surface,
892 cairo_operator_t op,
893 const cairo_pattern_t *source,
894 const cairo_path_fixed_t *path,
895 cairo_fill_rule_t fill_rule,
896 double tolerance,
897 cairo_antialias_t antialias,
898 const cairo_clip_t *clip)
900 cairo_surface_observer_t *surface = abstract_surface;
901 cairo_device_observer_t *device = to_device (surface);
902 cairo_composite_rectangles_t composite;
903 cairo_int_status_t status;
904 cairo_time_t t;
905 int x, y;
907 surface->log.fill.count++;
908 surface->log.fill.operators[op]++;
909 surface->log.fill.fill_rule[fill_rule]++;
910 surface->log.fill.antialias[antialias]++;
911 add_pattern (&surface->log.fill.source, source, surface->target);
912 add_path (&surface->log.fill.path, path, TRUE);
913 add_clip (&surface->log.fill.clip, clip);
915 device->log.fill.count++;
916 device->log.fill.operators[op]++;
917 device->log.fill.fill_rule[fill_rule]++;
918 device->log.fill.antialias[antialias]++;
919 add_pattern (&device->log.fill.source, source, surface->target);
920 add_path (&device->log.fill.path, path, TRUE);
921 add_clip (&device->log.fill.clip, clip);
923 status = _cairo_composite_rectangles_init_for_fill (&composite,
924 surface->target,
925 op, source, path,
926 clip);
927 if (unlikely (status)) {
928 surface->log.fill.noop++;
929 device->log.fill.noop++;
930 return status;
933 midpt (&composite, &x, &y);
935 add_extents (&surface->log.fill.extents, &composite);
936 add_extents (&device->log.fill.extents, &composite);
937 _cairo_composite_rectangles_fini (&composite);
939 t = _cairo_time_get ();
940 status = _cairo_surface_fill (surface->target,
941 op, source, path,
942 fill_rule, tolerance, antialias,
943 clip);
944 if (unlikely (status))
945 return status;
947 _cairo_surface_sync (surface->target, x, y);
948 t = _cairo_time_get_delta (t);
950 add_record_fill (&surface->log,
951 surface->target, op, source, path,
952 fill_rule, tolerance, antialias,
953 clip, t);
955 add_record_fill (&device->log,
956 surface->target, op, source, path,
957 fill_rule, tolerance, antialias,
958 clip, t);
960 do_callbacks (surface, &surface->fill_callbacks);
962 return CAIRO_STATUS_SUCCESS;
965 static void
966 add_record_stroke (cairo_observation_t *log,
967 cairo_surface_t *target,
968 cairo_operator_t op,
969 const cairo_pattern_t *source,
970 const cairo_path_fixed_t *path,
971 const cairo_stroke_style_t *style,
972 const cairo_matrix_t *ctm,
973 const cairo_matrix_t *ctm_inverse,
974 double tolerance,
975 cairo_antialias_t antialias,
976 const cairo_clip_t *clip,
977 cairo_time_t elapsed)
979 cairo_observation_record_t record;
980 cairo_int_status_t status;
982 add_record (log,
983 record_stroke (&record,
984 target, op, source,
985 path, style, ctm,ctm_inverse,
986 tolerance, antialias,
987 clip, elapsed));
989 if (log->record) {
990 status = log->record->base.backend->stroke (&log->record->base,
991 op, source,
992 path, style, ctm,ctm_inverse,
993 tolerance, antialias,
994 clip);
995 assert (status == CAIRO_INT_STATUS_SUCCESS);
998 if (_cairo_time_gt (elapsed, log->stroke.slowest.elapsed))
999 log->stroke.slowest = record;
1000 log->stroke.elapsed = _cairo_time_add (log->stroke.elapsed, elapsed);
1003 static cairo_int_status_t
1004 _cairo_surface_observer_stroke (void *abstract_surface,
1005 cairo_operator_t op,
1006 const cairo_pattern_t *source,
1007 const cairo_path_fixed_t *path,
1008 const cairo_stroke_style_t *style,
1009 const cairo_matrix_t *ctm,
1010 const cairo_matrix_t *ctm_inverse,
1011 double tolerance,
1012 cairo_antialias_t antialias,
1013 const cairo_clip_t *clip)
1015 cairo_surface_observer_t *surface = abstract_surface;
1016 cairo_device_observer_t *device = to_device (surface);
1017 cairo_composite_rectangles_t composite;
1018 cairo_int_status_t status;
1019 cairo_time_t t;
1020 int x, y;
1022 surface->log.stroke.count++;
1023 surface->log.stroke.operators[op]++;
1024 surface->log.stroke.antialias[antialias]++;
1025 surface->log.stroke.caps[style->line_cap]++;
1026 surface->log.stroke.joins[style->line_join]++;
1027 add_pattern (&surface->log.stroke.source, source, surface->target);
1028 add_path (&surface->log.stroke.path, path, FALSE);
1029 add_clip (&surface->log.stroke.clip, clip);
1031 device->log.stroke.count++;
1032 device->log.stroke.operators[op]++;
1033 device->log.stroke.antialias[antialias]++;
1034 device->log.stroke.caps[style->line_cap]++;
1035 device->log.stroke.joins[style->line_join]++;
1036 add_pattern (&device->log.stroke.source, source, surface->target);
1037 add_path (&device->log.stroke.path, path, FALSE);
1038 add_clip (&device->log.stroke.clip, clip);
1040 status = _cairo_composite_rectangles_init_for_stroke (&composite,
1041 surface->target,
1042 op, source,
1043 path, style, ctm,
1044 clip);
1045 if (unlikely (status)) {
1046 surface->log.stroke.noop++;
1047 device->log.stroke.noop++;
1048 return status;
1051 midpt (&composite, &x, &y);
1053 add_extents (&surface->log.stroke.extents, &composite);
1054 add_extents (&device->log.stroke.extents, &composite);
1055 _cairo_composite_rectangles_fini (&composite);
1057 t = _cairo_time_get ();
1058 status = _cairo_surface_stroke (surface->target,
1059 op, source, path,
1060 style, ctm, ctm_inverse,
1061 tolerance, antialias,
1062 clip);
1063 if (unlikely (status))
1064 return status;
1066 _cairo_surface_sync (surface->target, x, y);
1067 t = _cairo_time_get_delta (t);
1069 add_record_stroke (&surface->log,
1070 surface->target, op, source, path,
1071 style, ctm,ctm_inverse,
1072 tolerance, antialias,
1073 clip, t);
1075 add_record_stroke (&device->log,
1076 surface->target, op, source, path,
1077 style, ctm,ctm_inverse,
1078 tolerance, antialias,
1079 clip, t);
1081 do_callbacks (surface, &surface->stroke_callbacks);
1083 return CAIRO_STATUS_SUCCESS;
1086 static void
1087 add_record_glyphs (cairo_observation_t *log,
1088 cairo_surface_t *target,
1089 cairo_operator_t op,
1090 const cairo_pattern_t*source,
1091 cairo_glyph_t *glyphs,
1092 int num_glyphs,
1093 cairo_scaled_font_t *scaled_font,
1094 const cairo_clip_t *clip,
1095 cairo_time_t elapsed)
1097 cairo_observation_record_t record;
1098 cairo_int_status_t status;
1100 add_record (log,
1101 record_glyphs (&record,
1102 target, op, source,
1103 glyphs, num_glyphs, scaled_font,
1104 clip, elapsed));
1106 if (log->record) {
1107 status = log->record->base.backend->show_text_glyphs (&log->record->base,
1108 op, source,
1109 NULL, 0,
1110 glyphs, num_glyphs,
1111 NULL, 0, 0,
1112 scaled_font,
1113 clip);
1114 assert (status == CAIRO_INT_STATUS_SUCCESS);
1117 if (_cairo_time_gt (elapsed, log->glyphs.slowest.elapsed))
1118 log->glyphs.slowest = record;
1119 log->glyphs.elapsed = _cairo_time_add (log->glyphs.elapsed, elapsed);
1122 static cairo_int_status_t
1123 _cairo_surface_observer_glyphs (void *abstract_surface,
1124 cairo_operator_t op,
1125 const cairo_pattern_t *source,
1126 cairo_glyph_t *glyphs,
1127 int num_glyphs,
1128 cairo_scaled_font_t *scaled_font,
1129 const cairo_clip_t *clip)
1131 cairo_surface_observer_t *surface = abstract_surface;
1132 cairo_device_observer_t *device = to_device (surface);
1133 cairo_composite_rectangles_t composite;
1134 cairo_int_status_t status;
1135 cairo_glyph_t *dev_glyphs;
1136 cairo_time_t t;
1137 int x, y;
1139 surface->log.glyphs.count++;
1140 surface->log.glyphs.operators[op]++;
1141 add_pattern (&surface->log.glyphs.source, source, surface->target);
1142 add_clip (&surface->log.glyphs.clip, clip);
1144 device->log.glyphs.count++;
1145 device->log.glyphs.operators[op]++;
1146 add_pattern (&device->log.glyphs.source, source, surface->target);
1147 add_clip (&device->log.glyphs.clip, clip);
1149 status = _cairo_composite_rectangles_init_for_glyphs (&composite,
1150 surface->target,
1151 op, source,
1152 scaled_font,
1153 glyphs, num_glyphs,
1154 clip,
1155 NULL);
1156 if (unlikely (status)) {
1157 surface->log.glyphs.noop++;
1158 device->log.glyphs.noop++;
1159 return status;
1162 midpt (&composite, &x, &y);
1164 add_extents (&surface->log.glyphs.extents, &composite);
1165 add_extents (&device->log.glyphs.extents, &composite);
1166 _cairo_composite_rectangles_fini (&composite);
1168 /* XXX We have to copy the glyphs, because the backend is allowed to
1169 * modify! */
1170 dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
1171 if (unlikely (dev_glyphs == NULL))
1172 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1173 memcpy (dev_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t));
1175 t = _cairo_time_get ();
1176 status = _cairo_surface_show_text_glyphs (surface->target, op, source,
1177 NULL, 0,
1178 dev_glyphs, num_glyphs,
1179 NULL, 0, 0,
1180 scaled_font,
1181 clip);
1182 free (dev_glyphs);
1183 if (unlikely (status))
1184 return status;
1186 _cairo_surface_sync (surface->target, x, y);
1187 t = _cairo_time_get_delta (t);
1189 add_record_glyphs (&surface->log,
1190 surface->target, op, source,
1191 glyphs, num_glyphs, scaled_font,
1192 clip, t);
1194 add_record_glyphs (&device->log,
1195 surface->target, op, source,
1196 glyphs, num_glyphs, scaled_font,
1197 clip, t);
1199 do_callbacks (surface, &surface->glyphs_callbacks);
1201 return CAIRO_STATUS_SUCCESS;
1204 static cairo_status_t
1205 _cairo_surface_observer_flush (void *abstract_surface, unsigned flags)
1207 cairo_surface_observer_t *surface = abstract_surface;
1209 do_callbacks (surface, &surface->flush_callbacks);
1210 return _cairo_surface_flush (surface->target, flags);
1213 static cairo_status_t
1214 _cairo_surface_observer_mark_dirty (void *abstract_surface,
1215 int x, int y,
1216 int width, int height)
1218 cairo_surface_observer_t *surface = abstract_surface;
1219 cairo_status_t status;
1221 printf ("mark-dirty (%d, %d) x (%d, %d)\n", x, y, width, height);
1223 status = CAIRO_STATUS_SUCCESS;
1224 if (surface->target->backend->mark_dirty_rectangle)
1225 status = surface->target->backend->mark_dirty_rectangle (surface->target,
1226 x,y, width,height);
1228 return status;
1231 static cairo_int_status_t
1232 _cairo_surface_observer_copy_page (void *abstract_surface)
1234 cairo_surface_observer_t *surface = abstract_surface;
1235 cairo_status_t status;
1237 status = CAIRO_STATUS_SUCCESS;
1238 if (surface->target->backend->copy_page)
1239 status = surface->target->backend->copy_page (surface->target);
1241 return status;
1244 static cairo_int_status_t
1245 _cairo_surface_observer_show_page (void *abstract_surface)
1247 cairo_surface_observer_t *surface = abstract_surface;
1248 cairo_status_t status;
1250 status = CAIRO_STATUS_SUCCESS;
1251 if (surface->target->backend->show_page)
1252 status = surface->target->backend->show_page (surface->target);
1254 return status;
1257 static cairo_bool_t
1258 _cairo_surface_observer_get_extents (void *abstract_surface,
1259 cairo_rectangle_int_t *extents)
1261 cairo_surface_observer_t *surface = abstract_surface;
1262 return _cairo_surface_get_extents (surface->target, extents);
1265 static void
1266 _cairo_surface_observer_get_font_options (void *abstract_surface,
1267 cairo_font_options_t *options)
1269 cairo_surface_observer_t *surface = abstract_surface;
1271 if (surface->target->backend->get_font_options != NULL)
1272 surface->target->backend->get_font_options (surface->target, options);
1275 static cairo_surface_t *
1276 _cairo_surface_observer_source (void *abstract_surface,
1277 cairo_rectangle_int_t *extents)
1279 cairo_surface_observer_t *surface = abstract_surface;
1280 return _cairo_surface_get_source (surface->target, extents);
1283 static cairo_status_t
1284 _cairo_surface_observer_acquire_source_image (void *abstract_surface,
1285 cairo_image_surface_t **image_out,
1286 void **image_extra)
1288 cairo_surface_observer_t *surface = abstract_surface;
1290 surface->log.num_sources_acquired++;
1291 to_device (surface)->log.num_sources_acquired++;
1293 return _cairo_surface_acquire_source_image (surface->target,
1294 image_out, image_extra);
1297 static void
1298 _cairo_surface_observer_release_source_image (void *abstract_surface,
1299 cairo_image_surface_t *image,
1300 void *image_extra)
1302 cairo_surface_observer_t *surface = abstract_surface;
1304 _cairo_surface_release_source_image (surface->target, image, image_extra);
1307 static cairo_surface_t *
1308 _cairo_surface_observer_snapshot (void *abstract_surface)
1310 cairo_surface_observer_t *surface = abstract_surface;
1312 /* XXX hook onto the snapshot so that we measure number of reads */
1314 if (surface->target->backend->snapshot)
1315 return surface->target->backend->snapshot (surface->target);
1317 return NULL;
1320 static cairo_t *
1321 _cairo_surface_observer_create_context(void *target)
1323 cairo_surface_observer_t *surface = target;
1325 if (_cairo_surface_is_subsurface (&surface->base))
1326 surface = (cairo_surface_observer_t *)
1327 _cairo_surface_subsurface_get_target (&surface->base);
1329 surface->log.num_contexts++;
1330 to_device (surface)->log.num_contexts++;
1332 return surface->target->backend->create_context (target);
1335 static const cairo_surface_backend_t _cairo_surface_observer_backend = {
1336 CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER,
1337 _cairo_surface_observer_finish,
1339 _cairo_surface_observer_create_context,
1341 _cairo_surface_observer_create_similar,
1342 _cairo_surface_observer_create_similar_image,
1343 _cairo_surface_observer_map_to_image,
1344 _cairo_surface_observer_unmap_image,
1346 _cairo_surface_observer_source,
1347 _cairo_surface_observer_acquire_source_image,
1348 _cairo_surface_observer_release_source_image,
1349 _cairo_surface_observer_snapshot,
1351 _cairo_surface_observer_copy_page,
1352 _cairo_surface_observer_show_page,
1354 _cairo_surface_observer_get_extents,
1355 _cairo_surface_observer_get_font_options,
1357 _cairo_surface_observer_flush,
1358 _cairo_surface_observer_mark_dirty,
1360 _cairo_surface_observer_paint,
1361 _cairo_surface_observer_mask,
1362 _cairo_surface_observer_stroke,
1363 _cairo_surface_observer_fill,
1364 NULL, /* fill-stroke */
1365 _cairo_surface_observer_glyphs,
1369 * cairo_surface_create_observer:
1370 * @target: an existing surface for which the observer will watch
1371 * @mode: sets the mode of operation (normal vs. record)
1373 * Create a new surface that exists solely to watch another is doing. In
1374 * the process it will log operations and times, which are fast, which are
1375 * slow, which are frequent, etc.
1377 * The @mode parameter can be set to either CAIRO_SURFACE_OBSERVER_NORMAL
1378 * or CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS, to control whether or not
1379 * the internal observer should record operations.
1381 * Return value: a pointer to the newly allocated surface. The caller
1382 * owns the surface and should call cairo_surface_destroy() when done
1383 * with it.
1385 * This function always returns a valid pointer, but it will return a
1386 * pointer to a "nil" surface if @other is already in an error state
1387 * or any other error occurs.
1389 * Since: 1.12
1391 cairo_surface_t *
1392 cairo_surface_create_observer (cairo_surface_t *target,
1393 cairo_surface_observer_mode_t mode)
1395 cairo_device_t *device;
1396 cairo_surface_t *surface;
1397 cairo_bool_t record;
1399 if (unlikely (target->status))
1400 return _cairo_surface_create_in_error (target->status);
1401 if (unlikely (target->finished))
1402 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
1404 record = mode & CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS;
1405 device = _cairo_device_create_observer_internal (target->device, record);
1406 if (unlikely (device->status))
1407 return _cairo_surface_create_in_error (device->status);
1409 surface = _cairo_surface_create_observer_internal (device, target);
1410 cairo_device_destroy (device);
1412 return surface;
1415 static cairo_status_t
1416 _cairo_surface_observer_add_callback (cairo_list_t *head,
1417 cairo_surface_observer_callback_t func,
1418 void *data)
1420 struct callback_list *cb;
1422 cb = malloc (sizeof (*cb));
1423 if (unlikely (cb == NULL))
1424 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1426 cairo_list_add (&cb->link, head);
1427 cb->func = func;
1428 cb->data = data;
1430 return CAIRO_STATUS_SUCCESS;
1433 cairo_status_t
1434 cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface,
1435 cairo_surface_observer_callback_t func,
1436 void *data)
1438 cairo_surface_observer_t *surface;
1440 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1441 return abstract_surface->status;
1443 if (! _cairo_surface_is_observer (abstract_surface))
1444 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1446 surface = (cairo_surface_observer_t *)abstract_surface;
1447 return _cairo_surface_observer_add_callback (&surface->paint_callbacks,
1448 func, data);
1451 cairo_status_t
1452 cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface,
1453 cairo_surface_observer_callback_t func,
1454 void *data)
1456 cairo_surface_observer_t *surface;
1458 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1459 return abstract_surface->status;
1461 if (! _cairo_surface_is_observer (abstract_surface))
1462 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1464 surface = (cairo_surface_observer_t *)abstract_surface;
1465 return _cairo_surface_observer_add_callback (&surface->mask_callbacks,
1466 func, data);
1469 cairo_status_t
1470 cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface,
1471 cairo_surface_observer_callback_t func,
1472 void *data)
1474 cairo_surface_observer_t *surface;
1476 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1477 return abstract_surface->status;
1479 if (! _cairo_surface_is_observer (abstract_surface))
1480 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1482 surface = (cairo_surface_observer_t *)abstract_surface;
1483 return _cairo_surface_observer_add_callback (&surface->fill_callbacks,
1484 func, data);
1487 cairo_status_t
1488 cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface,
1489 cairo_surface_observer_callback_t func,
1490 void *data)
1492 cairo_surface_observer_t *surface;
1494 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1495 return abstract_surface->status;
1497 if (! _cairo_surface_is_observer (abstract_surface))
1498 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1500 surface = (cairo_surface_observer_t *)abstract_surface;
1501 return _cairo_surface_observer_add_callback (&surface->stroke_callbacks,
1502 func, data);
1505 cairo_status_t
1506 cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface,
1507 cairo_surface_observer_callback_t func,
1508 void *data)
1510 cairo_surface_observer_t *surface;
1512 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1513 return abstract_surface->status;
1515 if (! _cairo_surface_is_observer (abstract_surface))
1516 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1518 surface = (cairo_surface_observer_t *)abstract_surface;
1519 return _cairo_surface_observer_add_callback (&surface->glyphs_callbacks,
1520 func, data);
1523 cairo_status_t
1524 cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface,
1525 cairo_surface_observer_callback_t func,
1526 void *data)
1528 cairo_surface_observer_t *surface;
1530 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1531 return abstract_surface->status;
1533 if (! _cairo_surface_is_observer (abstract_surface))
1534 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1536 surface = (cairo_surface_observer_t *)abstract_surface;
1537 return _cairo_surface_observer_add_callback (&surface->flush_callbacks,
1538 func, data);
1541 cairo_status_t
1542 cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface,
1543 cairo_surface_observer_callback_t func,
1544 void *data)
1546 cairo_surface_observer_t *surface;
1548 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1549 return abstract_surface->status;
1551 if (! _cairo_surface_is_observer (abstract_surface))
1552 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1554 surface = (cairo_surface_observer_t *)abstract_surface;
1555 return _cairo_surface_observer_add_callback (&surface->finish_callbacks,
1556 func, data);
1559 static void
1560 print_extents (cairo_output_stream_t *stream, const struct extents *e)
1562 _cairo_output_stream_printf (stream,
1563 " extents: total %g, avg %g [unbounded %d]\n",
1564 e->area.sum,
1565 e->area.sum / e->area.count,
1566 e->unbounded);
1569 static inline int ordercmp (int a, int b, const unsigned int *array)
1571 /* high to low */
1572 return array[b] - array[a];
1574 CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_order, int, ordercmp)
1576 static void
1577 print_array (cairo_output_stream_t *stream,
1578 const unsigned int *array,
1579 const char **names,
1580 int count)
1582 int order[64];
1583 int i, j;
1585 assert (count < ARRAY_LENGTH (order));
1586 for (i = j = 0; i < count; i++) {
1587 if (array[i] != 0)
1588 order[j++] = i;
1591 sort_order (order, j, (void *)array);
1592 for (i = 0; i < j; i++)
1593 _cairo_output_stream_printf (stream, " %d %s%s",
1594 array[order[i]], names[order[i]],
1595 i < j -1 ? "," : "");
1598 static const char *operator_names[] = {
1599 "CLEAR", /* CAIRO_OPERATOR_CLEAR */
1601 "SOURCE", /* CAIRO_OPERATOR_SOURCE */
1602 "OVER", /* CAIRO_OPERATOR_OVER */
1603 "IN", /* CAIRO_OPERATOR_IN */
1604 "OUT", /* CAIRO_OPERATOR_OUT */
1605 "ATOP", /* CAIRO_OPERATOR_ATOP */
1607 "DEST", /* CAIRO_OPERATOR_DEST */
1608 "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */
1609 "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */
1610 "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */
1611 "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */
1613 "XOR", /* CAIRO_OPERATOR_XOR */
1614 "ADD", /* CAIRO_OPERATOR_ADD */
1615 "SATURATE", /* CAIRO_OPERATOR_SATURATE */
1617 "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */
1618 "SCREEN", /* CAIRO_OPERATOR_SCREEN */
1619 "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */
1620 "DARKEN", /* CAIRO_OPERATOR_DARKEN */
1621 "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */
1622 "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */
1623 "BURN", /* CAIRO_OPERATOR_COLOR_BURN */
1624 "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */
1625 "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */
1626 "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */
1627 "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */
1628 "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */
1629 "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
1630 "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */
1631 "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
1633 static void
1634 print_operators (cairo_output_stream_t *stream, unsigned int *array)
1636 _cairo_output_stream_printf (stream, " op:");
1637 print_array (stream, array, operator_names, NUM_OPERATORS);
1638 _cairo_output_stream_printf (stream, "\n");
1641 static const char *fill_rule_names[] = {
1642 "non-zero",
1643 "even-odd",
1645 static void
1646 print_fill_rule (cairo_output_stream_t *stream, unsigned int *array)
1648 _cairo_output_stream_printf (stream, " fill rule:");
1649 print_array (stream, array, fill_rule_names, ARRAY_LENGTH(fill_rule_names));
1650 _cairo_output_stream_printf (stream, "\n");
1653 static const char *cap_names[] = {
1654 "butt", /* CAIRO_LINE_CAP_BUTT */
1655 "round", /* CAIRO_LINE_CAP_ROUND */
1656 "square" /* CAIRO_LINE_CAP_SQUARE */
1658 static void
1659 print_line_caps (cairo_output_stream_t *stream, unsigned int *array)
1661 _cairo_output_stream_printf (stream, " caps:");
1662 print_array (stream, array, cap_names, NUM_CAPS);
1663 _cairo_output_stream_printf (stream, "\n");
1666 static const char *join_names[] = {
1667 "miter", /* CAIRO_LINE_JOIN_MITER */
1668 "round", /* CAIRO_LINE_JOIN_ROUND */
1669 "bevel", /* CAIRO_LINE_JOIN_BEVEL */
1671 static void
1672 print_line_joins (cairo_output_stream_t *stream, unsigned int *array)
1674 _cairo_output_stream_printf (stream, " joins:");
1675 print_array (stream, array, join_names, NUM_JOINS);
1676 _cairo_output_stream_printf (stream, "\n");
1679 static const char *antialias_names[] = {
1680 "default",
1681 "none",
1682 "gray",
1683 "subpixel",
1684 "fast",
1685 "good",
1686 "best"
1688 static void
1689 print_antialias (cairo_output_stream_t *stream, unsigned int *array)
1691 _cairo_output_stream_printf (stream, " antialias:");
1692 print_array (stream, array, antialias_names, NUM_ANTIALIAS);
1693 _cairo_output_stream_printf (stream, "\n");
1696 static const char *pattern_names[] = {
1697 "native",
1698 "record",
1699 "other surface",
1700 "solid",
1701 "linear",
1702 "radial",
1703 "mesh",
1704 "raster"
1706 static void
1707 print_pattern (cairo_output_stream_t *stream,
1708 const char *name,
1709 const struct pattern *p)
1711 _cairo_output_stream_printf (stream, " %s:", name);
1712 print_array (stream, p->type, pattern_names, ARRAY_LENGTH (pattern_names));
1713 _cairo_output_stream_printf (stream, "\n");
1716 static const char *path_names[] = {
1717 "empty",
1718 "pixel-aligned",
1719 "rectliinear",
1720 "straight",
1721 "curved",
1723 static void
1724 print_path (cairo_output_stream_t *stream,
1725 const struct path *p)
1727 _cairo_output_stream_printf (stream, " path:");
1728 print_array (stream, p->type, path_names, ARRAY_LENGTH (path_names));
1729 _cairo_output_stream_printf (stream, "\n");
1732 static const char *clip_names[] = {
1733 "none",
1734 "region",
1735 "boxes",
1736 "single path",
1737 "polygon",
1738 "general",
1740 static void
1741 print_clip (cairo_output_stream_t *stream, const struct clip *c)
1743 _cairo_output_stream_printf (stream, " clip:");
1744 print_array (stream, c->type, clip_names, ARRAY_LENGTH (clip_names));
1745 _cairo_output_stream_printf (stream, "\n");
1748 static void
1749 print_record (cairo_output_stream_t *stream,
1750 cairo_observation_record_t *r)
1752 _cairo_output_stream_printf (stream, " op: %s\n", operator_names[r->op]);
1753 _cairo_output_stream_printf (stream, " source: %s\n",
1754 pattern_names[r->source]);
1755 if (r->mask != -1)
1756 _cairo_output_stream_printf (stream, " mask: %s\n",
1757 pattern_names[r->mask]);
1758 if (r->num_glyphs != -1)
1759 _cairo_output_stream_printf (stream, " num_glyphs: %d\n",
1760 r->num_glyphs);
1761 if (r->path != -1)
1762 _cairo_output_stream_printf (stream, " path: %s\n",
1763 path_names[r->path]);
1764 if (r->fill_rule != -1)
1765 _cairo_output_stream_printf (stream, " fill rule: %s\n",
1766 fill_rule_names[r->fill_rule]);
1767 if (r->antialias != -1)
1768 _cairo_output_stream_printf (stream, " antialias: %s\n",
1769 antialias_names[r->antialias]);
1770 _cairo_output_stream_printf (stream, " clip: %s\n", clip_names[r->clip]);
1771 _cairo_output_stream_printf (stream, " elapsed: %f ns\n",
1772 _cairo_time_to_ns (r->elapsed));
1775 static double percent (cairo_time_t a, cairo_time_t b)
1777 /* Fake %.1f */
1778 return _cairo_round (_cairo_time_to_s (a) * 1000 /
1779 _cairo_time_to_s (b)) / 10;
1782 static cairo_bool_t
1783 replay_record (cairo_observation_t *log,
1784 cairo_observation_record_t *r,
1785 cairo_device_t *script)
1787 #if CAIRO_HAS_SCRIPT_SURFACE
1788 cairo_surface_t *surface;
1789 cairo_int_status_t status;
1791 if (log->record == NULL || script == NULL)
1792 return FALSE;
1794 surface = cairo_script_surface_create (script,
1795 r->target_content,
1796 r->target_width,
1797 r->target_height);
1798 status =
1799 _cairo_recording_surface_replay_one (log->record, r->index, surface);
1800 cairo_surface_destroy (surface);
1802 assert (status == CAIRO_INT_STATUS_SUCCESS);
1804 return TRUE;
1805 #else
1806 return FALSE;
1807 #endif
1810 static cairo_time_t
1811 _cairo_observation_total_elapsed (cairo_observation_t *log)
1813 cairo_time_t total;
1815 total = log->paint.elapsed;
1816 total = _cairo_time_add (total, log->mask.elapsed);
1817 total = _cairo_time_add (total, log->fill.elapsed);
1818 total = _cairo_time_add (total, log->stroke.elapsed);
1819 total = _cairo_time_add (total, log->glyphs.elapsed);
1821 return total;
1824 static void
1825 _cairo_observation_print (cairo_output_stream_t *stream,
1826 cairo_observation_t *log)
1828 cairo_device_t *script;
1829 cairo_time_t total;
1831 #if CAIRO_HAS_SCRIPT_SURFACE
1832 script = _cairo_script_context_create_internal (stream);
1833 _cairo_script_context_attach_snapshots (script, FALSE);
1834 #else
1835 script = NULL;
1836 #endif
1838 total = _cairo_observation_total_elapsed (log);
1840 _cairo_output_stream_printf (stream, "elapsed: %f\n",
1841 _cairo_time_to_ns (total));
1842 _cairo_output_stream_printf (stream, "surfaces: %d\n",
1843 log->num_surfaces);
1844 _cairo_output_stream_printf (stream, "contexts: %d\n",
1845 log->num_contexts);
1846 _cairo_output_stream_printf (stream, "sources acquired: %d\n",
1847 log->num_sources_acquired);
1850 _cairo_output_stream_printf (stream, "paint: count %d [no-op %d], elapsed %f [%f%%]\n",
1851 log->paint.count, log->paint.noop,
1852 _cairo_time_to_ns (log->paint.elapsed),
1853 percent (log->paint.elapsed, total));
1854 if (log->paint.count) {
1855 print_extents (stream, &log->paint.extents);
1856 print_operators (stream, log->paint.operators);
1857 print_pattern (stream, "source", &log->paint.source);
1858 print_clip (stream, &log->paint.clip);
1860 _cairo_output_stream_printf (stream, "slowest paint: %f%%\n",
1861 percent (log->paint.slowest.elapsed,
1862 log->paint.elapsed));
1863 print_record (stream, &log->paint.slowest);
1865 _cairo_output_stream_printf (stream, "\n");
1866 if (replay_record (log, &log->paint.slowest, script))
1867 _cairo_output_stream_printf (stream, "\n\n");
1870 _cairo_output_stream_printf (stream, "mask: count %d [no-op %d], elapsed %f [%f%%]\n",
1871 log->mask.count, log->mask.noop,
1872 _cairo_time_to_ns (log->mask.elapsed),
1873 percent (log->mask.elapsed, total));
1874 if (log->mask.count) {
1875 print_extents (stream, &log->mask.extents);
1876 print_operators (stream, log->mask.operators);
1877 print_pattern (stream, "source", &log->mask.source);
1878 print_pattern (stream, "mask", &log->mask.mask);
1879 print_clip (stream, &log->mask.clip);
1881 _cairo_output_stream_printf (stream, "slowest mask: %f%%\n",
1882 percent (log->mask.slowest.elapsed,
1883 log->mask.elapsed));
1884 print_record (stream, &log->mask.slowest);
1886 _cairo_output_stream_printf (stream, "\n");
1887 if (replay_record (log, &log->mask.slowest, script))
1888 _cairo_output_stream_printf (stream, "\n\n");
1891 _cairo_output_stream_printf (stream, "fill: count %d [no-op %d], elaspsed %f [%f%%]\n",
1892 log->fill.count, log->fill.noop,
1893 _cairo_time_to_ns (log->fill.elapsed),
1894 percent (log->fill.elapsed, total));
1895 if (log->fill.count) {
1896 print_extents (stream, &log->fill.extents);
1897 print_operators (stream, log->fill.operators);
1898 print_pattern (stream, "source", &log->fill.source);
1899 print_path (stream, &log->fill.path);
1900 print_fill_rule (stream, log->fill.fill_rule);
1901 print_antialias (stream, log->fill.antialias);
1902 print_clip (stream, &log->fill.clip);
1904 _cairo_output_stream_printf (stream, "slowest fill: %f%%\n",
1905 percent (log->fill.slowest.elapsed,
1906 log->fill.elapsed));
1907 print_record (stream, &log->fill.slowest);
1909 _cairo_output_stream_printf (stream, "\n");
1910 if (replay_record (log, &log->fill.slowest, script))
1911 _cairo_output_stream_printf (stream, "\n\n");
1914 _cairo_output_stream_printf (stream, "stroke: count %d [no-op %d], elapsed %f [%f%%]\n",
1915 log->stroke.count, log->stroke.noop,
1916 _cairo_time_to_ns (log->stroke.elapsed),
1917 percent (log->stroke.elapsed, total));
1918 if (log->stroke.count) {
1919 print_extents (stream, &log->stroke.extents);
1920 print_operators (stream, log->stroke.operators);
1921 print_pattern (stream, "source", &log->stroke.source);
1922 print_path (stream, &log->stroke.path);
1923 print_antialias (stream, log->stroke.antialias);
1924 print_line_caps (stream, log->stroke.caps);
1925 print_line_joins (stream, log->stroke.joins);
1926 print_clip (stream, &log->stroke.clip);
1928 _cairo_output_stream_printf (stream, "slowest stroke: %f%%\n",
1929 percent (log->stroke.slowest.elapsed,
1930 log->stroke.elapsed));
1931 print_record (stream, &log->stroke.slowest);
1933 _cairo_output_stream_printf (stream, "\n");
1934 if (replay_record (log, &log->stroke.slowest, script))
1935 _cairo_output_stream_printf (stream, "\n\n");
1938 _cairo_output_stream_printf (stream, "glyphs: count %d [no-op %d], elasped %f [%f%%]\n",
1939 log->glyphs.count, log->glyphs.noop,
1940 _cairo_time_to_ns (log->glyphs.elapsed),
1941 percent (log->glyphs.elapsed, total));
1942 if (log->glyphs.count) {
1943 print_extents (stream, &log->glyphs.extents);
1944 print_operators (stream, log->glyphs.operators);
1945 print_pattern (stream, "source", &log->glyphs.source);
1946 print_clip (stream, &log->glyphs.clip);
1948 _cairo_output_stream_printf (stream, "slowest glyphs: %f%%\n",
1949 percent (log->glyphs.slowest.elapsed,
1950 log->glyphs.elapsed));
1951 print_record (stream, &log->glyphs.slowest);
1953 _cairo_output_stream_printf (stream, "\n");
1954 if (replay_record (log, &log->glyphs.slowest, script))
1955 _cairo_output_stream_printf (stream, "\n\n");
1958 cairo_device_destroy (script);
1961 cairo_status_t
1962 cairo_surface_observer_print (cairo_surface_t *abstract_surface,
1963 cairo_write_func_t write_func,
1964 void *closure)
1966 cairo_output_stream_t *stream;
1967 cairo_surface_observer_t *surface;
1969 if (unlikely (abstract_surface->status))
1970 return abstract_surface->status;
1972 if (unlikely (! _cairo_surface_is_observer (abstract_surface)))
1973 return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1975 surface = (cairo_surface_observer_t *) abstract_surface;
1977 stream = _cairo_output_stream_create (write_func, NULL, closure);
1978 _cairo_observation_print (stream, &surface->log);
1979 return _cairo_output_stream_destroy (stream);
1982 double
1983 cairo_surface_observer_elapsed (cairo_surface_t *abstract_surface)
1985 cairo_surface_observer_t *surface;
1987 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
1988 return -1;
1990 if (! _cairo_surface_is_observer (abstract_surface))
1991 return -1;
1993 surface = (cairo_surface_observer_t *) abstract_surface;
1994 return _cairo_time_to_ns (_cairo_observation_total_elapsed (&surface->log));
1997 cairo_status_t
1998 cairo_device_observer_print (cairo_device_t *abstract_device,
1999 cairo_write_func_t write_func,
2000 void *closure)
2002 cairo_output_stream_t *stream;
2003 cairo_device_observer_t *device;
2005 if (unlikely (abstract_device->status))
2006 return abstract_device->status;
2008 if (unlikely (! _cairo_device_is_observer (abstract_device)))
2009 return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
2011 device = (cairo_device_observer_t *) abstract_device;
2013 stream = _cairo_output_stream_create (write_func, NULL, closure);
2014 _cairo_observation_print (stream, &device->log);
2015 return _cairo_output_stream_destroy (stream);
2018 double
2019 cairo_device_observer_elapsed (cairo_device_t *abstract_device)
2021 cairo_device_observer_t *device;
2023 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2024 return -1;
2026 if (! _cairo_device_is_observer (abstract_device))
2027 return -1;
2029 device = (cairo_device_observer_t *) abstract_device;
2030 return _cairo_time_to_ns (_cairo_observation_total_elapsed (&device->log));
2033 double
2034 cairo_device_observer_paint_elapsed (cairo_device_t *abstract_device)
2036 cairo_device_observer_t *device;
2038 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2039 return -1;
2041 if (! _cairo_device_is_observer (abstract_device))
2042 return -1;
2044 device = (cairo_device_observer_t *) abstract_device;
2045 return _cairo_time_to_ns (device->log.paint.elapsed);
2048 double
2049 cairo_device_observer_mask_elapsed (cairo_device_t *abstract_device)
2051 cairo_device_observer_t *device;
2053 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2054 return -1;
2056 if (! _cairo_device_is_observer (abstract_device))
2057 return -1;
2059 device = (cairo_device_observer_t *) abstract_device;
2060 return _cairo_time_to_ns (device->log.mask.elapsed);
2063 double
2064 cairo_device_observer_fill_elapsed (cairo_device_t *abstract_device)
2066 cairo_device_observer_t *device;
2068 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2069 return -1;
2071 if (! _cairo_device_is_observer (abstract_device))
2072 return -1;
2074 device = (cairo_device_observer_t *) abstract_device;
2075 return _cairo_time_to_ns (device->log.fill.elapsed);
2078 double
2079 cairo_device_observer_stroke_elapsed (cairo_device_t *abstract_device)
2081 cairo_device_observer_t *device;
2083 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2084 return -1;
2086 if (! _cairo_device_is_observer (abstract_device))
2087 return -1;
2089 device = (cairo_device_observer_t *) abstract_device;
2090 return _cairo_time_to_ns (device->log.stroke.elapsed);
2093 double
2094 cairo_device_observer_glyphs_elapsed (cairo_device_t *abstract_device)
2096 cairo_device_observer_t *device;
2098 if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
2099 return -1;
2101 if (! _cairo_device_is_observer (abstract_device))
2102 return -1;
2104 device = (cairo_device_observer_t *) abstract_device;
2105 return _cairo_time_to_ns (device->log.glyphs.elapsed);