/* -*- 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 */ /* 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 directory, * 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 #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; mmap[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); assert (prev != NULL); bb = _cairo_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 = _cairo_malloc (sizeof (*surface)); if (unlikely (surface == NULL)) return; _cairo_surface_init (&surface->base, &script_snapshot_backend, &ctx->base, source->content, source->is_vector); _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 row, width; ptrdiff_t stride; 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 = _cairo_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 CAIRO_STATUS_INVALID_PATH_DATA; } 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 = _cairo_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 = _cairo_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 = _cairo_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 = _cairo_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, TRUE); /* is_vector */ _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 = _cairo_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= ▭ } 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; }