blob: b975afafa4195870a5337c7b74db201e102dd5e1 [file] [log] [blame]
/* -*- 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., 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 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 recording-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-device-private.h"
#include "cairo-error-private.h"
#include "cairo-list-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-scaled-font-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-surface-snapshot-private.h"
#include "cairo-surface-subsurface-private.h"
#include "cairo-surface-wrapper-private.h"
#if CAIRO_HAS_FT_FONT
#include "cairo-ft-private.h"
#endif
#include <ctype.h>
#ifdef WORDS_BIGENDIAN
#define to_be32(x) x
#else
#define to_be32(x) bswap_32(x)
#endif
#define _cairo_output_stream_puts(S, STR) \
_cairo_output_stream_write ((S), (STR), strlen (STR))
#define static cairo_warn static
typedef struct _cairo_script_context cairo_script_context_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;
typedef struct _operand {
enum {
SURFACE,
DEFERRED,
} type;
cairo_list_t link;
} operand_t;
struct deferred_finish {
cairo_list_t link;
operand_t operand;
};
struct _cairo_script_context {
cairo_device_t base;
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_list_t fonts;
cairo_list_t defines;
};
struct _cairo_script_surface_font_private {
cairo_script_context_t *ctx;
cairo_bool_t has_sfnt;
unsigned long id;
unsigned long subset_glyph_index;
cairo_list_t link;
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_stroke_matrix;
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_surface_clipper_t clipper;
operand_t operand;
cairo_bool_t emitted;
cairo_bool_t defined;
cairo_bool_t active;
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_context_t *ctx,
cairo_content_t content,
double width,
double height,
cairo_surface_t *passthrough);
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
_cairo_script_implicit_context_reset (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 void
_bitmap_fini (struct _bitmap *b)
{
while (b != NULL) {
struct _bitmap *next = b->next;
free (b);
b = next;
}
}
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 inline cairo_script_context_t *
to_context (cairo_script_surface_t *surface)
{
return (cairo_script_context_t *) surface->base.device;
}
static cairo_bool_t
target_is_active (cairo_script_surface_t *surface)
{
return cairo_list_is_first (&surface->operand.link,
&to_context (surface)->operands);
}
static void
target_push (cairo_script_surface_t *surface)
{
cairo_list_move (&surface->operand.link, &to_context (surface)->operands);
}
static int
target_depth (cairo_script_surface_t *surface)
{
cairo_list_t *link;
int depth = 0;
cairo_list_foreach (link, &to_context (surface)->operands) {
if (link == &surface->operand.link)
break;
depth++;
}
return depth;
}
static void
_get_target (cairo_script_surface_t *surface)
{
cairo_script_context_t *ctx = to_context (surface);
if (surface->defined) {
_cairo_output_stream_printf (ctx->stream, "s%u ",
surface->base.unique_id);
} else {
assert (! cairo_list_is_empty (&surface->operand.link));
if (! target_is_active (surface)) {
int depth = target_depth (surface);
if (ctx->active) {
_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 (ctx->stream,
"exch\n");
} else {
_cairo_output_stream_printf (ctx->stream,
"%d -1 roll\n",
depth);
}
_cairo_output_stream_puts (ctx->stream, "/target get ");
}
} else {
_cairo_output_stream_puts (ctx->stream, "/target get ");
}
}
}
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_surface (cairo_script_surface_t *surface)
{
cairo_script_context_t *ctx = to_context (surface);
_cairo_output_stream_printf (ctx->stream,
"<< /content //%s",
_content_to_string (surface->base.content));
if (surface->width != -1 && surface->height != -1) {
_cairo_output_stream_printf (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 (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 (ctx->stream,
" /device-offset [%f %f]",
surface->base.device_transform.x0,
surface->base.device_transform.y0);
}
}
_cairo_output_stream_puts (ctx->stream, " >> surface context\n");
surface->emitted = TRUE;
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_context (cairo_script_surface_t *surface)
{
cairo_script_context_t *ctx = to_context (surface);
if (target_is_active (surface))
return CAIRO_STATUS_SUCCESS;
while (! cairo_list_is_empty (&ctx->operands)) {
operand_t *op;
cairo_script_surface_t *old;
op = cairo_list_first_entry (&ctx->operands,
operand_t,
link);
if (op->type == DEFERRED)
break;
old = cairo_container_of (op, cairo_script_surface_t, operand);
if (old == surface)
break;
if (old->active)
break;
if (! old->defined) {
assert (old->emitted);
_cairo_output_stream_printf (ctx->stream,
"/target get /s%u exch def pop\n",
old->base.unique_id);
old->defined = TRUE;
} else {
_cairo_output_stream_puts (ctx->stream, "pop\n");
}
cairo_list_del (&old->operand.link);
}
if (target_is_active (surface))
return CAIRO_STATUS_SUCCESS;
if (! surface->emitted) {
cairo_status_t status;
status = _emit_surface (surface);
if (unlikely (status))
return status;
} else if (cairo_list_is_empty (&surface->operand.link)) {
assert (surface->defined);
_cairo_output_stream_printf (ctx->stream,
"s%u context\n",
surface->base.unique_id);
_cairo_script_implicit_context_reset (&surface->cr);
_cairo_surface_clipper_reset (&surface->clipper);
} else {
int depth = target_depth (surface);
if (depth == 1) {
_cairo_output_stream_puts (ctx->stream, "exch\n");
} else {
_cairo_output_stream_printf (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 (to_context (surface)->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 (to_context (surface)->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 ||
fabs (tolerance - CAIRO_GSTATE_TOLERANCE_DEFAULT) < 1e-5) &&
surface->cr.current_tolerance == tolerance)
{
return CAIRO_STATUS_SUCCESS;
}
surface->cr.current_tolerance = tolerance;
_cairo_output_stream_printf (to_context (surface)->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 (to_context (surface)->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 ||
fabs (line_width - CAIRO_GSTATE_LINE_WIDTH_DEFAULT) < 1e-5) &&
surface->cr.current_style.line_width == line_width)
{
return CAIRO_STATUS_SUCCESS;
}
surface->cr.current_style.line_width = line_width;
_cairo_output_stream_printf (to_context (surface)->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 (to_context (surface)->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 (to_context (surface)->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 ||
fabs (miter_limit - CAIRO_GSTATE_MITER_LIMIT_DEFAULT) < 1e-5) &&
surface->cr.current_style.miter_limit == miter_limit)
{
return CAIRO_STATUS_SUCCESS;
}
surface->cr.current_style.miter_limit = miter_limit;
_cairo_output_stream_printf (to_context (surface)->stream,
"%f set-miter-limit\n",
miter_limit);
return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
_dashes_equal (const double *a, const double *b, int num_dashes)
{
while (num_dashes--) {
if (fabs (*a - *b) > 1e-5)
return FALSE;
a++, b++;
}
return TRUE;
}
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 ||
(fabs (surface->cr.current_style.dash_offset - offset) < 1e-5 &&
_dashes_equal (surface->cr.current_style.dash, dash, 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 (to_context (surface)->stream, "[");
for (n = 0; n < num_dashes; n++) {
_cairo_output_stream_printf (to_context (surface)->stream, "%f", dash[n]);
if (n < num_dashes-1)
_cairo_output_stream_puts (to_context (surface)->stream, " ");
}
_cairo_output_stream_printf (to_context (surface)->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)
{
switch (format) {
case CAIRO_FORMAT_ARGB32: return "ARGB32";
case CAIRO_FORMAT_RGB24: return "RGB24";
case CAIRO_FORMAT_RGB16_565: return "RGB16_565";
case CAIRO_FORMAT_A8: return "A8";
case CAIRO_FORMAT_A1: return "A1";
case CAIRO_FORMAT_INVALID: return "INVALID";
}
ASSERT_NOT_REACHED;
return "INVALID";
}
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;
cairo_script_context_t *ctx = to_context (surface);
if (! CAIRO_COLOR_IS_OPAQUE (&solid->color))
{
if (! (surface->base.content & CAIRO_CONTENT_COLOR) ||
((solid->color.red_short == 0 || solid->color.red_short == 0xffff) &&
(solid->color.green_short == 0 || solid->color.green_short == 0xffff) &&
(solid->color.blue_short == 0 || solid->color.blue_short == 0xffff) ))
{
_cairo_output_stream_printf (ctx->stream,
"%f a",
solid->color.alpha);
}
else
{
_cairo_output_stream_printf (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 (ctx->stream,
"%f g",
solid->color.red);
}
else
{
_cairo_output_stream_printf (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_script_context_t *ctx = to_context (surface);
cairo_linear_pattern_t *linear;
linear = (cairo_linear_pattern_t *) pattern;
_cairo_output_stream_printf (ctx->stream,
"%f %f %f %f linear",
linear->pd1.x, linear->pd1.y,
linear->pd2.x, linear->pd2.y);
return _emit_gradient_color_stops (&linear->base, ctx->stream);
}
static cairo_status_t
_emit_radial_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_radial_pattern_t *radial;
radial = (cairo_radial_pattern_t *) pattern;
_cairo_output_stream_printf (ctx->stream,
"%f %f %f %f %f %f radial",
radial->cd1.center.x,
radial->cd1.center.y,
radial->cd1.radius,
radial->cd2.center.x,
radial->cd2.center.y,
radial->cd2.radius);
return _emit_gradient_color_stops (&radial->base, ctx->stream);
}
static cairo_status_t
_emit_mesh_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_pattern_t *mesh;
cairo_status_t status;
unsigned int i, n;
mesh = (cairo_pattern_t *) pattern;
status = cairo_mesh_pattern_get_patch_count (mesh, &n);
if (unlikely (status))
return status;
_cairo_output_stream_printf (ctx->stream, "mesh");
for (i = 0; i < n; i++) {
cairo_path_t *path;
cairo_path_data_t *data;
int j;
_cairo_output_stream_printf (ctx->stream, "\n mesh-begin-patch");
path = cairo_mesh_pattern_get_path (mesh, i);
if (unlikely (path->status))
return path->status;
for (j = 0; j < path->num_data; j+=data[0].header.length) {
data = &path->data[j];
switch (data->header.type) {
case CAIRO_PATH_MOVE_TO:
_cairo_output_stream_printf (ctx->stream,
"\n %f %f mesh-move-to",
data[1].point.x, data[1].point.y);
break;
case CAIRO_PATH_LINE_TO:
_cairo_output_stream_printf (ctx->stream,
"\n %f %f mesh-line-to",
data[1].point.x, data[1].point.y);
break;
case CAIRO_PATH_CURVE_TO:
_cairo_output_stream_printf (ctx->stream,
"\n %f %f %f %f %f %f mesh-curve-to",
data[1].point.x, data[1].point.y,
data[2].point.x, data[2].point.y,
data[3].point.x, data[3].point.y);
break;
case CAIRO_PATH_CLOSE_PATH:
break;
}
}
cairo_path_destroy (path);
for (j = 0; j < 4; j++) {
double x, y;
status = cairo_mesh_pattern_get_control_point (mesh, i, j, &x, &y);
if (unlikely (status))
return status;
_cairo_output_stream_printf (ctx->stream,
"\n %d %f %f mesh-set-control-point",
j, x, y);
}
for (j = 0; j < 4; j++) {
double r, g, b, a;
status = cairo_mesh_pattern_get_corner_color_rgba (mesh, i, j, &r, &g, &b, &a);
if (unlikely (status))
return status;
_cairo_output_stream_printf (ctx->stream,
"\n %d %f %f %f %f mesh-set-corner-color",
j, r, g, b, a);
}
_cairo_output_stream_printf (ctx->stream, "\n mesh-end-patch");
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_recording_surface_pattern (cairo_script_surface_t *surface,
cairo_recording_surface_t *source)
{
cairo_script_implicit_context_t old_cr;
cairo_script_surface_t *similar;
cairo_status_t status;
cairo_box_t bbox;
cairo_rectangle_int_t rect;
/* first measure the extents */
status = _cairo_recording_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 (to_context (surface),
source->content,
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);
similar->base.is_clear = TRUE;
_get_target (surface);
_cairo_output_stream_printf (to_context (surface)->stream,
"%d %d //%s similar dup context\n",
rect.width, rect.height,
_content_to_string (source->content));
target_push (similar);
similar->emitted = TRUE;
old_cr = surface->cr;
_cairo_script_implicit_context_init (&surface->cr);
status = _cairo_recording_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.link);
assert (target_is_active (surface));
_cairo_output_stream_puts (to_context (surface)->stream, "pop ");
cairo_surface_destroy (&similar->base);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_script_surface_pattern (cairo_script_surface_t *surface,
cairo_script_surface_t *source)
{
_get_target (source);
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_RGB16_565:
for (row = image->height; row--; ) {
_cairo_output_stream_write (output, data, 2*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;
case CAIRO_FORMAT_INVALID:
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_RGB16_565:
for (row = image->height; row--; ) {
uint16_t *src = (uint16_t *) data;
uint16_t *dst = (uint16_t *) rowdata;
int col;
for (col = 0; col < width; col++)
dst[col] = bswap_16 (src[col]);
_cairo_output_stream_write (output, rowdata, 2*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;
case CAIRO_FORMAT_INVALID:
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_script_context_t *ctx = to_context (surface);
cairo_output_stream_t *base85_stream;
cairo_status_t status;
const uint8_t *mime_data;
unsigned long 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 (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 (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 (ctx->stream, "~> >> image ");
return CAIRO_STATUS_SUCCESS;
}
struct def {
cairo_script_context_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\n", def->tag);
free (def);
}
static cairo_status_t
_emit_image_surface (cairo_script_surface_t *surface,
cairo_image_surface_t *image)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_output_stream_t *base85_stream;
cairo_output_stream_t *zlib_stream;
cairo_status_t status, status2;
const uint8_t *mime_data;
unsigned long mime_data_length;
struct def *tag;
if (_cairo_user_data_array_get_data (&image->base.user_data,
(cairo_user_data_key_t *) ctx))
{
_cairo_output_stream_printf (ctx->stream,
"s%u ",
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);
} else {
clone = (cairo_image_surface_t *)
cairo_surface_reference (&image->base);
}
_cairo_output_stream_printf (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_RGB16_565:
len = clone->width * 2;
break;
case CAIRO_FORMAT_RGB24:
len = clone->width * 3;
break;
case CAIRO_FORMAT_ARGB32:
len = clone->width * 4;
break;
case CAIRO_FORMAT_INVALID:
ASSERT_NOT_REACHED;
break;
}
len *= clone->height;
if (len > 24) {
_cairo_output_stream_puts (ctx->stream, "<|");
base85_stream = _cairo_base85_stream_create (ctx->stream);
len = to_be32 (len);
_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 (ctx->stream, "<~");
base85_stream = _cairo_base85_stream_create (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 (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 = ctx;
tag->tag = image->base.unique_id;
tag->user_data = &image->base.user_data;
cairo_list_add (&tag->link, &ctx->defines);
status = _cairo_user_data_array_set_data (&image->base.user_data,
(cairo_user_data_key_t *) ctx,
tag, _undef);
if (unlikely (status)) {
free (tag);
return status;
}
_cairo_output_stream_printf (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 (ctx->stream,
"\n (%s) <~",
CAIRO_MIME_TYPE_JPEG);
base85_stream = _cairo_base85_stream_create (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 (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 (ctx->stream,
"\n (%s) <~",
CAIRO_MIME_TYPE_JP2);
base85_stream = _cairo_base85_stream_create (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 (ctx->stream, "~> set-mime-data\n");
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_image_surface_pattern (cairo_script_surface_t *surface,
cairo_surface_t *source)
{
cairo_surface_t *snapshot;
cairo_image_surface_t *image;
cairo_status_t status;
void *extra;
/* XXX keeping a copy is nasty, but we want to hook into the surface's
* lifetime. Using a snapshot is a convenient method.
*/
snapshot = _cairo_surface_snapshot (source);
status = _cairo_surface_acquire_source_image (snapshot, &image, &extra);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _emit_image_surface (surface, image);
_cairo_surface_release_source_image (snapshot, image, extra);
}
cairo_surface_destroy (snapshot);
return status;
}
static cairo_status_t
_emit_subsurface_pattern (cairo_script_surface_t *surface,
cairo_surface_subsurface_t *sub)
{
cairo_surface_t *source = sub->target;
cairo_status_t status;
switch ((int) source->backend->type) {
case CAIRO_SURFACE_TYPE_RECORDING:
status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source);
break;
case CAIRO_SURFACE_TYPE_SCRIPT:
status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source);
break;
default:
status = _emit_image_surface_pattern (surface, source);
break;
}
if (unlikely (status))
return status;
_cairo_output_stream_printf (to_context (surface)->stream,
"%d %d %d %d subsurface ",
sub->extents.x,
sub->extents.y,
sub->extents.width,
sub->extents.height);
return CAIRO_STATUS_SUCCESS;
}
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;
cairo_status_t status;
surface_pattern = (cairo_surface_pattern_t *) pattern;
source = surface_pattern->surface;
if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
source = ((cairo_surface_snapshot_t *) source)->target;
switch ((int) source->backend->type) {
case CAIRO_SURFACE_TYPE_RECORDING:
status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source);
break;
case CAIRO_SURFACE_TYPE_SCRIPT:
status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source);
break;
case CAIRO_SURFACE_TYPE_SUBSURFACE:
status = _emit_subsurface_pattern (surface, (cairo_surface_subsurface_t *) source);
break;
default:
status = _emit_image_surface_pattern (surface, source);
break;
}
if (unlikely (status))
return status;
_cairo_output_stream_puts (to_context (surface)->stream, "pattern");
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_script_context_t *ctx = to_context (surface);
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_MESH:
status = _emit_mesh_pattern (surface, pattern);
is_default_extend = TRUE;
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 (ctx->stream, "\n ");
need_newline = FALSE;
}
_cairo_output_stream_printf (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 (ctx->stream, "\n ");
need_newline = FALSE;
}
_cairo_output_stream_printf (ctx->stream,
" //%s set-filter\n ",
_filter_to_string (pattern->filter));
}
if (! is_default_extend ){
if (need_newline) {
_cairo_output_stream_puts (ctx->stream, "\n ");
need_newline = FALSE;
}
_cairo_output_stream_printf (ctx->stream,
" //%s set-extend\n ",
_extend_to_string (pattern->extend));
}
if (need_newline)
_cairo_output_stream_puts (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 (to_context (surface)->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;
assert (target_is_active (surface));
_cairo_output_stream_puts (to_context (surface)->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_script_context_t *ctx = to_context (surface);
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 (ctx->stream, "n");
if (path == NULL) {
_cairo_path_fixed_init (&surface->cr.current_path);
} else if (_cairo_path_fixed_is_box (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 (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,
_path_move_to,
_path_line_to,
_path_curve_to,
_path_close,
ctx->stream);
if (unlikely (status))
return status;
}
_cairo_output_stream_puts (ctx->stream, "\n");
return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
_scaling_matrix_equal (const cairo_matrix_t *a,
const cairo_matrix_t *b)
{
return fabs (a->xx - b->xx) < 1e-5 &&
fabs (a->xy - b->xy) < 1e-5 &&
fabs (a->yx - b->yx) < 1e-5 &&
fabs (a->yy - b->yy) < 1e-5;
}
static cairo_status_t
_emit_scaling_matrix (cairo_script_surface_t *surface,
const cairo_matrix_t *ctm,
cairo_bool_t *matrix_updated)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_bool_t was_identity;
assert (target_is_active (surface));
if (_scaling_matrix_equal (&surface->cr.current_ctm, ctm))
return CAIRO_STATUS_SUCCESS;
was_identity = _cairo_matrix_is_identity (&surface->cr.current_ctm);
*matrix_updated = TRUE;
surface->cr.current_ctm = *ctm;
surface->cr.current_ctm.x0 = 0.;
surface->cr.current_ctm.y0 = 0.;
if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) {
_cairo_output_stream_puts (ctx->stream,
"identity set-matrix\n");
} else if (was_identity && fabs (ctm->yx) < 1e-5 && fabs (ctm->xy) < 1e-5) {
_cairo_output_stream_printf (ctx->stream,
"%f %f scale\n",
ctm->xx, ctm->yy);
} else {
_cairo_output_stream_printf (ctx->stream,
"[%f %f %f %f 0 0] set-matrix\n",
ctm->xx, ctm->yx,
ctm->xy, ctm->yy);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_emit_font_matrix (cairo_script_surface_t *surface,
const cairo_matrix_t *font_matrix)
{
cairo_script_context_t *ctx = to_context (surface);
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 (ctx->stream,
"identity set-font-matrix\n");
} else {
_cairo_output_stream_printf (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_context_t *ctx;
cairo_status_t status;
ctx = to_context (other);
status = cairo_device_acquire (&ctx->base);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
if (! other->emitted) {
status = _emit_surface (other);
if (unlikely (status)) {
cairo_device_release (&ctx->base);
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)) {
cairo_device_release (&ctx->base);
return passthrough;
}
}
surface = _cairo_script_surface_create_internal (ctx,
content,
width, height,
passthrough);
cairo_surface_destroy (passthrough);
if (unlikely (surface->base.status)) {
cairo_device_release (&ctx->base);
return &surface->base;
}
_get_target (other);
_cairo_output_stream_printf (ctx->stream,
"%u %u //%s similar dup /s%u exch def context\n",
width, height,
_content_to_string (content),
surface->base.unique_id);
surface->emitted = TRUE;
surface->defined = TRUE;
surface->base.is_clear = TRUE;
target_push (surface);
cairo_device_release (&ctx->base);
return &surface->base;
}
static void
_device_flush (void *abstract_device)
{
cairo_script_context_t *ctx = abstract_device;
cairo_status_t status;
status = _cairo_output_stream_flush (ctx->stream);
}
static void
_device_destroy (void *abstract_device)
{
cairo_script_context_t *ctx = abstract_device;
cairo_status_t status;
while (! cairo_list_is_empty (&ctx->fonts)) {
cairo_script_surface_font_private_t *font;
font = cairo_list_first_entry (&ctx->fonts,
cairo_script_surface_font_private_t,
link);
cairo_list_del (&font->link);
if (font->parent->surface_private == font)
font->parent->surface_private = NULL;
free (font);
}
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);
}
_bitmap_fini (ctx->surface_id.next);
_bitmap_fini (ctx->font_id.next);
status = _cairo_output_stream_destroy (ctx->stream);
free (ctx);
}
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_script_context_t *ctx = to_context (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);
status = cairo_device_acquire (&ctx->base);
if (unlikely (status))
return status;
if (surface->emitted) {
assert (! surface->active);
if (! cairo_list_is_empty (&surface->operand.link)) {
if (! ctx->active) {
if (target_is_active (surface)) {
_cairo_output_stream_printf (ctx->stream,
"pop\n");
} else {
int depth = target_depth (surface);
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 (&surface->operand.link);
} 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;
cairo_list_del (&surface->operand.link);
} else {
link->operand.type = DEFERRED;
cairo_list_swap (&link->operand.link,
&surface->operand.link);
cairo_list_add (&link->link, &ctx->deferred);
}
}
}
if (surface->defined) {
_cairo_output_stream_printf (ctx->stream,
"/s%u undef\n",
surface->base.unique_id);
}
}
if (status == CAIRO_STATUS_SUCCESS)
status = _cairo_output_stream_flush (to_context (surface)->stream);
cairo_device_release (&ctx->base);
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 = cairo_device_acquire (surface->base.device);
if (unlikely (status))
return status;
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
_cairo_output_stream_puts (to_context (surface)->stream, "copy-page\n");
BAIL:
cairo_device_release (surface->base.device);
return status;
}
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 = cairo_device_acquire (surface->base.device);
if (unlikely (status))
return status;
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
_cairo_output_stream_puts (to_context (surface)->stream, "show-page\n");
BAIL:
cairo_device_release (surface->base.device);
return status;
}
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_script_context_t *ctx = to_context (surface);
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 (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_box (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->has_curve_to) {
status = _emit_tolerance (surface, tolerance, matrix_updated);
if (unlikely (status))
return status;
}
if (! _cairo_path_fixed_fill_maybe_region (path)) {
status = _emit_antialias (surface, antialias);
if (unlikely (status))
return status;
}
status = _emit_path (surface, path);
if (unlikely (status))
return status;
_cairo_output_stream_puts (ctx->stream, "clip+\n");
surface->cr.has_clip = TRUE;
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
active (cairo_script_surface_t *surface)
{
cairo_status_t status;
status = cairo_device_acquire (surface->base.device);
if (unlikely (status))
return status;
if (surface->active++ == 0)
to_context (surface)->active++;
return CAIRO_STATUS_SUCCESS;
}
static void
inactive (cairo_script_surface_t *surface)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_list_t sorted;
assert (surface->active > 0);
if (--surface->active)
goto DONE;
assert (ctx->active > 0);
if (--ctx->active)
goto DONE;
cairo_list_init (&sorted);
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.link)
break;
depth++;
}
df->operand.type = depth;
if (cairo_list_is_empty (&sorted)) {
cairo_list_move (&df->link, &sorted);
} else {
struct deferred_finish *pos;
cairo_list_foreach_entry (pos, struct deferred_finish,
&sorted,
link)
{
if (df->operand.type < pos->operand.type)
break;
}
cairo_list_move_tail (&df->link, &pos->link);
}
}
while (! cairo_list_is_empty (&sorted)) {
struct deferred_finish *df;
cairo_list_t *operand;
int depth;
df = cairo_list_first_entry (&sorted,
struct deferred_finish,
link);
depth = 0;
cairo_list_foreach (operand, &ctx->operands) {
if (operand == &df->operand.link)
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.link);
cairo_list_del (&df->link);
free (df);
}
DONE:
cairo_device_release (surface->base.device);
}
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;
status = active (surface);
if (unlikely (status))
return status;
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
goto BAIL;
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
status = _emit_source (surface, op, source);
if (unlikely (status))
goto BAIL;
status = _emit_operator (surface, op);
if (unlikely (status))
goto BAIL;
_cairo_output_stream_puts (to_context (surface)->stream,
"paint\n");
inactive (surface);
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_paint (&surface->wrapper,
op, source, clip);
}
return CAIRO_STATUS_SUCCESS;
BAIL:
inactive (surface);
return status;
}
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;
status = active (surface);
if (unlikely (status))
return status;
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
goto BAIL;
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
status = _emit_source (surface, op, source);
if (unlikely (status))
goto BAIL;
status = _emit_operator (surface, op);
if (unlikely (status))
goto BAIL;
if (_cairo_pattern_equal (source, mask)) {
_cairo_output_stream_puts (to_context (surface)->stream, "/source get");
} else {
status = _emit_pattern (surface, mask);
if (unlikely (status))
goto BAIL;
}
assert (surface->cr.current_operator == op);
_cairo_output_stream_puts (to_context (surface)->stream,
" mask\n");
inactive (surface);
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_mask (&surface->wrapper,
op, source, mask, clip);
}
return CAIRO_STATUS_SUCCESS;
BAIL:
inactive (surface);
return status;
}
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,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const 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;
status = active (surface);
if (unlikely (status))
return status;
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
goto BAIL;
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
status = _emit_identity (surface, &matrix_updated);
if (unlikely (status))
goto BAIL;
status = _emit_path (surface, path);
if (unlikely (status))
goto BAIL;
status = _emit_source (surface, op, source);
if (unlikely (status))
goto BAIL;
status = _emit_scaling_matrix (surface, ctm, &matrix_updated);
if (unlikely (status))
goto BAIL;
status = _emit_operator (surface, op);
if (unlikely (status))
goto BAIL;
if (_scaling_matrix_equal (&surface->cr.current_ctm,
&surface->cr.current_stroke_matrix))
{
matrix_updated = FALSE;
}
else
{
matrix_updated = TRUE;
surface->cr.current_stroke_matrix = surface->cr.current_ctm;
}
status = _emit_stroke_style (surface, style, matrix_updated);
if (unlikely (status))
goto BAIL;
status = _emit_tolerance (surface, tolerance, matrix_updated);
if (unlikely (status))
goto BAIL;
status = _emit_antialias (surface, antialias);
if (unlikely (status))
goto BAIL;
_cairo_output_stream_puts (to_context (surface)->stream, "stroke+\n");
inactive (surface);
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;
BAIL:
inactive (surface);
return status;
}
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;
cairo_box_t box;
status = active (surface);
if (unlikely (status))
return status;
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
goto BAIL;
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
status = _emit_identity (surface, &matrix_updated);
if (unlikely (status))
goto BAIL;
status = _emit_source (surface, op, source);
if (unlikely (status))
goto BAIL;
if (! _cairo_path_fixed_is_box (path, &box)) {
status = _emit_fill_rule (surface, fill_rule);
if (unlikely (status))
goto BAIL;
}
if (path->has_curve_to) {
status = _emit_tolerance (surface, tolerance, matrix_updated);
if (unlikely (status))
goto BAIL;
}
if (! _cairo_path_fixed_fill_maybe_region (path)) {
status = _emit_antialias (surface, antialias);
if (unlikely (status))
goto BAIL;
}
status = _emit_path (surface, path);
if (unlikely (status))
goto BAIL;
status = _emit_operator (surface, op);
if (unlikely (status))
goto BAIL;
_cairo_output_stream_puts (to_context (surface)->stream, "fill+\n");
inactive (surface);
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;
BAIL:
inactive (surface);
return status;
}
static cairo_surface_t *
_cairo_script_surface_snapshot (void *abstract_surface)
{
cairo_script_surface_t *surface = abstract_surface;
if (_cairo_surface_wrapper_is_active (&surface->wrapper))
return _cairo_surface_wrapper_snapshot (&surface->wrapper);
return NULL;
}
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)
{
cairo_script_context_t *ctx = to_context (surface);
if (cairo_font_options_equal (&surface->cr.current_font_options,
font_options))
{
return CAIRO_STATUS_SUCCESS;
}
_cairo_output_stream_printf (ctx->stream, "<<");
if (font_options->antialias != surface->cr.current_font_options.antialias) {
_cairo_output_stream_printf (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 (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 (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 (ctx->stream,
" /hint-metrics //%s",
_hint_metrics_to_string (font_options->hint_metrics));
}
_cairo_output_stream_printf (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_status_t status;
cairo_device_t *device;
status = cairo_device_acquire (device = &font_private->ctx->base);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
_cairo_output_stream_printf (font_private->ctx->stream,
"/f%lu undef /sf%lu undef\n",
font_private->id,
font_private->id);
_bitmap_release_id (&font_private->ctx->font_id, font_private->id);
cairo_list_del (&font_private->link);
free (font_private);
cairo_device_release (device);
}
scaled_font->surface_private = NULL;
}
}
static cairo_status_t
_emit_type42_font (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font)
{
cairo_script_context_t *ctx = to_context (surface);
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, &size);
if (unlikely (status)) {
free (buf);
return status;
}
#if CAIRO_HAS_FT_FONT
load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font);
#else
load_flags = 0;
#endif
_cairo_output_stream_printf (ctx->stream,
"<< "
"/type 42 "
"/index 0 "
"/flags %d "
"/source <|",
load_flags);
base85_stream = _cairo_base85_stream_create (ctx->stream);
len = to_be32 (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 (ctx->stream,
"~> >> font dup /f%lu exch def set-font-face",
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_context_t *ctx = to_context (surface);
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 = ctx;
font_private->parent = scaled_font;
font_private->subset_glyph_index = 0;
font_private->has_sfnt = TRUE;
cairo_list_add (&font_private->link, &ctx->fonts);
status = _bitmap_next_id (&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 (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",
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_script_context_t *ctx = to_context (surface);
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;
surface->cr.current_scaled_font = scaled_font;
if (! (scaled_font->surface_backend == NULL ||
scaled_font->surface_backend == &_cairo_script_surface_backend))
{
_cairo_scaled_font_revoke_ownership (scaled_font);
}
font_private = scaled_font->surface_private;
if (font_private == NULL) {
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;
status = _emit_scaled_font_init (surface, scaled_font);
if (unlikely (status))
return status;
font_private = scaled_font->surface_private;
assert (font_private != NULL);
assert (target_is_active (surface));
_cairo_output_stream_printf (ctx->stream,
" /scaled-font get /sf%lu exch def\n",
font_private->id);
} else {
_cairo_output_stream_printf (ctx->stream,
"sf%lu set-scaled-font\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_context_t *ctx = to_context (surface);
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 (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 (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_recording_surface_replay (scaled_glyph->recording_surface,
&surface->base);
surface->cr = old_cr;
_cairo_output_stream_puts (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_context_t *ctx = to_context (surface);
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 (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;
_cairo_output_stream_puts (ctx->stream, "pattern ");
if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) {
_cairo_output_stream_printf (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 (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 (to_context (surface)->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_RECORDING_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 (to_context (surface)->stream, "pop pop\n");
}
return status;
}
static void
to_octal (int value, char *buf, size_t size)
{
do {
buf[--size] = '0' + (value & 7);
value >>= 3;
} while (size);
}
static void
_emit_string_literal (cairo_script_surface_t *surface,
const char *utf8, int len)
{
cairo_script_context_t *ctx = to_context (surface);
char c;
const char *end;
_cairo_output_stream_puts (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 (ctx->stream, "\\%c", c);
break;
default:
if (isprint (c) || isspace (c)) {
_cairo_output_stream_printf (ctx->stream, "%c", c);
} else {
char buf[4] = { '\\' };
to_octal (c, buf+1, 3);
_cairo_output_stream_write (ctx->stream, buf, 4);
}
break;
}
}
_cairo_output_stream_puts (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_context_t *ctx = to_context (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;
status = active (surface);
if (unlikely (status))
return status;
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
goto BAIL;
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
status = _emit_source (surface, op, source);
if (unlikely (status))
goto BAIL;
status = _emit_scaled_font (surface, scaled_font);
if (unlikely (status))
goto BAIL;
status = _emit_operator (surface, op);
if (unlikely (status))
goto BAIL;
status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs);
if (unlikely (status))
goto BAIL;
/* (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 (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 (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);
goto BAIL;
}
if ((long unsigned) scaled_glyph->surface_private > 256)
break;
}
}
if (n == num_glyphs) {
_cairo_output_stream_puts (ctx->stream, "<~");
base85_stream = _cairo_base85_stream_create (ctx->stream);
} else
_cairo_output_stream_puts (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))
goto BAIL;
if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) {
if (fabs (glyphs[n].y - y) < 1e-5) {
if (base85_stream != NULL) {
status = _cairo_output_stream_destroy (base85_stream);
if (unlikely (status)) {
base85_stream = NULL;
break;
}
_cairo_output_stream_printf (ctx->stream,
"~> %f <~", glyphs[n].x - x);
base85_stream = _cairo_base85_stream_create (ctx->stream);
} else {
_cairo_output_stream_printf (ctx->stream,
" ] %f [ ", glyphs[n].x - x);
}
x = glyphs[n].x;
} else {
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 (ctx->stream,
"~> %f %f <~",
ix, iy);
base85_stream = _cairo_base85_stream_create (ctx->stream);
} else {
_cairo_output_stream_printf (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 (ctx->stream, " %lu",
glyphs[n].index);
else
_cairo_output_stream_printf (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;
_cairo_output_stream_printf (ctx->stream, "~>");
} else {
_cairo_output_stream_puts (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 (ctx->stream, "] [ ");
for (n = 0; n < num_clusters; n++) {
_cairo_output_stream_printf (ctx->stream,
"%d %d ",
clusters[n].num_bytes,
clusters[n].num_glyphs);
}
_cairo_output_stream_puts (ctx->stream, "]");
}
else
{
_cairo_output_stream_puts (ctx->stream, "] <~");
base85_stream = _cairo_base85_stream_create (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))
goto BAIL;
_cairo_output_stream_puts (ctx->stream, "~>");
}
_cairo_output_stream_printf (ctx->stream,
" //%s show-text-glyphs\n",
_direction_to_string (backward));
} else {
_cairo_output_stream_puts (ctx->stream,
"] show-glyphs\n");
}
inactive (surface);
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;
BAIL:
inactive (surface);
return status;
}
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,
_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 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_path_fixed_init (&cr->current_path);
cairo_matrix_init_identity (&cr->current_ctm);
cairo_matrix_init_identity (&cr->current_stroke_matrix);
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 void
_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr)
{
if (cr->current_style.dash != NULL) {
free (cr->current_style.dash);
cr->current_style.dash = NULL;
}
_cairo_pattern_fini (&cr->current_source.base);
_cairo_path_fixed_fini (&cr->current_path);
_cairo_script_implicit_context_init (cr);
}
static cairo_script_surface_t *
_cairo_script_surface_create_internal (cairo_script_context_t *ctx,
cairo_content_t content,
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_NULL_POINTER));
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,
&ctx->base,
content);
_cairo_surface_wrapper_init (&surface->wrapper, passthrough);
_cairo_surface_clipper_init (&surface->clipper,
_cairo_script_surface_clipper_intersect_clip_path);
surface->width = width;
surface->height = height;
surface->emitted = FALSE;
surface->defined = FALSE;
surface->active = FALSE;
surface->operand.type = SURFACE;
cairo_list_init (&surface->operand.link);
_cairo_script_implicit_context_init (&surface->cr);
return surface;
}
static const cairo_device_backend_t _cairo_script_device_backend = {
CAIRO_DEVICE_TYPE_SCRIPT,
NULL, NULL, /* lock, unlock */
_device_flush, /* flush */
NULL, /* finish */
_device_destroy
};
static cairo_device_t *
_cairo_script_context_create_internal (cairo_output_stream_t *stream)
{
cairo_script_context_t *ctx;
ctx = malloc (sizeof (cairo_script_context_t));
if (unlikely (ctx == NULL))
return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
memset (ctx, 0, sizeof (cairo_script_context_t));
_cairo_device_init (&ctx->base, &_cairo_script_device_backend);
cairo_list_init (&ctx->operands);
cairo_list_init (&ctx->deferred);
ctx->stream = stream;
ctx->mode = CAIRO_SCRIPT_MODE_ASCII;
cairo_list_init (&ctx->fonts);
cairo_list_init (&ctx->defines);
_cairo_output_stream_puts (ctx->stream, "%!CairoScript\n");
return &ctx->base;
}
cairo_device_t *
cairo_script_create (const char *filename)
{
cairo_output_stream_t *stream;
cairo_status_t status;
stream = _cairo_output_stream_create_for_filename (filename);
if ((status = _cairo_output_stream_get_status (stream)))
return _cairo_device_create_in_error (status);
return _cairo_script_context_create_internal (stream);
}
cairo_device_t *
cairo_script_create_for_stream (cairo_write_func_t write_func,
void *closure)
{
cairo_output_stream_t *stream;
cairo_status_t status;
stream = _cairo_output_stream_create (write_func, NULL, closure);
if ((status = _cairo_output_stream_get_status (stream)))
return _cairo_device_create_in_error (status);
return _cairo_script_context_create_internal (stream);
}
void
cairo_script_write_comment (cairo_device_t *device,
const char *comment,
int len)
{
cairo_script_context_t *context = (cairo_script_context_t *) device;
if (len < 0)
len = strlen (comment);
_cairo_output_stream_puts (context->stream, "% ");
_cairo_output_stream_write (context->stream, comment, len);
_cairo_output_stream_puts (context->stream, "\n");
}
void
cairo_script_set_mode (cairo_device_t *device,
cairo_script_mode_t mode)
{
cairo_script_context_t *context = (cairo_script_context_t *) device;
context->mode = mode;
}
cairo_script_mode_t
cairo_script_get_mode (cairo_device_t *device)
{
cairo_script_context_t *context = (cairo_script_context_t *) device;
return context->mode;
}
cairo_surface_t *
cairo_script_surface_create (cairo_device_t *device,
cairo_content_t content,
double width,
double height)
{
if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
if (unlikely (device->status))
return _cairo_surface_create_in_error (device->status);
return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device,
content,
width, height,
NULL)->base;
}
cairo_surface_t *
cairo_script_surface_create_for_target (cairo_device_t *device,
cairo_surface_t *target)
{
cairo_rectangle_int_t extents;
if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
if (unlikely (device->status))
return _cairo_surface_create_in_error (device->status);
if (unlikely (target->status))
return _cairo_surface_create_in_error (target->status);
if (! _cairo_surface_get_extents (target, &extents))
extents.width = extents.height = -1;
return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device,
target->content,
extents.width,
extents.height,
target)->base;
}
cairo_status_t
cairo_script_from_recording_surface (cairo_device_t *device,
cairo_surface_t *recording_surface)
{
cairo_box_t bbox;
cairo_rectangle_int_t extents;
cairo_surface_t *surface;
cairo_status_t status;
if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
if (unlikely (device->status))
return _cairo_error (device->status);
if (unlikely (recording_surface->status))
return recording_surface->status;
if (unlikely (! _cairo_surface_is_recording (recording_surface)))
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
&bbox, NULL);
if (unlikely (status))
return status;
_cairo_box_round_to_rectangle (&bbox, &extents);
surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) device,
recording_surface->content,
extents.width,
extents.height,
NULL)->base;
if (unlikely (surface->status))
return surface->status;
cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
status = _cairo_recording_surface_replay (recording_surface, surface);
cairo_surface_destroy (surface);
return status;
}