| /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ |
| /* cairo - a vector graphics library with display and print output |
| * |
| * Copyright © 2008 Chris Wilson |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it either under the terms of the GNU Lesser General Public |
| * License version 2.1 as published by the Free Software Foundation |
| * (the "LGPL") or, at your option, under the terms of the Mozilla |
| * Public License Version 1.1 (the "MPL"). If you do not alter this |
| * notice, a recipient may use your version of this file under either |
| * the MPL or the LGPL. |
| * |
| * You should have received a copy of the LGPL along with this library |
| * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
| * You should have received a copy of the MPL along with this library |
| * in the file COPYING-MPL-1.1 |
| * |
| * The contents of this file are subject to the Mozilla Public License |
| * Version 1.1 (the "License"); you may not use this file except in |
| * compliance with the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
| * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
| * the specific language governing rights and limitations. |
| * |
| * The Original Code is the cairo graphics library. |
| * |
| * The Initial Developer of the Original Code is Chris Wilson. |
| * |
| * Contributor(s): |
| * Chris Wilson <chris@chris-wilson.co.uk> |
| */ |
| |
| /* The script surface is one that records all operations performed on |
| * it in the form of a procedural script, similar in fashion to |
| * PostScript but using Cairo's imaging model. In essence, this is |
| * equivalent to the recording-surface, but as there is no impedance mismatch |
| * between Cairo and CairoScript, we can generate output immediately |
| * without having to copy and hold the data in memory. |
| */ |
| |
| #include "cairoint.h" |
| |
| #include "cairo-script.h" |
| |
| #include "cairo-analysis-surface-private.h" |
| #include "cairo-device-private.h" |
| #include "cairo-error-private.h" |
| #include "cairo-list-private.h" |
| #include "cairo-recording-surface-private.h" |
| #include "cairo-output-stream-private.h" |
| #include "cairo-scaled-font-private.h" |
| #include "cairo-surface-clipper-private.h" |
| #include "cairo-surface-snapshot-private.h" |
| #include "cairo-surface-subsurface-private.h" |
| #include "cairo-surface-wrapper-private.h" |
| |
| #if CAIRO_HAS_FT_FONT |
| #include "cairo-ft-private.h" |
| #endif |
| |
| #include <ctype.h> |
| |
| #ifdef WORDS_BIGENDIAN |
| #define to_be32(x) x |
| #else |
| #define to_be32(x) bswap_32(x) |
| #endif |
| |
| #define _cairo_output_stream_puts(S, STR) \ |
| _cairo_output_stream_write ((S), (STR), strlen (STR)) |
| |
| #define static cairo_warn static |
| |
| typedef struct _cairo_script_context cairo_script_context_t; |
| typedef struct _cairo_script_surface cairo_script_surface_t; |
| typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t; |
| typedef struct _cairo_script_surface_font_private cairo_script_surface_font_private_t; |
| |
| typedef struct _operand { |
| enum { |
| SURFACE, |
| DEFERRED, |
| } type; |
| cairo_list_t link; |
| } operand_t; |
| |
| |
| struct deferred_finish { |
| cairo_list_t link; |
| operand_t operand; |
| }; |
| |
| struct _cairo_script_context { |
| cairo_device_t base; |
| |
| int active; |
| |
| cairo_output_stream_t *stream; |
| cairo_script_mode_t mode; |
| |
| struct _bitmap { |
| unsigned long min; |
| unsigned long count; |
| unsigned int map[64]; |
| struct _bitmap *next; |
| } surface_id, font_id; |
| |
| cairo_list_t operands; |
| cairo_list_t deferred; |
| |
| cairo_list_t fonts; |
| cairo_list_t defines; |
| }; |
| |
| struct _cairo_script_surface_font_private { |
| cairo_script_context_t *ctx; |
| cairo_bool_t has_sfnt; |
| unsigned long id; |
| unsigned long subset_glyph_index; |
| cairo_list_t link; |
| cairo_scaled_font_t *parent; |
| }; |
| |
| struct _cairo_script_implicit_context { |
| cairo_operator_t current_operator; |
| cairo_fill_rule_t current_fill_rule; |
| double current_tolerance; |
| cairo_antialias_t current_antialias; |
| cairo_stroke_style_t current_style; |
| cairo_pattern_union_t current_source; |
| cairo_matrix_t current_ctm; |
| cairo_matrix_t current_stroke_matrix; |
| cairo_matrix_t current_font_matrix; |
| cairo_font_options_t current_font_options; |
| cairo_scaled_font_t *current_scaled_font; |
| cairo_path_fixed_t current_path; |
| cairo_bool_t has_clip; |
| }; |
| |
| struct _cairo_script_surface { |
| cairo_surface_t base; |
| |
| cairo_surface_wrapper_t wrapper; |
| |
| cairo_surface_clipper_t clipper; |
| |
| operand_t operand; |
| cairo_bool_t emitted; |
| cairo_bool_t defined; |
| cairo_bool_t active; |
| |
| double width, height; |
| |
| /* implicit flattened context */ |
| cairo_script_implicit_context_t cr; |
| }; |
| |
| static const cairo_surface_backend_t _cairo_script_surface_backend; |
| |
| static cairo_script_surface_t * |
| _cairo_script_surface_create_internal (cairo_script_context_t *ctx, |
| cairo_content_t content, |
| double width, |
| double height, |
| cairo_surface_t *passthrough); |
| |
| static void |
| _cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); |
| |
| static void |
| _cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr); |
| |
| static void |
| _cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr); |
| |
| static void |
| _bitmap_release_id (struct _bitmap *b, unsigned long token) |
| { |
| struct _bitmap **prev = NULL; |
| |
| do { |
| if (token < b->min + sizeof (b->map) * CHAR_BIT) { |
| unsigned int bit, elem; |
| |
| token -= b->min; |
| elem = token / (sizeof (b->map[0]) * CHAR_BIT); |
| bit = token % (sizeof (b->map[0]) * CHAR_BIT); |
| b->map[elem] &= ~(1 << bit); |
| if (! --b->count && prev) { |
| *prev = b->next; |
| free (b); |
| } |
| return; |
| } |
| prev = &b->next; |
| b = b->next; |
| } while (b != NULL); |
| } |
| |
| static cairo_status_t |
| _bitmap_next_id (struct _bitmap *b, |
| unsigned long *id) |
| { |
| struct _bitmap *bb, **prev = NULL; |
| unsigned long min = 0; |
| |
| do { |
| if (b->min != min) |
| break; |
| |
| if (b->count < sizeof (b->map) * CHAR_BIT) { |
| unsigned int n, m, bit; |
| for (n = 0; n < ARRAY_LENGTH (b->map); n++) { |
| if (b->map[n] == (unsigned int) -1) |
| continue; |
| |
| for (m=0, bit=1; m<sizeof (b->map[0])*CHAR_BIT; m++, bit<<=1) { |
| if ((b->map[n] & bit) == 0) { |
| b->map[n] |= bit; |
| b->count++; |
| *id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min; |
| return CAIRO_STATUS_SUCCESS; |
| } |
| } |
| } |
| } |
| min += sizeof (b->map) * CHAR_BIT; |
| |
| prev = &b->next; |
| b = b->next; |
| } while (b != NULL); |
| |
| bb = malloc (sizeof (struct _bitmap)); |
| if (unlikely (bb == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| *prev = bb; |
| bb->next = b; |
| bb->min = min; |
| bb->count = 1; |
| bb->map[0] = 0x1; |
| memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0])); |
| *id = min; |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static void |
| _bitmap_fini (struct _bitmap *b) |
| { |
| while (b != NULL) { |
| struct _bitmap *next = b->next; |
| free (b); |
| b = next; |
| } |
| } |
| |
| static const char * |
| _direction_to_string (cairo_bool_t backward) |
| { |
| static const char *names[] = { |
| "FORWARD", |
| "BACKWARD" |
| }; |
| assert (backward < ARRAY_LENGTH (names)); |
| return names[backward]; |
| } |
| |
| static const char * |
| _operator_to_string (cairo_operator_t op) |
| { |
| static const char *names[] = { |
| "CLEAR", /* CAIRO_OPERATOR_CLEAR */ |
| |
| "SOURCE", /* CAIRO_OPERATOR_SOURCE */ |
| "OVER", /* CAIRO_OPERATOR_OVER */ |
| "IN", /* CAIRO_OPERATOR_IN */ |
| "OUT", /* CAIRO_OPERATOR_OUT */ |
| "ATOP", /* CAIRO_OPERATOR_ATOP */ |
| |
| "DEST", /* CAIRO_OPERATOR_DEST */ |
| "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ |
| "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ |
| "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ |
| "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ |
| |
| "XOR", /* CAIRO_OPERATOR_XOR */ |
| "ADD", /* CAIRO_OPERATOR_ADD */ |
| "SATURATE", /* CAIRO_OPERATOR_SATURATE */ |
| |
| "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ |
| "SCREEN", /* CAIRO_OPERATOR_SCREEN */ |
| "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ |
| "DARKEN", /* CAIRO_OPERATOR_DARKEN */ |
| "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ |
| "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ |
| "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ |
| "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ |
| "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ |
| "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ |
| "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ |
| "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ |
| "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ |
| "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ |
| "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ |
| }; |
| assert (op < ARRAY_LENGTH (names)); |
| return names[op]; |
| } |
| |
| static const char * |
| _extend_to_string (cairo_extend_t extend) |
| { |
| static const char *names[] = { |
| "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ |
| "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */ |
| "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */ |
| "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ |
| }; |
| assert (extend < ARRAY_LENGTH (names)); |
| return names[extend]; |
| } |
| |
| static const char * |
| _filter_to_string (cairo_filter_t filter) |
| { |
| static const char *names[] = { |
| "FILTER_FAST", /* CAIRO_FILTER_FAST */ |
| "FILTER_GOOD", /* CAIRO_FILTER_GOOD */ |
| "FILTER_BEST", /* CAIRO_FILTER_BEST */ |
| "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ |
| "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ |
| "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ |
| }; |
| assert (filter < ARRAY_LENGTH (names)); |
| return names[filter]; |
| } |
| |
| static const char * |
| _fill_rule_to_string (cairo_fill_rule_t rule) |
| { |
| static const char *names[] = { |
| "WINDING", /* CAIRO_FILL_RULE_WINDING */ |
| "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ |
| }; |
| assert (rule < ARRAY_LENGTH (names)); |
| return names[rule]; |
| } |
| |
| static const char * |
| _antialias_to_string (cairo_antialias_t antialias) |
| { |
| static const char *names[] = { |
| "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ |
| "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ |
| "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ |
| "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */ |
| }; |
| assert (antialias < ARRAY_LENGTH (names)); |
| return names[antialias]; |
| } |
| |
| static const char * |
| _line_cap_to_string (cairo_line_cap_t line_cap) |
| { |
| static const char *names[] = { |
| "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ |
| "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ |
| "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ |
| }; |
| assert (line_cap < ARRAY_LENGTH (names)); |
| return names[line_cap]; |
| } |
| |
| static const char * |
| _line_join_to_string (cairo_line_join_t line_join) |
| { |
| static const char *names[] = { |
| "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ |
| "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ |
| "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ |
| }; |
| assert (line_join < ARRAY_LENGTH (names)); |
| return names[line_join]; |
| } |
| |
| static inline cairo_script_context_t * |
| to_context (cairo_script_surface_t *surface) |
| { |
| return (cairo_script_context_t *) surface->base.device; |
| } |
| |
| static cairo_bool_t |
| target_is_active (cairo_script_surface_t *surface) |
| { |
| return cairo_list_is_first (&surface->operand.link, |
| &to_context (surface)->operands); |
| } |
| |
| static void |
| target_push (cairo_script_surface_t *surface) |
| { |
| cairo_list_move (&surface->operand.link, &to_context (surface)->operands); |
| } |
| |
| static int |
| target_depth (cairo_script_surface_t *surface) |
| { |
| cairo_list_t *link; |
| int depth = 0; |
| |
| cairo_list_foreach (link, &to_context (surface)->operands) { |
| if (link == &surface->operand.link) |
| break; |
| depth++; |
| } |
| |
| return depth; |
| } |
| |
| static void |
| _get_target (cairo_script_surface_t *surface) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| |
| if (surface->defined) { |
| _cairo_output_stream_printf (ctx->stream, "s%u ", |
| surface->base.unique_id); |
| } else { |
| assert (! cairo_list_is_empty (&surface->operand.link)); |
| if (! target_is_active (surface)) { |
| int depth = target_depth (surface); |
| if (ctx->active) { |
| _cairo_output_stream_printf (ctx->stream, "%d index ", depth); |
| _cairo_output_stream_puts (ctx->stream, "/target get exch pop "); |
| } else { |
| if (depth == 1) { |
| _cairo_output_stream_puts (ctx->stream, |
| "exch\n"); |
| } else { |
| _cairo_output_stream_printf (ctx->stream, |
| "%d -1 roll\n", |
| depth); |
| } |
| _cairo_output_stream_puts (ctx->stream, "/target get "); |
| } |
| } else { |
| _cairo_output_stream_puts (ctx->stream, "/target get "); |
| } |
| } |
| } |
| |
| static const char * |
| _content_to_string (cairo_content_t content) |
| { |
| switch (content) { |
| case CAIRO_CONTENT_ALPHA: return "ALPHA"; |
| case CAIRO_CONTENT_COLOR: return "COLOR"; |
| default: |
| case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; |
| } |
| } |
| |
| static cairo_status_t |
| _emit_surface (cairo_script_surface_t *surface) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| |
| _cairo_output_stream_printf (ctx->stream, |
| "<< /content //%s", |
| _content_to_string (surface->base.content)); |
| if (surface->width != -1 && surface->height != -1) { |
| _cairo_output_stream_printf (ctx->stream, |
| " /width %f /height %f", |
| surface->width, |
| surface->height); |
| } |
| |
| if (surface->base.x_fallback_resolution != |
| CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT || |
| surface->base.y_fallback_resolution != |
| CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT) |
| { |
| _cairo_output_stream_printf (ctx->stream, |
| " /fallback-resolution [%f %f]", |
| surface->base.x_fallback_resolution, |
| surface->base.y_fallback_resolution); |
| } |
| |
| if (surface->base.device_transform.x0 != 0. || |
| surface->base.device_transform.y0 != 0.) |
| { |
| /* XXX device offset is encoded into the pattern matrices etc. */ |
| if (0) { |
| _cairo_output_stream_printf (ctx->stream, |
| " /device-offset [%f %f]", |
| surface->base.device_transform.x0, |
| surface->base.device_transform.y0); |
| } |
| } |
| |
| _cairo_output_stream_puts (ctx->stream, " >> surface context\n"); |
| surface->emitted = TRUE; |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_context (cairo_script_surface_t *surface) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| |
| if (target_is_active (surface)) |
| return CAIRO_STATUS_SUCCESS; |
| |
| while (! cairo_list_is_empty (&ctx->operands)) { |
| operand_t *op; |
| cairo_script_surface_t *old; |
| |
| op = cairo_list_first_entry (&ctx->operands, |
| operand_t, |
| link); |
| if (op->type == DEFERRED) |
| break; |
| |
| old = cairo_container_of (op, cairo_script_surface_t, operand); |
| if (old == surface) |
| break; |
| if (old->active) |
| break; |
| |
| if (! old->defined) { |
| assert (old->emitted); |
| _cairo_output_stream_printf (ctx->stream, |
| "/target get /s%u exch def pop\n", |
| old->base.unique_id); |
| old->defined = TRUE; |
| } else { |
| _cairo_output_stream_puts (ctx->stream, "pop\n"); |
| } |
| |
| cairo_list_del (&old->operand.link); |
| } |
| |
| if (target_is_active (surface)) |
| return CAIRO_STATUS_SUCCESS; |
| |
| if (! surface->emitted) { |
| cairo_status_t status; |
| |
| status = _emit_surface (surface); |
| if (unlikely (status)) |
| return status; |
| } else if (cairo_list_is_empty (&surface->operand.link)) { |
| assert (surface->defined); |
| _cairo_output_stream_printf (ctx->stream, |
| "s%u context\n", |
| surface->base.unique_id); |
| _cairo_script_implicit_context_reset (&surface->cr); |
| _cairo_surface_clipper_reset (&surface->clipper); |
| } else { |
| int depth = target_depth (surface); |
| if (depth == 1) { |
| _cairo_output_stream_puts (ctx->stream, "exch\n"); |
| } else { |
| _cairo_output_stream_printf (ctx->stream, |
| "%d -1 roll\n", |
| depth); |
| } |
| } |
| target_push (surface); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_operator (cairo_script_surface_t *surface, |
| cairo_operator_t op) |
| { |
| assert (target_is_active (surface)); |
| |
| if (surface->cr.current_operator == op) |
| return CAIRO_STATUS_SUCCESS; |
| |
| surface->cr.current_operator = op; |
| |
| _cairo_output_stream_printf (to_context (surface)->stream, |
| "//%s set-operator\n", |
| _operator_to_string (op)); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_fill_rule (cairo_script_surface_t *surface, |
| cairo_fill_rule_t fill_rule) |
| { |
| assert (target_is_active (surface)); |
| |
| if (surface->cr.current_fill_rule == fill_rule) |
| return CAIRO_STATUS_SUCCESS; |
| |
| surface->cr.current_fill_rule = fill_rule; |
| |
| _cairo_output_stream_printf (to_context (surface)->stream, |
| "//%s set-fill-rule\n", |
| _fill_rule_to_string (fill_rule)); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_tolerance (cairo_script_surface_t *surface, |
| double tolerance, |
| cairo_bool_t force) |
| { |
| assert (target_is_active (surface)); |
| |
| if ((! force || |
| fabs (tolerance - CAIRO_GSTATE_TOLERANCE_DEFAULT) < 1e-5) && |
| surface->cr.current_tolerance == tolerance) |
| { |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| surface->cr.current_tolerance = tolerance; |
| |
| _cairo_output_stream_printf (to_context (surface)->stream, |
| "%f set-tolerance\n", |
| tolerance); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_antialias (cairo_script_surface_t *surface, |
| cairo_antialias_t antialias) |
| { |
| assert (target_is_active (surface)); |
| |
| if (surface->cr.current_antialias == antialias) |
| return CAIRO_STATUS_SUCCESS; |
| |
| surface->cr.current_antialias = antialias; |
| |
| _cairo_output_stream_printf (to_context (surface)->stream, |
| "//%s set-antialias\n", |
| _antialias_to_string (antialias)); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_line_width (cairo_script_surface_t *surface, |
| double line_width, |
| cairo_bool_t force) |
| { |
| assert (target_is_active (surface)); |
| |
| if ((! force || |
| fabs (line_width - CAIRO_GSTATE_LINE_WIDTH_DEFAULT) < 1e-5) && |
| surface->cr.current_style.line_width == line_width) |
| { |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| surface->cr.current_style.line_width = line_width; |
| |
| _cairo_output_stream_printf (to_context (surface)->stream, |
| "%f set-line-width\n", |
| line_width); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_line_cap (cairo_script_surface_t *surface, |
| cairo_line_cap_t line_cap) |
| { |
| assert (target_is_active (surface)); |
| |
| if (surface->cr.current_style.line_cap == line_cap) |
| return CAIRO_STATUS_SUCCESS; |
| |
| surface->cr.current_style.line_cap = line_cap; |
| |
| _cairo_output_stream_printf (to_context (surface)->stream, |
| "//%s set-line-cap\n", |
| _line_cap_to_string (line_cap)); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_line_join (cairo_script_surface_t *surface, |
| cairo_line_join_t line_join) |
| { |
| assert (target_is_active (surface)); |
| |
| if (surface->cr.current_style.line_join == line_join) |
| return CAIRO_STATUS_SUCCESS; |
| |
| surface->cr.current_style.line_join = line_join; |
| |
| _cairo_output_stream_printf (to_context (surface)->stream, |
| "//%s set-line-join\n", |
| _line_join_to_string (line_join)); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_miter_limit (cairo_script_surface_t *surface, |
| double miter_limit, |
| cairo_bool_t force) |
| { |
| assert (target_is_active (surface)); |
| |
| if ((! force || |
| fabs (miter_limit - CAIRO_GSTATE_MITER_LIMIT_DEFAULT) < 1e-5) && |
| surface->cr.current_style.miter_limit == miter_limit) |
| { |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| surface->cr.current_style.miter_limit = miter_limit; |
| |
| _cairo_output_stream_printf (to_context (surface)->stream, |
| "%f set-miter-limit\n", |
| miter_limit); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_bool_t |
| _dashes_equal (const double *a, const double *b, int num_dashes) |
| { |
| while (num_dashes--) { |
| if (fabs (*a - *b) > 1e-5) |
| return FALSE; |
| a++, b++; |
| } |
| |
| return TRUE; |
| } |
| |
| static cairo_status_t |
| _emit_dash (cairo_script_surface_t *surface, |
| const double *dash, |
| unsigned int num_dashes, |
| double offset, |
| cairo_bool_t force) |
| { |
| unsigned int n; |
| |
| assert (target_is_active (surface)); |
| |
| if (force && |
| num_dashes == 0 && |
| surface->cr.current_style.num_dashes == 0) |
| { |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| if (! force && |
| (surface->cr.current_style.num_dashes == num_dashes && |
| (num_dashes == 0 || |
| (fabs (surface->cr.current_style.dash_offset - offset) < 1e-5 && |
| _dashes_equal (surface->cr.current_style.dash, dash, num_dashes))))) |
| { |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| |
| if (num_dashes) { |
| surface->cr.current_style.dash = _cairo_realloc_ab |
| (surface->cr.current_style.dash, num_dashes, sizeof (double)); |
| if (unlikely (surface->cr.current_style.dash == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| memcpy (surface->cr.current_style.dash, dash, |
| sizeof (double) * num_dashes); |
| } else { |
| if (surface->cr.current_style.dash != NULL) { |
| free (surface->cr.current_style.dash); |
| surface->cr.current_style.dash = NULL; |
| } |
| } |
| |
| surface->cr.current_style.num_dashes = num_dashes; |
| surface->cr.current_style.dash_offset = offset; |
| |
| _cairo_output_stream_puts (to_context (surface)->stream, "["); |
| for (n = 0; n < num_dashes; n++) { |
| _cairo_output_stream_printf (to_context (surface)->stream, "%f", dash[n]); |
| if (n < num_dashes-1) |
| _cairo_output_stream_puts (to_context (surface)->stream, " "); |
| } |
| _cairo_output_stream_printf (to_context (surface)->stream, |
| "] %f set-dash\n", |
| offset); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_stroke_style (cairo_script_surface_t *surface, |
| const cairo_stroke_style_t *style, |
| cairo_bool_t force) |
| { |
| cairo_status_t status; |
| |
| assert (target_is_active (surface)); |
| |
| status = _emit_line_width (surface, style->line_width, force); |
| if (unlikely (status)) |
| return status; |
| |
| status = _emit_line_cap (surface, style->line_cap); |
| if (unlikely (status)) |
| return status; |
| |
| status = _emit_line_join (surface, style->line_join); |
| if (unlikely (status)) |
| return status; |
| |
| status = _emit_miter_limit (surface, style->miter_limit, force); |
| if (unlikely (status)) |
| return status; |
| |
| status = _emit_dash (surface, |
| style->dash, style->num_dashes, style->dash_offset, |
| force); |
| if (unlikely (status)) |
| return status; |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static const char * |
| _format_to_string (cairo_format_t format) |
| { |
| switch (format) { |
| case CAIRO_FORMAT_ARGB32: return "ARGB32"; |
| case CAIRO_FORMAT_RGB24: return "RGB24"; |
| case CAIRO_FORMAT_RGB16_565: return "RGB16_565"; |
| case CAIRO_FORMAT_A8: return "A8"; |
| case CAIRO_FORMAT_A1: return "A1"; |
| case CAIRO_FORMAT_INVALID: return "INVALID"; |
| } |
| ASSERT_NOT_REACHED; |
| return "INVALID"; |
| } |
| |
| static cairo_status_t |
| _emit_solid_pattern (cairo_script_surface_t *surface, |
| const cairo_pattern_t *pattern) |
| { |
| cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; |
| cairo_script_context_t *ctx = to_context (surface); |
| |
| if (! CAIRO_COLOR_IS_OPAQUE (&solid->color)) |
| { |
| if (! (surface->base.content & CAIRO_CONTENT_COLOR) || |
| ((solid->color.red_short == 0 || solid->color.red_short == 0xffff) && |
| (solid->color.green_short == 0 || solid->color.green_short == 0xffff) && |
| (solid->color.blue_short == 0 || solid->color.blue_short == 0xffff) )) |
| { |
| _cairo_output_stream_printf (ctx->stream, |
| "%f a", |
| solid->color.alpha); |
| } |
| else |
| { |
| _cairo_output_stream_printf (ctx->stream, |
| "%f %f %f %f rgba", |
| solid->color.red, |
| solid->color.green, |
| solid->color.blue, |
| solid->color.alpha); |
| } |
| } |
| else |
| { |
| if (solid->color.red_short == solid->color.green_short && |
| solid->color.red_short == solid->color.blue_short) |
| { |
| _cairo_output_stream_printf (ctx->stream, |
| "%f g", |
| solid->color.red); |
| } |
| else |
| { |
| _cairo_output_stream_printf (ctx->stream, |
| "%f %f %f rgb", |
| solid->color.red, |
| solid->color.green, |
| solid->color.blue); |
| } |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| |
| static cairo_status_t |
| _emit_gradient_color_stops (cairo_gradient_pattern_t *gradient, |
| cairo_output_stream_t *output) |
| { |
| unsigned int n; |
| |
| for (n = 0; n < gradient->n_stops; n++) { |
| _cairo_output_stream_printf (output, |
| "\n %f %f %f %f %f add-color-stop", |
| gradient->stops[n].offset, |
| gradient->stops[n].color.red, |
| gradient->stops[n].color.green, |
| gradient->stops[n].color.blue, |
| gradient->stops[n].color.alpha); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_linear_pattern (cairo_script_surface_t *surface, |
| const cairo_pattern_t *pattern) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_linear_pattern_t *linear; |
| |
| linear = (cairo_linear_pattern_t *) pattern; |
| |
| _cairo_output_stream_printf (ctx->stream, |
| "%f %f %f %f linear", |
| linear->pd1.x, linear->pd1.y, |
| linear->pd2.x, linear->pd2.y); |
| return _emit_gradient_color_stops (&linear->base, ctx->stream); |
| } |
| |
| static cairo_status_t |
| _emit_radial_pattern (cairo_script_surface_t *surface, |
| const cairo_pattern_t *pattern) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_radial_pattern_t *radial; |
| |
| radial = (cairo_radial_pattern_t *) pattern; |
| |
| _cairo_output_stream_printf (ctx->stream, |
| "%f %f %f %f %f %f radial", |
| radial->cd1.center.x, |
| radial->cd1.center.y, |
| radial->cd1.radius, |
| radial->cd2.center.x, |
| radial->cd2.center.y, |
| radial->cd2.radius); |
| return _emit_gradient_color_stops (&radial->base, ctx->stream); |
| } |
| |
| static cairo_status_t |
| _emit_mesh_pattern (cairo_script_surface_t *surface, |
| const cairo_pattern_t *pattern) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_pattern_t *mesh; |
| cairo_status_t status; |
| unsigned int i, n; |
| |
| mesh = (cairo_pattern_t *) pattern; |
| status = cairo_mesh_pattern_get_patch_count (mesh, &n); |
| if (unlikely (status)) |
| return status; |
| |
| _cairo_output_stream_printf (ctx->stream, "mesh"); |
| for (i = 0; i < n; i++) { |
| cairo_path_t *path; |
| cairo_path_data_t *data; |
| int j; |
| |
| _cairo_output_stream_printf (ctx->stream, "\n mesh-begin-patch"); |
| |
| path = cairo_mesh_pattern_get_path (mesh, i); |
| if (unlikely (path->status)) |
| return path->status; |
| |
| for (j = 0; j < path->num_data; j+=data[0].header.length) { |
| data = &path->data[j]; |
| switch (data->header.type) { |
| case CAIRO_PATH_MOVE_TO: |
| _cairo_output_stream_printf (ctx->stream, |
| "\n %f %f mesh-move-to", |
| data[1].point.x, data[1].point.y); |
| break; |
| case CAIRO_PATH_LINE_TO: |
| _cairo_output_stream_printf (ctx->stream, |
| "\n %f %f mesh-line-to", |
| data[1].point.x, data[1].point.y); |
| break; |
| case CAIRO_PATH_CURVE_TO: |
| _cairo_output_stream_printf (ctx->stream, |
| "\n %f %f %f %f %f %f mesh-curve-to", |
| data[1].point.x, data[1].point.y, |
| data[2].point.x, data[2].point.y, |
| data[3].point.x, data[3].point.y); |
| break; |
| case CAIRO_PATH_CLOSE_PATH: |
| break; |
| } |
| } |
| cairo_path_destroy (path); |
| |
| for (j = 0; j < 4; j++) { |
| double x, y; |
| |
| status = cairo_mesh_pattern_get_control_point (mesh, i, j, &x, &y); |
| if (unlikely (status)) |
| return status; |
| _cairo_output_stream_printf (ctx->stream, |
| "\n %d %f %f mesh-set-control-point", |
| j, x, y); |
| } |
| |
| for (j = 0; j < 4; j++) { |
| double r, g, b, a; |
| |
| status = cairo_mesh_pattern_get_corner_color_rgba (mesh, i, j, &r, &g, &b, &a); |
| if (unlikely (status)) |
| return status; |
| |
| _cairo_output_stream_printf (ctx->stream, |
| "\n %d %f %f %f %f mesh-set-corner-color", |
| j, r, g, b, a); |
| } |
| |
| _cairo_output_stream_printf (ctx->stream, "\n mesh-end-patch"); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_recording_surface_pattern (cairo_script_surface_t *surface, |
| cairo_recording_surface_t *source) |
| { |
| cairo_script_implicit_context_t old_cr; |
| cairo_script_surface_t *similar; |
| cairo_status_t status; |
| cairo_box_t bbox; |
| cairo_rectangle_int_t rect; |
| |
| /* first measure the extents */ |
| status = _cairo_recording_surface_get_bbox (source, &bbox, NULL); |
| if (unlikely (status)) |
| return status; |
| |
| /* convert to extents so that it matches the public api */ |
| _cairo_box_round_to_rectangle (&bbox, &rect); |
| |
| similar = _cairo_script_surface_create_internal (to_context (surface), |
| source->content, |
| rect.width, |
| rect.height, |
| NULL); |
| if (unlikely (similar->base.status)) |
| return similar->base.status; |
| |
| cairo_surface_set_device_offset (&similar->base, -rect.x, -rect.y); |
| similar->base.is_clear = TRUE; |
| |
| _get_target (surface); |
| _cairo_output_stream_printf (to_context (surface)->stream, |
| "%d %d //%s similar dup context\n", |
| rect.width, rect.height, |
| _content_to_string (source->content)); |
| target_push (similar); |
| similar->emitted = TRUE; |
| |
| old_cr = surface->cr; |
| _cairo_script_implicit_context_init (&surface->cr); |
| status = _cairo_recording_surface_replay (&source->base, &similar->base); |
| surface->cr = old_cr; |
| |
| if (unlikely (status)) { |
| cairo_surface_destroy (&similar->base); |
| return status; |
| } |
| |
| cairo_list_del (&similar->operand.link); |
| assert (target_is_active (surface)); |
| |
| _cairo_output_stream_puts (to_context (surface)->stream, "pop "); |
| cairo_surface_destroy (&similar->base); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_script_surface_pattern (cairo_script_surface_t *surface, |
| cairo_script_surface_t *source) |
| { |
| _get_target (source); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _write_image_surface (cairo_output_stream_t *output, |
| const cairo_image_surface_t *image) |
| { |
| int stride, row, width; |
| uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE]; |
| uint8_t *rowdata; |
| uint8_t *data; |
| |
| stride = image->stride; |
| width = image->width; |
| data = image->data; |
| #if WORDS_BIGENDIAN |
| switch (image->format) { |
| case CAIRO_FORMAT_A1: |
| for (row = image->height; row--; ) { |
| _cairo_output_stream_write (output, data, (width+7)/8); |
| data += stride; |
| } |
| break; |
| case CAIRO_FORMAT_A8: |
| for (row = image->height; row--; ) { |
| _cairo_output_stream_write (output, data, width); |
| data += stride; |
| } |
| break; |
| case CAIRO_FORMAT_RGB16_565: |
| for (row = image->height; row--; ) { |
| _cairo_output_stream_write (output, data, 2*width); |
| data += stride; |
| } |
| break; |
| case CAIRO_FORMAT_RGB24: |
| for (row = image->height; row--; ) { |
| int col; |
| rowdata = data; |
| for (col = width; col--; ) { |
| _cairo_output_stream_write (output, rowdata, 3); |
| rowdata+=4; |
| } |
| data += stride; |
| } |
| break; |
| case CAIRO_FORMAT_ARGB32: |
| for (row = image->height; row--; ) { |
| _cairo_output_stream_write (output, data, 4*width); |
| data += stride; |
| } |
| break; |
| case CAIRO_FORMAT_INVALID: |
| default: |
| ASSERT_NOT_REACHED; |
| break; |
| } |
| #else |
| if (stride > ARRAY_LENGTH (row_stack)) { |
| rowdata = malloc (stride); |
| if (unlikely (rowdata == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| } else |
| rowdata = row_stack; |
| |
| switch (image->format) { |
| case CAIRO_FORMAT_A1: |
| for (row = image->height; row--; ) { |
| int col; |
| for (col = 0; col < (width + 7)/8; col++) |
| rowdata[col] = CAIRO_BITSWAP8 (data[col]); |
| _cairo_output_stream_write (output, rowdata, (width+7)/8); |
| data += stride; |
| } |
| break; |
| case CAIRO_FORMAT_A8: |
| for (row = image->height; row--; ) { |
| _cairo_output_stream_write (output, data, width); |
| data += stride; |
| } |
| break; |
| case CAIRO_FORMAT_RGB16_565: |
| for (row = image->height; row--; ) { |
| uint16_t *src = (uint16_t *) data; |
| uint16_t *dst = (uint16_t *) rowdata; |
| int col; |
| for (col = 0; col < width; col++) |
| dst[col] = bswap_16 (src[col]); |
| _cairo_output_stream_write (output, rowdata, 2*width); |
| data += stride; |
| } |
| break; |
| case CAIRO_FORMAT_RGB24: |
| for (row = image->height; row--; ) { |
| uint8_t *src = data; |
| int col; |
| for (col = 0; col < width; col++) { |
| rowdata[3*col+2] = *src++; |
| rowdata[3*col+1] = *src++; |
| rowdata[3*col+0] = *src++; |
| src++; |
| } |
| _cairo_output_stream_write (output, rowdata, 3*width); |
| data += stride; |
| } |
| break; |
| case CAIRO_FORMAT_ARGB32: |
| for (row = image->height; row--; ) { |
| uint32_t *src = (uint32_t *) data; |
| uint32_t *dst = (uint32_t *) rowdata; |
| int col; |
| for (col = 0; col < width; col++) |
| dst[col] = bswap_32 (src[col]); |
| _cairo_output_stream_write (output, rowdata, 4*width); |
| data += stride; |
| } |
| break; |
| case CAIRO_FORMAT_INVALID: |
| default: |
| ASSERT_NOT_REACHED; |
| break; |
| } |
| if (rowdata != row_stack) |
| free (rowdata); |
| #endif |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_int_status_t |
| _emit_png_surface (cairo_script_surface_t *surface, |
| cairo_image_surface_t *image) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_output_stream_t *base85_stream; |
| cairo_status_t status; |
| const uint8_t *mime_data; |
| unsigned long mime_data_length; |
| |
| cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_PNG, |
| &mime_data, &mime_data_length); |
| if (mime_data == NULL) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| _cairo_output_stream_printf (ctx->stream, |
| "<< " |
| "/width %d " |
| "/height %d " |
| "/format //%s " |
| "/mime-type (image/png) " |
| "/source <~", |
| image->width, image->height, |
| _format_to_string (image->format)); |
| |
| base85_stream = _cairo_base85_stream_create (ctx->stream); |
| _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); |
| status = _cairo_output_stream_destroy (base85_stream); |
| if (unlikely (status)) |
| return status; |
| |
| _cairo_output_stream_puts (ctx->stream, "~> >> image "); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| struct def { |
| cairo_script_context_t *ctx; |
| cairo_user_data_array_t *user_data; |
| unsigned int tag; |
| cairo_list_t link; |
| }; |
| |
| static void |
| _undef (void *data) |
| { |
| struct def *def = data; |
| |
| cairo_list_del (&def->link); |
| _cairo_output_stream_printf (def->ctx->stream, "/s%u undef\n", def->tag); |
| free (def); |
| } |
| |
| static cairo_status_t |
| _emit_image_surface (cairo_script_surface_t *surface, |
| cairo_image_surface_t *image) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_output_stream_t *base85_stream; |
| cairo_output_stream_t *zlib_stream; |
| cairo_status_t status, status2; |
| const uint8_t *mime_data; |
| unsigned long mime_data_length; |
| struct def *tag; |
| |
| if (_cairo_user_data_array_get_data (&image->base.user_data, |
| (cairo_user_data_key_t *) ctx)) |
| { |
| _cairo_output_stream_printf (ctx->stream, |
| "s%u ", |
| image->base.unique_id); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| status = _emit_png_surface (surface, image); |
| if (_cairo_status_is_error (status)) { |
| return status; |
| } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { |
| cairo_image_surface_t *clone; |
| uint32_t len; |
| |
| if (image->format == CAIRO_FORMAT_INVALID) { |
| clone = _cairo_image_surface_coerce (image); |
| } else { |
| clone = (cairo_image_surface_t *) |
| cairo_surface_reference (&image->base); |
| } |
| |
| _cairo_output_stream_printf (ctx->stream, |
| "<< " |
| "/width %d " |
| "/height %d " |
| "/format //%s " |
| "/source ", |
| clone->width, clone->height, |
| _format_to_string (clone->format)); |
| |
| switch (clone->format) { |
| case CAIRO_FORMAT_A1: |
| len = (clone->width + 7)/8; |
| break; |
| case CAIRO_FORMAT_A8: |
| len = clone->width; |
| break; |
| case CAIRO_FORMAT_RGB16_565: |
| len = clone->width * 2; |
| break; |
| case CAIRO_FORMAT_RGB24: |
| len = clone->width * 3; |
| break; |
| case CAIRO_FORMAT_ARGB32: |
| len = clone->width * 4; |
| break; |
| case CAIRO_FORMAT_INVALID: |
| ASSERT_NOT_REACHED; |
| break; |
| } |
| len *= clone->height; |
| |
| if (len > 24) { |
| _cairo_output_stream_puts (ctx->stream, "<|"); |
| |
| base85_stream = _cairo_base85_stream_create (ctx->stream); |
| |
| len = to_be32 (len); |
| _cairo_output_stream_write (base85_stream, &len, sizeof (len)); |
| |
| zlib_stream = _cairo_deflate_stream_create (base85_stream); |
| status = _write_image_surface (zlib_stream, clone); |
| |
| status2 = _cairo_output_stream_destroy (zlib_stream); |
| if (status == CAIRO_STATUS_SUCCESS) |
| status = status2; |
| status2 = _cairo_output_stream_destroy (base85_stream); |
| if (status == CAIRO_STATUS_SUCCESS) |
| status = status2; |
| if (unlikely (status)) |
| return status; |
| } else { |
| _cairo_output_stream_puts (ctx->stream, "<~"); |
| |
| base85_stream = _cairo_base85_stream_create (ctx->stream); |
| status = _write_image_surface (base85_stream, clone); |
| status2 = _cairo_output_stream_destroy (base85_stream); |
| if (status == CAIRO_STATUS_SUCCESS) |
| status = status2; |
| if (unlikely (status)) |
| return status; |
| } |
| _cairo_output_stream_puts (ctx->stream, "~> >> image "); |
| |
| cairo_surface_destroy (&clone->base); |
| } |
| |
| tag = malloc (sizeof (*tag)); |
| if (unlikely (tag == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| tag->ctx = ctx; |
| tag->tag = image->base.unique_id; |
| tag->user_data = &image->base.user_data; |
| cairo_list_add (&tag->link, &ctx->defines); |
| status = _cairo_user_data_array_set_data (&image->base.user_data, |
| (cairo_user_data_key_t *) ctx, |
| tag, _undef); |
| if (unlikely (status)) { |
| free (tag); |
| return status; |
| } |
| |
| _cairo_output_stream_printf (ctx->stream, |
| "dup /s%u exch def ", |
| image->base.unique_id); |
| |
| cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG, |
| &mime_data, &mime_data_length); |
| if (mime_data != NULL) { |
| _cairo_output_stream_printf (ctx->stream, |
| "\n (%s) <~", |
| CAIRO_MIME_TYPE_JPEG); |
| |
| base85_stream = _cairo_base85_stream_create (ctx->stream); |
| _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); |
| status = _cairo_output_stream_destroy (base85_stream); |
| if (unlikely (status)) |
| return status; |
| |
| _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n"); |
| } |
| |
| cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JP2, |
| &mime_data, &mime_data_length); |
| if (mime_data != NULL) { |
| _cairo_output_stream_printf (ctx->stream, |
| "\n (%s) <~", |
| CAIRO_MIME_TYPE_JP2); |
| |
| base85_stream = _cairo_base85_stream_create (ctx->stream); |
| _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); |
| status = _cairo_output_stream_destroy (base85_stream); |
| if (unlikely (status)) |
| return status; |
| |
| _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n"); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_image_surface_pattern (cairo_script_surface_t *surface, |
| cairo_surface_t *source) |
| { |
| cairo_surface_t *snapshot; |
| cairo_image_surface_t *image; |
| cairo_status_t status; |
| void *extra; |
| |
| /* XXX keeping a copy is nasty, but we want to hook into the surface's |
| * lifetime. Using a snapshot is a convenient method. |
| */ |
| snapshot = _cairo_surface_snapshot (source); |
| status = _cairo_surface_acquire_source_image (snapshot, &image, &extra); |
| if (likely (status == CAIRO_STATUS_SUCCESS)) { |
| status = _emit_image_surface (surface, image); |
| _cairo_surface_release_source_image (snapshot, image, extra); |
| } |
| cairo_surface_destroy (snapshot); |
| |
| return status; |
| } |
| |
| static cairo_status_t |
| _emit_subsurface_pattern (cairo_script_surface_t *surface, |
| cairo_surface_subsurface_t *sub) |
| { |
| cairo_surface_t *source = sub->target; |
| cairo_status_t status; |
| |
| switch ((int) source->backend->type) { |
| case CAIRO_SURFACE_TYPE_RECORDING: |
| status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source); |
| break; |
| case CAIRO_SURFACE_TYPE_SCRIPT: |
| status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source); |
| break; |
| default: |
| status = _emit_image_surface_pattern (surface, source); |
| break; |
| } |
| if (unlikely (status)) |
| return status; |
| |
| _cairo_output_stream_printf (to_context (surface)->stream, |
| "%d %d %d %d subsurface ", |
| sub->extents.x, |
| sub->extents.y, |
| sub->extents.width, |
| sub->extents.height); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_surface_pattern (cairo_script_surface_t *surface, |
| const cairo_pattern_t *pattern) |
| { |
| cairo_surface_pattern_t *surface_pattern; |
| cairo_surface_t *source; |
| cairo_status_t status; |
| |
| surface_pattern = (cairo_surface_pattern_t *) pattern; |
| source = surface_pattern->surface; |
| |
| if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) |
| source = ((cairo_surface_snapshot_t *) source)->target; |
| |
| switch ((int) source->backend->type) { |
| case CAIRO_SURFACE_TYPE_RECORDING: |
| status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source); |
| break; |
| case CAIRO_SURFACE_TYPE_SCRIPT: |
| status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source); |
| break; |
| case CAIRO_SURFACE_TYPE_SUBSURFACE: |
| status = _emit_subsurface_pattern (surface, (cairo_surface_subsurface_t *) source); |
| break; |
| default: |
| status = _emit_image_surface_pattern (surface, source); |
| break; |
| } |
| if (unlikely (status)) |
| return status; |
| |
| _cairo_output_stream_puts (to_context (surface)->stream, "pattern"); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_pattern (cairo_script_surface_t *surface, |
| const cairo_pattern_t *pattern) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_status_t status; |
| cairo_bool_t is_default_extend; |
| cairo_bool_t need_newline = TRUE; |
| |
| switch (pattern->type) { |
| case CAIRO_PATTERN_TYPE_SOLID: |
| /* solid colors do not need filter/extend/matrix */ |
| return _emit_solid_pattern (surface, pattern); |
| |
| case CAIRO_PATTERN_TYPE_LINEAR: |
| status = _emit_linear_pattern (surface, pattern); |
| is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT; |
| break; |
| case CAIRO_PATTERN_TYPE_RADIAL: |
| status = _emit_radial_pattern (surface, pattern); |
| is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT; |
| break; |
| case CAIRO_PATTERN_TYPE_MESH: |
| status = _emit_mesh_pattern (surface, pattern); |
| is_default_extend = TRUE; |
| break; |
| case CAIRO_PATTERN_TYPE_SURFACE: |
| status = _emit_surface_pattern (surface, pattern); |
| is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT; |
| break; |
| |
| default: |
| ASSERT_NOT_REACHED; |
| status = CAIRO_INT_STATUS_UNSUPPORTED; |
| } |
| if (unlikely (status)) |
| return status; |
| |
| if (! _cairo_matrix_is_identity (&pattern->matrix)) { |
| if (need_newline) { |
| _cairo_output_stream_puts (ctx->stream, "\n "); |
| need_newline = FALSE; |
| } |
| |
| _cairo_output_stream_printf (ctx->stream, |
| " [%f %f %f %f %f %f] set-matrix\n ", |
| pattern->matrix.xx, pattern->matrix.yx, |
| pattern->matrix.xy, pattern->matrix.yy, |
| pattern->matrix.x0, pattern->matrix.y0); |
| } |
| |
| /* XXX need to discriminate the user explicitly setting the default */ |
| if (pattern->filter != CAIRO_FILTER_DEFAULT) { |
| if (need_newline) { |
| _cairo_output_stream_puts (ctx->stream, "\n "); |
| need_newline = FALSE; |
| } |
| |
| _cairo_output_stream_printf (ctx->stream, |
| " //%s set-filter\n ", |
| _filter_to_string (pattern->filter)); |
| } |
| if (! is_default_extend ){ |
| if (need_newline) { |
| _cairo_output_stream_puts (ctx->stream, "\n "); |
| need_newline = FALSE; |
| } |
| |
| _cairo_output_stream_printf (ctx->stream, |
| " //%s set-extend\n ", |
| _extend_to_string (pattern->extend)); |
| } |
| |
| if (need_newline) |
| _cairo_output_stream_puts (ctx->stream, "\n "); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_identity (cairo_script_surface_t *surface, |
| cairo_bool_t *matrix_updated) |
| { |
| assert (target_is_active (surface)); |
| |
| if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) |
| return CAIRO_STATUS_SUCCESS; |
| |
| _cairo_output_stream_puts (to_context (surface)->stream, |
| "identity set-matrix\n"); |
| |
| *matrix_updated = TRUE; |
| cairo_matrix_init_identity (&surface->cr.current_ctm); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_source (cairo_script_surface_t *surface, |
| cairo_operator_t op, |
| const cairo_pattern_t *source) |
| { |
| cairo_bool_t matrix_updated = FALSE; |
| cairo_status_t status; |
| |
| assert (target_is_active (surface)); |
| |
| if (op == CAIRO_OPERATOR_CLEAR) { |
| /* the source is ignored, so don't change it */ |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| if (_cairo_pattern_equal (&surface->cr.current_source.base, source)) |
| return CAIRO_STATUS_SUCCESS; |
| |
| _cairo_pattern_fini (&surface->cr.current_source.base); |
| status = _cairo_pattern_init_copy (&surface->cr.current_source.base, |
| source); |
| if (unlikely (status)) |
| return status; |
| |
| status = _emit_identity (surface, &matrix_updated); |
| if (unlikely (status)) |
| return status; |
| |
| status = _emit_pattern (surface, source); |
| if (unlikely (status)) |
| return status; |
| |
| assert (target_is_active (surface)); |
| _cairo_output_stream_puts (to_context (surface)->stream, |
| " set-source\n"); |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _path_move_to (void *closure, |
| const cairo_point_t *point) |
| { |
| _cairo_output_stream_printf (closure, |
| " %f %f m", |
| _cairo_fixed_to_double (point->x), |
| _cairo_fixed_to_double (point->y)); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _path_line_to (void *closure, |
| const cairo_point_t *point) |
| { |
| _cairo_output_stream_printf (closure, |
| " %f %f l", |
| _cairo_fixed_to_double (point->x), |
| _cairo_fixed_to_double (point->y)); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _path_curve_to (void *closure, |
| const cairo_point_t *p1, |
| const cairo_point_t *p2, |
| const cairo_point_t *p3) |
| { |
| _cairo_output_stream_printf (closure, |
| " %f %f %f %f %f %f c", |
| _cairo_fixed_to_double (p1->x), |
| _cairo_fixed_to_double (p1->y), |
| _cairo_fixed_to_double (p2->x), |
| _cairo_fixed_to_double (p2->y), |
| _cairo_fixed_to_double (p3->x), |
| _cairo_fixed_to_double (p3->y)); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _path_close (void *closure) |
| { |
| _cairo_output_stream_printf (closure, |
| " h"); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_path (cairo_script_surface_t *surface, |
| cairo_path_fixed_t *path) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_box_t box; |
| cairo_status_t status; |
| |
| assert (target_is_active (surface)); |
| assert (_cairo_matrix_is_identity (&surface->cr.current_ctm)); |
| |
| if (_cairo_path_fixed_equal (&surface->cr.current_path, path)) |
| return CAIRO_STATUS_SUCCESS; |
| |
| _cairo_path_fixed_fini (&surface->cr.current_path); |
| |
| _cairo_output_stream_puts (ctx->stream, "n"); |
| |
| if (path == NULL) { |
| _cairo_path_fixed_init (&surface->cr.current_path); |
| } else if (_cairo_path_fixed_is_box (path, &box)) { |
| double x1 = _cairo_fixed_to_double (box.p1.x); |
| double y1 = _cairo_fixed_to_double (box.p1.y); |
| double x2 = _cairo_fixed_to_double (box.p2.x); |
| double y2 = _cairo_fixed_to_double (box.p2.y); |
| |
| status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); |
| if (unlikely (status)) |
| return status; |
| |
| _cairo_output_stream_printf (ctx->stream, |
| " %f %f %f %f rectangle", |
| x1, y1, x2 - x1, y2 - y1); |
| } else { |
| cairo_status_t status; |
| |
| status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); |
| if (unlikely (status)) |
| return status; |
| |
| status = _cairo_path_fixed_interpret (path, |
| _path_move_to, |
| _path_line_to, |
| _path_curve_to, |
| _path_close, |
| ctx->stream); |
| if (unlikely (status)) |
| return status; |
| } |
| |
| _cairo_output_stream_puts (ctx->stream, "\n"); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| static cairo_bool_t |
| _scaling_matrix_equal (const cairo_matrix_t *a, |
| const cairo_matrix_t *b) |
| { |
| return fabs (a->xx - b->xx) < 1e-5 && |
| fabs (a->xy - b->xy) < 1e-5 && |
| fabs (a->yx - b->yx) < 1e-5 && |
| fabs (a->yy - b->yy) < 1e-5; |
| } |
| |
| static cairo_status_t |
| _emit_scaling_matrix (cairo_script_surface_t *surface, |
| const cairo_matrix_t *ctm, |
| cairo_bool_t *matrix_updated) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_bool_t was_identity; |
| assert (target_is_active (surface)); |
| |
| if (_scaling_matrix_equal (&surface->cr.current_ctm, ctm)) |
| return CAIRO_STATUS_SUCCESS; |
| |
| was_identity = _cairo_matrix_is_identity (&surface->cr.current_ctm); |
| |
| *matrix_updated = TRUE; |
| surface->cr.current_ctm = *ctm; |
| surface->cr.current_ctm.x0 = 0.; |
| surface->cr.current_ctm.y0 = 0.; |
| |
| if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) { |
| _cairo_output_stream_puts (ctx->stream, |
| "identity set-matrix\n"); |
| } else if (was_identity && fabs (ctm->yx) < 1e-5 && fabs (ctm->xy) < 1e-5) { |
| _cairo_output_stream_printf (ctx->stream, |
| "%f %f scale\n", |
| ctm->xx, ctm->yy); |
| } else { |
| _cairo_output_stream_printf (ctx->stream, |
| "[%f %f %f %f 0 0] set-matrix\n", |
| ctm->xx, ctm->yx, |
| ctm->xy, ctm->yy); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_font_matrix (cairo_script_surface_t *surface, |
| const cairo_matrix_t *font_matrix) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| assert (target_is_active (surface)); |
| |
| if (memcmp (&surface->cr.current_font_matrix, |
| font_matrix, |
| sizeof (cairo_matrix_t)) == 0) |
| { |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| surface->cr.current_font_matrix = *font_matrix; |
| |
| if (_cairo_matrix_is_identity (font_matrix)) { |
| _cairo_output_stream_puts (ctx->stream, |
| "identity set-font-matrix\n"); |
| } else { |
| _cairo_output_stream_printf (ctx->stream, |
| "[%f %f %f %f %f %f] set-font-matrix\n", |
| font_matrix->xx, font_matrix->yx, |
| font_matrix->xy, font_matrix->yy, |
| font_matrix->x0, font_matrix->y0); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_surface_t * |
| _cairo_script_surface_create_similar (void *abstract_surface, |
| cairo_content_t content, |
| int width, |
| int height) |
| { |
| cairo_script_surface_t *surface, *other = abstract_surface; |
| cairo_surface_t *passthrough = NULL; |
| cairo_script_context_t *ctx; |
| cairo_status_t status; |
| |
| ctx = to_context (other); |
| |
| status = cairo_device_acquire (&ctx->base); |
| if (unlikely (status)) |
| return _cairo_surface_create_in_error (status); |
| |
| if (! other->emitted) { |
| status = _emit_surface (other); |
| if (unlikely (status)) { |
| cairo_device_release (&ctx->base); |
| return _cairo_surface_create_in_error (status); |
| } |
| |
| target_push (other); |
| } |
| |
| if (_cairo_surface_wrapper_is_active (&other->wrapper)) { |
| passthrough = |
| _cairo_surface_wrapper_create_similar (&other->wrapper, |
| content, width, height); |
| if (unlikely (passthrough->status)) { |
| cairo_device_release (&ctx->base); |
| return passthrough; |
| } |
| } |
| |
| surface = _cairo_script_surface_create_internal (ctx, |
| content, |
| width, height, |
| passthrough); |
| cairo_surface_destroy (passthrough); |
| |
| if (unlikely (surface->base.status)) { |
| cairo_device_release (&ctx->base); |
| return &surface->base; |
| } |
| |
| _get_target (other); |
| _cairo_output_stream_printf (ctx->stream, |
| "%u %u //%s similar dup /s%u exch def context\n", |
| width, height, |
| _content_to_string (content), |
| surface->base.unique_id); |
| surface->emitted = TRUE; |
| surface->defined = TRUE; |
| surface->base.is_clear = TRUE; |
| target_push (surface); |
| |
| cairo_device_release (&ctx->base); |
| return &surface->base; |
| } |
| |
| static void |
| _device_flush (void *abstract_device) |
| { |
| cairo_script_context_t *ctx = abstract_device; |
| cairo_status_t status; |
| |
| status = _cairo_output_stream_flush (ctx->stream); |
| } |
| |
| static void |
| _device_destroy (void *abstract_device) |
| { |
| cairo_script_context_t *ctx = abstract_device; |
| cairo_status_t status; |
| |
| while (! cairo_list_is_empty (&ctx->fonts)) { |
| cairo_script_surface_font_private_t *font; |
| |
| font = cairo_list_first_entry (&ctx->fonts, |
| cairo_script_surface_font_private_t, |
| link); |
| cairo_list_del (&font->link); |
| if (font->parent->surface_private == font) |
| font->parent->surface_private = NULL; |
| free (font); |
| } |
| |
| while (! cairo_list_is_empty (&ctx->defines)) { |
| struct def *def = cairo_list_first_entry (&ctx->defines, |
| struct def, link); |
| |
| status = _cairo_user_data_array_set_data (def->user_data, |
| (cairo_user_data_key_t *) ctx, |
| NULL, NULL); |
| assert (status == CAIRO_STATUS_SUCCESS); |
| } |
| |
| _bitmap_fini (ctx->surface_id.next); |
| _bitmap_fini (ctx->font_id.next); |
| |
| status = _cairo_output_stream_destroy (ctx->stream); |
| |
| free (ctx); |
| } |
| |
| static cairo_status_t |
| _cairo_script_surface_acquire_source_image (void *abstract_surface, |
| cairo_image_surface_t **image_out, |
| void **image_extra) |
| { |
| cairo_script_surface_t *surface = abstract_surface; |
| |
| if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { |
| return _cairo_surface_wrapper_acquire_source_image (&surface->wrapper, |
| image_out, |
| image_extra); |
| } |
| |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| } |
| |
| static void |
| _cairo_script_surface_release_source_image (void *abstract_surface, |
| cairo_image_surface_t *image, |
| void *image_extra) |
| { |
| cairo_script_surface_t *surface = abstract_surface; |
| |
| assert (_cairo_surface_wrapper_is_active (&surface->wrapper)); |
| _cairo_surface_wrapper_release_source_image (&surface->wrapper, |
| image, |
| image_extra); |
| } |
| |
| static cairo_status_t |
| _cairo_script_surface_finish (void *abstract_surface) |
| { |
| cairo_script_surface_t *surface = abstract_surface; |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_status_t status = CAIRO_STATUS_SUCCESS, status2; |
| |
| _cairo_surface_wrapper_fini (&surface->wrapper); |
| |
| if (surface->cr.current_style.dash != NULL) { |
| free (surface->cr.current_style.dash); |
| surface->cr.current_style.dash = NULL; |
| } |
| _cairo_pattern_fini (&surface->cr.current_source.base); |
| _cairo_path_fixed_fini (&surface->cr.current_path); |
| _cairo_surface_clipper_reset (&surface->clipper); |
| |
| status = cairo_device_acquire (&ctx->base); |
| if (unlikely (status)) |
| return status; |
| |
| if (surface->emitted) { |
| assert (! surface->active); |
| |
| if (! cairo_list_is_empty (&surface->operand.link)) { |
| if (! ctx->active) { |
| if (target_is_active (surface)) { |
| _cairo_output_stream_printf (ctx->stream, |
| "pop\n"); |
| } else { |
| int depth = target_depth (surface); |
| if (depth == 1) { |
| _cairo_output_stream_printf (ctx->stream, |
| "exch pop\n"); |
| } else { |
| _cairo_output_stream_printf (ctx->stream, |
| "%d -1 roll pop\n", |
| depth); |
| } |
| } |
| cairo_list_del (&surface->operand.link); |
| } else { |
| struct deferred_finish *link = malloc (sizeof (*link)); |
| if (link == NULL) { |
| status2 = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| if (status == CAIRO_STATUS_SUCCESS) |
| status = status2; |
| cairo_list_del (&surface->operand.link); |
| } else { |
| link->operand.type = DEFERRED; |
| cairo_list_swap (&link->operand.link, |
| &surface->operand.link); |
| cairo_list_add (&link->link, &ctx->deferred); |
| } |
| } |
| } |
| |
| if (surface->defined) { |
| _cairo_output_stream_printf (ctx->stream, |
| "/s%u undef\n", |
| surface->base.unique_id); |
| } |
| } |
| |
| if (status == CAIRO_STATUS_SUCCESS) |
| status = _cairo_output_stream_flush (to_context (surface)->stream); |
| |
| cairo_device_release (&ctx->base); |
| |
| return status; |
| } |
| |
| static cairo_int_status_t |
| _cairo_script_surface_copy_page (void *abstract_surface) |
| { |
| cairo_script_surface_t *surface = abstract_surface; |
| cairo_status_t status; |
| |
| status = cairo_device_acquire (surface->base.device); |
| if (unlikely (status)) |
| return status; |
| |
| status = _emit_context (surface); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| _cairo_output_stream_puts (to_context (surface)->stream, "copy-page\n"); |
| |
| BAIL: |
| cairo_device_release (surface->base.device); |
| return status; |
| } |
| |
| static cairo_int_status_t |
| _cairo_script_surface_show_page (void *abstract_surface) |
| { |
| cairo_script_surface_t *surface = abstract_surface; |
| cairo_status_t status; |
| |
| status = cairo_device_acquire (surface->base.device); |
| if (unlikely (status)) |
| return status; |
| |
| status = _emit_context (surface); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| _cairo_output_stream_puts (to_context (surface)->stream, "show-page\n"); |
| |
| BAIL: |
| cairo_device_release (surface->base.device); |
| return status; |
| } |
| |
| static cairo_status_t |
| _cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, |
| cairo_path_fixed_t *path, |
| cairo_fill_rule_t fill_rule, |
| double tolerance, |
| cairo_antialias_t antialias) |
| { |
| cairo_script_surface_t *surface = cairo_container_of (clipper, |
| cairo_script_surface_t, |
| clipper); |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_bool_t matrix_updated = FALSE; |
| cairo_status_t status; |
| cairo_box_t box; |
| |
| status = _emit_context (surface); |
| if (unlikely (status)) |
| return status; |
| |
| if (path == NULL) { |
| if (surface->cr.has_clip) { |
| _cairo_output_stream_puts (ctx->stream, "reset-clip\n"); |
| surface->cr.has_clip = FALSE; |
| } |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| /* skip the trivial clip covering the surface extents */ |
| if (surface->width >=0 && surface->height >= 0 && |
| _cairo_path_fixed_is_box (path, &box)) |
| { |
| if (box.p1.x <= 0 && box.p1.y <= 0 && |
| box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) && |
| box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height)) |
| { |
| return CAIRO_STATUS_SUCCESS; |
| } |
| } |
| |
| status = _emit_identity (surface, &matrix_updated); |
| if (unlikely (status)) |
| return status; |
| |
| status = _emit_fill_rule (surface, fill_rule); |
| if (unlikely (status)) |
| return status; |
| |
| if (path->has_curve_to) { |
| status = _emit_tolerance (surface, tolerance, matrix_updated); |
| if (unlikely (status)) |
| return status; |
| } |
| |
| if (! _cairo_path_fixed_fill_maybe_region (path)) { |
| status = _emit_antialias (surface, antialias); |
| if (unlikely (status)) |
| return status; |
| } |
| |
| status = _emit_path (surface, path); |
| if (unlikely (status)) |
| return status; |
| |
| _cairo_output_stream_puts (ctx->stream, "clip+\n"); |
| surface->cr.has_clip = TRUE; |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| active (cairo_script_surface_t *surface) |
| { |
| cairo_status_t status; |
| |
| status = cairo_device_acquire (surface->base.device); |
| if (unlikely (status)) |
| return status; |
| |
| if (surface->active++ == 0) |
| to_context (surface)->active++; |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static void |
| inactive (cairo_script_surface_t *surface) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_list_t sorted; |
| |
| assert (surface->active > 0); |
| if (--surface->active) |
| goto DONE; |
| |
| assert (ctx->active > 0); |
| if (--ctx->active) |
| goto DONE; |
| |
| cairo_list_init (&sorted); |
| while (! cairo_list_is_empty (&ctx->deferred)) { |
| struct deferred_finish *df; |
| cairo_list_t *operand; |
| int depth; |
| |
| df = cairo_list_first_entry (&ctx->deferred, |
| struct deferred_finish, |
| link); |
| |
| depth = 0; |
| cairo_list_foreach (operand, &ctx->operands) { |
| if (operand == &df->operand.link) |
| break; |
| depth++; |
| } |
| |
| df->operand.type = depth; |
| |
| if (cairo_list_is_empty (&sorted)) { |
| cairo_list_move (&df->link, &sorted); |
| } else { |
| struct deferred_finish *pos; |
| |
| cairo_list_foreach_entry (pos, struct deferred_finish, |
| &sorted, |
| link) |
| { |
| if (df->operand.type < pos->operand.type) |
| break; |
| } |
| cairo_list_move_tail (&df->link, &pos->link); |
| } |
| } |
| |
| while (! cairo_list_is_empty (&sorted)) { |
| struct deferred_finish *df; |
| cairo_list_t *operand; |
| int depth; |
| |
| df = cairo_list_first_entry (&sorted, |
| struct deferred_finish, |
| link); |
| |
| depth = 0; |
| cairo_list_foreach (operand, &ctx->operands) { |
| if (operand == &df->operand.link) |
| break; |
| depth++; |
| } |
| |
| if (depth == 0) { |
| _cairo_output_stream_printf (ctx->stream, |
| "pop\n"); |
| } else if (depth == 1) { |
| _cairo_output_stream_printf (ctx->stream, |
| "exch pop\n"); |
| } else { |
| _cairo_output_stream_printf (ctx->stream, |
| "%d -1 roll pop\n", |
| depth); |
| } |
| |
| cairo_list_del (&df->operand.link); |
| cairo_list_del (&df->link); |
| free (df); |
| } |
| |
| DONE: |
| cairo_device_release (surface->base.device); |
| } |
| |
| static cairo_int_status_t |
| _cairo_script_surface_paint (void *abstract_surface, |
| cairo_operator_t op, |
| const cairo_pattern_t *source, |
| cairo_clip_t *clip) |
| { |
| cairo_script_surface_t *surface = abstract_surface; |
| cairo_status_t status; |
| |
| status = active (surface); |
| if (unlikely (status)) |
| return status; |
| |
| status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_context (surface); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_source (surface, op, source); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_operator (surface, op); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| _cairo_output_stream_puts (to_context (surface)->stream, |
| "paint\n"); |
| |
| inactive (surface); |
| |
| if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { |
| return _cairo_surface_wrapper_paint (&surface->wrapper, |
| op, source, clip); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| |
| BAIL: |
| inactive (surface); |
| return status; |
| } |
| |
| static cairo_int_status_t |
| _cairo_script_surface_mask (void *abstract_surface, |
| cairo_operator_t op, |
| const cairo_pattern_t *source, |
| const cairo_pattern_t *mask, |
| cairo_clip_t *clip) |
| { |
| cairo_script_surface_t *surface = abstract_surface; |
| cairo_status_t status; |
| |
| status = active (surface); |
| if (unlikely (status)) |
| return status; |
| |
| status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_context (surface); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_source (surface, op, source); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_operator (surface, op); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| if (_cairo_pattern_equal (source, mask)) { |
| _cairo_output_stream_puts (to_context (surface)->stream, "/source get"); |
| } else { |
| status = _emit_pattern (surface, mask); |
| if (unlikely (status)) |
| goto BAIL; |
| } |
| |
| assert (surface->cr.current_operator == op); |
| |
| _cairo_output_stream_puts (to_context (surface)->stream, |
| " mask\n"); |
| |
| inactive (surface); |
| |
| if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { |
| return _cairo_surface_wrapper_mask (&surface->wrapper, |
| op, source, mask, clip); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| |
| BAIL: |
| inactive (surface); |
| return status; |
| } |
| |
| static cairo_int_status_t |
| _cairo_script_surface_stroke (void *abstract_surface, |
| cairo_operator_t op, |
| const cairo_pattern_t *source, |
| cairo_path_fixed_t *path, |
| const cairo_stroke_style_t *style, |
| const cairo_matrix_t *ctm, |
| const cairo_matrix_t *ctm_inverse, |
| double tolerance, |
| cairo_antialias_t antialias, |
| cairo_clip_t *clip) |
| { |
| cairo_script_surface_t *surface = abstract_surface; |
| cairo_bool_t matrix_updated = FALSE; |
| cairo_status_t status; |
| |
| status = active (surface); |
| if (unlikely (status)) |
| return status; |
| |
| status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_context (surface); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_identity (surface, &matrix_updated); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_path (surface, path); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_source (surface, op, source); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_scaling_matrix (surface, ctm, &matrix_updated); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_operator (surface, op); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| if (_scaling_matrix_equal (&surface->cr.current_ctm, |
| &surface->cr.current_stroke_matrix)) |
| { |
| matrix_updated = FALSE; |
| } |
| else |
| { |
| matrix_updated = TRUE; |
| surface->cr.current_stroke_matrix = surface->cr.current_ctm; |
| } |
| |
| status = _emit_stroke_style (surface, style, matrix_updated); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_tolerance (surface, tolerance, matrix_updated); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_antialias (surface, antialias); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| _cairo_output_stream_puts (to_context (surface)->stream, "stroke+\n"); |
| |
| inactive (surface); |
| |
| if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { |
| return _cairo_surface_wrapper_stroke (&surface->wrapper, |
| op, source, path, |
| style, |
| ctm, ctm_inverse, |
| tolerance, antialias, |
| clip); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| |
| BAIL: |
| inactive (surface); |
| return status; |
| } |
| |
| static cairo_int_status_t |
| _cairo_script_surface_fill (void *abstract_surface, |
| cairo_operator_t op, |
| const cairo_pattern_t *source, |
| cairo_path_fixed_t *path, |
| cairo_fill_rule_t fill_rule, |
| double tolerance, |
| cairo_antialias_t antialias, |
| cairo_clip_t *clip) |
| { |
| cairo_script_surface_t *surface = abstract_surface; |
| cairo_bool_t matrix_updated = FALSE; |
| cairo_status_t status; |
| cairo_box_t box; |
| |
| status = active (surface); |
| if (unlikely (status)) |
| return status; |
| |
| status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_context (surface); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_identity (surface, &matrix_updated); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_source (surface, op, source); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| if (! _cairo_path_fixed_is_box (path, &box)) { |
| status = _emit_fill_rule (surface, fill_rule); |
| if (unlikely (status)) |
| goto BAIL; |
| } |
| |
| if (path->has_curve_to) { |
| status = _emit_tolerance (surface, tolerance, matrix_updated); |
| if (unlikely (status)) |
| goto BAIL; |
| } |
| |
| if (! _cairo_path_fixed_fill_maybe_region (path)) { |
| status = _emit_antialias (surface, antialias); |
| if (unlikely (status)) |
| goto BAIL; |
| } |
| |
| status = _emit_path (surface, path); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_operator (surface, op); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| _cairo_output_stream_puts (to_context (surface)->stream, "fill+\n"); |
| |
| inactive (surface); |
| |
| if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { |
| return _cairo_surface_wrapper_fill (&surface->wrapper, |
| op, source, path, |
| fill_rule, |
| tolerance, |
| antialias, |
| clip); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| |
| BAIL: |
| inactive (surface); |
| return status; |
| } |
| |
| static cairo_surface_t * |
| _cairo_script_surface_snapshot (void *abstract_surface) |
| { |
| cairo_script_surface_t *surface = abstract_surface; |
| |
| if (_cairo_surface_wrapper_is_active (&surface->wrapper)) |
| return _cairo_surface_wrapper_snapshot (&surface->wrapper); |
| |
| return NULL; |
| } |
| |
| static cairo_bool_t |
| _cairo_script_surface_has_show_text_glyphs (void *abstract_surface) |
| { |
| return TRUE; |
| } |
| |
| static const char * |
| _subpixel_order_to_string (cairo_subpixel_order_t subpixel_order) |
| { |
| static const char *names[] = { |
| "SUBPIXEL_ORDER_DEFAULT", /* CAIRO_SUBPIXEL_ORDER_DEFAULT */ |
| "SUBPIXEL_ORDER_RGB", /* CAIRO_SUBPIXEL_ORDER_RGB */ |
| "SUBPIXEL_ORDER_BGR", /* CAIRO_SUBPIXEL_ORDER_BGR */ |
| "SUBPIXEL_ORDER_VRGB", /* CAIRO_SUBPIXEL_ORDER_VRGB */ |
| "SUBPIXEL_ORDER_VBGR" /* CAIRO_SUBPIXEL_ORDER_VBGR */ |
| }; |
| return names[subpixel_order]; |
| } |
| static const char * |
| _hint_style_to_string (cairo_hint_style_t hint_style) |
| { |
| static const char *names[] = { |
| "HINT_STYLE_DEFAULT", /* CAIRO_HINT_STYLE_DEFAULT */ |
| "HINT_STYLE_NONE", /* CAIRO_HINT_STYLE_NONE */ |
| "HINT_STYLE_SLIGHT", /* CAIRO_HINT_STYLE_SLIGHT */ |
| "HINT_STYLE_MEDIUM", /* CAIRO_HINT_STYLE_MEDIUM */ |
| "HINT_STYLE_FULL" /* CAIRO_HINT_STYLE_FULL */ |
| }; |
| return names[hint_style]; |
| } |
| static const char * |
| _hint_metrics_to_string (cairo_hint_metrics_t hint_metrics) |
| { |
| static const char *names[] = { |
| "HINT_METRICS_DEFAULT", /* CAIRO_HINT_METRICS_DEFAULT */ |
| "HINT_METRICS_OFF", /* CAIRO_HINT_METRICS_OFF */ |
| "HINT_METRICS_ON" /* CAIRO_HINT_METRICS_ON */ |
| }; |
| return names[hint_metrics]; |
| } |
| |
| static cairo_status_t |
| _emit_font_options (cairo_script_surface_t *surface, |
| cairo_font_options_t *font_options) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| |
| if (cairo_font_options_equal (&surface->cr.current_font_options, |
| font_options)) |
| { |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| _cairo_output_stream_printf (ctx->stream, "<<"); |
| |
| if (font_options->antialias != surface->cr.current_font_options.antialias) { |
| _cairo_output_stream_printf (ctx->stream, |
| " /antialias //%s", |
| _antialias_to_string (font_options->antialias)); |
| } |
| |
| if (font_options->subpixel_order != |
| surface->cr.current_font_options.subpixel_order) |
| { |
| _cairo_output_stream_printf (ctx->stream, |
| " /subpixel-order //%s", |
| _subpixel_order_to_string (font_options->subpixel_order)); |
| } |
| |
| if (font_options->hint_style != |
| surface->cr.current_font_options.hint_style) |
| { |
| _cairo_output_stream_printf (ctx->stream, |
| " /hint-style //%s", |
| _hint_style_to_string (font_options->hint_style)); |
| } |
| |
| if (font_options->hint_metrics != |
| surface->cr.current_font_options.hint_metrics) |
| { |
| _cairo_output_stream_printf (ctx->stream, |
| " /hint-metrics //%s", |
| _hint_metrics_to_string (font_options->hint_metrics)); |
| } |
| |
| _cairo_output_stream_printf (ctx->stream, |
| " >> set-font-options\n"); |
| |
| surface->cr.current_font_options = *font_options; |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static void |
| _cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) |
| { |
| cairo_script_surface_font_private_t *font_private; |
| |
| font_private = scaled_font->surface_private; |
| if (font_private != NULL) { |
| cairo_status_t status; |
| cairo_device_t *device; |
| |
| status = cairo_device_acquire (device = &font_private->ctx->base); |
| if (likely (status == CAIRO_STATUS_SUCCESS)) { |
| _cairo_output_stream_printf (font_private->ctx->stream, |
| "/f%lu undef /sf%lu undef\n", |
| font_private->id, |
| font_private->id); |
| |
| _bitmap_release_id (&font_private->ctx->font_id, font_private->id); |
| cairo_list_del (&font_private->link); |
| free (font_private); |
| |
| cairo_device_release (device); |
| } |
| |
| scaled_font->surface_private = NULL; |
| } |
| } |
| |
| static cairo_status_t |
| _emit_type42_font (cairo_script_surface_t *surface, |
| cairo_scaled_font_t *scaled_font) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| const cairo_scaled_font_backend_t *backend; |
| cairo_script_surface_font_private_t *font_private; |
| cairo_output_stream_t *base85_stream; |
| cairo_output_stream_t *zlib_stream; |
| cairo_status_t status, status2; |
| unsigned long size; |
| unsigned int load_flags; |
| uint32_t len; |
| uint8_t *buf; |
| |
| backend = scaled_font->backend; |
| if (backend->load_truetype_table == NULL) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| |
| size = 0; |
| status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); |
| if (unlikely (status)) |
| return status; |
| |
| buf = malloc (size); |
| if (unlikely (buf == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size); |
| if (unlikely (status)) { |
| free (buf); |
| return status; |
| } |
| |
| #if CAIRO_HAS_FT_FONT |
| load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font); |
| #else |
| load_flags = 0; |
| #endif |
| _cairo_output_stream_printf (ctx->stream, |
| "<< " |
| "/type 42 " |
| "/index 0 " |
| "/flags %d " |
| "/source <|", |
| load_flags); |
| |
| base85_stream = _cairo_base85_stream_create (ctx->stream); |
| len = to_be32 (size); |
| _cairo_output_stream_write (base85_stream, &len, sizeof (len)); |
| |
| zlib_stream = _cairo_deflate_stream_create (base85_stream); |
| |
| _cairo_output_stream_write (zlib_stream, buf, size); |
| free (buf); |
| |
| status2 = _cairo_output_stream_destroy (zlib_stream); |
| if (status == CAIRO_STATUS_SUCCESS) |
| status = status2; |
| |
| status2 = _cairo_output_stream_destroy (base85_stream); |
| if (status == CAIRO_STATUS_SUCCESS) |
| status = status2; |
| |
| font_private = scaled_font->surface_private; |
| _cairo_output_stream_printf (ctx->stream, |
| "~> >> font dup /f%lu exch def set-font-face", |
| font_private->id); |
| |
| return status; |
| } |
| |
| static cairo_status_t |
| _emit_scaled_font_init (cairo_script_surface_t *surface, |
| cairo_scaled_font_t *scaled_font) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_script_surface_font_private_t *font_private; |
| cairo_status_t status; |
| |
| font_private = malloc (sizeof (cairo_script_surface_font_private_t)); |
| if (unlikely (font_private == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| font_private->ctx = ctx; |
| font_private->parent = scaled_font; |
| font_private->subset_glyph_index = 0; |
| font_private->has_sfnt = TRUE; |
| |
| cairo_list_add (&font_private->link, &ctx->fonts); |
| |
| status = _bitmap_next_id (&ctx->font_id, |
| &font_private->id); |
| if (unlikely (status)) { |
| free (font_private); |
| return status; |
| } |
| |
| scaled_font->surface_private = font_private; |
| scaled_font->surface_backend = &_cairo_script_surface_backend; |
| |
| status = _emit_context (surface); |
| if (unlikely (status)) |
| return status; |
| |
| status = _emit_type42_font (surface, scaled_font); |
| if (status != CAIRO_INT_STATUS_UNSUPPORTED) |
| return status; |
| |
| font_private->has_sfnt = FALSE; |
| _cairo_output_stream_printf (ctx->stream, |
| "dict\n" |
| " /type 3 set\n" |
| " /metrics [%f %f %f %f %f] set\n" |
| " /glyphs array set\n" |
| " font dup /f%lu exch def set-font-face", |
| scaled_font->fs_extents.ascent, |
| scaled_font->fs_extents.descent, |
| scaled_font->fs_extents.height, |
| scaled_font->fs_extents.max_x_advance, |
| scaled_font->fs_extents.max_y_advance, |
| font_private->id); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_scaled_font (cairo_script_surface_t *surface, |
| cairo_scaled_font_t *scaled_font) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_matrix_t matrix; |
| cairo_font_options_t options; |
| cairo_bool_t matrix_updated = FALSE; |
| cairo_status_t status; |
| cairo_script_surface_font_private_t *font_private; |
| |
| cairo_scaled_font_get_ctm (scaled_font, &matrix); |
| status = _emit_scaling_matrix (surface, &matrix, &matrix_updated); |
| if (unlikely (status)) |
| return status; |
| |
| if (! matrix_updated && surface->cr.current_scaled_font == scaled_font) |
| return CAIRO_STATUS_SUCCESS; |
| |
| surface->cr.current_scaled_font = scaled_font; |
| |
| if (! (scaled_font->surface_backend == NULL || |
| scaled_font->surface_backend == &_cairo_script_surface_backend)) |
| { |
| _cairo_scaled_font_revoke_ownership (scaled_font); |
| } |
| |
| font_private = scaled_font->surface_private; |
| if (font_private == NULL) { |
| cairo_scaled_font_get_font_matrix (scaled_font, &matrix); |
| status = _emit_font_matrix (surface, &matrix); |
| if (unlikely (status)) |
| return status; |
| |
| cairo_scaled_font_get_font_options (scaled_font, &options); |
| status = _emit_font_options (surface, &options); |
| if (unlikely (status)) |
| return status; |
| |
| status = _emit_scaled_font_init (surface, scaled_font); |
| if (unlikely (status)) |
| return status; |
| |
| font_private = scaled_font->surface_private; |
| assert (font_private != NULL); |
| |
| assert (target_is_active (surface)); |
| _cairo_output_stream_printf (ctx->stream, |
| " /scaled-font get /sf%lu exch def\n", |
| font_private->id); |
| } else { |
| _cairo_output_stream_printf (ctx->stream, |
| "sf%lu set-scaled-font\n", |
| font_private->id); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_scaled_glyph_vector (cairo_script_surface_t *surface, |
| cairo_scaled_font_t *scaled_font, |
| cairo_scaled_glyph_t *scaled_glyph) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_script_surface_font_private_t *font_private; |
| cairo_script_implicit_context_t old_cr; |
| cairo_status_t status; |
| unsigned long index; |
| |
| font_private = scaled_font->surface_private; |
| index = ++font_private->subset_glyph_index; |
| scaled_glyph->surface_private = (void *) index; |
| |
| _cairo_output_stream_printf (ctx->stream, |
| "%lu <<\n" |
| " /metrics [%f %f %f %f %f %f]\n" |
| " /render {\n", |
| index, |
| scaled_glyph->fs_metrics.x_bearing, |
| scaled_glyph->fs_metrics.y_bearing, |
| scaled_glyph->fs_metrics.width, |
| scaled_glyph->fs_metrics.height, |
| scaled_glyph->fs_metrics.x_advance, |
| scaled_glyph->fs_metrics.y_advance); |
| |
| if (! _cairo_matrix_is_identity (&scaled_font->scale_inverse)) { |
| _cairo_output_stream_printf (ctx->stream, |
| "[%f %f %f %f %f %f] transform\n", |
| scaled_font->scale_inverse.xx, |
| scaled_font->scale_inverse.yx, |
| scaled_font->scale_inverse.xy, |
| scaled_font->scale_inverse.yy, |
| scaled_font->scale_inverse.x0, |
| scaled_font->scale_inverse.y0); |
| } |
| |
| old_cr = surface->cr; |
| _cairo_script_implicit_context_init (&surface->cr); |
| status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, |
| &surface->base); |
| surface->cr = old_cr; |
| |
| _cairo_output_stream_puts (ctx->stream, "} >> set\n"); |
| |
| return status; |
| } |
| |
| static cairo_status_t |
| _emit_scaled_glyph_bitmap (cairo_script_surface_t *surface, |
| cairo_scaled_font_t *scaled_font, |
| cairo_scaled_glyph_t *scaled_glyph) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_script_surface_font_private_t *font_private; |
| cairo_status_t status; |
| unsigned long index; |
| |
| font_private = scaled_font->surface_private; |
| index = ++font_private->subset_glyph_index; |
| scaled_glyph->surface_private = (void *) index; |
| |
| _cairo_output_stream_printf (ctx->stream, |
| "%lu <<\n" |
| " /metrics [%f %f %f %f %f %f]\n" |
| " /render {\n" |
| "%f %f translate\n", |
| index, |
| scaled_glyph->fs_metrics.x_bearing, |
| scaled_glyph->fs_metrics.y_bearing, |
| scaled_glyph->fs_metrics.width, |
| scaled_glyph->fs_metrics.height, |
| scaled_glyph->fs_metrics.x_advance, |
| scaled_glyph->fs_metrics.y_advance, |
| scaled_glyph->fs_metrics.x_bearing, |
| scaled_glyph->fs_metrics.y_bearing); |
| |
| status = _emit_image_surface (surface, scaled_glyph->surface); |
| if (unlikely (status)) |
| return status; |
| |
| _cairo_output_stream_puts (ctx->stream, "pattern "); |
| |
| if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) { |
| _cairo_output_stream_printf (ctx->stream, |
| "\n [%f %f %f %f %f %f] set-matrix\n", |
| scaled_font->font_matrix.xx, |
| scaled_font->font_matrix.yx, |
| scaled_font->font_matrix.xy, |
| scaled_font->font_matrix.yy, |
| scaled_font->font_matrix.x0, |
| scaled_font->font_matrix.y0); |
| } |
| _cairo_output_stream_puts (ctx->stream, |
| "mask\n} >> set\n"); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_scaled_glyph_prologue (cairo_script_surface_t *surface, |
| cairo_scaled_font_t *scaled_font) |
| { |
| cairo_script_surface_font_private_t *font_private; |
| |
| assert (scaled_font->surface_backend == &_cairo_script_surface_backend); |
| |
| font_private = scaled_font->surface_private; |
| |
| _cairo_output_stream_printf (to_context (surface)->stream, |
| "f%lu /glyphs get\n", |
| font_private->id); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _emit_scaled_glyphs (cairo_script_surface_t *surface, |
| cairo_scaled_font_t *scaled_font, |
| cairo_glyph_t *glyphs, |
| unsigned int num_glyphs) |
| { |
| cairo_script_surface_font_private_t *font_private; |
| cairo_status_t status; |
| unsigned int n; |
| cairo_bool_t have_glyph_prologue = FALSE; |
| |
| if (num_glyphs == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| font_private = scaled_font->surface_private; |
| if (font_private->has_sfnt) |
| return CAIRO_STATUS_SUCCESS; |
| |
| _cairo_scaled_font_freeze_cache (scaled_font); |
| for (n = 0; n < num_glyphs; n++) { |
| cairo_scaled_glyph_t *scaled_glyph; |
| |
| status = _cairo_scaled_glyph_lookup (scaled_font, |
| glyphs[n].index, |
| CAIRO_SCALED_GLYPH_INFO_METRICS, |
| &scaled_glyph); |
| if (unlikely (status)) |
| break; |
| |
| if (scaled_glyph->surface_private != NULL) |
| continue; |
| |
| status = _cairo_scaled_glyph_lookup (scaled_font, |
| glyphs[n].index, |
| CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, |
| &scaled_glyph); |
| if (_cairo_status_is_error (status)) |
| break; |
| |
| if (status == CAIRO_STATUS_SUCCESS) { |
| if (! have_glyph_prologue) { |
| status = _emit_scaled_glyph_prologue (surface, scaled_font); |
| if (unlikely (status)) |
| break; |
| |
| have_glyph_prologue = TRUE; |
| } |
| |
| status = _emit_scaled_glyph_vector (surface, |
| scaled_font, |
| scaled_glyph); |
| if (unlikely (status)) |
| break; |
| |
| continue; |
| } |
| |
| status = _cairo_scaled_glyph_lookup (scaled_font, |
| glyphs[n].index, |
| CAIRO_SCALED_GLYPH_INFO_SURFACE, |
| &scaled_glyph); |
| if (_cairo_status_is_error (status)) |
| break; |
| |
| if (status == CAIRO_STATUS_SUCCESS) { |
| if (! have_glyph_prologue) { |
| status = _emit_scaled_glyph_prologue (surface, scaled_font); |
| if (unlikely (status)) |
| break; |
| |
| have_glyph_prologue = TRUE; |
| } |
| |
| status = _emit_scaled_glyph_bitmap (surface, |
| scaled_font, |
| scaled_glyph); |
| if (unlikely (status)) |
| break; |
| |
| continue; |
| } |
| } |
| _cairo_scaled_font_thaw_cache (scaled_font); |
| |
| if (have_glyph_prologue) { |
| _cairo_output_stream_puts (to_context (surface)->stream, "pop pop\n"); |
| } |
| |
| return status; |
| } |
| |
| static void |
| to_octal (int value, char *buf, size_t size) |
| { |
| do { |
| buf[--size] = '0' + (value & 7); |
| value >>= 3; |
| } while (size); |
| } |
| |
| static void |
| _emit_string_literal (cairo_script_surface_t *surface, |
| const char *utf8, int len) |
| { |
| cairo_script_context_t *ctx = to_context (surface); |
| char c; |
| const char *end; |
| |
| _cairo_output_stream_puts (ctx->stream, "("); |
| |
| if (utf8 == NULL) { |
| end = utf8; |
| } else { |
| if (len < 0) |
| len = strlen (utf8); |
| end = utf8 + len; |
| } |
| |
| while (utf8 < end) { |
| switch ((c = *utf8++)) { |
| case '\n': |
| c = 'n'; |
| goto ESCAPED_CHAR; |
| case '\r': |
| c = 'r'; |
| goto ESCAPED_CHAR; |
| case '\t': |
| c = 't'; |
| goto ESCAPED_CHAR; |
| case '\b': |
| c = 'b'; |
| goto ESCAPED_CHAR; |
| case '\f': |
| c = 'f'; |
| goto ESCAPED_CHAR; |
| case '\\': |
| case '(': |
| case ')': |
| ESCAPED_CHAR: |
| _cairo_output_stream_printf (ctx->stream, "\\%c", c); |
| break; |
| default: |
| if (isprint (c) || isspace (c)) { |
| _cairo_output_stream_printf (ctx->stream, "%c", c); |
| } else { |
| char buf[4] = { '\\' }; |
| |
| to_octal (c, buf+1, 3); |
| _cairo_output_stream_write (ctx->stream, buf, 4); |
| } |
| break; |
| } |
| } |
| _cairo_output_stream_puts (ctx->stream, ")"); |
| } |
| |
| static cairo_int_status_t |
| _cairo_script_surface_show_text_glyphs (void *abstract_surface, |
| cairo_operator_t op, |
| const cairo_pattern_t *source, |
| const char *utf8, |
| int utf8_len, |
| cairo_glyph_t *glyphs, |
| int num_glyphs, |
| const cairo_text_cluster_t *clusters, |
| int num_clusters, |
| cairo_text_cluster_flags_t backward, |
| cairo_scaled_font_t *scaled_font, |
| cairo_clip_t *clip) |
| { |
| cairo_script_surface_t *surface = abstract_surface; |
| cairo_script_context_t *ctx = to_context (surface); |
| cairo_script_surface_font_private_t *font_private; |
| cairo_scaled_glyph_t *scaled_glyph; |
| cairo_matrix_t matrix; |
| cairo_status_t status; |
| double x, y, ix, iy; |
| int n; |
| cairo_output_stream_t *base85_stream = NULL; |
| |
| status = active (surface); |
| if (unlikely (status)) |
| return status; |
| |
| status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_context (surface); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_source (surface, op, source); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_scaled_font (surface, scaled_font); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_operator (surface, op); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| /* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */ |
| /* [cx cy [glyphs]] show_glyphs */ |
| |
| if (utf8 != NULL && clusters != NULL) { |
| _emit_string_literal (surface, utf8, utf8_len); |
| _cairo_output_stream_puts (ctx->stream, " "); |
| } |
| |
| matrix = surface->cr.current_ctm; |
| status = cairo_matrix_invert (&matrix); |
| assert (status == CAIRO_STATUS_SUCCESS); |
| |
| ix = x = glyphs[0].x; |
| iy = y = glyphs[0].y; |
| cairo_matrix_transform_point (&matrix, &ix, &iy); |
| ix -= scaled_font->font_matrix.x0; |
| iy -= scaled_font->font_matrix.y0; |
| |
| _cairo_scaled_font_freeze_cache (scaled_font); |
| font_private = scaled_font->surface_private; |
| |
| _cairo_output_stream_printf (ctx->stream, |
| "[%f %f ", |
| ix, iy); |
| |
| for (n = 0; n < num_glyphs; n++) { |
| if (font_private->has_sfnt) { |
| if (glyphs[n].index > 256) |
| break; |
| } else { |
| status = _cairo_scaled_glyph_lookup (scaled_font, |
| glyphs[n].index, |
| CAIRO_SCALED_GLYPH_INFO_METRICS, |
| &scaled_glyph); |
| if (unlikely (status)) { |
| _cairo_scaled_font_thaw_cache (scaled_font); |
| goto BAIL; |
| } |
| |
| if ((long unsigned) scaled_glyph->surface_private > 256) |
| break; |
| } |
| } |
| |
| if (n == num_glyphs) { |
| _cairo_output_stream_puts (ctx->stream, "<~"); |
| base85_stream = _cairo_base85_stream_create (ctx->stream); |
| } else |
| _cairo_output_stream_puts (ctx->stream, "["); |
| |
| for (n = 0; n < num_glyphs; n++) { |
| double dx, dy; |
| |
| status = _cairo_scaled_glyph_lookup (scaled_font, |
| glyphs[n].index, |
| CAIRO_SCALED_GLYPH_INFO_METRICS, |
| &scaled_glyph); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) { |
| if (fabs (glyphs[n].y - y) < 1e-5) { |
| if (base85_stream != NULL) { |
| status = _cairo_output_stream_destroy (base85_stream); |
| if (unlikely (status)) { |
| base85_stream = NULL; |
| break; |
| } |
| |
| _cairo_output_stream_printf (ctx->stream, |
| "~> %f <~", glyphs[n].x - x); |
| base85_stream = _cairo_base85_stream_create (ctx->stream); |
| } else { |
| _cairo_output_stream_printf (ctx->stream, |
| " ] %f [ ", glyphs[n].x - x); |
| } |
| |
| x = glyphs[n].x; |
| } else { |
| ix = x = glyphs[n].x; |
| iy = y = glyphs[n].y; |
| cairo_matrix_transform_point (&matrix, &ix, &iy); |
| ix -= scaled_font->font_matrix.x0; |
| iy -= scaled_font->font_matrix.y0; |
| if (base85_stream != NULL) { |
| status = _cairo_output_stream_destroy (base85_stream); |
| if (unlikely (status)) { |
| base85_stream = NULL; |
| break; |
| } |
| |
| _cairo_output_stream_printf (ctx->stream, |
| "~> %f %f <~", |
| ix, iy); |
| base85_stream = _cairo_base85_stream_create (ctx->stream); |
| } else { |
| _cairo_output_stream_printf (ctx->stream, |
| " ] %f %f [ ", |
| ix, iy); |
| } |
| } |
| } |
| if (base85_stream != NULL) { |
| uint8_t c; |
| |
| if (font_private->has_sfnt) |
| c = glyphs[n].index; |
| else |
| c = (uint8_t) (long unsigned) scaled_glyph->surface_private; |
| |
| _cairo_output_stream_write (base85_stream, &c, 1); |
| } else { |
| if (font_private->has_sfnt) |
| _cairo_output_stream_printf (ctx->stream, " %lu", |
| glyphs[n].index); |
| else |
| _cairo_output_stream_printf (ctx->stream, " %lu", |
| (long unsigned) scaled_glyph->surface_private); |
| } |
| |
| dx = scaled_glyph->metrics.x_advance; |
| dy = scaled_glyph->metrics.y_advance; |
| cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy); |
| x += dx; |
| y += dy; |
| } |
| _cairo_scaled_font_thaw_cache (scaled_font); |
| |
| if (base85_stream != NULL) { |
| cairo_status_t status2; |
| |
| status2 = _cairo_output_stream_destroy (base85_stream); |
| if (status == CAIRO_STATUS_SUCCESS) |
| status = status2; |
| |
| _cairo_output_stream_printf (ctx->stream, "~>"); |
| } else { |
| _cairo_output_stream_puts (ctx->stream, " ]"); |
| } |
| if (unlikely (status)) |
| return status; |
| |
| if (utf8 != NULL && clusters != NULL) { |
| for (n = 0; n < num_clusters; n++) { |
| if (clusters[n].num_bytes > UCHAR_MAX || |
| clusters[n].num_glyphs > UCHAR_MAX) |
| { |
| break; |
| } |
| } |
| |
| if (n < num_clusters) { |
| _cairo_output_stream_puts (ctx->stream, "] [ "); |
| for (n = 0; n < num_clusters; n++) { |
| _cairo_output_stream_printf (ctx->stream, |
| "%d %d ", |
| clusters[n].num_bytes, |
| clusters[n].num_glyphs); |
| } |
| _cairo_output_stream_puts (ctx->stream, "]"); |
| } |
| else |
| { |
| _cairo_output_stream_puts (ctx->stream, "] <~"); |
| base85_stream = _cairo_base85_stream_create (ctx->stream); |
| for (n = 0; n < num_clusters; n++) { |
| uint8_t c[2]; |
| c[0] = clusters[n].num_bytes; |
| c[1] = clusters[n].num_glyphs; |
| _cairo_output_stream_write (base85_stream, c, 2); |
| } |
| status = _cairo_output_stream_destroy (base85_stream); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| _cairo_output_stream_puts (ctx->stream, "~>"); |
| } |
| |
| _cairo_output_stream_printf (ctx->stream, |
| " //%s show-text-glyphs\n", |
| _direction_to_string (backward)); |
| } else { |
| _cairo_output_stream_puts (ctx->stream, |
| "] show-glyphs\n"); |
| } |
| |
| inactive (surface); |
| |
| if (_cairo_surface_wrapper_is_active (&surface->wrapper)){ |
| return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper, |
| op, source, |
| utf8, utf8_len, |
| glyphs, num_glyphs, |
| clusters, num_clusters, |
| backward, |
| scaled_font, |
| clip); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| |
| BAIL: |
| inactive (surface); |
| return status; |
| } |
| |
| static cairo_bool_t |
| _cairo_script_surface_get_extents (void *abstract_surface, |
| cairo_rectangle_int_t *rectangle) |
| { |
| cairo_script_surface_t *surface = abstract_surface; |
| |
| if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { |
| return _cairo_surface_wrapper_get_extents (&surface->wrapper, |
| rectangle); |
| } |
| |
| if (surface->width < 0 || surface->height < 0) |
| return FALSE; |
| |
| rectangle->x = 0; |
| rectangle->y = 0; |
| rectangle->width = surface->width; |
| rectangle->height = surface->height; |
| |
| return TRUE; |
| } |
| |
| static const cairo_surface_backend_t |
| _cairo_script_surface_backend = { |
| CAIRO_SURFACE_TYPE_SCRIPT, |
| _cairo_script_surface_create_similar, |
| _cairo_script_surface_finish, |
| _cairo_script_surface_acquire_source_image, |
| _cairo_script_surface_release_source_image, |
| NULL, /* acquire_dest_image */ |
| NULL, /* release_dest_image */ |
| NULL, /* clone_similar */ |
| NULL, /* composite */ |
| NULL, /* fill_rectangles */ |
| NULL, /* composite_trapezoids */ |
| NULL, /* create_span_renderer */ |
| NULL, /* check_span_renderer */ |
| _cairo_script_surface_copy_page, |
| _cairo_script_surface_show_page, |
| _cairo_script_surface_get_extents, |
| NULL, /* old_show_glyphs */ |
| NULL, /* get_font_options */ |
| NULL, /* flush */ |
| NULL, /* mark_dirty_rectangle */ |
| _cairo_script_surface_scaled_font_fini, |
| NULL, /* scaled_glyph_fini */ |
| |
| /* The 5 high level operations */ |
| _cairo_script_surface_paint, |
| _cairo_script_surface_mask, |
| _cairo_script_surface_stroke, |
| _cairo_script_surface_fill, |
| NULL, |
| |
| _cairo_script_surface_snapshot, |
| |
| NULL, /* is_similar */ |
| /* XXX need fill-stroke for passthrough */ |
| NULL, /* fill_stroke */ |
| NULL, /* create_solid_pattern_surface */ |
| NULL, /* can_repaint_solid_pattern_surface */ |
| |
| /* The alternate high-level text operation */ |
| _cairo_script_surface_has_show_text_glyphs, |
| _cairo_script_surface_show_text_glyphs |
| }; |
| |
| static void |
| _cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr) |
| { |
| cr->current_operator = CAIRO_GSTATE_OPERATOR_DEFAULT; |
| cr->current_fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; |
| cr->current_tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; |
| cr->current_antialias = CAIRO_ANTIALIAS_DEFAULT; |
| _cairo_stroke_style_init (&cr->current_style); |
| _cairo_pattern_init_solid (&cr->current_source.solid, |
| CAIRO_COLOR_BLACK); |
| _cairo_path_fixed_init (&cr->current_path); |
| cairo_matrix_init_identity (&cr->current_ctm); |
| cairo_matrix_init_identity (&cr->current_stroke_matrix); |
| cairo_matrix_init_identity (&cr->current_font_matrix); |
| _cairo_font_options_init_default (&cr->current_font_options); |
| cr->current_scaled_font = NULL; |
| cr->has_clip = FALSE; |
| } |
| |
| static void |
| _cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr) |
| { |
| if (cr->current_style.dash != NULL) { |
| free (cr->current_style.dash); |
| cr->current_style.dash = NULL; |
| } |
| _cairo_pattern_fini (&cr->current_source.base); |
| _cairo_path_fixed_fini (&cr->current_path); |
| |
| _cairo_script_implicit_context_init (cr); |
| } |
| |
| static cairo_script_surface_t * |
| _cairo_script_surface_create_internal (cairo_script_context_t *ctx, |
| cairo_content_t content, |
| double width, |
| double height, |
| cairo_surface_t *passthrough) |
| { |
| cairo_script_surface_t *surface; |
| |
| if (unlikely (ctx == NULL)) |
| return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); |
| |
| surface = malloc (sizeof (cairo_script_surface_t)); |
| if (unlikely (surface == NULL)) |
| return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
| |
| _cairo_surface_init (&surface->base, |
| &_cairo_script_surface_backend, |
| &ctx->base, |
| content); |
| |
| _cairo_surface_wrapper_init (&surface->wrapper, passthrough); |
| |
| _cairo_surface_clipper_init (&surface->clipper, |
| _cairo_script_surface_clipper_intersect_clip_path); |
| |
| surface->width = width; |
| surface->height = height; |
| |
| surface->emitted = FALSE; |
| surface->defined = FALSE; |
| surface->active = FALSE; |
| surface->operand.type = SURFACE; |
| cairo_list_init (&surface->operand.link); |
| |
| _cairo_script_implicit_context_init (&surface->cr); |
| |
| return surface; |
| } |
| |
| static const cairo_device_backend_t _cairo_script_device_backend = { |
| CAIRO_DEVICE_TYPE_SCRIPT, |
| |
| NULL, NULL, /* lock, unlock */ |
| |
| _device_flush, /* flush */ |
| NULL, /* finish */ |
| _device_destroy |
| }; |
| |
| static cairo_device_t * |
| _cairo_script_context_create_internal (cairo_output_stream_t *stream) |
| { |
| cairo_script_context_t *ctx; |
| |
| ctx = malloc (sizeof (cairo_script_context_t)); |
| if (unlikely (ctx == NULL)) |
| return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
| |
| memset (ctx, 0, sizeof (cairo_script_context_t)); |
| |
| _cairo_device_init (&ctx->base, &_cairo_script_device_backend); |
| |
| cairo_list_init (&ctx->operands); |
| cairo_list_init (&ctx->deferred); |
| ctx->stream = stream; |
| ctx->mode = CAIRO_SCRIPT_MODE_ASCII; |
| |
| cairo_list_init (&ctx->fonts); |
| cairo_list_init (&ctx->defines); |
| |
| _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n"); |
| |
| return &ctx->base; |
| } |
| |
| cairo_device_t * |
| cairo_script_create (const char *filename) |
| { |
| cairo_output_stream_t *stream; |
| cairo_status_t status; |
| |
| stream = _cairo_output_stream_create_for_filename (filename); |
| if ((status = _cairo_output_stream_get_status (stream))) |
| return _cairo_device_create_in_error (status); |
| |
| return _cairo_script_context_create_internal (stream); |
| } |
| |
| cairo_device_t * |
| cairo_script_create_for_stream (cairo_write_func_t write_func, |
| void *closure) |
| { |
| cairo_output_stream_t *stream; |
| cairo_status_t status; |
| |
| stream = _cairo_output_stream_create (write_func, NULL, closure); |
| if ((status = _cairo_output_stream_get_status (stream))) |
| return _cairo_device_create_in_error (status); |
| |
| return _cairo_script_context_create_internal (stream); |
| } |
| |
| void |
| cairo_script_write_comment (cairo_device_t *device, |
| const char *comment, |
| int len) |
| { |
| cairo_script_context_t *context = (cairo_script_context_t *) device; |
| |
| if (len < 0) |
| len = strlen (comment); |
| |
| _cairo_output_stream_puts (context->stream, "% "); |
| _cairo_output_stream_write (context->stream, comment, len); |
| _cairo_output_stream_puts (context->stream, "\n"); |
| } |
| |
| void |
| cairo_script_set_mode (cairo_device_t *device, |
| cairo_script_mode_t mode) |
| { |
| cairo_script_context_t *context = (cairo_script_context_t *) device; |
| |
| context->mode = mode; |
| } |
| |
| cairo_script_mode_t |
| cairo_script_get_mode (cairo_device_t *device) |
| { |
| cairo_script_context_t *context = (cairo_script_context_t *) device; |
| |
| return context->mode; |
| } |
| |
| cairo_surface_t * |
| cairo_script_surface_create (cairo_device_t *device, |
| cairo_content_t content, |
| double width, |
| double height) |
| { |
| if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) |
| return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); |
| |
| if (unlikely (device->status)) |
| return _cairo_surface_create_in_error (device->status); |
| |
| return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device, |
| content, |
| width, height, |
| NULL)->base; |
| } |
| |
| cairo_surface_t * |
| cairo_script_surface_create_for_target (cairo_device_t *device, |
| cairo_surface_t *target) |
| { |
| cairo_rectangle_int_t extents; |
| |
| if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) |
| return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); |
| |
| if (unlikely (device->status)) |
| return _cairo_surface_create_in_error (device->status); |
| |
| if (unlikely (target->status)) |
| return _cairo_surface_create_in_error (target->status); |
| |
| if (! _cairo_surface_get_extents (target, &extents)) |
| extents.width = extents.height = -1; |
| |
| return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device, |
| target->content, |
| extents.width, |
| extents.height, |
| target)->base; |
| } |
| |
| cairo_status_t |
| cairo_script_from_recording_surface (cairo_device_t *device, |
| cairo_surface_t *recording_surface) |
| { |
| cairo_box_t bbox; |
| cairo_rectangle_int_t extents; |
| cairo_surface_t *surface; |
| cairo_status_t status; |
| |
| if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) |
| return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); |
| |
| if (unlikely (device->status)) |
| return _cairo_error (device->status); |
| |
| if (unlikely (recording_surface->status)) |
| return recording_surface->status; |
| |
| if (unlikely (! _cairo_surface_is_recording (recording_surface))) |
| return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); |
| |
| status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, |
| &bbox, NULL); |
| if (unlikely (status)) |
| return status; |
| |
| _cairo_box_round_to_rectangle (&bbox, &extents); |
| |
| surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) device, |
| recording_surface->content, |
| extents.width, |
| extents.height, |
| NULL)->base; |
| if (unlikely (surface->status)) |
| return surface->status; |
| |
| cairo_surface_set_device_offset (surface, -extents.x, -extents.y); |
| status = _cairo_recording_surface_replay (recording_surface, surface); |
| cairo_surface_destroy (surface); |
| |
| return status; |
| } |