mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-06-09 00:28:23 +02:00
Having spent the last dev cycle looking at how we could specialize the compositors for various backends, we once again look for the commonalities in order to reduce the duplication. In part this is motivated by the idea that spans is a good interface for both the existent GL backend and pixman, and so they deserve a dedicated compositor. xcb/xlib target an identical rendering system and so they should be using the same compositor, and it should be possible to run that same compositor locally against pixman to generate reference tests. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> P.S. This brings massive upheaval (read breakage) I've tried delaying in order to fix as many things as possible but now this one patch does far, far, far too much. Apologies in advance for breaking your favourite backend, but trust me in that the end result will be much better. :)
2081 lines
58 KiB
C
2081 lines
58 KiB
C
/* cairo - a vector graphics library with display and print output
|
|
*
|
|
* Copyright © 2011 Intel Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it either under the terms of the GNU Lesser General Public
|
|
* License version 2.1 as published by the Free Software Foundation
|
|
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
|
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
|
* notice, a recipient may use your version of this file under either
|
|
* the MPL or the LGPL.
|
|
*
|
|
* You should have received a copy of the LGPL along with this library
|
|
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
|
|
* You should have received a copy of the MPL along with this library
|
|
* in the file COPYING-MPL-1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License
|
|
* Version 1.1 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
|
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
|
* the specific language governing rights and limitations.
|
|
*
|
|
* The Original Code is the cairo graphics library.
|
|
*
|
|
* The Initial Developer of the Original Code is Intel Corporation.
|
|
*
|
|
* Contributor(s):
|
|
* Chris Wilson <chris@chris-wilson.co.uk>
|
|
*/
|
|
|
|
#include "cairoint.h"
|
|
|
|
#include "cairo-surface-observer-private.h"
|
|
|
|
#include "cairo-array-private.h"
|
|
#include "cairo-combsort-private.h"
|
|
#include "cairo-composite-rectangles-private.h"
|
|
#include "cairo-error-private.h"
|
|
#include "cairo-image-surface-private.h"
|
|
#include "cairo-pattern-private.h"
|
|
#include "cairo-output-stream-private.h"
|
|
#include "cairo-recording-surface-private.h"
|
|
#include "cairo-script-private.h"
|
|
#include "cairo-surface-subsurface-private.h"
|
|
#include "cairo-reference-count-private.h"
|
|
|
|
static const cairo_surface_backend_t _cairo_surface_observer_backend;
|
|
|
|
/* observation/stats */
|
|
|
|
static void init_stats (struct stat *s)
|
|
{
|
|
s->min = HUGE_VAL;
|
|
s->max = -HUGE_VAL;
|
|
}
|
|
|
|
static void init_extents (struct extents *e)
|
|
{
|
|
init_stats (&e->area);
|
|
}
|
|
|
|
static void init_pattern (struct pattern *p)
|
|
{
|
|
}
|
|
|
|
static void init_path (struct path *p)
|
|
{
|
|
}
|
|
|
|
static void init_clip (struct clip *c)
|
|
{
|
|
}
|
|
|
|
static void init_paint (struct paint *p)
|
|
{
|
|
init_extents (&p->extents);
|
|
init_pattern (&p->source);
|
|
init_clip (&p->clip);
|
|
}
|
|
|
|
static void init_mask (struct mask *m)
|
|
{
|
|
init_extents (&m->extents);
|
|
init_pattern (&m->source);
|
|
init_pattern (&m->mask);
|
|
init_clip (&m->clip);
|
|
}
|
|
|
|
static void init_fill (struct fill *f)
|
|
{
|
|
init_extents (&f->extents);
|
|
init_pattern (&f->source);
|
|
init_path (&f->path);
|
|
init_clip (&f->clip);
|
|
}
|
|
|
|
static void init_stroke (struct stroke *s)
|
|
{
|
|
init_extents (&s->extents);
|
|
init_pattern (&s->source);
|
|
init_path (&s->path);
|
|
init_clip (&s->clip);
|
|
}
|
|
|
|
static void init_glyphs (struct glyphs *g)
|
|
{
|
|
init_extents (&g->extents);
|
|
init_pattern (&g->source);
|
|
init_clip (&g->clip);
|
|
}
|
|
|
|
static cairo_status_t
|
|
log_init (cairo_observation_t *log,
|
|
cairo_bool_t record)
|
|
{
|
|
memset (log, 0, sizeof(*log));
|
|
|
|
init_paint (&log->paint);
|
|
init_mask (&log->mask);
|
|
init_fill (&log->fill);
|
|
init_stroke (&log->stroke);
|
|
init_glyphs (&log->glyphs);
|
|
|
|
_cairo_array_init (&log->timings, sizeof (cairo_observation_record_t));
|
|
|
|
if (record) {
|
|
log->record = (cairo_recording_surface_t *)
|
|
cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
|
|
if (unlikely (log->record->base.status))
|
|
return log->record->base.status;
|
|
|
|
log->record->optimize_clears = FALSE;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
log_fini (cairo_observation_t *log)
|
|
{
|
|
_cairo_array_fini (&log->timings);
|
|
cairo_surface_destroy (&log->record->base);
|
|
}
|
|
|
|
static cairo_surface_t*
|
|
get_pattern_surface (const cairo_pattern_t *pattern)
|
|
{
|
|
return ((cairo_surface_pattern_t *)pattern)->surface;
|
|
}
|
|
|
|
static int
|
|
classify_pattern (const cairo_pattern_t *pattern,
|
|
const cairo_surface_t *target)
|
|
{
|
|
int classify;
|
|
|
|
switch (pattern->type) {
|
|
case CAIRO_PATTERN_TYPE_SURFACE:
|
|
if (get_pattern_surface (pattern)->type == target->type)
|
|
classify = 0;
|
|
else if (get_pattern_surface (pattern)->type == CAIRO_SURFACE_TYPE_RECORDING)
|
|
classify = 1;
|
|
else
|
|
classify = 2;
|
|
break;
|
|
default:
|
|
case CAIRO_PATTERN_TYPE_SOLID:
|
|
classify = 3;
|
|
break;
|
|
case CAIRO_PATTERN_TYPE_LINEAR:
|
|
classify = 4;
|
|
break;
|
|
case CAIRO_PATTERN_TYPE_RADIAL:
|
|
classify = 5;
|
|
break;
|
|
case CAIRO_PATTERN_TYPE_MESH:
|
|
classify = 6;
|
|
break;
|
|
}
|
|
return classify;
|
|
}
|
|
|
|
static void
|
|
add_pattern (struct pattern *stats,
|
|
const cairo_pattern_t *pattern,
|
|
const cairo_surface_t *target)
|
|
{
|
|
stats->type[classify_pattern(pattern, target)]++;
|
|
}
|
|
|
|
static int
|
|
classify_path (const cairo_path_fixed_t *path,
|
|
cairo_bool_t is_fill)
|
|
{
|
|
int classify;
|
|
|
|
/* XXX improve for stroke */
|
|
classify = -1;
|
|
if (is_fill) {
|
|
if (path->fill_is_empty)
|
|
classify = 0;
|
|
else if (_cairo_path_fixed_fill_is_rectilinear (path))
|
|
classify = path->fill_maybe_region ? 1 : 2;
|
|
} else {
|
|
if (_cairo_path_fixed_stroke_is_rectilinear (path))
|
|
classify = 2;
|
|
}
|
|
if (classify == -1)
|
|
classify = 3 + (path->has_curve_to != 0);
|
|
|
|
return classify;
|
|
}
|
|
|
|
static void
|
|
add_path (struct path *stats,
|
|
const cairo_path_fixed_t *path,
|
|
cairo_bool_t is_fill)
|
|
{
|
|
stats->type[classify_path(path, is_fill)]++;
|
|
}
|
|
|
|
static int
|
|
classify_clip (const cairo_clip_t *clip)
|
|
{
|
|
int classify;
|
|
|
|
if (clip == NULL)
|
|
classify = 0;
|
|
else if (_cairo_clip_is_region (clip))
|
|
classify = 1;
|
|
else if (clip->path == NULL)
|
|
classify = 2;
|
|
else if (clip->path->prev == NULL)
|
|
classify = 3;
|
|
else if (_cairo_clip_is_polygon (clip))
|
|
classify = 4;
|
|
else
|
|
classify = 5;
|
|
|
|
return classify;
|
|
}
|
|
|
|
static void
|
|
add_clip (struct clip *stats,
|
|
const cairo_clip_t *clip)
|
|
{
|
|
stats->type[classify_clip (clip)]++;
|
|
}
|
|
|
|
static void
|
|
stats_add (struct stat *s, double v)
|
|
{
|
|
if (v < s->min)
|
|
s->min = v;
|
|
if (v > s->max)
|
|
s->max = v;
|
|
s->sum += v;
|
|
s->sum_sq += v*v;
|
|
s->count++;
|
|
}
|
|
|
|
static void
|
|
add_extents (struct extents *stats,
|
|
const cairo_composite_rectangles_t *extents)
|
|
{
|
|
const cairo_rectangle_int_t *r = extents->is_bounded ? &extents->bounded :&extents->unbounded;
|
|
stats_add (&stats->area, r->width * r->height);
|
|
stats->bounded += extents->is_bounded != 0;
|
|
stats->unbounded += extents->is_bounded == 0;
|
|
}
|
|
|
|
/* device interface */
|
|
|
|
static void
|
|
_cairo_device_observer_lock (void *_device)
|
|
{
|
|
cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
|
|
cairo_device_acquire (device->target);
|
|
}
|
|
|
|
static void
|
|
_cairo_device_observer_unlock (void *_device)
|
|
{
|
|
cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
|
|
cairo_device_release (device->target);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_device_observer_flush (void *_device)
|
|
{
|
|
cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
|
|
|
|
if (device->target == NULL)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
cairo_device_flush (device->target);
|
|
return device->target->status;
|
|
}
|
|
|
|
static void
|
|
_cairo_device_observer_finish (void *_device)
|
|
{
|
|
cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
|
|
log_fini (&device->log);
|
|
cairo_device_finish (device->target);
|
|
}
|
|
|
|
static void
|
|
_cairo_device_observer_destroy (void *_device)
|
|
{
|
|
cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
|
|
cairo_device_destroy (device->target);
|
|
free (device);
|
|
}
|
|
|
|
static const cairo_device_backend_t _cairo_device_observer_backend = {
|
|
CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER,
|
|
|
|
_cairo_device_observer_lock,
|
|
_cairo_device_observer_unlock,
|
|
|
|
_cairo_device_observer_flush,
|
|
_cairo_device_observer_finish,
|
|
_cairo_device_observer_destroy,
|
|
};
|
|
|
|
static cairo_device_t *
|
|
_cairo_device_create_observer_internal (cairo_device_t *target,
|
|
cairo_bool_t record)
|
|
{
|
|
cairo_device_observer_t *device;
|
|
cairo_status_t status;
|
|
|
|
device = malloc (sizeof (cairo_device_observer_t));
|
|
if (unlikely (device == NULL))
|
|
return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
|
|
_cairo_device_init (&device->base, &_cairo_device_observer_backend);
|
|
status = log_init (&device->log, record);
|
|
if (unlikely (status)) {
|
|
free (device);
|
|
return _cairo_device_create_in_error (status);
|
|
}
|
|
|
|
device->target = cairo_device_reference (target);
|
|
|
|
return &device->base;
|
|
}
|
|
|
|
/* surface interface */
|
|
|
|
static cairo_device_observer_t *
|
|
to_device (cairo_surface_observer_t *suface)
|
|
{
|
|
return (cairo_device_observer_t *)suface->base.device;
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_cairo_surface_create_observer_internal (cairo_device_t *device,
|
|
cairo_surface_t *target)
|
|
{
|
|
cairo_surface_observer_t *surface;
|
|
cairo_status_t status;
|
|
|
|
surface = malloc (sizeof (cairo_surface_observer_t));
|
|
if (unlikely (surface == NULL))
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
|
|
_cairo_surface_init (&surface->base,
|
|
&_cairo_surface_observer_backend, device,
|
|
target->content);
|
|
|
|
status = log_init (&surface->log,
|
|
((cairo_device_observer_t *)device)->log.record != NULL);
|
|
if (unlikely (status)) {
|
|
free (surface);
|
|
return _cairo_surface_create_in_error (status);
|
|
}
|
|
|
|
surface->target = cairo_surface_reference (target);
|
|
surface->base.type = surface->target->type;
|
|
surface->base.is_clear = surface->target->is_clear;
|
|
|
|
cairo_list_init (&surface->paint_callbacks);
|
|
cairo_list_init (&surface->mask_callbacks);
|
|
cairo_list_init (&surface->fill_callbacks);
|
|
cairo_list_init (&surface->stroke_callbacks);
|
|
cairo_list_init (&surface->glyphs_callbacks);
|
|
|
|
cairo_list_init (&surface->flush_callbacks);
|
|
cairo_list_init (&surface->finish_callbacks);
|
|
|
|
surface->log.num_surfaces++;
|
|
to_device (surface)->log.num_surfaces++;
|
|
|
|
return &surface->base;
|
|
}
|
|
|
|
static inline void
|
|
do_callbacks (cairo_surface_observer_t *surface, cairo_list_t *head)
|
|
{
|
|
struct callback_list *cb;
|
|
|
|
cairo_list_foreach_entry (cb, struct callback_list, head, link)
|
|
cb->func (&surface->base, surface->target, cb->data);
|
|
}
|
|
|
|
|
|
static cairo_status_t
|
|
_cairo_surface_observer_finish (void *abstract_surface)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
|
|
do_callbacks (surface, &surface->finish_callbacks);
|
|
|
|
cairo_surface_destroy (surface->target);
|
|
log_fini (&surface->log);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_cairo_surface_observer_create_similar (void *abstract_other,
|
|
cairo_content_t content,
|
|
int width, int height)
|
|
{
|
|
cairo_surface_observer_t *other = abstract_other;
|
|
cairo_surface_t *target, *surface;
|
|
|
|
target = NULL;
|
|
if (other->target->backend->create_similar)
|
|
target = other->target->backend->create_similar (other->target, content,
|
|
width, height);
|
|
if (target == NULL)
|
|
target = _cairo_image_surface_create_with_content (content,
|
|
width, height);
|
|
|
|
surface = _cairo_surface_create_observer_internal (other->base.device,
|
|
target);
|
|
cairo_surface_destroy (target);
|
|
|
|
return surface;
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_cairo_surface_observer_create_similar_image (void *other,
|
|
cairo_format_t format,
|
|
int width, int height)
|
|
{
|
|
cairo_surface_observer_t *surface = other;
|
|
|
|
if (surface->target->backend->create_similar_image)
|
|
return surface->target->backend->create_similar_image (surface->target,
|
|
format,
|
|
width, height);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_cairo_surface_observer_map_to_image (void *abstract_surface,
|
|
const cairo_rectangle_int_t *extents)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
|
|
if (surface->target->backend->map_to_image == NULL)
|
|
return NULL;
|
|
|
|
return surface->target->backend->map_to_image (surface->target, extents);
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_surface_observer_unmap_image (void *abstract_surface,
|
|
cairo_image_surface_t *image)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
|
|
if (surface->target->backend->unmap_image == NULL)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
return surface->target->backend->unmap_image (surface->target, image);
|
|
}
|
|
|
|
static void
|
|
record_target (cairo_observation_record_t *r,
|
|
cairo_surface_t *target)
|
|
{
|
|
cairo_rectangle_int_t extents;
|
|
|
|
r->target_content = target->content;
|
|
if (_cairo_surface_get_extents (target, &extents)) {
|
|
r->target_width = extents.width;
|
|
r->target_height = extents.height;
|
|
} else {
|
|
r->target_width = -1;
|
|
r->target_height = -1;
|
|
}
|
|
}
|
|
|
|
static cairo_observation_record_t *
|
|
record_paint (cairo_observation_record_t *r,
|
|
cairo_surface_t *target,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_clip_t *clip,
|
|
cairo_time_t elapsed)
|
|
{
|
|
record_target (r, target);
|
|
|
|
r->op = op;
|
|
r->source = classify_pattern (source, target);
|
|
r->mask = -1;
|
|
r->num_glyphs = -1;
|
|
r->path = -1;
|
|
r->fill_rule = -1;
|
|
r->tolerance = -1;
|
|
r->antialias = -1;
|
|
r->clip = classify_clip (clip);
|
|
r->elapsed = elapsed;
|
|
|
|
return r;
|
|
}
|
|
|
|
static cairo_observation_record_t *
|
|
record_mask (cairo_observation_record_t *r,
|
|
cairo_surface_t *target,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_pattern_t *mask,
|
|
const cairo_clip_t *clip,
|
|
cairo_time_t elapsed)
|
|
{
|
|
record_target (r, target);
|
|
|
|
r->op = op;
|
|
r->source = classify_pattern (source, target);
|
|
r->mask = classify_pattern (mask, target);
|
|
r->num_glyphs = -1;
|
|
r->path = -1;
|
|
r->fill_rule = -1;
|
|
r->tolerance = -1;
|
|
r->antialias = -1;
|
|
r->clip = classify_clip (clip);
|
|
r->elapsed = elapsed;
|
|
|
|
return r;
|
|
}
|
|
|
|
static cairo_observation_record_t *
|
|
record_fill (cairo_observation_record_t *r,
|
|
cairo_surface_t *target,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_path_fixed_t *path,
|
|
cairo_fill_rule_t fill_rule,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
const cairo_clip_t *clip,
|
|
cairo_time_t elapsed)
|
|
{
|
|
record_target (r, target);
|
|
|
|
r->op = op;
|
|
r->source = classify_pattern (source, target);
|
|
r->mask = -1;
|
|
r->num_glyphs = -1;
|
|
r->path = classify_path (path, TRUE);
|
|
r->fill_rule = fill_rule;
|
|
r->tolerance = tolerance;
|
|
r->antialias = antialias;
|
|
r->clip = classify_clip (clip);
|
|
r->elapsed = elapsed;
|
|
|
|
return r;
|
|
}
|
|
|
|
static cairo_observation_record_t *
|
|
record_stroke (cairo_observation_record_t *r,
|
|
cairo_surface_t *target,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_path_fixed_t *path,
|
|
const cairo_stroke_style_t *style,
|
|
const cairo_matrix_t *ctm,
|
|
const cairo_matrix_t *ctm_inverse,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
const cairo_clip_t *clip,
|
|
cairo_time_t elapsed)
|
|
{
|
|
record_target (r, target);
|
|
|
|
r->op = op;
|
|
r->source = classify_pattern (source, target);
|
|
r->mask = -1;
|
|
r->num_glyphs = -1;
|
|
r->path = classify_path (path, FALSE);
|
|
r->fill_rule = -1;
|
|
r->tolerance = tolerance;
|
|
r->antialias = antialias;
|
|
r->clip = classify_clip (clip);
|
|
r->elapsed = elapsed;
|
|
|
|
return r;
|
|
}
|
|
|
|
static cairo_observation_record_t *
|
|
record_glyphs (cairo_observation_record_t *r,
|
|
cairo_surface_t *target,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
cairo_scaled_font_t *scaled_font,
|
|
const cairo_clip_t *clip,
|
|
cairo_time_t elapsed)
|
|
{
|
|
record_target (r, target);
|
|
|
|
r->op = op;
|
|
r->source = classify_pattern (source, target);
|
|
r->mask = -1;
|
|
r->path = -1;
|
|
r->num_glyphs = num_glyphs;
|
|
r->fill_rule = -1;
|
|
r->tolerance = -1;
|
|
r->antialias = -1;
|
|
r->clip = classify_clip (clip);
|
|
r->elapsed = elapsed;
|
|
|
|
return r;
|
|
}
|
|
|
|
static void
|
|
add_record (cairo_observation_t *log,
|
|
cairo_observation_record_t *r)
|
|
{
|
|
cairo_int_status_t status;
|
|
|
|
r->index = log->record ? log->record->commands.num_elements : 0;
|
|
|
|
status = _cairo_array_append (&log->timings, r);
|
|
assert (status == CAIRO_INT_STATUS_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
sync (cairo_surface_t *target, int x, int y)
|
|
{
|
|
cairo_rectangle_int_t extents;
|
|
|
|
extents.x = x;
|
|
extents.y = y;
|
|
extents.width = 1;
|
|
extents.height = 1;
|
|
|
|
cairo_surface_unmap_image (target,
|
|
cairo_surface_map_to_image (target,
|
|
&extents));
|
|
}
|
|
|
|
static void
|
|
midpt (const cairo_composite_rectangles_t *extents, int *x, int *y)
|
|
{
|
|
*x = extents->bounded.x + extents->bounded.width / 2;
|
|
*y = extents->bounded.y + extents->bounded.height / 2;
|
|
}
|
|
|
|
static void
|
|
add_record_paint (cairo_observation_t *log,
|
|
cairo_surface_t *target,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_clip_t *clip,
|
|
cairo_time_t elapsed)
|
|
{
|
|
cairo_observation_record_t record;
|
|
cairo_int_status_t status;
|
|
|
|
add_record (log,
|
|
record_paint (&record, target, op, source, clip, elapsed));
|
|
|
|
/* We have to bypass the high-level surface layer in case it tries to be
|
|
* too smart and discard operations; we need to record exactly what just
|
|
* happened on the target.
|
|
*/
|
|
if (log->record) {
|
|
status = log->record->base.backend->paint (&log->record->base,
|
|
op, source, clip);
|
|
assert (status == CAIRO_INT_STATUS_SUCCESS);
|
|
}
|
|
|
|
if (_cairo_time_gt (elapsed, log->paint.slowest.elapsed))
|
|
log->paint.slowest = record;
|
|
log->paint.elapsed = _cairo_time_add (log->paint.elapsed, elapsed);
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_surface_observer_paint (void *abstract_surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_clip_t *clip)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
cairo_device_observer_t *device = to_device (surface);
|
|
cairo_composite_rectangles_t composite;
|
|
cairo_int_status_t status;
|
|
cairo_time_t t;
|
|
int x, y;
|
|
|
|
/* XXX device locking */
|
|
|
|
surface->log.paint.count++;
|
|
surface->log.paint.operators[op]++;
|
|
add_pattern (&surface->log.paint.source, source, surface->target);
|
|
add_clip (&surface->log.paint.clip, clip);
|
|
|
|
device->log.paint.count++;
|
|
device->log.paint.operators[op]++;
|
|
add_pattern (&device->log.paint.source, source, surface->target);
|
|
add_clip (&device->log.paint.clip, clip);
|
|
|
|
status = _cairo_composite_rectangles_init_for_paint (&composite,
|
|
surface->target,
|
|
op, source,
|
|
clip);
|
|
if (unlikely (status)) {
|
|
surface->log.paint.noop++;
|
|
device->log.paint.noop++;
|
|
return status;
|
|
}
|
|
|
|
midpt (&composite, &x, &y);
|
|
|
|
add_extents (&surface->log.paint.extents, &composite);
|
|
add_extents (&device->log.paint.extents, &composite);
|
|
_cairo_composite_rectangles_fini (&composite);
|
|
|
|
t = _cairo_time_get ();
|
|
status = _cairo_surface_paint (surface->target,
|
|
op, source,
|
|
clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
sync (surface->target, x, y);
|
|
t = _cairo_time_get_delta (t);
|
|
|
|
add_record_paint (&surface->log, surface->target, op, source, clip, t);
|
|
add_record_paint (&device->log, surface->target, op, source, clip, t);
|
|
|
|
do_callbacks (surface, &surface->paint_callbacks);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
add_record_mask (cairo_observation_t *log,
|
|
cairo_surface_t *target,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_pattern_t *mask,
|
|
const cairo_clip_t *clip,
|
|
cairo_time_t elapsed)
|
|
{
|
|
cairo_observation_record_t record;
|
|
cairo_int_status_t status;
|
|
|
|
add_record (log,
|
|
record_mask (&record, target, op, source, mask, clip, elapsed));
|
|
|
|
if (log->record) {
|
|
status = log->record->base.backend->mask (&log->record->base,
|
|
op, source, mask, clip);
|
|
assert (status == CAIRO_INT_STATUS_SUCCESS);
|
|
}
|
|
|
|
if (_cairo_time_gt (elapsed, log->mask.slowest.elapsed))
|
|
log->mask.slowest = record;
|
|
log->mask.elapsed = _cairo_time_add (log->mask.elapsed, elapsed);
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_surface_observer_mask (void *abstract_surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_pattern_t *mask,
|
|
const cairo_clip_t *clip)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
cairo_device_observer_t *device = to_device (surface);
|
|
cairo_composite_rectangles_t composite;
|
|
cairo_int_status_t status;
|
|
cairo_time_t t;
|
|
int x, y;
|
|
|
|
surface->log.mask.count++;
|
|
surface->log.mask.operators[op]++;
|
|
add_pattern (&surface->log.mask.source, source, surface->target);
|
|
add_pattern (&surface->log.mask.mask, mask, surface->target);
|
|
add_clip (&surface->log.mask.clip, clip);
|
|
|
|
device->log.mask.count++;
|
|
device->log.mask.operators[op]++;
|
|
add_pattern (&device->log.mask.source, source, surface->target);
|
|
add_pattern (&device->log.mask.mask, mask, surface->target);
|
|
add_clip (&device->log.mask.clip, clip);
|
|
|
|
status = _cairo_composite_rectangles_init_for_mask (&composite,
|
|
surface->target,
|
|
op, source, mask,
|
|
clip);
|
|
if (unlikely (status)) {
|
|
surface->log.mask.noop++;
|
|
device->log.mask.noop++;
|
|
return status;
|
|
}
|
|
|
|
midpt (&composite, &x, &y);
|
|
|
|
add_extents (&surface->log.mask.extents, &composite);
|
|
add_extents (&device->log.mask.extents, &composite);
|
|
_cairo_composite_rectangles_fini (&composite);
|
|
|
|
t = _cairo_time_get ();
|
|
status = _cairo_surface_mask (surface->target,
|
|
op, source, mask,
|
|
clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
sync (surface->target, x, y);
|
|
t = _cairo_time_get_delta (t);
|
|
|
|
add_record_mask (&surface->log,
|
|
surface->target, op, source, mask, clip,
|
|
t);
|
|
add_record_mask (&device->log,
|
|
surface->target, op, source, mask, clip,
|
|
t);
|
|
|
|
do_callbacks (surface, &surface->mask_callbacks);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
add_record_fill (cairo_observation_t *log,
|
|
cairo_surface_t *target,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_path_fixed_t *path,
|
|
cairo_fill_rule_t fill_rule,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
const cairo_clip_t *clip,
|
|
cairo_time_t elapsed)
|
|
{
|
|
cairo_observation_record_t record;
|
|
cairo_int_status_t status;
|
|
|
|
add_record (log,
|
|
record_fill (&record,
|
|
target, op, source,
|
|
path, fill_rule, tolerance, antialias,
|
|
clip, elapsed));
|
|
|
|
if (log->record) {
|
|
status = log->record->base.backend->fill (&log->record->base,
|
|
op, source,
|
|
path, fill_rule,
|
|
tolerance, antialias,
|
|
clip);
|
|
assert (status == CAIRO_INT_STATUS_SUCCESS);
|
|
}
|
|
|
|
if (_cairo_time_gt (elapsed, log->fill.slowest.elapsed))
|
|
log->fill.slowest = record;
|
|
log->fill.elapsed = _cairo_time_add (log->fill.elapsed, elapsed);
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_surface_observer_fill (void *abstract_surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_path_fixed_t *path,
|
|
cairo_fill_rule_t fill_rule,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
const cairo_clip_t *clip)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
cairo_device_observer_t *device = to_device (surface);
|
|
cairo_composite_rectangles_t composite;
|
|
cairo_int_status_t status;
|
|
cairo_time_t t;
|
|
int x, y;
|
|
|
|
surface->log.fill.count++;
|
|
surface->log.fill.operators[op]++;
|
|
surface->log.fill.fill_rule[fill_rule]++;
|
|
surface->log.fill.antialias[antialias]++;
|
|
add_pattern (&surface->log.fill.source, source, surface->target);
|
|
add_path (&surface->log.fill.path, path, TRUE);
|
|
add_clip (&surface->log.fill.clip, clip);
|
|
|
|
device->log.fill.count++;
|
|
device->log.fill.operators[op]++;
|
|
device->log.fill.fill_rule[fill_rule]++;
|
|
device->log.fill.antialias[antialias]++;
|
|
add_pattern (&device->log.fill.source, source, surface->target);
|
|
add_path (&device->log.fill.path, path, TRUE);
|
|
add_clip (&device->log.fill.clip, clip);
|
|
|
|
status = _cairo_composite_rectangles_init_for_fill (&composite,
|
|
surface->target,
|
|
op, source, path,
|
|
clip);
|
|
if (unlikely (status)) {
|
|
surface->log.fill.noop++;
|
|
device->log.fill.noop++;
|
|
return status;
|
|
}
|
|
|
|
midpt (&composite, &x, &y);
|
|
|
|
add_extents (&surface->log.fill.extents, &composite);
|
|
add_extents (&device->log.fill.extents, &composite);
|
|
_cairo_composite_rectangles_fini (&composite);
|
|
|
|
t = _cairo_time_get ();
|
|
status = _cairo_surface_fill (surface->target,
|
|
op, source, path,
|
|
fill_rule, tolerance, antialias,
|
|
clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
sync (surface->target, x, y);
|
|
t = _cairo_time_get_delta (t);
|
|
|
|
add_record_fill (&surface->log,
|
|
surface->target, op, source, path,
|
|
fill_rule, tolerance, antialias,
|
|
clip, t);
|
|
|
|
add_record_fill (&device->log,
|
|
surface->target, op, source, path,
|
|
fill_rule, tolerance, antialias,
|
|
clip, t);
|
|
|
|
do_callbacks (surface, &surface->fill_callbacks);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
add_record_stroke (cairo_observation_t *log,
|
|
cairo_surface_t *target,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_path_fixed_t *path,
|
|
const cairo_stroke_style_t *style,
|
|
const cairo_matrix_t *ctm,
|
|
const cairo_matrix_t *ctm_inverse,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
const cairo_clip_t *clip,
|
|
cairo_time_t elapsed)
|
|
{
|
|
cairo_observation_record_t record;
|
|
cairo_int_status_t status;
|
|
|
|
add_record (log,
|
|
record_stroke (&record,
|
|
target, op, source,
|
|
path, style, ctm,ctm_inverse,
|
|
tolerance, antialias,
|
|
clip, elapsed));
|
|
|
|
if (log->record) {
|
|
status = log->record->base.backend->stroke (&log->record->base,
|
|
op, source,
|
|
path, style, ctm,ctm_inverse,
|
|
tolerance, antialias,
|
|
clip);
|
|
assert (status == CAIRO_INT_STATUS_SUCCESS);
|
|
}
|
|
|
|
if (_cairo_time_gt (elapsed, log->stroke.slowest.elapsed))
|
|
log->stroke.slowest = record;
|
|
log->stroke.elapsed = _cairo_time_add (log->stroke.elapsed, elapsed);
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_surface_observer_stroke (void *abstract_surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_path_fixed_t *path,
|
|
const cairo_stroke_style_t *style,
|
|
const cairo_matrix_t *ctm,
|
|
const cairo_matrix_t *ctm_inverse,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
const cairo_clip_t *clip)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
cairo_device_observer_t *device = to_device (surface);
|
|
cairo_composite_rectangles_t composite;
|
|
cairo_int_status_t status;
|
|
cairo_time_t t;
|
|
int x, y;
|
|
|
|
surface->log.stroke.count++;
|
|
surface->log.stroke.operators[op]++;
|
|
surface->log.stroke.antialias[antialias]++;
|
|
surface->log.stroke.caps[style->line_cap]++;
|
|
surface->log.stroke.joins[style->line_join]++;
|
|
add_pattern (&surface->log.stroke.source, source, surface->target);
|
|
add_path (&surface->log.stroke.path, path, FALSE);
|
|
add_clip (&surface->log.stroke.clip, clip);
|
|
|
|
device->log.stroke.count++;
|
|
device->log.stroke.operators[op]++;
|
|
device->log.stroke.antialias[antialias]++;
|
|
device->log.stroke.caps[style->line_cap]++;
|
|
device->log.stroke.joins[style->line_join]++;
|
|
add_pattern (&device->log.stroke.source, source, surface->target);
|
|
add_path (&device->log.stroke.path, path, FALSE);
|
|
add_clip (&device->log.stroke.clip, clip);
|
|
|
|
status = _cairo_composite_rectangles_init_for_stroke (&composite,
|
|
surface->target,
|
|
op, source,
|
|
path, style, ctm,
|
|
clip);
|
|
if (unlikely (status)) {
|
|
surface->log.stroke.noop++;
|
|
device->log.stroke.noop++;
|
|
return status;
|
|
}
|
|
|
|
midpt (&composite, &x, &y);
|
|
|
|
add_extents (&surface->log.stroke.extents, &composite);
|
|
add_extents (&device->log.stroke.extents, &composite);
|
|
_cairo_composite_rectangles_fini (&composite);
|
|
|
|
t = _cairo_time_get ();
|
|
status = _cairo_surface_stroke (surface->target,
|
|
op, source, path,
|
|
style, ctm, ctm_inverse,
|
|
tolerance, antialias,
|
|
clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
sync (surface->target, x, y);
|
|
t = _cairo_time_get_delta (t);
|
|
|
|
add_record_stroke (&surface->log,
|
|
surface->target, op, source, path,
|
|
style, ctm,ctm_inverse,
|
|
tolerance, antialias,
|
|
clip, t);
|
|
|
|
add_record_stroke (&device->log,
|
|
surface->target, op, source, path,
|
|
style, ctm,ctm_inverse,
|
|
tolerance, antialias,
|
|
clip, t);
|
|
|
|
do_callbacks (surface, &surface->stroke_callbacks);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
add_record_glyphs (cairo_observation_t *log,
|
|
cairo_surface_t *target,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t*source,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
cairo_scaled_font_t *scaled_font,
|
|
const cairo_clip_t *clip,
|
|
cairo_time_t elapsed)
|
|
{
|
|
cairo_observation_record_t record;
|
|
cairo_int_status_t status;
|
|
|
|
add_record (log,
|
|
record_glyphs (&record,
|
|
target, op, source,
|
|
glyphs, num_glyphs, scaled_font,
|
|
clip, elapsed));
|
|
|
|
if (log->record) {
|
|
status = log->record->base.backend->show_text_glyphs (&log->record->base,
|
|
op, source,
|
|
NULL, 0,
|
|
glyphs, num_glyphs,
|
|
NULL, 0, 0,
|
|
scaled_font,
|
|
clip);
|
|
assert (status == CAIRO_INT_STATUS_SUCCESS);
|
|
}
|
|
|
|
if (_cairo_time_gt (elapsed, log->glyphs.slowest.elapsed))
|
|
log->glyphs.slowest = record;
|
|
log->glyphs.elapsed = _cairo_time_add (log->glyphs.elapsed, elapsed);
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_surface_observer_glyphs (void *abstract_surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
cairo_scaled_font_t *scaled_font,
|
|
const cairo_clip_t *clip)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
cairo_device_observer_t *device = to_device (surface);
|
|
cairo_composite_rectangles_t composite;
|
|
cairo_int_status_t status;
|
|
cairo_glyph_t *dev_glyphs;
|
|
cairo_time_t t;
|
|
int x, y;
|
|
|
|
surface->log.glyphs.count++;
|
|
surface->log.glyphs.operators[op]++;
|
|
add_pattern (&surface->log.glyphs.source, source, surface->target);
|
|
add_clip (&surface->log.glyphs.clip, clip);
|
|
|
|
device->log.glyphs.count++;
|
|
device->log.glyphs.operators[op]++;
|
|
add_pattern (&device->log.glyphs.source, source, surface->target);
|
|
add_clip (&device->log.glyphs.clip, clip);
|
|
|
|
status = _cairo_composite_rectangles_init_for_glyphs (&composite,
|
|
surface->target,
|
|
op, source,
|
|
scaled_font,
|
|
glyphs, num_glyphs,
|
|
clip,
|
|
NULL);
|
|
if (unlikely (status)) {
|
|
surface->log.glyphs.noop++;
|
|
device->log.glyphs.noop++;
|
|
return status;
|
|
}
|
|
|
|
midpt (&composite, &x, &y);
|
|
|
|
add_extents (&surface->log.glyphs.extents, &composite);
|
|
add_extents (&device->log.glyphs.extents, &composite);
|
|
_cairo_composite_rectangles_fini (&composite);
|
|
|
|
/* XXX We have to copy the glyphs, because the backend is allowed to
|
|
* modify! */
|
|
dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
|
|
if (unlikely (dev_glyphs == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
memcpy (dev_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t));
|
|
|
|
t = _cairo_time_get ();
|
|
status = _cairo_surface_show_text_glyphs (surface->target, op, source,
|
|
NULL, 0,
|
|
dev_glyphs, num_glyphs,
|
|
NULL, 0, 0,
|
|
scaled_font,
|
|
clip);
|
|
free (dev_glyphs);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
sync (surface->target, x, y);
|
|
t = _cairo_time_get_delta (t);
|
|
|
|
add_record_glyphs (&surface->log,
|
|
surface->target, op, source,
|
|
glyphs, num_glyphs, scaled_font,
|
|
clip, t);
|
|
|
|
add_record_glyphs (&device->log,
|
|
surface->target, op, source,
|
|
glyphs, num_glyphs, scaled_font,
|
|
clip, t);
|
|
|
|
do_callbacks (surface, &surface->glyphs_callbacks);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_surface_observer_flush (void *abstract_surface)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
|
|
do_callbacks (surface, &surface->flush_callbacks);
|
|
|
|
cairo_surface_flush (surface->target);
|
|
return surface->target->status;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_surface_observer_mark_dirty (void *abstract_surface,
|
|
int x, int y,
|
|
int width, int height)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
cairo_status_t status;
|
|
|
|
printf ("mark-dirty (%d, %d) x (%d, %d)\n", x, y, width, height);
|
|
|
|
status = CAIRO_STATUS_SUCCESS;
|
|
if (surface->target->backend->mark_dirty_rectangle)
|
|
status = surface->target->backend->mark_dirty_rectangle (surface->target,
|
|
x,y, width,height);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_surface_observer_copy_page (void *abstract_surface)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
cairo_status_t status;
|
|
|
|
status = CAIRO_STATUS_SUCCESS;
|
|
if (surface->target->backend->copy_page)
|
|
status = surface->target->backend->copy_page (surface->target);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_surface_observer_show_page (void *abstract_surface)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
cairo_status_t status;
|
|
|
|
status = CAIRO_STATUS_SUCCESS;
|
|
if (surface->target->backend->show_page)
|
|
status = surface->target->backend->show_page (surface->target);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_cairo_surface_observer_get_extents (void *abstract_surface,
|
|
cairo_rectangle_int_t *extents)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
return _cairo_surface_get_extents (surface->target, extents);
|
|
}
|
|
|
|
static void
|
|
_cairo_surface_observer_get_font_options (void *abstract_surface,
|
|
cairo_font_options_t *options)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
|
|
if (surface->target->backend->get_font_options != NULL)
|
|
surface->target->backend->get_font_options (surface->target, options);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_surface_observer_acquire_source_image (void *abstract_surface,
|
|
cairo_image_surface_t **image_out,
|
|
void **image_extra)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
|
|
surface->log.num_sources_acquired++;
|
|
to_device (surface)->log.num_sources_acquired++;
|
|
|
|
return _cairo_surface_acquire_source_image (surface->target,
|
|
image_out, image_extra);
|
|
}
|
|
|
|
static void
|
|
_cairo_surface_observer_release_source_image (void *abstract_surface,
|
|
cairo_image_surface_t *image,
|
|
void *image_extra)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
|
|
_cairo_surface_release_source_image (surface->target, image, image_extra);
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_cairo_surface_observer_snapshot (void *abstract_surface)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
|
|
/* XXX hook onto the snapshot so that we measure number of reads */
|
|
|
|
if (surface->target->backend->snapshot)
|
|
return surface->target->backend->snapshot (surface->target);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static cairo_t *
|
|
_cairo_surface_observer_create_context(void *target)
|
|
{
|
|
cairo_surface_observer_t *surface = target;
|
|
|
|
if (_cairo_surface_is_subsurface (&surface->base))
|
|
surface = (cairo_surface_observer_t *)
|
|
_cairo_surface_subsurface_get_target (&surface->base);
|
|
|
|
surface->log.num_contexts++;
|
|
to_device (surface)->log.num_contexts++;
|
|
|
|
return surface->target->backend->create_context (target);
|
|
}
|
|
|
|
static const cairo_surface_backend_t _cairo_surface_observer_backend = {
|
|
CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER,
|
|
_cairo_surface_observer_finish,
|
|
|
|
_cairo_surface_observer_create_context,
|
|
|
|
_cairo_surface_observer_create_similar,
|
|
_cairo_surface_observer_create_similar_image,
|
|
_cairo_surface_observer_map_to_image,
|
|
_cairo_surface_observer_unmap_image,
|
|
|
|
_cairo_surface_observer_acquire_source_image,
|
|
_cairo_surface_observer_release_source_image,
|
|
_cairo_surface_observer_snapshot,
|
|
|
|
_cairo_surface_observer_copy_page,
|
|
_cairo_surface_observer_show_page,
|
|
|
|
_cairo_surface_observer_get_extents,
|
|
_cairo_surface_observer_get_font_options,
|
|
|
|
_cairo_surface_observer_flush,
|
|
_cairo_surface_observer_mark_dirty,
|
|
|
|
_cairo_surface_observer_paint,
|
|
_cairo_surface_observer_mask,
|
|
_cairo_surface_observer_stroke,
|
|
_cairo_surface_observer_fill,
|
|
NULL, /* fill-stroke */
|
|
_cairo_surface_observer_glyphs,
|
|
};
|
|
|
|
/**
|
|
* cairo_surface_create_observer:
|
|
* @target: an existing surface for which the observer will watch
|
|
*
|
|
* Create a new surface that exists solely to watch another is doing. In
|
|
* the process it will log operations and times, which are fast, which are
|
|
* slow, which are frequent, etc.
|
|
*
|
|
* Return value: a pointer to the newly allocated surface. The caller
|
|
* owns the surface and should call cairo_surface_destroy() when done
|
|
* with it.
|
|
*
|
|
* This function always returns a valid pointer, but it will return a
|
|
* pointer to a "nil" surface if @other is already in an error state
|
|
* or any other error occurs.
|
|
*
|
|
* Since: 1.12
|
|
**/
|
|
cairo_surface_t *
|
|
cairo_surface_create_observer (cairo_surface_t *target,
|
|
cairo_surface_observer_mode_t mode)
|
|
{
|
|
cairo_device_t *device;
|
|
cairo_surface_t *surface;
|
|
cairo_bool_t record;
|
|
|
|
if (unlikely (target->status))
|
|
return _cairo_surface_create_in_error (target->status);
|
|
if (unlikely (target->finished))
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
|
|
|
|
record = mode & CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS;
|
|
device = _cairo_device_create_observer_internal (target->device, record);
|
|
if (unlikely (device->status))
|
|
return _cairo_surface_create_in_error (device->status);
|
|
|
|
surface = _cairo_surface_create_observer_internal (device, target);
|
|
cairo_device_destroy (device);
|
|
|
|
return surface;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_surface_observer_add_callback (cairo_list_t *head,
|
|
cairo_surface_observer_callback_t func,
|
|
void *data)
|
|
{
|
|
struct callback_list *cb;
|
|
|
|
cb = malloc (sizeof (*cb));
|
|
if (unlikely (cb == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
cairo_list_add (&cb->link, head);
|
|
cb->func = func;
|
|
cb->data = data;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
cairo_status_t
|
|
cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface,
|
|
cairo_surface_observer_callback_t func,
|
|
void *data)
|
|
{
|
|
cairo_surface_observer_t *surface;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
|
|
return abstract_surface->status;
|
|
|
|
if (! _cairo_surface_is_observer (abstract_surface))
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
|
|
surface = (cairo_surface_observer_t *)abstract_surface;
|
|
return _cairo_surface_observer_add_callback (&surface->paint_callbacks,
|
|
func, data);
|
|
}
|
|
|
|
cairo_status_t
|
|
cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface,
|
|
cairo_surface_observer_callback_t func,
|
|
void *data)
|
|
{
|
|
cairo_surface_observer_t *surface;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
|
|
return abstract_surface->status;
|
|
|
|
if (! _cairo_surface_is_observer (abstract_surface))
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
|
|
surface = (cairo_surface_observer_t *)abstract_surface;
|
|
return _cairo_surface_observer_add_callback (&surface->mask_callbacks,
|
|
func, data);
|
|
}
|
|
|
|
cairo_status_t
|
|
cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface,
|
|
cairo_surface_observer_callback_t func,
|
|
void *data)
|
|
{
|
|
cairo_surface_observer_t *surface;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
|
|
return abstract_surface->status;
|
|
|
|
if (! _cairo_surface_is_observer (abstract_surface))
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
|
|
surface = (cairo_surface_observer_t *)abstract_surface;
|
|
return _cairo_surface_observer_add_callback (&surface->fill_callbacks,
|
|
func, data);
|
|
}
|
|
|
|
cairo_status_t
|
|
cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface,
|
|
cairo_surface_observer_callback_t func,
|
|
void *data)
|
|
{
|
|
cairo_surface_observer_t *surface;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
|
|
return abstract_surface->status;
|
|
|
|
if (! _cairo_surface_is_observer (abstract_surface))
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
|
|
surface = (cairo_surface_observer_t *)abstract_surface;
|
|
return _cairo_surface_observer_add_callback (&surface->stroke_callbacks,
|
|
func, data);
|
|
}
|
|
|
|
cairo_status_t
|
|
cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface,
|
|
cairo_surface_observer_callback_t func,
|
|
void *data)
|
|
{
|
|
cairo_surface_observer_t *surface;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
|
|
return abstract_surface->status;
|
|
|
|
if (! _cairo_surface_is_observer (abstract_surface))
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
|
|
surface = (cairo_surface_observer_t *)abstract_surface;
|
|
return _cairo_surface_observer_add_callback (&surface->glyphs_callbacks,
|
|
func, data);
|
|
}
|
|
|
|
cairo_status_t
|
|
cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface,
|
|
cairo_surface_observer_callback_t func,
|
|
void *data)
|
|
{
|
|
cairo_surface_observer_t *surface;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
|
|
return abstract_surface->status;
|
|
|
|
if (! _cairo_surface_is_observer (abstract_surface))
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
|
|
surface = (cairo_surface_observer_t *)abstract_surface;
|
|
return _cairo_surface_observer_add_callback (&surface->flush_callbacks,
|
|
func, data);
|
|
}
|
|
|
|
cairo_status_t
|
|
cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface,
|
|
cairo_surface_observer_callback_t func,
|
|
void *data)
|
|
{
|
|
cairo_surface_observer_t *surface;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
|
|
return abstract_surface->status;
|
|
|
|
if (! _cairo_surface_is_observer (abstract_surface))
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
|
|
surface = (cairo_surface_observer_t *)abstract_surface;
|
|
return _cairo_surface_observer_add_callback (&surface->finish_callbacks,
|
|
func, data);
|
|
}
|
|
|
|
static void
|
|
print_extents (cairo_output_stream_t *stream, const struct extents *e)
|
|
{
|
|
_cairo_output_stream_printf (stream,
|
|
" extents: total %g, avg %g [unbounded %d]\n",
|
|
e->area.sum,
|
|
e->area.sum / e->area.count,
|
|
e->unbounded);
|
|
}
|
|
|
|
static inline int ordercmp (int a, int b, const unsigned int *array)
|
|
{
|
|
/* high to low */
|
|
return array[b] - array[a];
|
|
}
|
|
CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_order, int, ordercmp)
|
|
|
|
static void
|
|
print_array (cairo_output_stream_t *stream,
|
|
const unsigned int *array,
|
|
const char **names,
|
|
int count)
|
|
{
|
|
int order[64];
|
|
int i, j;
|
|
|
|
assert (count < ARRAY_LENGTH (order));
|
|
for (i = j = 0; i < count; i++) {
|
|
if (array[i] != 0)
|
|
order[j++] = i;
|
|
}
|
|
|
|
sort_order (order, j, (void *)array);
|
|
for (i = 0; i < j; i++)
|
|
_cairo_output_stream_printf (stream, " %d %s%s",
|
|
array[order[i]], names[order[i]],
|
|
i < j -1 ? "," : "");
|
|
}
|
|
|
|
static const char *operator_names[] = {
|
|
"CLEAR", /* CAIRO_OPERATOR_CLEAR */
|
|
|
|
"SOURCE", /* CAIRO_OPERATOR_SOURCE */
|
|
"OVER", /* CAIRO_OPERATOR_OVER */
|
|
"IN", /* CAIRO_OPERATOR_IN */
|
|
"OUT", /* CAIRO_OPERATOR_OUT */
|
|
"ATOP", /* CAIRO_OPERATOR_ATOP */
|
|
|
|
"DEST", /* CAIRO_OPERATOR_DEST */
|
|
"DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */
|
|
"DEST_IN", /* CAIRO_OPERATOR_DEST_IN */
|
|
"DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */
|
|
"DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */
|
|
|
|
"XOR", /* CAIRO_OPERATOR_XOR */
|
|
"ADD", /* CAIRO_OPERATOR_ADD */
|
|
"SATURATE", /* CAIRO_OPERATOR_SATURATE */
|
|
|
|
"MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */
|
|
"SCREEN", /* CAIRO_OPERATOR_SCREEN */
|
|
"OVERLAY", /* CAIRO_OPERATOR_OVERLAY */
|
|
"DARKEN", /* CAIRO_OPERATOR_DARKEN */
|
|
"LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */
|
|
"DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */
|
|
"BURN", /* CAIRO_OPERATOR_COLOR_BURN */
|
|
"HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */
|
|
"SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */
|
|
"DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */
|
|
"EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */
|
|
"HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */
|
|
"HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
|
|
"HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */
|
|
"HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
|
|
};
|
|
static void
|
|
print_operators (cairo_output_stream_t *stream, unsigned int *array)
|
|
{
|
|
_cairo_output_stream_printf (stream, " op:");
|
|
print_array (stream, array, operator_names, NUM_OPERATORS);
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
}
|
|
|
|
static const char *fill_rule_names[] = {
|
|
"non-zero",
|
|
"even-odd",
|
|
};
|
|
static void
|
|
print_fill_rule (cairo_output_stream_t *stream, unsigned int *array)
|
|
{
|
|
_cairo_output_stream_printf (stream, " fill rule:");
|
|
print_array (stream, array, fill_rule_names, ARRAY_LENGTH(fill_rule_names));
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
}
|
|
|
|
static const char *cap_names[] = {
|
|
"butt", /* CAIRO_LINE_CAP_BUTT */
|
|
"round", /* CAIRO_LINE_CAP_ROUND */
|
|
"square" /* CAIRO_LINE_CAP_SQUARE */
|
|
};
|
|
static void
|
|
print_line_caps (cairo_output_stream_t *stream, unsigned int *array)
|
|
{
|
|
_cairo_output_stream_printf (stream, " caps:");
|
|
print_array (stream, array, cap_names, NUM_CAPS);
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
}
|
|
|
|
static const char *join_names[] = {
|
|
"miter", /* CAIRO_LINE_JOIN_MITER */
|
|
"round", /* CAIRO_LINE_JOIN_ROUND */
|
|
"bevel", /* CAIRO_LINE_JOIN_BEVEL */
|
|
};
|
|
static void
|
|
print_line_joins (cairo_output_stream_t *stream, unsigned int *array)
|
|
{
|
|
_cairo_output_stream_printf (stream, " joins:");
|
|
print_array (stream, array, join_names, NUM_JOINS);
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
}
|
|
|
|
static const char *antialias_names[] = {
|
|
"default",
|
|
"none",
|
|
"gray",
|
|
"subpixel",
|
|
"fast",
|
|
"good",
|
|
"best"
|
|
};
|
|
static void
|
|
print_antialias (cairo_output_stream_t *stream, unsigned int *array)
|
|
{
|
|
_cairo_output_stream_printf (stream, " antialias:");
|
|
print_array (stream, array, antialias_names, NUM_ANTIALIAS);
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
}
|
|
|
|
static const char *pattern_names[] = {
|
|
"native",
|
|
"record",
|
|
"other surface",
|
|
"solid",
|
|
"linear",
|
|
"radial",
|
|
"mesh"
|
|
};
|
|
static void
|
|
print_pattern (cairo_output_stream_t *stream,
|
|
const char *name,
|
|
const struct pattern *p)
|
|
{
|
|
_cairo_output_stream_printf (stream, " %s:", name);
|
|
print_array (stream, p->type, pattern_names, ARRAY_LENGTH (pattern_names));
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
}
|
|
|
|
static const char *path_names[] = {
|
|
"empty",
|
|
"pixel-aligned",
|
|
"rectliinear",
|
|
"straight",
|
|
"curved",
|
|
};
|
|
static void
|
|
print_path (cairo_output_stream_t *stream,
|
|
const struct path *p)
|
|
{
|
|
_cairo_output_stream_printf (stream, " path:");
|
|
print_array (stream, p->type, path_names, ARRAY_LENGTH (path_names));
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
}
|
|
|
|
static const char *clip_names[] = {
|
|
"none",
|
|
"region",
|
|
"boxes",
|
|
"single path",
|
|
"polygon",
|
|
"general",
|
|
};
|
|
static void
|
|
print_clip (cairo_output_stream_t *stream, const struct clip *c)
|
|
{
|
|
_cairo_output_stream_printf (stream, " clip:");
|
|
print_array (stream, c->type, clip_names, ARRAY_LENGTH (clip_names));
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
}
|
|
|
|
static void
|
|
print_record (cairo_output_stream_t *stream,
|
|
cairo_observation_record_t *r)
|
|
{
|
|
_cairo_output_stream_printf (stream, " op: %s\n", operator_names[r->op]);
|
|
_cairo_output_stream_printf (stream, " source: %s\n",
|
|
pattern_names[r->source]);
|
|
if (r->mask != -1)
|
|
_cairo_output_stream_printf (stream, " mask: %s\n",
|
|
pattern_names[r->mask]);
|
|
if (r->num_glyphs != -1)
|
|
_cairo_output_stream_printf (stream, " num_glyphs: %d\n",
|
|
r->num_glyphs);
|
|
if (r->path != -1)
|
|
_cairo_output_stream_printf (stream, " path: %s\n",
|
|
path_names[r->path]);
|
|
if (r->fill_rule != -1)
|
|
_cairo_output_stream_printf (stream, " fill rule: %s\n",
|
|
fill_rule_names[r->fill_rule]);
|
|
if (r->antialias != -1)
|
|
_cairo_output_stream_printf (stream, " antialias: %s\n",
|
|
antialias_names[r->antialias]);
|
|
_cairo_output_stream_printf (stream, " clip: %s\n", clip_names[r->clip]);
|
|
_cairo_output_stream_printf (stream, " elapsed: %f ns\n",
|
|
_cairo_time_to_ns (r->elapsed));
|
|
}
|
|
|
|
static double percent (cairo_time_t a, cairo_time_t b)
|
|
{
|
|
/* Fake %.1f */
|
|
return _cairo_round (_cairo_time_to_s (a) * 1000 /
|
|
_cairo_time_to_s (b)) / 10;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
replay_record (cairo_observation_t *log,
|
|
cairo_observation_record_t *r,
|
|
cairo_device_t *script)
|
|
{
|
|
cairo_surface_t *surface;
|
|
cairo_int_status_t status;
|
|
|
|
if (log->record == NULL)
|
|
return FALSE;
|
|
|
|
surface = cairo_script_surface_create (script,
|
|
r->target_content,
|
|
r->target_width,
|
|
r->target_height);
|
|
status =
|
|
_cairo_recording_surface_replay_one (log->record, r->index, surface);
|
|
cairo_surface_destroy (surface);
|
|
|
|
assert (status == CAIRO_INT_STATUS_SUCCESS);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static cairo_time_t
|
|
_cairo_observation_total_elapsed (cairo_observation_t *log)
|
|
{
|
|
cairo_time_t total;
|
|
|
|
total = log->paint.elapsed;
|
|
total = _cairo_time_add (total, log->mask.elapsed);
|
|
total = _cairo_time_add (total, log->fill.elapsed);
|
|
total = _cairo_time_add (total, log->stroke.elapsed);
|
|
total = _cairo_time_add (total, log->glyphs.elapsed);
|
|
|
|
return total;
|
|
}
|
|
|
|
static void
|
|
_cairo_observation_print (cairo_output_stream_t *stream,
|
|
cairo_observation_t *log)
|
|
{
|
|
cairo_device_t *script;
|
|
cairo_time_t total;
|
|
|
|
script = _cairo_script_context_create_internal (stream);
|
|
_cairo_script_context_attach_snapshots (script, FALSE);
|
|
|
|
total = _cairo_observation_total_elapsed (log);
|
|
|
|
_cairo_output_stream_printf (stream, "elapsed: %f\n",
|
|
_cairo_time_to_ns (total));
|
|
_cairo_output_stream_printf (stream, "surfaces: %d\n",
|
|
log->num_surfaces);
|
|
_cairo_output_stream_printf (stream, "contexts: %d\n",
|
|
log->num_contexts);
|
|
_cairo_output_stream_printf (stream, "sources acquired: %d\n",
|
|
log->num_sources_acquired);
|
|
|
|
|
|
_cairo_output_stream_printf (stream, "paint: count %d [no-op %d], elapsed %f [%f%%]\n",
|
|
log->paint.count, log->paint.noop,
|
|
_cairo_time_to_ns (log->paint.elapsed),
|
|
percent (log->paint.elapsed, total));
|
|
if (log->paint.count) {
|
|
print_extents (stream, &log->paint.extents);
|
|
print_operators (stream, log->paint.operators);
|
|
print_pattern (stream, "source", &log->paint.source);
|
|
print_clip (stream, &log->paint.clip);
|
|
|
|
_cairo_output_stream_printf (stream, "slowest paint: %f%%\n",
|
|
percent (log->paint.slowest.elapsed,
|
|
log->paint.elapsed));
|
|
print_record (stream, &log->paint.slowest);
|
|
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
if (replay_record (log, &log->paint.slowest, script))
|
|
_cairo_output_stream_printf (stream, "\n\n");
|
|
}
|
|
|
|
_cairo_output_stream_printf (stream, "mask: count %d [no-op %d], elapsed %f [%f%%]\n",
|
|
log->mask.count, log->mask.noop,
|
|
_cairo_time_to_ns (log->mask.elapsed),
|
|
percent (log->mask.elapsed, total));
|
|
if (log->mask.count) {
|
|
print_extents (stream, &log->mask.extents);
|
|
print_operators (stream, log->mask.operators);
|
|
print_pattern (stream, "source", &log->mask.source);
|
|
print_pattern (stream, "mask", &log->mask.mask);
|
|
print_clip (stream, &log->mask.clip);
|
|
|
|
_cairo_output_stream_printf (stream, "slowest mask: %f%%\n",
|
|
percent (log->mask.slowest.elapsed,
|
|
log->mask.elapsed));
|
|
print_record (stream, &log->mask.slowest);
|
|
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
if (replay_record (log, &log->mask.slowest, script))
|
|
_cairo_output_stream_printf (stream, "\n\n");
|
|
}
|
|
|
|
_cairo_output_stream_printf (stream, "fill: count %d [no-op %d], elaspsed %f [%f%%]\n",
|
|
log->fill.count, log->fill.noop,
|
|
_cairo_time_to_ns (log->fill.elapsed),
|
|
percent (log->fill.elapsed, total));
|
|
if (log->fill.count) {
|
|
print_extents (stream, &log->fill.extents);
|
|
print_operators (stream, log->fill.operators);
|
|
print_pattern (stream, "source", &log->fill.source);
|
|
print_path (stream, &log->fill.path);
|
|
print_fill_rule (stream, log->fill.fill_rule);
|
|
print_antialias (stream, log->fill.antialias);
|
|
print_clip (stream, &log->fill.clip);
|
|
|
|
_cairo_output_stream_printf (stream, "slowest fill: %f%%\n",
|
|
percent (log->fill.slowest.elapsed,
|
|
log->fill.elapsed));
|
|
print_record (stream, &log->fill.slowest);
|
|
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
if (replay_record (log, &log->fill.slowest, script))
|
|
_cairo_output_stream_printf (stream, "\n\n");
|
|
}
|
|
|
|
_cairo_output_stream_printf (stream, "stroke: count %d [no-op %d], elapsed %f [%f%%]\n",
|
|
log->stroke.count, log->stroke.noop,
|
|
_cairo_time_to_ns (log->stroke.elapsed),
|
|
percent (log->stroke.elapsed, total));
|
|
if (log->stroke.count) {
|
|
print_extents (stream, &log->stroke.extents);
|
|
print_operators (stream, log->stroke.operators);
|
|
print_pattern (stream, "source", &log->stroke.source);
|
|
print_path (stream, &log->stroke.path);
|
|
print_antialias (stream, log->stroke.antialias);
|
|
print_line_caps (stream, log->stroke.caps);
|
|
print_line_joins (stream, log->stroke.joins);
|
|
print_clip (stream, &log->stroke.clip);
|
|
|
|
_cairo_output_stream_printf (stream, "slowest stroke: %f%%\n",
|
|
percent (log->stroke.slowest.elapsed,
|
|
log->stroke.elapsed));
|
|
print_record (stream, &log->stroke.slowest);
|
|
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
if (replay_record (log, &log->stroke.slowest, script))
|
|
_cairo_output_stream_printf (stream, "\n\n");
|
|
}
|
|
|
|
_cairo_output_stream_printf (stream, "glyphs: count %d [no-op %d], elasped %f [%f%%]\n",
|
|
log->glyphs.count, log->glyphs.noop,
|
|
_cairo_time_to_ns (log->glyphs.elapsed),
|
|
percent (log->glyphs.elapsed, total));
|
|
if (log->glyphs.count) {
|
|
print_extents (stream, &log->glyphs.extents);
|
|
print_operators (stream, log->glyphs.operators);
|
|
print_pattern (stream, "source", &log->glyphs.source);
|
|
print_clip (stream, &log->glyphs.clip);
|
|
|
|
_cairo_output_stream_printf (stream, "slowest glyphs: %f%%\n",
|
|
percent (log->glyphs.slowest.elapsed,
|
|
log->glyphs.elapsed));
|
|
print_record (stream, &log->glyphs.slowest);
|
|
|
|
_cairo_output_stream_printf (stream, "\n");
|
|
if (replay_record (log, &log->glyphs.slowest, script))
|
|
_cairo_output_stream_printf (stream, "\n\n");
|
|
}
|
|
|
|
cairo_device_destroy (script);
|
|
}
|
|
|
|
void
|
|
cairo_surface_observer_print (cairo_surface_t *abstract_surface,
|
|
cairo_write_func_t write_func,
|
|
void *closure)
|
|
{
|
|
cairo_output_stream_t *stream;
|
|
cairo_surface_observer_t *surface;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
|
|
return;
|
|
|
|
if (! _cairo_surface_is_observer (abstract_surface))
|
|
return;
|
|
|
|
surface = (cairo_surface_observer_t *) abstract_surface;
|
|
|
|
stream = _cairo_output_stream_create (write_func, NULL, closure);
|
|
_cairo_observation_print (stream, &surface->log);
|
|
_cairo_output_stream_destroy (stream);
|
|
}
|
|
|
|
double
|
|
cairo_surface_observer_elapsed (cairo_surface_t *abstract_surface)
|
|
{
|
|
cairo_surface_observer_t *surface;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
|
|
return -1;
|
|
|
|
if (! _cairo_surface_is_observer (abstract_surface))
|
|
return -1;
|
|
|
|
surface = (cairo_surface_observer_t *) abstract_surface;
|
|
return _cairo_time_to_ns (_cairo_observation_total_elapsed (&surface->log));
|
|
}
|
|
|
|
void
|
|
cairo_device_observer_print (cairo_device_t *abstract_device,
|
|
cairo_write_func_t write_func,
|
|
void *closure)
|
|
{
|
|
cairo_output_stream_t *stream;
|
|
cairo_device_observer_t *device;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
|
|
return;
|
|
|
|
if (! _cairo_device_is_observer (abstract_device))
|
|
return;
|
|
|
|
device = (cairo_device_observer_t *) abstract_device;
|
|
|
|
stream = _cairo_output_stream_create (write_func, NULL, closure);
|
|
_cairo_observation_print (stream, &device->log);
|
|
_cairo_output_stream_destroy (stream);
|
|
}
|
|
|
|
double
|
|
cairo_device_observer_elapsed (cairo_device_t *abstract_device)
|
|
{
|
|
cairo_device_observer_t *device;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
|
|
return -1;
|
|
|
|
if (! _cairo_device_is_observer (abstract_device))
|
|
return -1;
|
|
|
|
device = (cairo_device_observer_t *) abstract_device;
|
|
return _cairo_time_to_ns (_cairo_observation_total_elapsed (&device->log));
|
|
}
|
|
|
|
double
|
|
cairo_device_observer_paint_elapsed (cairo_device_t *abstract_device)
|
|
{
|
|
cairo_device_observer_t *device;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
|
|
return -1;
|
|
|
|
if (! _cairo_device_is_observer (abstract_device))
|
|
return -1;
|
|
|
|
device = (cairo_device_observer_t *) abstract_device;
|
|
return _cairo_time_to_ns (device->log.paint.elapsed);
|
|
}
|
|
|
|
double
|
|
cairo_device_observer_mask_elapsed (cairo_device_t *abstract_device)
|
|
{
|
|
cairo_device_observer_t *device;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
|
|
return -1;
|
|
|
|
if (! _cairo_device_is_observer (abstract_device))
|
|
return -1;
|
|
|
|
device = (cairo_device_observer_t *) abstract_device;
|
|
return _cairo_time_to_ns (device->log.mask.elapsed);
|
|
}
|
|
|
|
double
|
|
cairo_device_observer_fill_elapsed (cairo_device_t *abstract_device)
|
|
{
|
|
cairo_device_observer_t *device;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
|
|
return -1;
|
|
|
|
if (! _cairo_device_is_observer (abstract_device))
|
|
return -1;
|
|
|
|
device = (cairo_device_observer_t *) abstract_device;
|
|
return _cairo_time_to_ns (device->log.fill.elapsed);
|
|
}
|
|
|
|
double
|
|
cairo_device_observer_stroke_elapsed (cairo_device_t *abstract_device)
|
|
{
|
|
cairo_device_observer_t *device;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
|
|
return -1;
|
|
|
|
if (! _cairo_device_is_observer (abstract_device))
|
|
return -1;
|
|
|
|
device = (cairo_device_observer_t *) abstract_device;
|
|
return _cairo_time_to_ns (device->log.stroke.elapsed);
|
|
}
|
|
|
|
double
|
|
cairo_device_observer_glyphs_elapsed (cairo_device_t *abstract_device)
|
|
{
|
|
cairo_device_observer_t *device;
|
|
|
|
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
|
|
return -1;
|
|
|
|
if (! _cairo_device_is_observer (abstract_device))
|
|
return -1;
|
|
|
|
device = (cairo_device_observer_t *) abstract_device;
|
|
return _cairo_time_to_ns (device->log.glyphs.elapsed);
|
|
}
|