summaryrefslogtreecommitdiff
path: root/src/cairo-script-surface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-script-surface.c')
-rw-r--r--src/cairo-script-surface.c3999
1 files changed, 3999 insertions, 0 deletions
diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
new file mode 100644
index 000000000..b84aed976
--- /dev/null
+++ b/src/cairo-script-surface.c
@@ -0,0 +1,3999 @@
+/* -*- 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.
+ */
+
+/**
+ * SECTION:cairo-script
+ * @Title: Script Surfaces
+ * @Short_Description: Rendering to replayable scripts
+ * @See_Also: #cairo_surface_t
+ *
+ * The script surface provides the ability to render to a native
+ * script that matches the cairo drawing model. The scripts can
+ * be replayed using tools under the util/cairo-script directoriy,
+ * or with cairo-perf-trace.
+ **/
+
+/**
+ * CAIRO_HAS_SCRIPT_SURFACE:
+ *
+ * Defined if the script surface backend is available.
+ * The script surface backend is always built in since 1.12.
+ *
+ * Since: 1.12
+ **/
+
+
+#include "cairoint.h"
+
+#include "cairo-script.h"
+#include "cairo-script-private.h"
+
+#include "cairo-analysis-surface-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-list-inline.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-pattern-private.h"
+#include "cairo-recording-surface-inline.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-surface-snapshot-inline.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_font cairo_script_font_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;
+ int attach_snapshots;
+
+ cairo_bool_t owns_stream;
+ 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_font {
+ cairo_scaled_font_private_t base;
+
+ 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,
+ cairo_rectangle_t *extents,
+ cairo_surface_t *passthrough);
+
+static void
+_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private,
+ 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 */
+ "ANTIALIAS_FAST", /* CAIRO_ANTIALIAS_FAST */
+ "ANTIALIAS_GOOD", /* CAIRO_ANTIALIAS_GOOD */
+ "ANTIALIAS_BEST" /* CAIRO_ANTIALIAS_BEST */
+ };
+ 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 (target_is_active (surface)) {
+ _cairo_output_stream_puts (ctx->stream, "dup ");
+ return;
+ }
+
+ if (surface->defined) {
+ _cairo_output_stream_printf (ctx->stream, "s%u ",
+ surface->base.unique_id);
+ } else {
+ int depth = target_depth (surface);
+
+ assert (! cairo_list_is_empty (&surface->operand.link));
+ assert (! target_is_active (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 ");
+ } else {
+ _cairo_output_stream_printf (ctx->stream,
+ "%d -1 roll ", depth);
+ }
+ target_push (surface);
+ _cairo_output_stream_puts (ctx->stream, "dup ");
+ }
+ }
+}
+
+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 {
+ 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_RGB30: return "RGB30";
+ 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 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 m",
+ data[1].point.x, data[1].point.y);
+ break;
+ case CAIRO_PATH_LINE_TO:
+ _cairo_output_stream_printf (ctx->stream,
+ "\n %f %f l",
+ 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 c",
+ 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 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 set-corner-color",
+ j, r, g, b, a);
+ }
+
+ _cairo_output_stream_printf (ctx->stream, "\n end-patch");
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+struct script_snapshot {
+ cairo_surface_t base;
+};
+
+static cairo_status_t
+script_snapshot_finish (void *abstract_surface)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t script_snapshot_backend = {
+ CAIRO_SURFACE_TYPE_SCRIPT,
+ script_snapshot_finish,
+};
+
+static void
+detach_snapshot (cairo_surface_t *abstract_surface)
+{
+ cairo_script_surface_t *surface = (cairo_script_surface_t *)abstract_surface;
+ cairo_script_context_t *ctx = to_context (surface);
+
+ _cairo_output_stream_printf (ctx->stream,
+ "/s%d undef\n",
+ surface->base.unique_id);
+}
+
+static void
+attach_snapshot (cairo_script_context_t *ctx,
+ cairo_surface_t *source)
+{
+ struct script_snapshot *surface;
+
+ if (! ctx->attach_snapshots)
+ return;
+
+ surface = malloc (sizeof (*surface));
+ if (unlikely (surface == NULL))
+ return;
+
+ _cairo_surface_init (&surface->base,
+ &script_snapshot_backend,
+ &ctx->base,
+ source->content);
+
+ _cairo_output_stream_printf (ctx->stream,
+ "dup /s%d exch def ",
+ surface->base.unique_id);
+
+ _cairo_surface_attach_snapshot (source, &surface->base, detach_snapshot);
+ cairo_surface_destroy (&surface->base);
+}
+
+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_context_t *ctx = to_context (surface);
+ cairo_script_surface_t *similar;
+ cairo_surface_t *snapshot;
+ cairo_rectangle_t r, *extents;
+ cairo_status_t status;
+
+ snapshot = _cairo_surface_has_snapshot (&source->base, &script_snapshot_backend);
+ if (snapshot) {
+ _cairo_output_stream_printf (ctx->stream, "s%d", snapshot->unique_id);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ extents = NULL;
+ if (_cairo_recording_surface_get_bounds (&source->base, &r))
+ extents = &r;
+
+ similar = _cairo_script_surface_create_internal (ctx,
+ source->base.content,
+ extents,
+ NULL);
+ if (unlikely (similar->base.status))
+ return similar->base.status;
+
+ similar->base.is_clear = TRUE;
+
+ _cairo_output_stream_printf (ctx->stream, "//%s ",
+ _content_to_string (source->base.content));
+ if (extents) {
+ _cairo_output_stream_printf (ctx->stream, "[%f %f %f %f]",
+ extents->x, extents->y,
+ extents->width, extents->height);
+ } else
+ _cairo_output_stream_puts (ctx->stream, "[]");
+ _cairo_output_stream_puts (ctx->stream, " record\n");
+
+ attach_snapshot (ctx, &source->base);
+
+ _cairo_output_stream_puts (ctx->stream, "dup context\n");
+
+ 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 (ctx->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_RGB30:
+ 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;
+}
+
+static cairo_int_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_int_status_t status, status2;
+ cairo_surface_t *snapshot;
+ const uint8_t *mime_data;
+ unsigned long mime_data_length;
+
+ snapshot = _cairo_surface_has_snapshot (&image->base,
+ &script_snapshot_backend);
+ if (snapshot) {
+ _cairo_output_stream_printf (ctx->stream, "s%u ", snapshot->unique_id);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ status = _emit_png_surface (surface, image);
+ if (_cairo_int_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_RGB30:
+ case CAIRO_FORMAT_ARGB32:
+ len = clone->width * 4;
+ break;
+ case CAIRO_FORMAT_INVALID:
+ default:
+ ASSERT_NOT_REACHED;
+ len = 0;
+ 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_INT_STATUS_SUCCESS)
+ status = status2;
+ status2 = _cairo_output_stream_destroy (base85_stream);
+ if (status == CAIRO_INT_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_INT_STATUS_SUCCESS)
+ status = status2;
+ if (unlikely (status))
+ return status;
+ }
+ _cairo_output_stream_puts (ctx->stream, "~> >> image ");
+
+ cairo_surface_destroy (&clone->base);
+ }
+
+ 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_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_image_surface_pattern (cairo_script_surface_t *surface,
+ cairo_surface_t *source)
+{
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+ void *extra;
+
+ status = _cairo_surface_acquire_source_image (source, &image, &extra);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _emit_image_surface (surface, image);
+ _cairo_surface_release_source_image (source, image, extra);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_emit_subsurface_pattern (cairo_script_surface_t *surface,
+ cairo_surface_subsurface_t *sub)
+{
+ cairo_surface_t *source = sub->target;
+ cairo_int_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_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_surface_pattern (cairo_script_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_surface_pattern_t *surface_pattern;
+ cairo_surface_t *source, *snapshot, *free_me = NULL;
+ cairo_surface_t *take_snapshot = NULL;
+ cairo_int_status_t status;
+
+ surface_pattern = (cairo_surface_pattern_t *) pattern;
+ source = surface_pattern->surface;
+
+ if (_cairo_surface_is_snapshot (source)) {
+ snapshot = _cairo_surface_has_snapshot (source, &script_snapshot_backend);
+ if (snapshot) {
+ _cairo_output_stream_printf (ctx->stream,
+ "s%d pattern ",
+ snapshot->unique_id);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ if (_cairo_surface_snapshot_is_reused (source))
+ take_snapshot = source;
+
+ free_me = source = _cairo_surface_snapshot_get_target (source);
+ }
+
+ 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;
+ }
+ cairo_surface_destroy (free_me);
+ if (unlikely (status))
+ return status;
+
+ if (take_snapshot)
+ attach_snapshot (ctx, take_snapshot);
+
+ _cairo_output_stream_puts (ctx->stream, "pattern");
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_raster_pattern (cairo_script_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_surface_t *source;
+ cairo_int_status_t status;
+
+ source = _cairo_raster_source_pattern_acquire (pattern, &surface->base, NULL);
+ if (unlikely (source == NULL)) {
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ if (unlikely (source->status))
+ return source->status;
+
+ status = _emit_image_surface_pattern (surface, source);
+ _cairo_raster_source_pattern_release (pattern, source);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_puts (to_context(surface)->stream, "pattern");
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_emit_pattern (cairo_script_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_int_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;
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ status = _emit_raster_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_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_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_INT_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_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_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_int_status_t status;
+
+ assert (target_is_active (surface));
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ /* the source is ignored, so don't change it */
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ if (_cairo_pattern_equal (&surface->cr.current_source.base, source))
+ return CAIRO_INT_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_INT_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_boxes (cairo_script_surface_t *surface,
+ const cairo_path_fixed_t *path)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_path_fixed_iter_t iter;
+ cairo_status_t status;
+ struct _cairo_boxes_chunk *chunk;
+ cairo_boxes_t boxes;
+ cairo_box_t box;
+ int i;
+
+ _cairo_boxes_init (&boxes);
+ _cairo_path_fixed_iter_init (&iter, path);
+ while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
+ if (box.p1.y == box.p2.y || box.p1.x == box.p2.x)
+ continue;
+
+ status = _cairo_boxes_add (&boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
+ if (unlikely (status)) {
+ _cairo_boxes_fini (&boxes);
+ return status;
+ }
+ }
+
+ if (! _cairo_path_fixed_iter_at_end (&iter)) {
+ _cairo_boxes_fini (&boxes);
+ return FALSE;
+ }
+
+ for (chunk = &boxes.chunks; chunk; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ const cairo_box_t *b = &chunk->base[i];
+ double x1 = _cairo_fixed_to_double (b->p1.x);
+ double y1 = _cairo_fixed_to_double (b->p1.y);
+ double x2 = _cairo_fixed_to_double (b->p2.x);
+ double y2 = _cairo_fixed_to_double (b->p2.y);
+
+ _cairo_output_stream_printf (ctx->stream,
+ "\n %f %f %f %f rectangle",
+ x1, y1, x2 - x1, y2 - y1);
+ }
+ }
+
+ _cairo_boxes_fini (&boxes);
+ return status;
+}
+
+static cairo_status_t
+_emit_path (cairo_script_surface_t *surface,
+ const cairo_path_fixed_t *path,
+ cairo_bool_t is_fill)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_box_t box;
+ cairo_int_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);
+ _cairo_output_stream_puts (ctx->stream, "\n");
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path);
+ if (unlikely (status))
+ return status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ 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);
+
+ assert (x1 > -9999);
+
+ _cairo_output_stream_printf (ctx->stream,
+ " %f %f %f %f rectangle",
+ x1, y1, x2 - x1, y2 - y1);
+ status = CAIRO_INT_STATUS_SUCCESS;
+ } else if (is_fill && _cairo_path_fixed_fill_is_rectilinear (path)) {
+ status = _emit_path_boxes (surface, path);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_path_fixed_interpret (path,
+ _path_move_to,
+ _path_line_to,
+ _path_curve_to,
+ _path_close,
+ ctx->stream);
+ }
+
+ _cairo_output_stream_puts (ctx->stream, "\n");
+
+ return status;
+}
+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_rectangle_t extents;
+ 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;
+ }
+ }
+
+ extents.x = extents.y = 0;
+ extents.width = width;
+ extents.height = height;
+ surface = _cairo_script_surface_create_internal (ctx, content,
+ &extents, 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 cairo_status_t
+_device_flush (void *abstract_device)
+{
+ cairo_script_context_t *ctx = abstract_device;
+
+ return _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_font_t *font;
+
+ font = cairo_list_first_entry (&ctx->fonts, cairo_script_font_t, link);
+ cairo_list_del (&font->base.link);
+ cairo_list_del (&font->link);
+ free (font);
+ }
+
+ _bitmap_fini (ctx->surface_id.next);
+ _bitmap_fini (ctx->font_id.next);
+
+ if (ctx->owns_stream)
+ status = _cairo_output_stream_destroy (ctx->stream);
+
+ free (ctx);
+}
+
+static cairo_surface_t *
+_cairo_script_surface_source (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+
+ if (extents) {
+ extents->x = extents->y = 0;
+ extents->width = surface->width;
+ extents->height = surface->height;
+ }
+
+ return &surface->base;
+}
+
+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);
+
+ 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 >= _cairo_fixed_from_double (surface->width) &&
+ box.p2.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, TRUE);
+ 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,
+ const 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,
+ const 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,
+ const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const cairo_clip_t *clip)
+{
+ cairo_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, FALSE);
+ 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,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ const 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, TRUE);
+ 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_scaled_font_fini (cairo_scaled_font_private_t *abstract_private,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_script_font_t *priv = (cairo_script_font_t *)abstract_private;
+ cairo_script_context_t *ctx = (cairo_script_context_t *)abstract_private->key;
+ cairo_status_t status;
+
+ status = cairo_device_acquire (&ctx->base);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ _cairo_output_stream_printf (ctx->stream,
+ "/f%lu undef /sf%lu undef\n",
+ priv->id,
+ priv->id);
+
+ _bitmap_release_id (&ctx->font_id, priv->id);
+ cairo_device_release (&ctx->base);
+ }
+
+ cairo_list_del (&priv->link);
+ cairo_list_del (&priv->base.link);
+ free (priv);
+}
+
+static cairo_script_font_t *
+_cairo_script_font_get (cairo_script_context_t *ctx, cairo_scaled_font_t *font)
+{
+ return (cairo_script_font_t *) _cairo_scaled_font_find_private (font, ctx);
+}
+
+static long unsigned
+_cairo_script_font_id (cairo_script_context_t *ctx, cairo_scaled_font_t *font)
+{
+ return _cairo_script_font_get (ctx, font)->id;
+}
+
+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_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;
+
+ _cairo_output_stream_printf (ctx->stream,
+ "~> >> font dup /f%lu exch def set-font-face",
+ _cairo_script_font_id (ctx, scaled_font));
+
+ return status;
+}
+
+static cairo_status_t
+_emit_scaled_font_init (cairo_script_surface_t *surface,
+ cairo_scaled_font_t *scaled_font,
+ cairo_script_font_t **font_out)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_script_font_t *font_private;
+ cairo_int_status_t status;
+
+ font_private = malloc (sizeof (cairo_script_font_t));
+ if (unlikely (font_private == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_scaled_font_attach_private (scaled_font, &font_private->base, ctx,
+ _cairo_script_scaled_font_fini);
+
+ 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;
+ }
+
+ status = _emit_context (surface);
+ if (unlikely (status)) {
+ free (font_private);
+ return status;
+ }
+
+ status = _emit_type42_font (surface, scaled_font);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+ *font_out = font_private;
+ 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);
+
+ *font_out = font_private;
+ 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_font_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;
+
+ font_private = _cairo_script_font_get (ctx, scaled_font);
+ 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, &font_private);
+ if (unlikely (status))
+ return status;
+
+ 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_script_font_t *font_private,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_script_implicit_context_t old_cr;
+ cairo_status_t status;
+ unsigned long index;
+
+ index = ++font_private->subset_glyph_index;
+ scaled_glyph->dev_private_key = ctx;
+ scaled_glyph->dev_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_script_font_t *font_private,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_status_t status;
+ unsigned long index;
+
+ index = ++font_private->subset_glyph_index;
+ scaled_glyph->dev_private_key = ctx;
+ scaled_glyph->dev_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_context_t *ctx = to_context (surface);
+
+ _cairo_output_stream_printf (ctx->stream, "f%lu /glyphs get\n",
+ _cairo_script_font_id (ctx, scaled_font));
+
+ 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_context_t *ctx = to_context (surface);
+ cairo_script_font_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 = _cairo_script_font_get (ctx, scaled_font);
+ 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->dev_private_key == ctx)
+ 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, font_private,
+ 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,
+ font_private,
+ 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,
+ const cairo_clip_t *clip)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_script_font_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 = _cairo_script_font_get (ctx, scaled_font);
+
+ _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->dev_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)) {
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ 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->dev_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->dev_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_finish,
+
+ _cairo_default_context_create,
+
+ _cairo_script_surface_create_similar,
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_script_surface_source,
+ _cairo_script_surface_acquire_source_image,
+ _cairo_script_surface_release_source_image,
+ _cairo_script_surface_snapshot,
+
+ _cairo_script_surface_copy_page,
+ _cairo_script_surface_show_page,
+
+ _cairo_script_surface_get_extents,
+ NULL, /* get_font_options */
+
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+
+ _cairo_script_surface_paint,
+ _cairo_script_surface_mask,
+ _cairo_script_surface_stroke,
+ _cairo_script_surface_fill,
+ NULL, /* fill/stroke */
+ NULL, /* glyphs */
+ _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)
+{
+ 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,
+ cairo_rectangle_t *extents,
+ 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 = surface->height = -1;
+ if (extents) {
+ surface->width = extents->width;
+ surface->height = extents->height;
+ cairo_surface_set_device_offset (&surface->base,
+ -extents->x, -extents->y);
+ }
+
+ 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
+};
+
+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);
+
+ ctx->attach_snapshots = TRUE;
+
+ return &ctx->base;
+}
+
+void
+_cairo_script_context_attach_snapshots (cairo_device_t *device,
+ cairo_bool_t enable)
+{
+ cairo_script_context_t *ctx;
+
+ ctx = (cairo_script_context_t *) device;
+ ctx->attach_snapshots = enable;
+}
+
+static cairo_device_t *
+_cairo_script_context_create (cairo_output_stream_t *stream)
+{
+ cairo_script_context_t *ctx;
+
+ ctx = (cairo_script_context_t *)
+ _cairo_script_context_create_internal (stream);
+ if (unlikely (ctx->base.status))
+ return &ctx->base;
+
+ ctx->owns_stream = TRUE;
+ _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n");
+ return &ctx->base;
+}
+
+/**
+ * cairo_script_create:
+ * @filename: the name (path) of the file to write the script to
+ *
+ * Creates a output device for emitting the script, used when
+ * creating the individual surfaces.
+ *
+ * Return value: a pointer to the newly created device. The caller
+ * owns the surface and should call cairo_device_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" device if an error such as out of memory
+ * occurs. You can use cairo_device_status() to check for this.
+ *
+ * Since: 1.12
+ **/
+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 (stream);
+}
+
+/**
+ * cairo_script_create_for_stream:
+ * @write_func: callback function passed the bytes written to the script
+ * @closure: user data to be passed to the callback
+ *
+ * Creates a output device for emitting the script, used when
+ * creating the individual surfaces.
+ *
+ * Return value: a pointer to the newly created device. The caller
+ * owns the surface and should call cairo_device_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" device if an error such as out of memory
+ * occurs. You can use cairo_device_status() to check for this.
+ *
+ * Since: 1.12
+ **/
+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 (stream);
+}
+
+/**
+ * cairo_script_write_comment:
+ * @script: the script (output device)
+ * @comment: the string to emit
+ * @len:the length of the sting to write, or -1 to use strlen()
+ *
+ * Emit a string verbatim into the script.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_script_write_comment (cairo_device_t *script,
+ const char *comment,
+ int len)
+{
+ cairo_script_context_t *context = (cairo_script_context_t *) script;
+
+ 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");
+}
+
+/**
+ * cairo_script_set_mode:
+ * @script: The script (output device)
+ * @mode: the new mode
+ *
+ * Change the output mode of the script
+ *
+ * Since: 1.12
+ **/
+void
+cairo_script_set_mode (cairo_device_t *script,
+ cairo_script_mode_t mode)
+{
+ cairo_script_context_t *context = (cairo_script_context_t *) script;
+
+ context->mode = mode;
+}
+
+/**
+ * cairo_script_get_mode:
+ * @script: The script (output device) to query
+ *
+ * Queries the script for its current output mode.
+ *
+ * Return value: the current output mode of the script
+ *
+ * Since: 1.12
+ **/
+cairo_script_mode_t
+cairo_script_get_mode (cairo_device_t *script)
+{
+ cairo_script_context_t *context = (cairo_script_context_t *) script;
+
+ return context->mode;
+}
+
+/**
+ * cairo_script_surface_create:
+ * @script: the script (output device)
+ * @content: the content of the surface
+ * @width: width in pixels
+ * @height: height in pixels
+ *
+ * Create a new surface that will emit its rendering through @script
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_script_surface_create (cairo_device_t *script,
+ cairo_content_t content,
+ double width,
+ double height)
+{
+ cairo_rectangle_t *extents, r;
+
+ if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ if (unlikely (script->status))
+ return _cairo_surface_create_in_error (script->status);
+
+ extents = NULL;
+ if (width > 0 && height > 0) {
+ r.x = r.y = 0;
+ r.width = width;
+ r.height = height;
+ extents = &r;
+ }
+ return &_cairo_script_surface_create_internal ((cairo_script_context_t *) script,
+ content, extents,
+ NULL)->base;
+}
+slim_hidden_def (cairo_script_surface_create);
+
+/**
+ * cairo_script_surface_create_for_target:
+ * @script: the script (output device)
+ * @target: a target surface to wrap
+ *
+ * Create a pxoy surface that will render to @target and record
+ * the operations to @device.
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_script_surface_create_for_target (cairo_device_t *script,
+ cairo_surface_t *target)
+{
+ cairo_rectangle_int_t extents;
+ cairo_rectangle_t rect, *r;
+
+ if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ if (unlikely (script->status))
+ return _cairo_surface_create_in_error (script->status);
+
+ if (unlikely (target->status))
+ return _cairo_surface_create_in_error (target->status);
+
+ r = NULL;
+ if (_cairo_surface_get_extents (target, &extents)) {
+ rect.x = rect.y = 0;
+ rect.width = extents.width;
+ rect.height = extents.height;
+ r= &rect;
+ }
+ return &_cairo_script_surface_create_internal ((cairo_script_context_t *) script,
+ target->content, r,
+ target)->base;
+}
+
+/**
+ * cairo_script_from_recording_surface:
+ * @script: the script (output device)
+ * @recording_surface: the recording surface to replay
+ *
+ * Converts the record operations in @recording_surface into a script.
+ *
+ * Return value: #CAIRO_STATUS_SUCCESS on successful completion or an error code.
+ *
+ * Since: 1.12
+ **/
+cairo_status_t
+cairo_script_from_recording_surface (cairo_device_t *script,
+ cairo_surface_t *recording_surface)
+{
+ cairo_rectangle_t r, *extents;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+
+ if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
+ return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ if (unlikely (script->status))
+ return _cairo_error (script->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);
+
+ extents = NULL;
+ if (_cairo_recording_surface_get_bounds (recording_surface, &r))
+ extents = &r;
+
+ surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) script,
+ recording_surface->content,
+ extents,
+ NULL)->base;
+ if (unlikely (surface->status))
+ return surface->status;
+
+ status = _cairo_recording_surface_replay (recording_surface, surface);
+ cairo_surface_destroy (surface);
+
+ return status;
+}