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_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
);
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
;
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
,
513 const cairo_pattern_t
*source
,
514 const cairo_clip_t
*clip
,
515 cairo_time_t elapsed
)
517 record_target (r
, target
);
520 r
->source
= classify_pattern (source
, target
);
527 r
->clip
= classify_clip (clip
);
528 r
->elapsed
= elapsed
;
533 static cairo_observation_record_t
*
534 record_mask (cairo_observation_record_t
*r
,
535 cairo_surface_t
*target
,
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
);
545 r
->source
= classify_pattern (source
, target
);
546 r
->mask
= classify_pattern (mask
, target
);
552 r
->clip
= classify_clip (clip
);
553 r
->elapsed
= elapsed
;
558 static cairo_observation_record_t
*
559 record_fill (cairo_observation_record_t
*r
,
560 cairo_surface_t
*target
,
562 const cairo_pattern_t
*source
,
563 const cairo_path_fixed_t
*path
,
564 cairo_fill_rule_t fill_rule
,
566 cairo_antialias_t antialias
,
567 const cairo_clip_t
*clip
,
568 cairo_time_t elapsed
)
570 record_target (r
, target
);
573 r
->source
= classify_pattern (source
, target
);
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
;
586 static cairo_observation_record_t
*
587 record_stroke (cairo_observation_record_t
*r
,
588 cairo_surface_t
*target
,
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
,
596 cairo_antialias_t antialias
,
597 const cairo_clip_t
*clip
,
598 cairo_time_t elapsed
)
600 record_target (r
, target
);
603 r
->source
= classify_pattern (source
, target
);
606 r
->path
= classify_path (path
, FALSE
);
608 r
->tolerance
= tolerance
;
609 r
->antialias
= antialias
;
610 r
->clip
= classify_clip (clip
);
611 r
->elapsed
= elapsed
;
616 static cairo_observation_record_t
*
617 record_glyphs (cairo_observation_record_t
*r
,
618 cairo_surface_t
*target
,
620 const cairo_pattern_t
*source
,
621 cairo_glyph_t
*glyphs
,
623 cairo_scaled_font_t
*scaled_font
,
624 const cairo_clip_t
*clip
,
625 cairo_time_t elapsed
)
627 record_target (r
, target
);
630 r
->source
= classify_pattern (source
, target
);
633 r
->num_glyphs
= num_glyphs
;
637 r
->clip
= classify_clip (clip
);
638 r
->elapsed
= elapsed
;
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
);
656 _cairo_surface_sync (cairo_surface_t
*target
, int x
, int y
)
658 cairo_rectangle_int_t extents
;
665 _cairo_surface_unmap_image (target
,
666 _cairo_surface_map_to_image (target
,
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;
678 add_record_paint (cairo_observation_t
*log
,
679 cairo_surface_t
*target
,
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
;
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.
696 status
= log
->record
->base
.backend
->paint (&log
->record
->base
,
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
,
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
;
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
,
735 if (unlikely (status
)) {
736 surface
->log
.paint
.noop
++;
737 device
->log
.paint
.noop
++;
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
,
751 if (unlikely (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
;
766 add_record_mask (cairo_observation_t
*log
,
767 cairo_surface_t
*target
,
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
;
778 record_mask (&record
, target
, op
, source
, mask
, clip
, elapsed
));
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
,
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
;
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
,
821 if (unlikely (status
)) {
822 surface
->log
.mask
.noop
++;
823 device
->log
.mask
.noop
++;
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
,
837 if (unlikely (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
;
856 add_record_fill (cairo_observation_t
*log
,
857 cairo_surface_t
*target
,
859 const cairo_pattern_t
*source
,
860 const cairo_path_fixed_t
*path
,
861 cairo_fill_rule_t fill_rule
,
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
;
871 record_fill (&record
,
873 path
, fill_rule
, tolerance
, antialias
,
877 status
= log
->record
->base
.backend
->fill (&log
->record
->base
,
880 tolerance
, antialias
,
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
,
893 const cairo_pattern_t
*source
,
894 const cairo_path_fixed_t
*path
,
895 cairo_fill_rule_t fill_rule
,
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
;
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
,
927 if (unlikely (status
)) {
928 surface
->log
.fill
.noop
++;
929 device
->log
.fill
.noop
++;
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
,
942 fill_rule
, tolerance
, antialias
,
944 if (unlikely (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
,
955 add_record_fill (&device
->log
,
956 surface
->target
, op
, source
, path
,
957 fill_rule
, tolerance
, antialias
,
960 do_callbacks (surface
, &surface
->fill_callbacks
);
962 return CAIRO_STATUS_SUCCESS
;
966 add_record_stroke (cairo_observation_t
*log
,
967 cairo_surface_t
*target
,
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
,
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
;
983 record_stroke (&record
,
985 path
, style
, ctm
,ctm_inverse
,
986 tolerance
, antialias
,
990 status
= log
->record
->base
.backend
->stroke (&log
->record
->base
,
992 path
, style
, ctm
,ctm_inverse
,
993 tolerance
, antialias
,
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
,
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
;
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
,
1045 if (unlikely (status
)) {
1046 surface
->log
.stroke
.noop
++;
1047 device
->log
.stroke
.noop
++;
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
,
1060 style
, ctm
, ctm_inverse
,
1061 tolerance
, antialias
,
1063 if (unlikely (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
,
1075 add_record_stroke (&device
->log
,
1076 surface
->target
, op
, source
, path
,
1077 style
, ctm
,ctm_inverse
,
1078 tolerance
, antialias
,
1081 do_callbacks (surface
, &surface
->stroke_callbacks
);
1083 return CAIRO_STATUS_SUCCESS
;
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
,
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
;
1101 record_glyphs (&record
,
1103 glyphs
, num_glyphs
, scaled_font
,
1107 status
= log
->record
->base
.backend
->show_text_glyphs (&log
->record
->base
,
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
,
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
;
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
,
1156 if (unlikely (status
)) {
1157 surface
->log
.glyphs
.noop
++;
1158 device
->log
.glyphs
.noop
++;
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
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
,
1178 dev_glyphs
, num_glyphs
,
1183 if (unlikely (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
,
1194 add_record_glyphs (&device
->log
,
1195 surface
->target
, op
, source
,
1196 glyphs
, num_glyphs
, scaled_font
,
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
,
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
,
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
);
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
);
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
);
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
,
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
);
1298 _cairo_surface_observer_release_source_image (void *abstract_surface
,
1299 cairo_image_surface_t
*image
,
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
);
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
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.
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
);
1415 static cairo_status_t
1416 _cairo_surface_observer_add_callback (cairo_list_t
*head
,
1417 cairo_surface_observer_callback_t func
,
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
);
1430 return CAIRO_STATUS_SUCCESS
;
1434 cairo_surface_observer_add_paint_callback (cairo_surface_t
*abstract_surface
,
1435 cairo_surface_observer_callback_t func
,
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
,
1452 cairo_surface_observer_add_mask_callback (cairo_surface_t
*abstract_surface
,
1453 cairo_surface_observer_callback_t func
,
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
,
1470 cairo_surface_observer_add_fill_callback (cairo_surface_t
*abstract_surface
,
1471 cairo_surface_observer_callback_t func
,
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
,
1488 cairo_surface_observer_add_stroke_callback (cairo_surface_t
*abstract_surface
,
1489 cairo_surface_observer_callback_t func
,
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
,
1506 cairo_surface_observer_add_glyphs_callback (cairo_surface_t
*abstract_surface
,
1507 cairo_surface_observer_callback_t func
,
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
,
1524 cairo_surface_observer_add_flush_callback (cairo_surface_t
*abstract_surface
,
1525 cairo_surface_observer_callback_t func
,
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
,
1542 cairo_surface_observer_add_finish_callback (cairo_surface_t
*abstract_surface
,
1543 cairo_surface_observer_callback_t func
,
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
,
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",
1565 e
->area
.sum
/ e
->area
.count
,
1569 static inline int ordercmp (int a
, int b
, const unsigned int *array
)
1572 return array
[b
] - array
[a
];
1574 CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_order
, int, ordercmp
)
1577 print_array (cairo_output_stream_t
*stream
,
1578 const unsigned int *array
,
1585 assert (count
< ARRAY_LENGTH (order
));
1586 for (i
= j
= 0; i
< count
; 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 */
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
[] = {
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 */
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 */
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
[] = {
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
[] = {
1707 print_pattern (cairo_output_stream_t
*stream
,
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
[] = {
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
[] = {
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");
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
]);
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",
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
)
1778 return _cairo_round (_cairo_time_to_s (a
) * 1000 /
1779 _cairo_time_to_s (b
)) / 10;
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
)
1794 surface
= cairo_script_surface_create (script
,
1799 _cairo_recording_surface_replay_one (log
->record
, r
->index
, surface
);
1800 cairo_surface_destroy (surface
);
1802 assert (status
== CAIRO_INT_STATUS_SUCCESS
);
1811 _cairo_observation_total_elapsed (cairo_observation_t
*log
)
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
);
1825 _cairo_observation_print (cairo_output_stream_t
*stream
,
1826 cairo_observation_t
*log
)
1828 cairo_device_t
*script
;
1831 #if CAIRO_HAS_SCRIPT_SURFACE
1832 script
= _cairo_script_context_create_internal (stream
);
1833 _cairo_script_context_attach_snapshots (script
, FALSE
);
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",
1844 _cairo_output_stream_printf (stream
, "contexts: %d\n",
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
);
1962 cairo_surface_observer_print (cairo_surface_t
*abstract_surface
,
1963 cairo_write_func_t write_func
,
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
);
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
)))
1990 if (! _cairo_surface_is_observer (abstract_surface
))
1993 surface
= (cairo_surface_observer_t
*) abstract_surface
;
1994 return _cairo_time_to_ns (_cairo_observation_total_elapsed (&surface
->log
));
1998 cairo_device_observer_print (cairo_device_t
*abstract_device
,
1999 cairo_write_func_t write_func
,
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
);
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
)))
2026 if (! _cairo_device_is_observer (abstract_device
))
2029 device
= (cairo_device_observer_t
*) abstract_device
;
2030 return _cairo_time_to_ns (_cairo_observation_total_elapsed (&device
->log
));
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
)))
2041 if (! _cairo_device_is_observer (abstract_device
))
2044 device
= (cairo_device_observer_t
*) abstract_device
;
2045 return _cairo_time_to_ns (device
->log
.paint
.elapsed
);
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
)))
2056 if (! _cairo_device_is_observer (abstract_device
))
2059 device
= (cairo_device_observer_t
*) abstract_device
;
2060 return _cairo_time_to_ns (device
->log
.mask
.elapsed
);
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
)))
2071 if (! _cairo_device_is_observer (abstract_device
))
2074 device
= (cairo_device_observer_t
*) abstract_device
;
2075 return _cairo_time_to_ns (device
->log
.fill
.elapsed
);
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
)))
2086 if (! _cairo_device_is_observer (abstract_device
))
2089 device
= (cairo_device_observer_t
*) abstract_device
;
2090 return _cairo_time_to_ns (device
->log
.stroke
.elapsed
);
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
)))
2101 if (! _cairo_device_is_observer (abstract_device
))
2104 device
= (cairo_device_observer_t
*) abstract_device
;
2105 return _cairo_time_to_ns (device
->log
.glyphs
.elapsed
);