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