forked from KolibriOS/kolibrios
754f9336f0
git-svn-id: svn://kolibrios.org@4349 a494cfbc-eb01-0410-851d-a64ba20cac60
2102 lines
59 KiB
C
2102 lines
59 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-surface-observer-inline.h"
|
|
|
|
#include "cairo-array-private.h"
|
|
#include "cairo-combsort-inline.h"
|
|
#include "cairo-composite-rectangles-private.h"
|
|
#include "cairo-error-private.h"
|
|
#include "cairo-image-surface-private.h"
|
|
#include "cairo-list-inline.h"
|
|
#include "cairo-pattern-private.h"
|
|
#include "cairo-output-stream-private.h"
|
|
#include "cairo-recording-surface-private.h"
|
|
#include "cairo-surface-subsurface-inline.h"
|
|
#include "cairo-reference-count-private.h"
|
|
|
|
#if CAIRO_HAS_SCRIPT_SURFACE
|
|
#include "cairo-script-private.h"
|
|
#endif
|
|
|
|
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;
|
|
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
|
|
classify = 7;
|
|
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_status_t ignored;
|
|
|
|
/* cairo_device_acquire() can fail for nil and finished
|
|
* devices. We don't care about observing them. */
|
|
ignored = 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_image_surface_t *
|
|
_cairo_surface_observer_map_to_image (void *abstract_surface,
|
|
const cairo_rectangle_int_t *extents)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
return _cairo_surface_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;
|
|
return _cairo_surface_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, unsigned flags)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
|
|
do_callbacks (surface, &surface->flush_callbacks);
|
|
return _cairo_surface_flush (surface->target, flags);
|
|
}
|
|
|
|
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_surface_t *
|
|
_cairo_surface_observer_source (void *abstract_surface,
|
|
cairo_rectangle_int_t *extents)
|
|
{
|
|
cairo_surface_observer_t *surface = abstract_surface;
|
|
return _cairo_surface_get_source (surface->target, extents);
|
|
}
|
|
|
|
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_source,
|
|
_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",
|
|
"raster"
|
|
};
|
|
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)
|
|
{
|
|
#if CAIRO_HAS_SCRIPT_SURFACE
|
|
cairo_surface_t *surface;
|
|
cairo_int_status_t status;
|
|
|
|
if (log->record == NULL || script == 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;
|
|
#else
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
|
|
#if CAIRO_HAS_SCRIPT_SURFACE
|
|
script = _cairo_script_context_create_internal (stream);
|
|
_cairo_script_context_attach_snapshots (script, FALSE);
|
|
#else
|
|
script = NULL;
|
|
#endif
|
|
|
|
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);
|
|
}
|
|
|
|
cairo_status_t
|
|
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 (abstract_surface->status))
|
|
return abstract_surface->status;
|
|
|
|
if (unlikely (! _cairo_surface_is_observer (abstract_surface)))
|
|
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
|
|
surface = (cairo_surface_observer_t *) abstract_surface;
|
|
|
|
stream = _cairo_output_stream_create (write_func, NULL, closure);
|
|
_cairo_observation_print (stream, &surface->log);
|
|
return _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));
|
|
}
|
|
|
|
cairo_status_t
|
|
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 (abstract_device->status))
|
|
return abstract_device->status;
|
|
|
|
if (unlikely (! _cairo_device_is_observer (abstract_device)))
|
|
return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
|
|
|
|
device = (cairo_device_observer_t *) abstract_device;
|
|
|
|
stream = _cairo_output_stream_create (write_func, NULL, closure);
|
|
_cairo_observation_print (stream, &device->log);
|
|
return _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);
|
|
}
|