cairo/src/cairo-script-surface.c
Chris Wilson bed2701e1c Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)

In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.

Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:

ppc:
Speedups
========
image-rgba         evolution-20090607-0    1026085.22 0.18% -> 672972.07 0.77%:  1.52x speedup
▌
image-rgba         evolution-20090618-0    680579.98 0.12% -> 573237.66  0.16%:  1.19x speedup
▎
image-rgba      swfdec-fill-rate-4xaa-0    460296.92 0.36% -> 407464.63  0.42%:  1.13x speedup
▏
image-rgba      swfdec-fill-rate-2xaa-0    128431.95 0.47% -> 115051.86  0.42%:  1.12x speedup
▏
Slowdowns
=========
image-rgba     firefox-periodic-table-0    56837.61 0.78% -> 66055.17    3.20%:  1.09x slowdown
▏
2009-07-23 15:32:14 +01:00

3192 lines
85 KiB
C

/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2008 Chris Wilson
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Chris Wilson.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
/* The script surface is one that records all operations performed on
* it in the form of a procedural script, similar in fashion to
* PostScript but using Cairo's imaging model. In essence, this is
* equivalent to the meta-surface, but as there is no impedance mismatch
* between Cairo and CairoScript, we can generate output immediately
* without having to copy and hold the data in memory.
*/
#include "cairoint.h"
#include "cairo-script.h"
#include "cairo-analysis-surface-private.h"
#include "cairo-ft-private.h"
#include "cairo-list-private.h"
#include "cairo-meta-surface-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-surface-wrapper-private.h"
#include <ctype.h>
#define _cairo_output_stream_puts(S, STR) \
_cairo_output_stream_write ((S), (STR), strlen (STR))
#define static cairo_warn static
typedef struct _cairo_script_vmcontext cairo_script_vmcontext_t;
typedef struct _cairo_script_surface cairo_script_surface_t;
typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t;
typedef struct _cairo_script_surface_font_private cairo_script_surface_font_private_t;
struct deferred_finish {
cairo_list_t link;
cairo_list_t operand;
};
struct _cairo_script_vmcontext {
int ref;
int active;
cairo_output_stream_t *stream;
cairo_script_mode_t mode;
struct _bitmap {
unsigned long min;
unsigned long count;
unsigned int map[64];
struct _bitmap *next;
} surface_id, font_id;
cairo_list_t operands;
cairo_list_t deferred;
cairo_script_surface_font_private_t *fonts;
cairo_list_t defines;
};
struct _cairo_script_surface_font_private {
cairo_script_vmcontext_t *ctx;
cairo_bool_t has_sfnt;
unsigned long id;
unsigned long subset_glyph_index;
cairo_script_surface_font_private_t *prev, *next;
cairo_scaled_font_t *parent;
};
struct _cairo_script_implicit_context {
cairo_operator_t current_operator;
cairo_fill_rule_t current_fill_rule;
double current_tolerance;
cairo_antialias_t current_antialias;
cairo_stroke_style_t current_style;
cairo_pattern_union_t current_source;
cairo_matrix_t current_ctm;
cairo_matrix_t current_font_matrix;
cairo_font_options_t current_font_options;
cairo_scaled_font_t *current_scaled_font;
cairo_path_fixed_t current_path;
cairo_bool_t has_clip;
};
struct _cairo_script_surface {
cairo_surface_t base;
cairo_surface_wrapper_t wrapper;
cairo_script_vmcontext_t *ctx;
cairo_surface_clipper_t clipper;
unsigned long id;
cairo_list_t operand;
cairo_bool_t defined;
double width, height;
/* implicit flattened context */
cairo_script_implicit_context_t cr;
};
static const cairo_surface_backend_t _cairo_script_surface_backend;
static cairo_script_surface_t *
_cairo_script_surface_create_internal (cairo_script_vmcontext_t *ctx,
double width,
double height,
cairo_surface_t *passthrough);
static cairo_status_t
_vmcontext_destroy (cairo_script_vmcontext_t *ctx);
static void
_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font);
static void
_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr);
static void
_bitmap_release_id (struct _bitmap *b, unsigned long token)
{
struct _bitmap **prev = NULL;
do {
if (token < b->min + sizeof (b->map) * CHAR_BIT) {
unsigned int bit, elem;
token -= b->min;
elem = token / (sizeof (b->map[0]) * CHAR_BIT);
bit = token % (sizeof (b->map[0]) * CHAR_BIT);
b->map[elem] &= ~(1 << bit);
if (! --b->count && prev) {
*prev = b->next;
free (b);
}
return;
}
prev = &b->next;
b = b->next;
} while (b != NULL);
}
static cairo_status_t
_bitmap_next_id (struct _bitmap *b,
unsigned long *id)
{
struct _bitmap *bb, **prev = NULL;
unsigned long min = 0;
do {
if (b->min != min)
break;
if (b->count < sizeof (b->map) * CHAR_BIT) {
unsigned int n, m, bit;
for (n = 0; n < ARRAY_LENGTH (b->map); n++) {
if (b->map[n] == (unsigned int) -1)
continue;
for (m=0, bit=1; m<sizeof (b->map[0])*CHAR_BIT; m++, bit<<=1) {
if ((b->map[n] & bit) == 0) {
b->map[n] |= bit;
b->count++;
*id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min;
return CAIRO_STATUS_SUCCESS;
}
}
}
}
min += sizeof (b->map) * CHAR_BIT;
prev = &b->next;
b = b->next;
} while (b != NULL);
bb = malloc (sizeof (struct _bitmap));
if (unlikely (bb == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
*prev = bb;
bb->next = b;
bb->min = min;
bb->count = 1;
bb->map[0] = 0x1;
memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0]));
*id = min;
return CAIRO_STATUS_SUCCESS;
}
static const char *
_direction_to_string (cairo_bool_t backward)
{
static const char *names[] = {
"FORWARD",
"BACKWARD"
};
assert (backward < ARRAY_LENGTH (names));
return names[backward];
}
static const char *
_operator_to_string (cairo_operator_t op)
{
static const char *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 */
};
assert (op < ARRAY_LENGTH (names));
return names[op];
}
static const char *
_extend_to_string (cairo_extend_t extend)
{
static const char *names[] = {
"EXTEND_NONE", /* CAIRO_EXTEND_NONE */
"EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */
"EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */
"EXTEND_PAD" /* CAIRO_EXTEND_PAD */
};
assert (extend < ARRAY_LENGTH (names));
return names[extend];
}
static const char *
_filter_to_string (cairo_filter_t filter)
{
static const char *names[] = {
"FILTER_FAST", /* CAIRO_FILTER_FAST */
"FILTER_GOOD", /* CAIRO_FILTER_GOOD */
"FILTER_BEST", /* CAIRO_FILTER_BEST */
"FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */
"FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */
"FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */
};
assert (filter < ARRAY_LENGTH (names));
return names[filter];
}
static const char *
_fill_rule_to_string (cairo_fill_rule_t rule)
{
static const char *names[] = {
"WINDING", /* CAIRO_FILL_RULE_WINDING */
"EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */
};
assert (rule < ARRAY_LENGTH (names));
return names[rule];
}
static const char *
_antialias_to_string (cairo_antialias_t antialias)
{
static const char *names[] = {
"ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */
"ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */
"ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */
"ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */
};
assert (antialias < ARRAY_LENGTH (names));
return names[antialias];
}
static const char *
_line_cap_to_string (cairo_line_cap_t line_cap)
{
static const char *names[] = {
"LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */
"LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */
"LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */
};
assert (line_cap < ARRAY_LENGTH (names));
return names[line_cap];
}
static const char *
_line_join_to_string (cairo_line_join_t line_join)
{
static const char *names[] = {
"LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */
"LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */
"LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */
};
assert (line_join < ARRAY_LENGTH (names));
return names[line_join];
}
static cairo_bool_t
target_is_active (cairo_script_surface_t *surface)
{
return cairo_list_is_first (&surface->operand, &surface->ctx->operands);
}
static void
target_push (cairo_script_surface_t *surface)
{
cairo_list_move (&surface->operand, &surface->ctx->operands);
}
static int
target_depth (cairo_script_surface_t *surface)
{
cairo_list_t *link;
int depth = 0;
cairo_list_foreach (link, &surface->ctx->operands) {
if (link == &surface->operand)
break;
depth++;
}
return depth;
}
static void
_get_target (cairo_script_surface_t *surface)
{
cairo_script_vmcontext_t *ctx = surface->ctx;
if (! target_is_active (surface)) {
int depth = target_depth (surface);
if (ctx->active) {
if (surface->defined) {
_cairo_output_stream_printf (ctx->stream, "s%ld ", surface->id);
} else {
_cairo_output_stream_printf (ctx->stream, "%d index ", depth);
_cairo_output_stream_puts (ctx->stream, "/target get exch pop ");
}
} else {
if (depth == 1) {
_cairo_output_stream_puts (surface->ctx->stream,
"exch\n");
} else {
_cairo_output_stream_printf (surface->ctx->stream,
"%d -1 roll\n",
depth);
}
_cairo_output_stream_puts (ctx->stream, "/target get ");
target_push (surface);
}
} else
_cairo_output_stream_puts (ctx->stream, "/target get ");
}
static cairo_status_t
_emit_surface (cairo_script_surface_t *surface)
{
cairo_status_t status;
status = _bitmap_next_id (&surface->ctx->surface_id,
&surface->id);
if (unlikely (status))
return status;
_cairo_output_stream_printf (surface->ctx->stream,
"<< /width %f /height %f",
surface->width,
surface->height);
if (surface->base.x_fallback_resolution !=
CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT ||
surface->base.y_fallback_resolution !=
CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT)
{
_cairo_output_stream_printf (surface->ctx->stream,
" /fallback-resolution [%f %f]",
surface->base.x_fallback_resolution,
surface->base.y_fallback_resolution);
}
if (surface->base.device_transform.x0 != 0. ||
surface->base.device_transform.y0 != 0.)
{
/* XXX device offset is encoded into the pattern matrices etc. */
if (0) {
_cairo_output_stream_printf (surface->ctx->stream,
" /device-offset [%f %f]",
surface->base.device_transform.x0,
surface->base.device_transform.y0);
}
}
_cairo_output_stream_puts (surface->ctx->stream, " >> surface context\n");
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_context (cairo_script_surface_t *surface)
{
if (target_is_active (surface))
return CAIRO_STATUS_SUCCESS;
if (surface->id == (unsigned long) -1) {
cairo_status_t status;
status = _emit_surface (surface);
if (unlikely (status))
return status;
} else {
int depth = target_depth (surface);
if (depth == 1) {
_cairo_output_stream_puts (surface->ctx->stream, "exch\n");
} else {
_cairo_output_stream_printf (surface->ctx->stream,
"%d -1 roll\n",
depth);
}
}
target_push (surface);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_operator (cairo_script_surface_t *surface,
cairo_operator_t op)
{
assert (target_is_active (surface));
if (surface->cr.current_operator == op)
return CAIRO_STATUS_SUCCESS;
surface->cr.current_operator = op;
_cairo_output_stream_printf (surface->ctx->stream,
"//%s set-operator\n",
_operator_to_string (op));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_fill_rule (cairo_script_surface_t *surface,
cairo_fill_rule_t fill_rule)
{
assert (target_is_active (surface));
if (surface->cr.current_fill_rule == fill_rule)
return CAIRO_STATUS_SUCCESS;
surface->cr.current_fill_rule = fill_rule;
_cairo_output_stream_printf (surface->ctx->stream,
"//%s set-fill-rule\n",
_fill_rule_to_string (fill_rule));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_tolerance (cairo_script_surface_t *surface,
double tolerance,
cairo_bool_t force)
{
assert (target_is_active (surface));
if (! force && surface->cr.current_tolerance == tolerance)
return CAIRO_STATUS_SUCCESS;
surface->cr.current_tolerance = tolerance;
_cairo_output_stream_printf (surface->ctx->stream,
"%f set-tolerance\n",
tolerance);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_antialias (cairo_script_surface_t *surface,
cairo_antialias_t antialias)
{
assert (target_is_active (surface));
if (surface->cr.current_antialias == antialias)
return CAIRO_STATUS_SUCCESS;
surface->cr.current_antialias = antialias;
_cairo_output_stream_printf (surface->ctx->stream,
"//%s set-antialias\n",
_antialias_to_string (antialias));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_line_width (cairo_script_surface_t *surface,
double line_width,
cairo_bool_t force)
{
assert (target_is_active (surface));
if (! force && surface->cr.current_style.line_width == line_width)
return CAIRO_STATUS_SUCCESS;
surface->cr.current_style.line_width = line_width;
_cairo_output_stream_printf (surface->ctx->stream,
"%f set-line-width\n",
line_width);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_line_cap (cairo_script_surface_t *surface,
cairo_line_cap_t line_cap)
{
assert (target_is_active (surface));
if (surface->cr.current_style.line_cap == line_cap)
return CAIRO_STATUS_SUCCESS;
surface->cr.current_style.line_cap = line_cap;
_cairo_output_stream_printf (surface->ctx->stream,
"//%s set-line-cap\n",
_line_cap_to_string (line_cap));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_line_join (cairo_script_surface_t *surface,
cairo_line_join_t line_join)
{
assert (target_is_active (surface));
if (surface->cr.current_style.line_join == line_join)
return CAIRO_STATUS_SUCCESS;
surface->cr.current_style.line_join = line_join;
_cairo_output_stream_printf (surface->ctx->stream,
"//%s set-line-join\n",
_line_join_to_string (line_join));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_miter_limit (cairo_script_surface_t *surface,
double miter_limit,
cairo_bool_t force)
{
assert (target_is_active (surface));
if (! force && surface->cr.current_style.miter_limit == miter_limit)
return CAIRO_STATUS_SUCCESS;
surface->cr.current_style.miter_limit = miter_limit;
_cairo_output_stream_printf (surface->ctx->stream,
"%f set-miter-limit\n",
miter_limit);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_dash (cairo_script_surface_t *surface,
const double *dash,
unsigned int num_dashes,
double offset,
cairo_bool_t force)
{
unsigned int n;
assert (target_is_active (surface));
if (force &&
num_dashes == 0 &&
surface->cr.current_style.num_dashes == 0)
{
return CAIRO_STATUS_SUCCESS;
}
if (! force &&
(surface->cr.current_style.num_dashes == num_dashes &&
(num_dashes == 0 ||
(surface->cr.current_style.dash_offset == offset &&
memcmp (surface->cr.current_style.dash, dash,
sizeof (double) * num_dashes)))))
{
return CAIRO_STATUS_SUCCESS;
}
if (num_dashes) {
surface->cr.current_style.dash = _cairo_realloc_ab
(surface->cr.current_style.dash, num_dashes, sizeof (double));
if (unlikely (surface->cr.current_style.dash == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
memcpy (surface->cr.current_style.dash, dash,
sizeof (double) * num_dashes);
} else {
if (surface->cr.current_style.dash != NULL) {
free (surface->cr.current_style.dash);
surface->cr.current_style.dash = NULL;
}
}
surface->cr.current_style.num_dashes = num_dashes;
surface->cr.current_style.dash_offset = offset;
_cairo_output_stream_puts (surface->ctx->stream, "[");
for (n = 0; n < num_dashes; n++) {
_cairo_output_stream_printf (surface->ctx->stream, "%f", dash[n]);
if (n < num_dashes-1)
_cairo_output_stream_puts (surface->ctx->stream, " ");
}
_cairo_output_stream_printf (surface->ctx->stream,
"] %f set-dash\n",
offset);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_stroke_style (cairo_script_surface_t *surface,
const cairo_stroke_style_t *style,
cairo_bool_t force)
{
cairo_status_t status;
assert (target_is_active (surface));
status = _emit_line_width (surface, style->line_width, force);
if (unlikely (status))
return status;
status = _emit_line_cap (surface, style->line_cap);
if (unlikely (status))
return status;
status = _emit_line_join (surface, style->line_join);
if (unlikely (status))
return status;
status = _emit_miter_limit (surface, style->miter_limit, force);
if (unlikely (status))
return status;
status = _emit_dash (surface,
style->dash, style->num_dashes, style->dash_offset,
force);
if (unlikely (status))
return status;
return CAIRO_STATUS_SUCCESS;
}
static const char *
_format_to_string (cairo_format_t format)
{
static const char *names[] = {
"ARGB32", /* CAIRO_FORMAT_ARGB32 */
"RGB24", /* CAIRO_FORMAT_RGB24 */
"A8", /* CAIRO_FORMAT_A8 */
"A1" /* CAIRO_FORMAT_A1 */
};
assert (format < ARRAY_LENGTH (names));
return names[format];
}
static cairo_status_t
_emit_solid_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
if (solid->content & CAIRO_CONTENT_ALPHA &&
! CAIRO_COLOR_IS_OPAQUE (&solid->color))
{
if (! (solid->content & CAIRO_CONTENT_COLOR) ||
(solid->color.red_short == 0 &&
solid->color.green_short == 0 &&
solid->color.blue_short == 0))
{
_cairo_output_stream_printf (surface->ctx->stream,
"%f a",
solid->color.alpha);
}
else
{
_cairo_output_stream_printf (surface->ctx->stream,
"%f %f %f %f rgba",
solid->color.red,
solid->color.green,
solid->color.blue,
solid->color.alpha);
}
}
else
{
if (solid->color.red_short == solid->color.green_short &&
solid->color.red_short == solid->color.blue_short)
{
_cairo_output_stream_printf (surface->ctx->stream,
"%f g",
solid->color.red);
}
else
{
_cairo_output_stream_printf (surface->ctx->stream,
"%f %f %f rgb",
solid->color.red,
solid->color.green,
solid->color.blue);
}
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_gradient_color_stops (cairo_gradient_pattern_t *gradient,
cairo_output_stream_t *output)
{
unsigned int n;
for (n = 0; n < gradient->n_stops; n++) {
_cairo_output_stream_printf (output,
"\n %f %f %f %f %f add-color-stop",
gradient->stops[n].offset,
gradient->stops[n].color.red,
gradient->stops[n].color.green,
gradient->stops[n].color.blue,
gradient->stops[n].color.alpha);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_linear_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_linear_pattern_t *linear;
linear = (cairo_linear_pattern_t *) pattern;
_cairo_output_stream_printf (surface->ctx->stream,
"%f %f %f %f linear",
_cairo_fixed_to_double (linear->p1.x),
_cairo_fixed_to_double (linear->p1.y),
_cairo_fixed_to_double (linear->p2.x),
_cairo_fixed_to_double (linear->p2.y));
return _emit_gradient_color_stops (&linear->base, surface->ctx->stream);
}
static cairo_status_t
_emit_radial_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_radial_pattern_t *radial;
radial = (cairo_radial_pattern_t *) pattern;
_cairo_output_stream_printf (surface->ctx->stream,
"%f %f %f %f %f %f radial",
_cairo_fixed_to_double (radial->c1.x),
_cairo_fixed_to_double (radial->c1.y),
_cairo_fixed_to_double (radial->r1),
_cairo_fixed_to_double (radial->c2.x),
_cairo_fixed_to_double (radial->c2.y),
_cairo_fixed_to_double (radial->r2));
return _emit_gradient_color_stops (&radial->base, surface->ctx->stream);
}
static const char *
_content_to_string (cairo_content_t content)
{
switch (content) {
case CAIRO_CONTENT_ALPHA: return "ALPHA";
case CAIRO_CONTENT_COLOR: return "COLOR";
default:
case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA";
}
}
static cairo_status_t
_emit_meta_surface_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_script_implicit_context_t old_cr;
cairo_surface_pattern_t *surface_pattern;
cairo_meta_surface_t *source;
cairo_script_surface_t *similar;
cairo_status_t status;
cairo_box_t bbox;
cairo_rectangle_int_t rect;
surface_pattern = (cairo_surface_pattern_t *) pattern;
source = (cairo_meta_surface_t *) surface_pattern->surface;
/* first measure the extents */
status = _cairo_meta_surface_get_bbox (source, &bbox, NULL);
if (unlikely (status))
return status;
/* convert to extents so that it matches the public api */
_cairo_box_round_to_rectangle (&bbox, &rect);
similar = _cairo_script_surface_create_internal (surface->ctx,
rect.width,
rect.height,
NULL);
if (unlikely (similar->base.status))
return similar->base.status;
cairo_surface_set_device_offset (&similar->base, -rect.x, -rect.y);
_get_target (surface);
_cairo_output_stream_printf (surface->ctx->stream,
"%d %d //%s similar dup context\n",
rect.width, rect.height,
_content_to_string (source->content));
target_push (similar);
old_cr = surface->cr;
_cairo_script_implicit_context_init (&surface->cr);
status = cairo_meta_surface_replay (&source->base, &similar->base);
surface->cr = old_cr;
if (unlikely (status)) {
cairo_surface_destroy (&similar->base);
return status;
}
cairo_list_del (&similar->operand);
assert (target_is_active (surface));
_cairo_output_stream_puts (surface->ctx->stream, "pop pattern");
cairo_surface_destroy (&similar->base);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_script_surface_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_surface_pattern_t *surface_pattern;
cairo_script_surface_t *source;
surface_pattern = (cairo_surface_pattern_t *) pattern;
source = (cairo_script_surface_t *) surface_pattern->surface;
_get_target (source);
_cairo_output_stream_puts (surface->ctx->stream, "pattern");
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_write_image_surface (cairo_output_stream_t *output,
const cairo_image_surface_t *image)
{
int stride, row, width;
uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE];
uint8_t *rowdata;
uint8_t *data;
stride = image->stride;
width = image->width;
data = image->data;
#if WORDS_BIGENDIAN
switch (image->format) {
case CAIRO_FORMAT_A1:
for (row = image->height; row--; ) {
_cairo_output_stream_write (output, data, (width+7)/8);
data += stride;
}
break;
case CAIRO_FORMAT_A8:
for (row = image->height; row--; ) {
_cairo_output_stream_write (output, data, width);
data += stride;
}
break;
case CAIRO_FORMAT_RGB24:
for (row = image->height; row--; ) {
int col;
rowdata = data;
for (col = width; col--; ) {
_cairo_output_stream_write (output, rowdata, 3);
rowdata+=4;
}
data += stride;
}
break;
case CAIRO_FORMAT_ARGB32:
for (row = image->height; row--; ) {
_cairo_output_stream_write (output, data, 4*width);
data += stride;
}
break;
default:
ASSERT_NOT_REACHED;
break;
}
#else
if (stride > ARRAY_LENGTH (row_stack)) {
rowdata = malloc (stride);
if (unlikely (rowdata == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
} else
rowdata = row_stack;
switch (image->format) {
case CAIRO_FORMAT_A1:
for (row = image->height; row--; ) {
int col;
for (col = 0; col < (width + 7)/8; col++)
rowdata[col] = CAIRO_BITSWAP8 (data[col]);
_cairo_output_stream_write (output, rowdata, (width+7)/8);
data += stride;
}
break;
case CAIRO_FORMAT_A8:
for (row = image->height; row--; ) {
_cairo_output_stream_write (output, data, width);
data += stride;
}
break;
case CAIRO_FORMAT_RGB24:
for (row = image->height; row--; ) {
uint8_t *src = data;
int col;
for (col = 0; col < width; col++) {
rowdata[3*col+2] = *src++;
rowdata[3*col+1] = *src++;
rowdata[3*col+0] = *src++;
src++;
}
_cairo_output_stream_write (output, rowdata, 3*width);
data += stride;
}
break;
case CAIRO_FORMAT_ARGB32:
for (row = image->height; row--; ) {
uint32_t *src = (uint32_t *) data;
uint32_t *dst = (uint32_t *) rowdata;
int col;
for (col = 0; col < width; col++)
dst[col] = bswap_32 (src[col]);
_cairo_output_stream_write (output, rowdata, 4*width);
data += stride;
}
break;
default:
ASSERT_NOT_REACHED;
break;
}
if (rowdata != row_stack)
free (rowdata);
#endif
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_emit_png_surface (cairo_script_surface_t *surface,
cairo_image_surface_t *image)
{
cairo_output_stream_t *base85_stream;
cairo_status_t status;
const uint8_t *mime_data;
unsigned int mime_data_length;
cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_PNG,
&mime_data, &mime_data_length);
if (mime_data == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
_cairo_output_stream_printf (surface->ctx->stream,
"<< "
"/width %d "
"/height %d "
"/format //%s "
"/mime-type (image/png) "
"/source <~",
image->width, image->height,
_format_to_string (image->format));
base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
_cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
status = _cairo_output_stream_destroy (base85_stream);
if (unlikely (status))
return status;
_cairo_output_stream_puts (surface->ctx->stream, " >> image ");
return CAIRO_STATUS_SUCCESS;
}
struct def {
cairo_script_vmcontext_t *ctx;
cairo_user_data_array_t *user_data;
unsigned int tag;
cairo_list_t link;
};
static void
_undef (void *data)
{
struct def *def = data;
cairo_list_del (&def->link);
_cairo_output_stream_printf (def->ctx->stream, "/s%u undef ", def->tag);
free (def);
}
static cairo_status_t
_emit_image_surface (cairo_script_surface_t *surface,
cairo_image_surface_t *image)
{
cairo_output_stream_t *base85_stream;
cairo_output_stream_t *zlib_stream;
cairo_status_t status, status2;
const uint8_t *mime_data;
unsigned int mime_data_length;
struct def *tag;
if (_cairo_user_data_array_get_data (&image->base.user_data,
(cairo_user_data_key_t *) surface->ctx))
{
_cairo_output_stream_printf (surface->ctx->stream,
"s%u pattern ",
image->base.unique_id);
return CAIRO_STATUS_SUCCESS;
}
status = _emit_png_surface (surface, image);
if (_cairo_status_is_error (status)) {
return status;
} else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_image_surface_t *clone;
uint32_t len;
if (image->format == CAIRO_FORMAT_INVALID) {
clone =
_cairo_image_surface_coerce (image,
_cairo_format_from_content (image->base.content));
} else {
clone = (cairo_image_surface_t *)
cairo_surface_reference (&image->base);
}
_cairo_output_stream_printf (surface->ctx->stream,
"<< "
"/width %d "
"/height %d "
"/format //%s "
"/source ",
clone->width, clone->height,
_format_to_string (clone->format));
switch (clone->format) {
case CAIRO_FORMAT_A1:
len = (clone->width + 7)/8;
break;
case CAIRO_FORMAT_A8:
len = clone->width;
break;
case CAIRO_FORMAT_RGB24:
len = clone->width * 3;
break;
case CAIRO_FORMAT_ARGB32:
len = clone->width * 4;
break;
}
len *= clone->height;
if (len > 24) {
_cairo_output_stream_puts (surface->ctx->stream, "<|");
base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
_cairo_output_stream_write (base85_stream, &len, sizeof (len));
zlib_stream = _cairo_deflate_stream_create (base85_stream);
status = _write_image_surface (zlib_stream, clone);
status2 = _cairo_output_stream_destroy (zlib_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
status2 = _cairo_output_stream_destroy (base85_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
if (unlikely (status))
return status;
} else {
_cairo_output_stream_puts (surface->ctx->stream, "<~");
base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
status = _write_image_surface (base85_stream, clone);
status2 = _cairo_output_stream_destroy (base85_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
if (unlikely (status))
return status;
}
_cairo_output_stream_puts (surface->ctx->stream, " >> image ");
cairo_surface_destroy (&clone->base);
}
tag = malloc (sizeof (*tag));
if (unlikely (tag == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
tag->ctx = surface->ctx;
tag->tag = image->base.unique_id;
tag->user_data = &image->base.user_data;
cairo_list_add (&tag->link, &surface->ctx->defines);
status = _cairo_user_data_array_set_data (&image->base.user_data,
(cairo_user_data_key_t *) surface->ctx,
tag, _undef);
if (unlikely (status)) {
free (tag);
return status;
}
_cairo_output_stream_printf (surface->ctx->stream,
"dup /s%u exch def ",
image->base.unique_id);
cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG,
&mime_data, &mime_data_length);
if (mime_data != NULL) {
_cairo_output_stream_printf (surface->ctx->stream,
"\n (%s) <~",
CAIRO_MIME_TYPE_JPEG);
base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
_cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
status = _cairo_output_stream_destroy (base85_stream);
if (unlikely (status))
return status;
_cairo_output_stream_puts (surface->ctx->stream, " set-mime-data\n");
}
cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JP2,
&mime_data, &mime_data_length);
if (mime_data != NULL) {
_cairo_output_stream_printf (surface->ctx->stream,
"\n (%s) <~",
CAIRO_MIME_TYPE_JP2);
base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
_cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
status = _cairo_output_stream_destroy (base85_stream);
if (unlikely (status))
return status;
_cairo_output_stream_puts (surface->ctx->stream, " set-mime-data\n");
}
_cairo_output_stream_puts (surface->ctx->stream, "pattern");
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_image_surface_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_surface_pattern_t *surface_pattern;
cairo_image_surface_t *image;
cairo_status_t status;
/* XXX keeping a copy is nasty, but we want to hook into the surface's
* lifetime. Using a snapshot is a convenient method.
*/
surface_pattern = (cairo_surface_pattern_t *) pattern;
image = (cairo_image_surface_t *)
_cairo_surface_snapshot (surface_pattern->surface);
status = _emit_image_surface (surface, image);
cairo_surface_destroy (&image->base);
return status;
}
static cairo_status_t
_emit_surface_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_surface_pattern_t *surface_pattern;
cairo_surface_t *source;
surface_pattern = (cairo_surface_pattern_t *) pattern;
source = surface_pattern->surface;
switch ((int) source->type) {
case CAIRO_SURFACE_TYPE_META:
return _emit_meta_surface_pattern (surface, pattern);
case CAIRO_SURFACE_TYPE_SCRIPT:
return _emit_script_surface_pattern (surface, pattern);
default:
return _emit_image_surface_pattern (surface, pattern);
}
}
static cairo_status_t
_emit_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_status_t status;
cairo_bool_t is_default_extend;
cairo_bool_t need_newline = TRUE;
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
/* solid colors do not need filter/extend/matrix */
return _emit_solid_pattern (surface, pattern);
case CAIRO_PATTERN_TYPE_LINEAR:
status = _emit_linear_pattern (surface, pattern);
is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT;
break;
case CAIRO_PATTERN_TYPE_RADIAL:
status = _emit_radial_pattern (surface, pattern);
is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT;
break;
case CAIRO_PATTERN_TYPE_SURFACE:
status = _emit_surface_pattern (surface, pattern);
is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT;
break;
default:
ASSERT_NOT_REACHED;
status = CAIRO_INT_STATUS_UNSUPPORTED;
}
if (unlikely (status))
return status;
if (! _cairo_matrix_is_identity (&pattern->matrix)) {
if (need_newline) {
_cairo_output_stream_puts (surface->ctx->stream, "\n ");
need_newline = FALSE;
}
_cairo_output_stream_printf (surface->ctx->stream,
" [%f %f %f %f %f %f] set-matrix\n ",
pattern->matrix.xx, pattern->matrix.yx,
pattern->matrix.xy, pattern->matrix.yy,
pattern->matrix.x0, pattern->matrix.y0);
}
/* XXX need to discriminate the user explicitly setting the default */
if (pattern->filter != CAIRO_FILTER_DEFAULT) {
if (need_newline) {
_cairo_output_stream_puts (surface->ctx->stream, "\n ");
need_newline = FALSE;
}
_cairo_output_stream_printf (surface->ctx->stream,
" //%s set-filter\n ",
_filter_to_string (pattern->filter));
}
if (! is_default_extend ){
if (need_newline) {
_cairo_output_stream_puts (surface->ctx->stream, "\n ");
need_newline = FALSE;
}
_cairo_output_stream_printf (surface->ctx->stream,
" //%s set-extend\n ",
_extend_to_string (pattern->extend));
}
if (need_newline)
_cairo_output_stream_puts (surface->ctx->stream, "\n ");
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_identity (cairo_script_surface_t *surface,
cairo_bool_t *matrix_updated)
{
assert (target_is_active (surface));
if (_cairo_matrix_is_identity (&surface->cr.current_ctm))
return CAIRO_STATUS_SUCCESS;
_cairo_output_stream_puts (surface->ctx->stream,
"identity set-matrix\n");
*matrix_updated = TRUE;
cairo_matrix_init_identity (&surface->cr.current_ctm);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_source (cairo_script_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source)
{
cairo_bool_t matrix_updated = FALSE;
cairo_status_t status;
assert (target_is_active (surface));
if (op == CAIRO_OPERATOR_CLEAR) {
/* the source is ignored, so don't change it */
return CAIRO_STATUS_SUCCESS;
}
if (_cairo_pattern_equal (&surface->cr.current_source.base, source))
return CAIRO_STATUS_SUCCESS;
_cairo_pattern_fini (&surface->cr.current_source.base);
status = _cairo_pattern_init_copy (&surface->cr.current_source.base,
source);
if (unlikely (status))
return status;
status = _emit_identity (surface, &matrix_updated);
if (unlikely (status))
return status;
status = _emit_pattern (surface, source);
if (unlikely (status))
return status;
_cairo_output_stream_puts (surface->ctx->stream,
" set-source\n");
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_path_move_to (void *closure,
const cairo_point_t *point)
{
_cairo_output_stream_printf (closure,
" %f %f m",
_cairo_fixed_to_double (point->x),
_cairo_fixed_to_double (point->y));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_path_line_to (void *closure,
const cairo_point_t *point)
{
_cairo_output_stream_printf (closure,
" %f %f l",
_cairo_fixed_to_double (point->x),
_cairo_fixed_to_double (point->y));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_path_curve_to (void *closure,
const cairo_point_t *p1,
const cairo_point_t *p2,
const cairo_point_t *p3)
{
_cairo_output_stream_printf (closure,
" %f %f %f %f %f %f c",
_cairo_fixed_to_double (p1->x),
_cairo_fixed_to_double (p1->y),
_cairo_fixed_to_double (p2->x),
_cairo_fixed_to_double (p2->y),
_cairo_fixed_to_double (p3->x),
_cairo_fixed_to_double (p3->y));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_path_close (void *closure)
{
_cairo_output_stream_printf (closure,
" h");
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_path (cairo_script_surface_t *surface,
cairo_path_fixed_t *path)
{
cairo_box_t box;
cairo_status_t status;
assert (target_is_active (surface));
assert (_cairo_matrix_is_identity (&surface->cr.current_ctm));
if (_cairo_path_fixed_equal (&surface->cr.current_path, path))
return CAIRO_STATUS_SUCCESS;
_cairo_path_fixed_fini (&surface->cr.current_path);
_cairo_output_stream_puts (surface->ctx->stream, "n");
if (path == NULL) {
_cairo_path_fixed_init (&surface->cr.current_path);
} else if (_cairo_path_fixed_is_rectangle (path, &box)) {
double x1 = _cairo_fixed_to_double (box.p1.x);
double y1 = _cairo_fixed_to_double (box.p1.y);
double x2 = _cairo_fixed_to_double (box.p2.x);
double y2 = _cairo_fixed_to_double (box.p2.y);
status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path);
if (unlikely (status))
return status;
_cairo_output_stream_printf (surface->ctx->stream,
" %f %f %f %f rectangle",
x1, y1, x2 - x1, y2 - y1);
} else {
cairo_status_t status;
status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path);
if (unlikely (status))
return status;
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_path_move_to,
_path_line_to,
_path_curve_to,
_path_close,
surface->ctx->stream);
if (unlikely (status))
return status;
}
_cairo_output_stream_puts (surface->ctx->stream, "\n");
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_scaling_matrix (cairo_script_surface_t *surface,
const cairo_matrix_t *ctm,
cairo_bool_t *matrix_updated)
{
cairo_matrix_t current, ctm_scaling;
assert (target_is_active (surface));
current = surface->cr.current_ctm;
current.x0 = current.y0 = 0;
ctm_scaling = *ctm;
ctm_scaling.x0 = ctm_scaling.y0 = 0;
if (memcmp (&current, &ctm_scaling, sizeof (cairo_matrix_t)) == 0)
return CAIRO_STATUS_SUCCESS;
*matrix_updated = TRUE;
surface->cr.current_ctm = *ctm;
if (_cairo_matrix_is_identity (ctm)) {
_cairo_output_stream_puts (surface->ctx->stream,
"identity set-matrix\n");
} else {
_cairo_output_stream_printf (surface->ctx->stream,
"[%f %f %f %f %f %f] set-matrix\n",
ctm->xx, ctm->yx,
ctm->xy, ctm->yy,
ctm->x0, ctm->y0);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_font_matrix (cairo_script_surface_t *surface,
const cairo_matrix_t *font_matrix)
{
assert (target_is_active (surface));
if (memcmp (&surface->cr.current_font_matrix,
font_matrix,
sizeof (cairo_matrix_t)) == 0)
{
return CAIRO_STATUS_SUCCESS;
}
surface->cr.current_font_matrix = *font_matrix;
if (_cairo_matrix_is_identity (font_matrix)) {
_cairo_output_stream_puts (surface->ctx->stream,
"identity set-font-matrix\n");
} else {
_cairo_output_stream_printf (surface->ctx->stream,
"[%f %f %f %f %f %f] set-font-matrix\n",
font_matrix->xx, font_matrix->yx,
font_matrix->xy, font_matrix->yy,
font_matrix->x0, font_matrix->y0);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_surface_t *
_cairo_script_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
cairo_script_surface_t *surface, *other = abstract_surface;
cairo_surface_t *passthrough = NULL;
cairo_script_vmcontext_t *ctx;
cairo_status_t status;
ctx = other->ctx;
if (other->id == (unsigned long) -1) {
status = _emit_surface (other);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
target_push (other);
}
if (_cairo_surface_wrapper_is_active (&other->wrapper)) {
passthrough =
_cairo_surface_wrapper_create_similar (&other->wrapper,
content, width, height);
if (unlikely (passthrough->status))
return passthrough;
}
surface = _cairo_script_surface_create_internal (ctx,
width, height,
passthrough);
cairo_surface_destroy (passthrough);
if (unlikely (surface->base.status))
return &surface->base;
status = _bitmap_next_id (&ctx->surface_id,
&surface->id);
if (unlikely (status)) {
cairo_surface_destroy (&surface->base);
return _cairo_surface_create_in_error (status);
}
_get_target (other);
_cairo_output_stream_printf (ctx->stream,
"%u %u //%s similar dup /s%ld exch def context\n",
width, height,
_content_to_string (content),
surface->id);
surface->defined = TRUE;
target_push (surface);
return &surface->base;
}
static cairo_status_t
_vmcontext_destroy (cairo_script_vmcontext_t *ctx)
{
cairo_status_t status;
if (--ctx->ref)
return _cairo_output_stream_flush (ctx->stream);
while (ctx->fonts != NULL ){
cairo_script_surface_font_private_t *font = ctx->fonts;
ctx->fonts = font->next;
_cairo_script_surface_scaled_font_fini (font->parent);
}
while (! cairo_list_is_empty (&ctx->defines)) {
struct def *def = cairo_list_first_entry (&ctx->defines,
struct def, link);
status = _cairo_user_data_array_set_data (def->user_data,
(cairo_user_data_key_t *) ctx,
NULL, NULL);
assert (status == CAIRO_STATUS_SUCCESS);
}
status = _cairo_output_stream_destroy (ctx->stream);
free (ctx);
return status;
}
static cairo_status_t
_cairo_script_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_script_surface_t *surface = abstract_surface;
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_acquire_source_image (&surface->wrapper,
image_out,
image_extra);
}
return CAIRO_INT_STATUS_UNSUPPORTED;
}
static void
_cairo_script_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_script_surface_t *surface = abstract_surface;
assert (_cairo_surface_wrapper_is_active (&surface->wrapper));
_cairo_surface_wrapper_release_source_image (&surface->wrapper,
image,
image_extra);
}
static cairo_status_t
_cairo_script_surface_finish (void *abstract_surface)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_status_t status = CAIRO_STATUS_SUCCESS, status2;
_cairo_surface_wrapper_fini (&surface->wrapper);
if (surface->cr.current_style.dash != NULL) {
free (surface->cr.current_style.dash);
surface->cr.current_style.dash = NULL;
}
_cairo_pattern_fini (&surface->cr.current_source.base);
_cairo_path_fixed_fini (&surface->cr.current_path);
_cairo_surface_clipper_reset (&surface->clipper);
if (surface->id != (unsigned long) -1) {
if (! surface->ctx->active) {
if (cairo_list_is_first (&surface->operand,
&surface->ctx->operands))
{
_cairo_output_stream_printf (surface->ctx->stream,
"pop\n");
}
else
{
int depth = target_depth (surface);
if (depth == 1) {
_cairo_output_stream_printf (surface->ctx->stream,
"exch pop\n");
} else {
_cairo_output_stream_printf (surface->ctx->stream,
"%d -1 roll pop\n",
depth);
}
}
cairo_list_del (&surface->operand);
} else {
struct deferred_finish *link = malloc (sizeof (*link));
if (link == NULL) {
status2 = _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
} else {
cairo_list_swap (&link->operand, &surface->operand);
cairo_list_add (&link->link, &surface->ctx->deferred);
}
}
if (surface->defined) {
_cairo_output_stream_printf (surface->ctx->stream,
"/s%ld undef\n",
surface->id);
}
_bitmap_release_id (&surface->ctx->surface_id, surface->id);
}
status2 = _vmcontext_destroy (surface->ctx);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
return status;
}
static cairo_int_status_t
_cairo_script_surface_copy_page (void *abstract_surface)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_status_t status;
status = _emit_context (surface);
if (unlikely (status))
return status;
_cairo_output_stream_puts (surface->ctx->stream, "copy-page\n");
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_script_surface_show_page (void *abstract_surface)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_status_t status;
status = _emit_context (surface);
if (unlikely (status))
return status;
_cairo_output_stream_puts (surface->ctx->stream, "show-page\n");
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_script_surface_t *surface = cairo_container_of (clipper,
cairo_script_surface_t,
clipper);
cairo_bool_t matrix_updated = FALSE;
cairo_status_t status;
cairo_box_t box;
status = _emit_context (surface);
if (unlikely (status))
return status;
if (path == NULL) {
if (surface->cr.has_clip) {
_cairo_output_stream_puts (surface->ctx->stream, "reset-clip\n");
surface->cr.has_clip = FALSE;
}
return CAIRO_STATUS_SUCCESS;
}
/* skip the trivial clip covering the surface extents */
if (surface->width >=0 && surface->height >= 0 &&
_cairo_path_fixed_is_rectangle (path, &box))
{
if (box.p1.x <= 0 && box.p1.y <= 0 &&
box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) &&
box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height))
{
return CAIRO_STATUS_SUCCESS;
}
}
status = _emit_identity (surface, &matrix_updated);
if (unlikely (status))
return status;
status = _emit_fill_rule (surface, fill_rule);
if (unlikely (status))
return status;
if (! path->is_rectilinear) {
status = _emit_tolerance (surface, tolerance, matrix_updated);
if (unlikely (status))
return status;
}
if (! path->maybe_fill_region) {
status = _emit_antialias (surface, antialias);
if (unlikely (status))
return status;
}
status = _emit_path (surface, path);
if (unlikely (status))
return status;
_cairo_output_stream_puts (surface->ctx->stream, "clip+\n");
surface->cr.has_clip = TRUE;
return CAIRO_STATUS_SUCCESS;
}
static void
ctx_active (cairo_script_vmcontext_t *ctx)
{
ctx->active++;
}
static void
ctx_inactive (cairo_script_vmcontext_t *ctx)
{
if (--ctx->active > 0)
return;
while (! cairo_list_is_empty (&ctx->deferred)) {
struct deferred_finish *df;
cairo_list_t *operand;
int depth;
df = cairo_list_first_entry (&ctx->deferred,
struct deferred_finish,
link);
depth = 0;
cairo_list_foreach (operand, &ctx->operands) {
if (operand == &df->operand)
break;
depth++;
}
if (depth == 0) {
_cairo_output_stream_printf (ctx->stream,
"pop\n");
} else if (depth == 1) {
_cairo_output_stream_printf (ctx->stream,
"exch pop\n");
} else {
_cairo_output_stream_printf (ctx->stream,
"%d -1 roll pop\n",
depth);
}
cairo_list_del (&df->operand);
cairo_list_del (&df->link);
free (df);
}
}
static cairo_int_status_t
_cairo_script_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_status_t status;
ctx_active (surface->ctx);
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
status = _emit_context (surface);
if (unlikely (status))
return status;
status = _emit_source (surface, op, source);
if (unlikely (status))
return status;
status = _emit_operator (surface, op);
if (unlikely (status))
return status;
_cairo_output_stream_puts (surface->ctx->stream,
"paint\n");
ctx_inactive (surface->ctx);
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_paint (&surface->wrapper,
op, source, clip);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_script_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_status_t status;
ctx_active (surface->ctx);
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
status = _emit_context (surface);
if (unlikely (status))
return status;
status = _emit_source (surface, op, source);
if (unlikely (status))
return status;
status = _emit_operator (surface, op);
if (unlikely (status))
return status;
if (_cairo_pattern_equal (source, mask)) {
_cairo_output_stream_puts (surface->ctx->stream, "/source get");
} else {
status = _emit_pattern (surface, mask);
if (unlikely (status))
return status;
}
assert (surface->cr.current_operator == op);
_cairo_output_stream_puts (surface->ctx->stream,
" mask\n");
ctx_inactive (surface->ctx);
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_mask (&surface->wrapper,
op, source, mask, clip);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_script_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_stroke_style_t *style,
cairo_matrix_t *ctm,
cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_bool_t matrix_updated = FALSE;
cairo_status_t status;
ctx_active (surface->ctx);
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
status = _emit_context (surface);
if (unlikely (status))
return status;
status = _emit_identity (surface, &matrix_updated);
if (unlikely (status))
return status;
status = _emit_path (surface, path);
if (unlikely (status))
return status;
status = _emit_source (surface, op, source);
if (unlikely (status))
return status;
status = _emit_scaling_matrix (surface, ctm, &matrix_updated);
if (unlikely (status))
return status;
status = _emit_operator (surface, op);
if (unlikely (status))
return status;
status = _emit_stroke_style (surface, style, matrix_updated);
if (unlikely (status))
return status;
if (! path->is_rectilinear) {
status = _emit_tolerance (surface, tolerance, matrix_updated);
if (unlikely (status))
return status;
}
status = _emit_antialias (surface, antialias);
if (unlikely (status))
return status;
_cairo_output_stream_puts (surface->ctx->stream, "stroke+\n");
ctx_inactive (surface->ctx);
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_stroke (&surface->wrapper,
op, source, path,
style,
ctm, ctm_inverse,
tolerance, antialias,
clip);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_script_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_bool_t matrix_updated = FALSE;
cairo_status_t status;
ctx_active (surface->ctx);
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
status = _emit_context (surface);
if (unlikely (status))
return status;
status = _emit_identity (surface, &matrix_updated);
if (unlikely (status))
return status;
status = _emit_source (surface, op, source);
if (unlikely (status))
return status;
status = _emit_fill_rule (surface, fill_rule);
if (unlikely (status))
return status;
if (! path->is_rectilinear) {
status = _emit_tolerance (surface, tolerance, matrix_updated);
if (unlikely (status))
return status;
}
if (! path->maybe_fill_region) {
status = _emit_antialias (surface, antialias);
if (unlikely (status))
return status;
}
status = _emit_path (surface, path);
if (unlikely (status))
return status;
status = _emit_operator (surface, op);
if (unlikely (status))
return status;
_cairo_output_stream_puts (surface->ctx->stream, "fill+\n");
ctx_inactive (surface->ctx);
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_fill (&surface->wrapper,
op, source, path,
fill_rule,
tolerance,
antialias,
clip);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
_cairo_script_surface_has_show_text_glyphs (void *abstract_surface)
{
return TRUE;
}
static const char *
_subpixel_order_to_string (cairo_subpixel_order_t subpixel_order)
{
static const char *names[] = {
"SUBPIXEL_ORDER_DEFAULT", /* CAIRO_SUBPIXEL_ORDER_DEFAULT */
"SUBPIXEL_ORDER_RGB", /* CAIRO_SUBPIXEL_ORDER_RGB */
"SUBPIXEL_ORDER_BGR", /* CAIRO_SUBPIXEL_ORDER_BGR */
"SUBPIXEL_ORDER_VRGB", /* CAIRO_SUBPIXEL_ORDER_VRGB */
"SUBPIXEL_ORDER_VBGR" /* CAIRO_SUBPIXEL_ORDER_VBGR */
};
return names[subpixel_order];
}
static const char *
_hint_style_to_string (cairo_hint_style_t hint_style)
{
static const char *names[] = {
"HINT_STYLE_DEFAULT", /* CAIRO_HINT_STYLE_DEFAULT */
"HINT_STYLE_NONE", /* CAIRO_HINT_STYLE_NONE */
"HINT_STYLE_SLIGHT", /* CAIRO_HINT_STYLE_SLIGHT */
"HINT_STYLE_MEDIUM", /* CAIRO_HINT_STYLE_MEDIUM */
"HINT_STYLE_FULL" /* CAIRO_HINT_STYLE_FULL */
};
return names[hint_style];
}
static const char *
_hint_metrics_to_string (cairo_hint_metrics_t hint_metrics)
{
static const char *names[] = {
"HINT_METRICS_DEFAULT", /* CAIRO_HINT_METRICS_DEFAULT */
"HINT_METRICS_OFF", /* CAIRO_HINT_METRICS_OFF */
"HINT_METRICS_ON" /* CAIRO_HINT_METRICS_ON */
};
return names[hint_metrics];
}
static cairo_status_t
_emit_font_options (cairo_script_surface_t *surface,
cairo_font_options_t *font_options)
{
if (cairo_font_options_equal (&surface->cr.current_font_options,
font_options))
{
return CAIRO_STATUS_SUCCESS;
}
_cairo_output_stream_printf (surface->ctx->stream, "<<");
if (font_options->antialias != surface->cr.current_font_options.antialias) {
_cairo_output_stream_printf (surface->ctx->stream,
" /antialias //%s",
_antialias_to_string (font_options->antialias));
}
if (font_options->subpixel_order !=
surface->cr.current_font_options.subpixel_order)
{
_cairo_output_stream_printf (surface->ctx->stream,
" /subpixel-order //%s",
_subpixel_order_to_string (font_options->subpixel_order));
}
if (font_options->hint_style !=
surface->cr.current_font_options.hint_style)
{
_cairo_output_stream_printf (surface->ctx->stream,
" /hint-style //%s",
_hint_style_to_string (font_options->hint_style));
}
if (font_options->hint_metrics !=
surface->cr.current_font_options.hint_metrics)
{
_cairo_output_stream_printf (surface->ctx->stream,
" /hint-metrics //%s",
_hint_metrics_to_string (font_options->hint_metrics));
}
_cairo_output_stream_printf (surface->ctx->stream,
" >> set-font-options\n");
surface->cr.current_font_options = *font_options;
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
{
cairo_script_surface_font_private_t *font_private;
font_private = scaled_font->surface_private;
if (font_private != NULL) {
_cairo_output_stream_printf (font_private->ctx->stream,
"/f%lu undef\n",
font_private->id);
_bitmap_release_id (&font_private->ctx->font_id, font_private->id);
if (font_private->prev != NULL)
font_private->prev = font_private->next;
else
font_private->ctx->fonts = font_private->next;
if (font_private->next != NULL)
font_private->next = font_private->prev;
free (font_private);
scaled_font->surface_private = NULL;
}
}
static cairo_status_t
_emit_type42_font (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font)
{
const cairo_scaled_font_backend_t *backend;
cairo_script_surface_font_private_t *font_private;
cairo_output_stream_t *base85_stream;
cairo_output_stream_t *zlib_stream;
cairo_status_t status, status2;
unsigned long size;
unsigned int load_flags;
uint32_t len;
uint8_t *buf;
backend = scaled_font->backend;
if (backend->load_truetype_table == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
size = 0;
status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size);
if (unlikely (status))
return status;
buf = malloc (size);
if (unlikely (buf == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
status = backend->load_truetype_table (scaled_font, 0, 0, buf, NULL);
if (unlikely (status)) {
free (buf);
return status;
}
load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font);
_cairo_output_stream_printf (surface->ctx->stream,
"<< "
"/type 42 "
"/index 0 "
"/flags %d "
"/source <|",
load_flags);
base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
len = size;
_cairo_output_stream_write (base85_stream, &len, sizeof (len));
zlib_stream = _cairo_deflate_stream_create (base85_stream);
_cairo_output_stream_write (zlib_stream, buf, size);
free (buf);
status2 = _cairo_output_stream_destroy (zlib_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
status2 = _cairo_output_stream_destroy (base85_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
font_private = scaled_font->surface_private;
_cairo_output_stream_printf (surface->ctx->stream,
" >> font dup /f%lu exch def set-font-face\n",
font_private->id);
return status;
}
static cairo_status_t
_emit_scaled_font_init (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font)
{
cairo_script_surface_font_private_t *font_private;
cairo_status_t status;
font_private = malloc (sizeof (cairo_script_surface_font_private_t));
if (unlikely (font_private == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
font_private->ctx = surface->ctx;
font_private->parent = scaled_font;
font_private->subset_glyph_index = 0;
font_private->has_sfnt = TRUE;
font_private->next = font_private->ctx->fonts;
font_private->prev = NULL;
if (font_private->ctx->fonts != NULL)
font_private->ctx->fonts->prev = font_private;
font_private->ctx->fonts = font_private;
status = _bitmap_next_id (&surface->ctx->font_id,
&font_private->id);
if (unlikely (status)) {
free (font_private);
return status;
}
scaled_font->surface_private = font_private;
scaled_font->surface_backend = &_cairo_script_surface_backend;
status = _emit_context (surface);
if (unlikely (status))
return status;
status = _emit_type42_font (surface, scaled_font);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
font_private->has_sfnt = FALSE;
_cairo_output_stream_printf (surface->ctx->stream,
"dict\n"
" /type 3 set\n"
" /metrics [%f %f %f %f %f] set\n"
" /glyphs array set\n"
" font dup /f%lu exch def set-font-face\n",
scaled_font->fs_extents.ascent,
scaled_font->fs_extents.descent,
scaled_font->fs_extents.height,
scaled_font->fs_extents.max_x_advance,
scaled_font->fs_extents.max_y_advance,
font_private->id);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_scaled_font (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font)
{
cairo_matrix_t matrix;
cairo_font_options_t options;
cairo_bool_t matrix_updated = FALSE;
cairo_status_t status;
cairo_script_surface_font_private_t *font_private;
cairo_scaled_font_get_ctm (scaled_font, &matrix);
status = _emit_scaling_matrix (surface, &matrix, &matrix_updated);
if (unlikely (status))
return status;
if (! matrix_updated && surface->cr.current_scaled_font == scaled_font)
return CAIRO_STATUS_SUCCESS;
cairo_scaled_font_get_font_matrix (scaled_font, &matrix);
status = _emit_font_matrix (surface, &matrix);
if (unlikely (status))
return status;
cairo_scaled_font_get_font_options (scaled_font, &options);
status = _emit_font_options (surface, &options);
if (unlikely (status))
return status;
surface->cr.current_scaled_font = scaled_font;
assert (scaled_font->surface_backend == NULL ||
scaled_font->surface_backend == &_cairo_script_surface_backend);
font_private = scaled_font->surface_private;
if (font_private == NULL) {
status = _emit_scaled_font_init (surface, scaled_font);
if (unlikely (status))
return status;
} else {
status = _emit_context (surface);
if (unlikely (status))
return status;
_cairo_output_stream_printf (surface->ctx->stream,
"f%lu set-font-face\n",
font_private->id);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_scaled_glyph_vector (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font,
cairo_scaled_glyph_t *scaled_glyph)
{
cairo_script_surface_font_private_t *font_private;
cairo_script_implicit_context_t old_cr;
cairo_status_t status;
unsigned long index;
font_private = scaled_font->surface_private;
index = ++font_private->subset_glyph_index;
scaled_glyph->surface_private = (void *) index;
_cairo_output_stream_printf (surface->ctx->stream,
"%lu <<\n"
" /metrics [%f %f %f %f %f %f]\n"
" /render {\n",
index,
scaled_glyph->fs_metrics.x_bearing,
scaled_glyph->fs_metrics.y_bearing,
scaled_glyph->fs_metrics.width,
scaled_glyph->fs_metrics.height,
scaled_glyph->fs_metrics.x_advance,
scaled_glyph->fs_metrics.y_advance);
if (! _cairo_matrix_is_identity (&scaled_font->scale_inverse)) {
_cairo_output_stream_printf (surface->ctx->stream,
"[%f %f %f %f %f %f] transform\n",
scaled_font->scale_inverse.xx,
scaled_font->scale_inverse.yx,
scaled_font->scale_inverse.xy,
scaled_font->scale_inverse.yy,
scaled_font->scale_inverse.x0,
scaled_font->scale_inverse.y0);
}
old_cr = surface->cr;
_cairo_script_implicit_context_init (&surface->cr);
status = cairo_meta_surface_replay (scaled_glyph->meta_surface,
&surface->base);
surface->cr = old_cr;
_cairo_output_stream_puts (surface->ctx->stream, "} >> set\n");
return status;
}
static cairo_status_t
_emit_scaled_glyph_bitmap (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font,
cairo_scaled_glyph_t *scaled_glyph)
{
cairo_script_surface_font_private_t *font_private;
cairo_status_t status;
unsigned long index;
font_private = scaled_font->surface_private;
index = ++font_private->subset_glyph_index;
scaled_glyph->surface_private = (void *) index;
_cairo_output_stream_printf (surface->ctx->stream,
"%lu <<\n"
" /metrics [%f %f %f %f %f %f]\n"
" /render {\n"
"%f %f translate\n",
index,
scaled_glyph->fs_metrics.x_bearing,
scaled_glyph->fs_metrics.y_bearing,
scaled_glyph->fs_metrics.width,
scaled_glyph->fs_metrics.height,
scaled_glyph->fs_metrics.x_advance,
scaled_glyph->fs_metrics.y_advance,
scaled_glyph->fs_metrics.x_bearing,
scaled_glyph->fs_metrics.y_bearing);
status = _emit_image_surface (surface, scaled_glyph->surface);
if (unlikely (status))
return status;
if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) {
_cairo_output_stream_printf (surface->ctx->stream,
"\n [%f %f %f %f %f %f] set-matrix\n",
scaled_font->font_matrix.xx,
scaled_font->font_matrix.yx,
scaled_font->font_matrix.xy,
scaled_font->font_matrix.yy,
scaled_font->font_matrix.x0,
scaled_font->font_matrix.y0);
}
_cairo_output_stream_puts (surface->ctx->stream,
"mask\n} >> set\n");
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_scaled_glyph_prologue (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font)
{
cairo_script_surface_font_private_t *font_private;
assert (scaled_font->surface_backend == &_cairo_script_surface_backend);
font_private = scaled_font->surface_private;
_cairo_output_stream_printf (surface->ctx->stream,
"f%lu /glyphs get\n",
font_private->id);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_scaled_glyphs (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
unsigned int num_glyphs)
{
cairo_script_surface_font_private_t *font_private;
cairo_status_t status;
unsigned int n;
cairo_bool_t have_glyph_prologue = FALSE;
if (num_glyphs == 0)
return CAIRO_STATUS_SUCCESS;
font_private = scaled_font->surface_private;
if (font_private->has_sfnt)
return CAIRO_STATUS_SUCCESS;
_cairo_scaled_font_freeze_cache (scaled_font);
for (n = 0; n < num_glyphs; n++) {
cairo_scaled_glyph_t *scaled_glyph;
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[n].index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&scaled_glyph);
if (unlikely (status))
break;
if (scaled_glyph->surface_private != NULL)
continue;
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[n].index,
CAIRO_SCALED_GLYPH_INFO_META_SURFACE,
&scaled_glyph);
if (_cairo_status_is_error (status))
break;
if (status == CAIRO_STATUS_SUCCESS) {
if (! have_glyph_prologue) {
status = _emit_scaled_glyph_prologue (surface, scaled_font);
if (unlikely (status))
break;
have_glyph_prologue = TRUE;
}
status = _emit_scaled_glyph_vector (surface,
scaled_font,
scaled_glyph);
if (unlikely (status))
break;
continue;
}
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[n].index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
if (_cairo_status_is_error (status))
break;
if (status == CAIRO_STATUS_SUCCESS) {
if (! have_glyph_prologue) {
status = _emit_scaled_glyph_prologue (surface, scaled_font);
if (unlikely (status))
break;
have_glyph_prologue = TRUE;
}
status = _emit_scaled_glyph_bitmap (surface,
scaled_font,
scaled_glyph);
if (unlikely (status))
break;
continue;
}
}
_cairo_scaled_font_thaw_cache (scaled_font);
if (have_glyph_prologue) {
_cairo_output_stream_puts (surface->ctx->stream, "pop pop\n");
}
return status;
}
static void
_emit_string_literal (cairo_script_surface_t *surface,
const char *utf8, int len)
{
char c;
const char *end;
_cairo_output_stream_puts (surface->ctx->stream, "(");
if (utf8 == NULL) {
end = utf8;
} else {
if (len < 0)
len = strlen (utf8);
end = utf8 + len;
}
while (utf8 < end) {
switch ((c = *utf8++)) {
case '\n':
c = 'n';
goto ESCAPED_CHAR;
case '\r':
c = 'r';
goto ESCAPED_CHAR;
case '\t':
c = 't';
goto ESCAPED_CHAR;
case '\b':
c = 'b';
goto ESCAPED_CHAR;
case '\f':
c = 'f';
goto ESCAPED_CHAR;
case '\\':
case '(':
case ')':
ESCAPED_CHAR:
_cairo_output_stream_printf (surface->ctx->stream, "\\%c", c);
break;
default:
if (isprint (c) || isspace (c)) {
_cairo_output_stream_printf (surface->ctx->stream, "%c", c);
} else {
int octal = 0;
while (c) {
octal *= 10;
octal += c&7;
c /= 8;
}
_cairo_output_stream_printf (surface->ctx->stream,
"\\%03d", octal);
}
break;
}
}
_cairo_output_stream_puts (surface->ctx->stream, ")");
}
static cairo_int_status_t
_cairo_script_surface_show_text_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t backward,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_script_surface_font_private_t *font_private;
cairo_scaled_glyph_t *scaled_glyph;
cairo_matrix_t matrix;
cairo_status_t status;
double x, y, ix, iy;
int n;
cairo_output_stream_t *base85_stream = NULL;
ctx_active (surface->ctx);
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
status = _emit_context (surface);
if (unlikely (status))
return status;
status = _emit_source (surface, op, source);
if (unlikely (status))
return status;
status = _emit_scaled_font (surface, scaled_font);
if (unlikely (status))
return status;
status = _emit_operator (surface, op);
if (unlikely (status))
return status;
status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs);
if (unlikely (status))
return status;
/* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */
/* [cx cy [glyphs]] show_glyphs */
if (utf8 != NULL && clusters != NULL) {
_emit_string_literal (surface, utf8, utf8_len);
_cairo_output_stream_puts (surface->ctx->stream, " ");
}
matrix = surface->cr.current_ctm;
status = cairo_matrix_invert (&matrix);
assert (status == CAIRO_STATUS_SUCCESS);
ix = x = glyphs[0].x;
iy = y = glyphs[0].y;
cairo_matrix_transform_point (&matrix, &ix, &iy);
ix -= scaled_font->font_matrix.x0;
iy -= scaled_font->font_matrix.y0;
_cairo_scaled_font_freeze_cache (scaled_font);
font_private = scaled_font->surface_private;
_cairo_output_stream_printf (surface->ctx->stream,
"[%f %f ",
ix, iy);
for (n = 0; n < num_glyphs; n++) {
if (font_private->has_sfnt) {
if (glyphs[n].index > 256)
break;
} else {
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[n].index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&scaled_glyph);
if (unlikely (status)) {
_cairo_scaled_font_thaw_cache (scaled_font);
return status;
}
}
}
if (n == num_glyphs) {
_cairo_output_stream_puts (surface->ctx->stream, "<~");
base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
} else
_cairo_output_stream_puts (surface->ctx->stream, "[");
for (n = 0; n < num_glyphs; n++) {
double dx, dy;
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[n].index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&scaled_glyph);
if (unlikely (status))
break;
if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) {
ix = x = glyphs[n].x;
iy = y = glyphs[n].y;
cairo_matrix_transform_point (&matrix, &ix, &iy);
ix -= scaled_font->font_matrix.x0;
iy -= scaled_font->font_matrix.y0;
if (base85_stream != NULL) {
status = _cairo_output_stream_destroy (base85_stream);
if (unlikely (status)) {
base85_stream = NULL;
break;
}
_cairo_output_stream_printf (surface->ctx->stream,
" %f %f <~",
ix, iy);
base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
} else {
_cairo_output_stream_printf (surface->ctx->stream,
" ] %f %f [ ",
ix, iy);
}
}
if (base85_stream != NULL) {
uint8_t c;
if (font_private->has_sfnt)
c = glyphs[n].index;
else
c = (uint8_t) (long unsigned) scaled_glyph->surface_private;
_cairo_output_stream_write (base85_stream, &c, 1);
} else {
if (font_private->has_sfnt)
_cairo_output_stream_printf (surface->ctx->stream, " %lu",
glyphs[n].index);
else
_cairo_output_stream_printf (surface->ctx->stream, " %lu",
(long unsigned) scaled_glyph->surface_private);
}
dx = scaled_glyph->metrics.x_advance;
dy = scaled_glyph->metrics.y_advance;
cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy);
x += dx;
y += dy;
}
_cairo_scaled_font_thaw_cache (scaled_font);
if (base85_stream != NULL) {
cairo_status_t status2;
status2 = _cairo_output_stream_destroy (base85_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
} else {
_cairo_output_stream_puts (surface->ctx->stream, " ]");
}
if (unlikely (status))
return status;
if (utf8 != NULL && clusters != NULL) {
for (n = 0; n < num_clusters; n++) {
if (clusters[n].num_bytes > UCHAR_MAX ||
clusters[n].num_glyphs > UCHAR_MAX)
{
break;
}
}
if (n < num_clusters) {
_cairo_output_stream_puts (surface->ctx->stream, "] [ ");
for (n = 0; n < num_clusters; n++) {
_cairo_output_stream_printf (surface->ctx->stream,
"%d %d ",
clusters[n].num_bytes,
clusters[n].num_glyphs);
}
_cairo_output_stream_puts (surface->ctx->stream, "]");
}
else
{
_cairo_output_stream_puts (surface->ctx->stream, "] <~");
base85_stream = _cairo_base85_stream_create (surface->ctx->stream);
for (n = 0; n < num_clusters; n++) {
uint8_t c[2];
c[0] = clusters[n].num_bytes;
c[1] = clusters[n].num_glyphs;
_cairo_output_stream_write (base85_stream, c, 2);
}
status = _cairo_output_stream_destroy (base85_stream);
if (unlikely (status))
return status;
}
_cairo_output_stream_printf (surface->ctx->stream,
" //%s show-text-glyphs\n",
_direction_to_string (backward));
} else {
_cairo_output_stream_puts (surface->ctx->stream,
"] show-glyphs\n");
}
ctx_inactive (surface->ctx);
if (_cairo_surface_wrapper_is_active (&surface->wrapper)){
return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper,
op, source,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
backward,
scaled_font,
clip);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
_cairo_script_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_script_surface_t *surface = abstract_surface;
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_get_extents (&surface->wrapper,
rectangle);
}
if (surface->width < 0 || surface->height < 0)
return FALSE;
rectangle->x = 0;
rectangle->y = 0;
rectangle->width = surface->width;
rectangle->height = surface->height;
return TRUE;
}
static const cairo_surface_backend_t
_cairo_script_surface_backend = {
CAIRO_SURFACE_TYPE_SCRIPT,
_cairo_script_surface_create_similar,
_cairo_script_surface_finish,
_cairo_script_surface_acquire_source_image,
_cairo_script_surface_release_source_image,
NULL, /* acquire_dest_image */
NULL, /* release_dest_image */
NULL, /* clone_similar */
NULL, /* composite */
NULL, /* fill_rectangles */
NULL, /* composite_trapezoids */
NULL, /* create_span_renderer */
NULL, /* check_span_renderer */
_cairo_script_surface_copy_page,
_cairo_script_surface_show_page,
_cairo_script_surface_get_extents,
NULL, /* old_show_glyphs */
NULL, /* get_font_options */
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
_cairo_script_surface_scaled_font_fini,
NULL, /* scaled_glyph_fini */
/* The 5 high level operations */
_cairo_script_surface_paint,
_cairo_script_surface_mask,
_cairo_script_surface_stroke,
_cairo_script_surface_fill,
NULL,
NULL, /* _cairo_script_surface_snapshot, */
NULL, /* is_similar */
/* XXX need fill-stroke for passthrough */
NULL, /* fill_stroke */
NULL, /* create_solid_pattern_surface */
NULL, /* can_repaint_solid_pattern_surface */
/* The alternate high-level text operation */
_cairo_script_surface_has_show_text_glyphs,
_cairo_script_surface_show_text_glyphs
};
static cairo_bool_t
_cairo_surface_is_script (cairo_surface_t *surface)
{
return surface->backend == &_cairo_script_surface_backend;
}
static cairo_script_vmcontext_t *
_cairo_script_vmcontext_create (cairo_output_stream_t *stream)
{
cairo_script_vmcontext_t *ctx;
ctx = malloc (sizeof (cairo_script_vmcontext_t));
if (unlikely (ctx == NULL))
return NULL;
memset (ctx, 0, sizeof (cairo_script_vmcontext_t));
cairo_list_init (&ctx->operands);
cairo_list_init (&ctx->deferred);
ctx->stream = stream;
ctx->mode = CAIRO_SCRIPT_MODE_ASCII;
cairo_list_init (&ctx->defines);
return ctx;
}
static void
_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr)
{
cr->current_operator = CAIRO_GSTATE_OPERATOR_DEFAULT;
cr->current_fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT;
cr->current_tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT;
cr->current_antialias = CAIRO_ANTIALIAS_DEFAULT;
_cairo_stroke_style_init (&cr->current_style);
_cairo_pattern_init_solid (&cr->current_source.solid,
CAIRO_COLOR_BLACK,
CAIRO_CONTENT_COLOR);
_cairo_path_fixed_init (&cr->current_path);
cairo_matrix_init_identity (&cr->current_ctm);
cairo_matrix_init_identity (&cr->current_font_matrix);
_cairo_font_options_init_default (&cr->current_font_options);
cr->current_scaled_font = NULL;
cr->has_clip = FALSE;
}
static cairo_script_surface_t *
_cairo_script_surface_create_internal (cairo_script_vmcontext_t *ctx,
double width,
double height,
cairo_surface_t *passthrough)
{
cairo_script_surface_t *surface;
if (unlikely (ctx == NULL))
return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
surface = malloc (sizeof (cairo_script_surface_t));
if (unlikely (surface == NULL))
return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
_cairo_surface_init (&surface->base,
&_cairo_script_surface_backend,
CAIRO_CONTENT_COLOR_ALPHA);
_cairo_surface_wrapper_init (&surface->wrapper, passthrough);
_cairo_surface_clipper_init (&surface->clipper,
_cairo_script_surface_clipper_intersect_clip_path);
surface->ctx = ctx;
ctx->ref++;
surface->width = width;
surface->height = height;
surface->defined = FALSE;
surface->id = (unsigned long) -1;
cairo_list_init (&surface->operand);
_cairo_script_implicit_context_init (&surface->cr);
return surface;
}
cairo_surface_t *
cairo_script_surface_create (const char *filename,
double width,
double height)
{
cairo_output_stream_t *stream;
cairo_script_surface_t *surface;
stream = _cairo_output_stream_create_for_filename (filename);
if (_cairo_output_stream_get_status (stream))
return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
surface = _cairo_script_surface_create_internal
(_cairo_script_vmcontext_create (stream), width, height, NULL);
if (surface->base.status)
return &surface->base;
_cairo_output_stream_puts (surface->ctx->stream, "%!CairoScript\n");
return &surface->base;
}
cairo_surface_t *
cairo_script_surface_create_for_stream (cairo_write_func_t write_func,
void *closure,
double width,
double height)
{
cairo_output_stream_t *stream;
stream = _cairo_output_stream_create (write_func, NULL, closure);
if (_cairo_output_stream_get_status (stream))
return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
return &_cairo_script_surface_create_internal
(_cairo_script_vmcontext_create (stream), width, height, NULL)->base;
}
cairo_surface_t *
cairo_script_surface_create_for_target (cairo_surface_t *target,
cairo_write_func_t write_func,
void *closure)
{
cairo_output_stream_t *stream;
cairo_script_surface_t *surface;
cairo_rectangle_int_t extents;
if (unlikely (target->status))
return _cairo_surface_create_in_error (target->status);
stream = _cairo_output_stream_create (write_func, NULL, closure);
if (_cairo_output_stream_get_status (stream))
return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
if (! _cairo_surface_get_extents (target, &extents))
extents.width = extents.height = -1;
surface = _cairo_script_surface_create_internal
(_cairo_script_vmcontext_create (stream),
extents.width, extents.height,
target);
if (unlikely (surface->base.status))
return &surface->base;
_cairo_output_stream_puts (surface->ctx->stream, "%!CairoScript\n");
return &surface->base;
}
void
cairo_script_surface_write_comment (cairo_surface_t *abstract_surface,
const char *comment,
int len)
{
cairo_script_surface_t *surface;
cairo_status_t status_ignored;
if (! _cairo_surface_is_script (abstract_surface)) {
status_ignored = _cairo_surface_set_error (abstract_surface,
CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return;
}
if (abstract_surface->status)
return;
surface = (cairo_script_surface_t *) abstract_surface;
if (len < 0)
len = strlen (comment);
_cairo_output_stream_puts (surface->ctx->stream, "% ");
_cairo_output_stream_write (surface->ctx->stream, comment, len);
_cairo_output_stream_puts (surface->ctx->stream, "\n");
}
void
cairo_script_surface_set_mode (cairo_surface_t *abstract_surface,
cairo_script_mode_t mode)
{
cairo_script_surface_t *surface;
cairo_status_t status_ignored;
if (! _cairo_surface_is_script (abstract_surface)) {
status_ignored = _cairo_surface_set_error (abstract_surface,
CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return;
}
if (abstract_surface->status)
return;
surface = (cairo_script_surface_t *) abstract_surface;
surface->ctx->mode = mode;
}
cairo_script_mode_t
cairo_script_surface_get_mode (cairo_surface_t *abstract_surface)
{
cairo_script_surface_t *surface;
if (! _cairo_surface_is_script (abstract_surface) ||
abstract_surface->status)
{
return CAIRO_SCRIPT_MODE_ASCII;
}
surface = (cairo_script_surface_t *) abstract_surface;
return surface->ctx->mode;
}