diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2008-11-04 10:45:34 +0000 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2008-11-13 11:36:54 +0000 |
commit | a856371bef496da0e84226f4fd2fc3cb72e955ac (patch) | |
tree | 95f1f503c692a03aae722a28a44eb44c96143106 /src | |
parent | 47a56e08501ec9375f75c15e35a68c77b313ada4 (diff) |
Add CairoScript backend.
A new meta-surface backend for serialising drawing operations to a
CairoScript file. The principal use (as currently envisaged) is to provide
a round-trip testing mechanism for CairoScript - i.e. we can generate
script files for every test in the suite and check that we can replay them
with perfect fidelity. (Obviously this does not provide complete coverage
of CairoScript's syntax, but should give reasonable coverage over the
operators.)
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.sources | 3 | ||||
-rw-r--r-- | src/cairo-base85-stream.c | 1 | ||||
-rw-r--r-- | src/cairo-cache-private.h | 2 | ||||
-rw-r--r-- | src/cairo-cache.c | 41 | ||||
-rw-r--r-- | src/cairo-deflate-stream.c | 1 | ||||
-rw-r--r-- | src/cairo-ft-font.c | 12 | ||||
-rw-r--r-- | src/cairo-ft-private.h | 3 | ||||
-rw-r--r-- | src/cairo-gstate.c | 3 | ||||
-rw-r--r-- | src/cairo-output-stream-private.h | 19 | ||||
-rw-r--r-- | src/cairo-output-stream.c | 43 | ||||
-rw-r--r-- | src/cairo-path-fixed-private.h | 11 | ||||
-rw-r--r-- | src/cairo-path-fixed.c | 164 | ||||
-rw-r--r-- | src/cairo-pattern.c | 255 | ||||
-rw-r--r-- | src/cairo-pdf-operators.c | 1 | ||||
-rw-r--r-- | src/cairo-ps-surface.c | 2 | ||||
-rw-r--r-- | src/cairo-scaled-font-private.h | 9 | ||||
-rw-r--r-- | src/cairo-scaled-font.c | 5 | ||||
-rw-r--r-- | src/cairo-script-surface.c | 2598 | ||||
-rw-r--r-- | src/cairo-script.h | 74 | ||||
-rw-r--r-- | src/cairo-types-private.h | 1 | ||||
-rw-r--r-- | src/cairo.h | 4 | ||||
-rw-r--r-- | src/cairoint.h | 18 |
22 files changed, 3236 insertions, 34 deletions
diff --git a/src/Makefile.sources b/src/Makefile.sources index 0b44bbf2..58781dd9 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -249,3 +249,6 @@ cairo_directfb_sources = cairo-directfb-surface.c cairo_sdl_headers = cairo-sdl.h cairo_sdl_sources = cairo-sdl-surface.c + +cairo_script_headers = cairo-script.h +cairo_script_sources = cairo-script-surface.c diff --git a/src/cairo-base85-stream.c b/src/cairo-base85-stream.c index 88974039..4d5a4652 100644 --- a/src/cairo-base85-stream.c +++ b/src/cairo-base85-stream.c @@ -124,6 +124,7 @@ _cairo_base85_stream_create (cairo_output_stream_t *output) _cairo_output_stream_init (&stream->base, _cairo_base85_stream_write, + NULL, _cairo_base85_stream_close); stream->output = output; stream->pending = 0; diff --git a/src/cairo-cache-private.h b/src/cairo-cache-private.h index 6a9b8b8d..5ac8cc8b 100644 --- a/src/cairo-cache-private.h +++ b/src/cairo-cache-private.h @@ -118,7 +118,7 @@ _cairo_cache_insert (cairo_cache_t *cache, cairo_cache_entry_t *entry); cairo_private void -_cairo_cache_foreach (cairo_cache_t *cache, +_cairo_cache_foreach (cairo_cache_t *cache, cairo_cache_callback_func_t cache_callback, void *closure); diff --git a/src/cairo-cache.c b/src/cairo-cache.c index 01e5713d..f5caba4b 100644 --- a/src/cairo-cache.c +++ b/src/cairo-cache.c @@ -44,7 +44,7 @@ _cairo_cache_remove (cairo_cache_t *cache, static void _cairo_cache_shrink_to_accommodate (cairo_cache_t *cache, - unsigned long additional); + unsigned long additional); static cairo_status_t _cairo_cache_init (cairo_cache_t *cache, @@ -67,24 +67,19 @@ _cairo_cache_init (cairo_cache_t *cache, } static void -_cairo_cache_fini (cairo_cache_t *cache) +_cairo_cache_pluck (void *entry, void *closure) { - cairo_cache_entry_t *entry; - - /* We have to manually remove all entries from the cache ourselves - * rather than relying on _cairo_hash_table_destroy() to do that - * since otherwise the cache->entry_destroy callback would not get - * called on each entry. */ - - while (1) { - entry = _cairo_hash_table_random_entry (cache->hash_table, NULL); - if (entry == NULL) - break; - _cairo_cache_remove (cache, entry); - } + _cairo_cache_remove (closure, entry); +} +static void +_cairo_cache_fini (cairo_cache_t *cache) +{ + _cairo_hash_table_foreach (cache->hash_table, + _cairo_cache_pluck, + cache); + assert (cache->size == 0); _cairo_hash_table_destroy (cache->hash_table); - cache->size = 0; } /** @@ -354,8 +349,20 @@ unsigned long _cairo_hash_string (const char *c) { /* This is the djb2 hash. */ - unsigned long hash = 5381; + unsigned long hash = _CAIRO_HASH_INIT_VALUE; while (c && *c) hash = ((hash << 5) + hash) + *c++; return hash; } + +unsigned long +_cairo_hash_bytes (unsigned long hash, + const void *ptr, + unsigned int length) +{ + const uint8_t *bytes = ptr; + /* This is the djb2 hash. */ + while (length--) + hash = ((hash << 5) + hash) + *bytes++; + return hash; +} diff --git a/src/cairo-deflate-stream.c b/src/cairo-deflate-stream.c index bf2784a2..3bb884ca 100644 --- a/src/cairo-deflate-stream.c +++ b/src/cairo-deflate-stream.c @@ -128,6 +128,7 @@ _cairo_deflate_stream_create (cairo_output_stream_t *output) _cairo_output_stream_init (&stream->base, _cairo_deflate_stream_write, + NULL, _cairo_deflate_stream_close); stream->output = output; diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index 7cae0467..4245448e 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -2739,6 +2739,18 @@ _cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font) return FALSE; } +unsigned int +_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font) +{ + cairo_ft_scaled_font_t *ft_scaled_font; + + if (! _cairo_scaled_font_is_ft (scaled_font)) + return 0; + + ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font; + return ft_scaled_font->ft_options.load_flags; +} + void _cairo_ft_font_reset_static_data (void) { diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h index 3e7d3de0..00f7f77c 100644 --- a/src/cairo-ft-private.h +++ b/src/cairo-ft-private.h @@ -64,6 +64,9 @@ _cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled); cairo_private cairo_bool_t _cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font); +cairo_private unsigned int +_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font); + CAIRO_END_DECLS #endif /* CAIRO_HAS_FT_FONT */ diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 1d532c71..33880d84 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -1056,7 +1056,8 @@ _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) path, gstate->fill_rule, gstate->tolerance, - gstate->antialias, NULL); + gstate->antialias, + NULL); if (pattern == &pattern_stack.base) _cairo_pattern_fini (&pattern_stack.base); diff --git a/src/cairo-output-stream-private.h b/src/cairo-output-stream-private.h index 6f10483f..2b3d584e 100644 --- a/src/cairo-output-stream-private.h +++ b/src/cairo-output-stream-private.h @@ -43,14 +43,20 @@ #include <stdio.h> #include <stdarg.h> -typedef cairo_status_t (*cairo_output_stream_write_func_t) (cairo_output_stream_t *output_stream, - const unsigned char *data, - unsigned int length); +typedef cairo_status_t +(*cairo_output_stream_write_func_t) (cairo_output_stream_t *output_stream, + const unsigned char *data, + unsigned int length); -typedef cairo_status_t (*cairo_output_stream_close_func_t) (cairo_output_stream_t *output_stream); +typedef cairo_status_t +(*cairo_output_stream_flush_func_t) (cairo_output_stream_t *output_stream); + +typedef cairo_status_t +(*cairo_output_stream_close_func_t) (cairo_output_stream_t *output_stream); struct _cairo_output_stream { cairo_output_stream_write_func_t write_func; + cairo_output_stream_flush_func_t flush_func; cairo_output_stream_close_func_t close_func; unsigned long position; cairo_status_t status; @@ -62,6 +68,7 @@ extern const cairo_private cairo_output_stream_t _cairo_output_stream_nil; cairo_private void _cairo_output_stream_init (cairo_output_stream_t *stream, cairo_output_stream_write_func_t write_func, + cairo_output_stream_flush_func_t flush_func, cairo_output_stream_close_func_t close_func); cairo_private cairo_status_t @@ -93,6 +100,10 @@ _cairo_output_stream_create (cairo_write_func_t write_func, cairo_private cairo_output_stream_t * _cairo_output_stream_create_in_error (cairo_status_t status); +/* Tries to flush any buffer maintained by the stream or its delegates. */ +cairo_private cairo_status_t +_cairo_output_stream_flush (cairo_output_stream_t *stream); + /* Returns the final status value associated with this object, just * before its last gasp. This final status value will capture any * status failure returned by the stream's close_func as well. */ diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c index c31c2da5..9a58aac2 100644 --- a/src/cairo-output-stream.c +++ b/src/cairo-output-stream.c @@ -70,9 +70,11 @@ void _cairo_output_stream_init (cairo_output_stream_t *stream, cairo_output_stream_write_func_t write_func, + cairo_output_stream_flush_func_t flush_func, cairo_output_stream_close_func_t close_func) { stream->write_func = write_func; + stream->flush_func = flush_func; stream->close_func = close_func; stream->position = 0; stream->status = CAIRO_STATUS_SUCCESS; @@ -87,6 +89,7 @@ _cairo_output_stream_fini (cairo_output_stream_t *stream) const cairo_output_stream_t _cairo_output_stream_nil = { NULL, /* write_func */ + NULL, /* flush_func */ NULL, /* close_func */ 0, /* position */ CAIRO_STATUS_NO_MEMORY, @@ -95,6 +98,7 @@ const cairo_output_stream_t _cairo_output_stream_nil = { static const cairo_output_stream_t _cairo_output_stream_nil_write_error = { NULL, /* write_func */ + NULL, /* flush_func */ NULL, /* close_func */ 0, /* position */ CAIRO_STATUS_WRITE_ERROR, @@ -148,7 +152,8 @@ _cairo_output_stream_create (cairo_write_func_t write_func, return (cairo_output_stream_t *) &_cairo_output_stream_nil; } - _cairo_output_stream_init (&stream->base, closure_write, closure_close); + _cairo_output_stream_init (&stream->base, + closure_write, NULL, closure_close); stream->write_func = write_func; stream->close_func = close_func; stream->closure = closure; @@ -173,13 +178,37 @@ _cairo_output_stream_create_in_error (cairo_status_t status) return (cairo_output_stream_t *) &_cairo_output_stream_nil; } - _cairo_output_stream_init (stream, NULL, NULL); + _cairo_output_stream_init (stream, NULL, NULL, NULL); stream->status = status; return stream; } cairo_status_t +_cairo_output_stream_flush (cairo_output_stream_t *stream) +{ + cairo_status_t status; + + if (stream->closed) + return stream->status; + + if (stream == &_cairo_output_stream_nil || + stream == &_cairo_output_stream_nil_write_error) + { + return stream->status; + } + + if (stream->flush_func) { + status = stream->flush_func (stream); + /* Don't overwrite a pre-existing status failure. */ + if (stream->status == CAIRO_STATUS_SUCCESS) + stream->status = status; + } + + return stream->status; +} + +cairo_status_t _cairo_output_stream_close (cairo_output_stream_t *stream) { cairo_status_t status; @@ -574,7 +603,8 @@ _cairo_output_stream_create_for_file (FILE *file) return (cairo_output_stream_t *) &_cairo_output_stream_nil; } - _cairo_output_stream_init (&stream->base, stdio_write, stdio_flush); + _cairo_output_stream_init (&stream->base, + stdio_write, stdio_flush, stdio_flush); stream->file = file; return &stream->base; @@ -608,7 +638,8 @@ _cairo_output_stream_create_for_filename (const char *filename) return (cairo_output_stream_t *) &_cairo_output_stream_nil; } - _cairo_output_stream_init (&stream->base, stdio_write, stdio_close); + _cairo_output_stream_init (&stream->base, + stdio_write, stdio_flush, stdio_close); stream->file = file; return &stream->base; @@ -650,7 +681,7 @@ _cairo_memory_stream_create (void) return (cairo_output_stream_t *) &_cairo_output_stream_nil; } - _cairo_output_stream_init (&stream->base, memory_write, memory_close); + _cairo_output_stream_init (&stream->base, memory_write, NULL, memory_close); _cairo_array_init (&stream->array, 1); return &stream->base; @@ -727,7 +758,7 @@ _cairo_null_stream_create (void) return (cairo_output_stream_t *) &_cairo_output_stream_nil; } - _cairo_output_stream_init (stream, null_write, NULL); + _cairo_output_stream_init (stream, null_write, NULL, NULL); return stream; } diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h index 4a5990d4..43c33c18 100644 --- a/src/cairo-path-fixed-private.h +++ b/src/cairo-path-fixed-private.h @@ -37,6 +37,7 @@ #define CAIRO_PATH_FIXED_PRIVATE_H #include "cairo-types-private.h" +#include "cairo-compiler-private.h" enum cairo_path_op { CAIRO_PATH_OP_MOVE_TO = 0, @@ -77,4 +78,14 @@ struct _cairo_path_fixed { cairo_path_buf_fixed_t buf_head; }; +cairo_private unsigned long +_cairo_path_fixed_hash (const cairo_path_fixed_t *path); + +cairo_private unsigned long +_cairo_path_fixed_size (const cairo_path_fixed_t *path); + +cairo_private cairo_bool_t +_cairo_path_fixed_equal (const cairo_path_fixed_t *a, + const cairo_path_fixed_t *b); + #endif /* CAIRO_PATH_FIXED_PRIVATE_H */ diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c index 90861198..027ebedf 100644 --- a/src/cairo-path-fixed.c +++ b/src/cairo-path-fixed.c @@ -144,6 +144,170 @@ _cairo_path_fixed_init_copy (cairo_path_fixed_t *path, return CAIRO_STATUS_SUCCESS; } +unsigned long +_cairo_path_fixed_hash (const cairo_path_fixed_t *path) +{ + unsigned long hash = 0; + const cairo_path_buf_t *buf; + int num_points, num_ops; + + hash = _cairo_hash_bytes (hash, + &path->current_point, + sizeof (path->current_point)); + hash = _cairo_hash_bytes (hash, + &path->last_move_point, + sizeof (path->last_move_point)); + + num_ops = path->buf_head.base.num_ops; + num_points = path->buf_head.base.num_points; + for (buf = path->buf_head.base.next; + buf != NULL; + buf = buf->next) + { + hash = _cairo_hash_bytes (hash, buf->op, + buf->num_ops * sizeof (buf->op[0])); + hash = _cairo_hash_bytes (hash, buf->points, + buf->num_points * sizeof (buf->points[0])); + + num_ops += buf->num_ops; + num_points += buf->num_points; + } + + hash = _cairo_hash_bytes (hash, &num_ops, sizeof (num_ops)); + hash = _cairo_hash_bytes (hash, &num_points, sizeof (num_points)); + + return hash; +} + +unsigned long +_cairo_path_fixed_size (const cairo_path_fixed_t *path) +{ + const cairo_path_buf_t *buf; + int num_points, num_ops; + + num_ops = path->buf_head.base.num_ops; + num_points = path->buf_head.base.num_points; + for (buf = path->buf_head.base.next; + buf != NULL; + buf = buf->next) + { + num_ops += buf->num_ops; + num_points += buf->num_points; + } + + return num_ops * sizeof (buf->op[0]) + + num_points * sizeof (buf->points[0]); +} + +cairo_bool_t +_cairo_path_fixed_equal (const cairo_path_fixed_t *a, + const cairo_path_fixed_t *b) +{ + const cairo_path_buf_t *buf_a, *buf_b; + const cairo_path_op_t *ops_a, *ops_b; + const cairo_point_t *points_a, *points_b; + int num_points_a, num_ops_a; + int num_points_b, num_ops_b; + + if (a == b) + return TRUE; + + if (a != NULL) { + num_ops_a = a->buf_head.base.num_ops; + num_points_a = a->buf_head.base.num_points; + for (buf_a = a->buf_head.base.next; + buf_a != NULL; + buf_a = buf_a->next) + { + num_ops_a += buf_a->num_ops; + num_points_a += buf_a->num_points; + } + } else + num_ops_a = num_points_a = 0; + + if (b != NULL) { + num_ops_b = b->buf_head.base.num_ops; + num_points_b = b->buf_head.base.num_points; + for (buf_b = b->buf_head.base.next; + buf_b != NULL; + buf_b = buf_b->next) + { + num_ops_b += buf_b->num_ops; + num_points_b += buf_b->num_points; + } + } else + num_ops_b = num_points_b = 0; + + if (num_ops_a == 0 && num_ops_b == 0) + return TRUE; + + if (num_ops_a != num_ops_b || num_points_a != num_points_b) + return FALSE; + + assert (a != NULL && b != NULL); + + buf_a = &a->buf_head.base; + num_points_a = buf_a->num_points; + num_ops_a = buf_a->num_ops; + ops_a = buf_a->op; + points_a = buf_a->points; + + buf_b = &b->buf_head.base; + num_points_b = buf_b->num_points; + num_ops_b = buf_b->num_ops; + ops_b = buf_b->op; + points_b = buf_b->points; + + while (TRUE) { + int num_ops = MIN (num_ops_a, num_ops_b); + int num_points = MIN (num_points_a, num_points_b); + + if (memcmp (ops_a, ops_b, num_ops * sizeof (cairo_path_op_t))) + return FALSE; + if (memcmp (points_a, points_b, num_points * sizeof (cairo_point_t))) + return FALSE; + + num_ops_a -= num_ops; + ops_a += num_ops; + num_points_a -= num_points; + points_a += num_points; + if (num_ops_a == 0 || num_points_a == 0) { + if (num_ops_a || num_points_a) + return FALSE; + + buf_a = buf_a->next; + if (buf_a == NULL) + break; + + num_points_a = buf_a->num_points; + num_ops_a = buf_a->num_ops; + ops_a = buf_a->op; + points_a = buf_a->points; + } + + num_ops_b -= num_ops; + ops_b += num_ops; + num_points_b -= num_points; + points_b += num_points; + if (num_ops_b == 0 || num_points_b == 0) { + if (num_ops_b || num_points_b) + return FALSE; + + buf_b = buf_b->next; + if (buf_b == NULL) + break; + + num_points_b = buf_b->num_points; + num_ops_b = buf_b->num_ops; + ops_b = buf_b->op; + points_b = buf_b->points; + } + } + + return TRUE; +} + + cairo_path_fixed_t * _cairo_path_fixed_create (void) { diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index 610ba61b..c78317eb 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -2293,6 +2293,261 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern, return CAIRO_STATUS_SUCCESS; } + +static unsigned long +_cairo_solid_pattern_hash (unsigned long hash, + const cairo_pattern_t *pattern) +{ + const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + + hash = _cairo_hash_bytes (hash, &solid->content, sizeof (solid->content)); + hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color)); + + return hash; +} + +static unsigned long +_cairo_gradient_color_stops_hash (unsigned long hash, + const cairo_gradient_pattern_t *gradient) +{ + unsigned int n; + + hash = _cairo_hash_bytes (hash, + &gradient->n_stops, + sizeof (gradient->n_stops)); + + for (n = 0; n < gradient->n_stops; n++) { + hash = _cairo_hash_bytes (hash, + &gradient->stops[n].offset, + sizeof (double)); + hash = _cairo_hash_bytes (hash, + &gradient->stops[n].color, + sizeof (cairo_color_t)); + } + + return hash; +} + +static unsigned long +_cairo_linear_pattern_hash (unsigned long hash, + const cairo_pattern_t *pattern) +{ + const cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; + + hash = _cairo_hash_bytes (hash, &linear->p1, sizeof (linear->p1)); + hash = _cairo_hash_bytes (hash, &linear->p2, sizeof (linear->p2)); + + return _cairo_gradient_color_stops_hash (hash, &linear->base); +} + +static unsigned long +_cairo_radial_pattern_hash (unsigned long hash, const cairo_pattern_t *pattern) +{ + const cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; + + hash = _cairo_hash_bytes (hash, &radial->c1, sizeof (radial->c1)); + hash = _cairo_hash_bytes (hash, &radial->r1, sizeof (radial->r1)); + hash = _cairo_hash_bytes (hash, &radial->c2, sizeof (radial->c2)); + hash = _cairo_hash_bytes (hash, &radial->r2, sizeof (radial->r2)); + + return _cairo_gradient_color_stops_hash (hash, &radial->base); +} + +static unsigned long +_cairo_surface_pattern_hash (unsigned long hash, + const cairo_pattern_t *pattern) +{ + /* XXX requires cow-snapshots */ + return hash; +} + +unsigned long +_cairo_pattern_hash (const cairo_pattern_t *pattern) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + + if (pattern->status) + return 0; + + hash = _cairo_hash_bytes (hash, &pattern->type, sizeof (pattern->type)); + hash = _cairo_hash_bytes (hash, &pattern->matrix, sizeof (pattern->matrix)); + hash = _cairo_hash_bytes (hash, &pattern->filter, sizeof (pattern->filter)); + hash = _cairo_hash_bytes (hash, &pattern->extend, sizeof (pattern->extend)); + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_solid_pattern_hash (hash, pattern); + case CAIRO_PATTERN_TYPE_LINEAR: + return _cairo_linear_pattern_hash (hash, pattern); + case CAIRO_PATTERN_TYPE_RADIAL: + return _cairo_radial_pattern_hash (hash, pattern); + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_surface_pattern_hash (hash, pattern); + default: + ASSERT_NOT_REACHED; + return FALSE; + } +} + +static unsigned long +_cairo_gradient_pattern_color_stops_size (const cairo_pattern_t *pattern) +{ + cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; + + return gradient->n_stops * (sizeof (double) + sizeof (cairo_color_t)); +} + +unsigned long +_cairo_pattern_size (const cairo_pattern_t *pattern) +{ + if (pattern->status) + return 0; + + /* XXX */ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return sizeof (cairo_solid_pattern_t); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + return sizeof (cairo_surface_pattern_t); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + return sizeof (cairo_linear_pattern_t) + + _cairo_gradient_pattern_color_stops_size (pattern); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + return sizeof (cairo_radial_pattern_t) + + _cairo_gradient_pattern_color_stops_size (pattern); + default: + ASSERT_NOT_REACHED; + return 0; + } +} + + +static cairo_bool_t +_cairo_solid_pattern_equal (const cairo_pattern_t *A, + const cairo_pattern_t *B) +{ + const cairo_solid_pattern_t *a = (cairo_solid_pattern_t *) A; + const cairo_solid_pattern_t *b = (cairo_solid_pattern_t *) B; + + if (a->content != b->content) + return FALSE; + + return _cairo_color_equal (&a->color, &b->color); +} + +static cairo_bool_t +_cairo_gradient_color_stops_equal (const cairo_gradient_pattern_t *a, + const cairo_gradient_pattern_t *b) +{ + unsigned int n; + + if (a->n_stops != b->n_stops) + return FALSE; + + for (n = 0; n < a->n_stops; n++) { + if (a->stops[n].offset != b->stops[n].offset) + return FALSE; + if (! _cairo_color_equal (&a->stops[n].color, &b->stops[n].color)) + return FALSE; + } + + return TRUE; +} + +static cairo_bool_t +_cairo_linear_pattern_equal (const cairo_pattern_t *A, + const cairo_pattern_t *B) +{ + const cairo_linear_pattern_t *a = (cairo_linear_pattern_t *) A; + const cairo_linear_pattern_t *b = (cairo_linear_pattern_t *) B; + + if (a->p1.x != b->p1.x) + return FALSE; + + if (a->p1.y != b->p1.y) + return FALSE; + + if (a->p2.x != b->p2.x) + return FALSE; + + if (a->p2.y != b->p2.y) + return FALSE; + + return _cairo_gradient_color_stops_equal (&a->base, &b->base); +} + +static cairo_bool_t +_cairo_radial_pattern_equal (const cairo_pattern_t *A, + const cairo_pattern_t *B) +{ + const cairo_radial_pattern_t *a = (cairo_radial_pattern_t *) A; + const cairo_radial_pattern_t *b = (cairo_radial_pattern_t *) B; + + if (a->c1.x != b->c1.x) + return FALSE; + + if (a->c1.y != b->c1.y) + return FALSE; + + if (a->r1 != b->r1) + return FALSE; + + if (a->c2.x != b->c2.x) + return FALSE; + + if (a->c2.y != b->c2.y) + return FALSE; + + if (a->r2 != b->r2) + return FALSE; + + return _cairo_gradient_color_stops_equal (&a->base, &b->base); +} + +static cairo_bool_t +_cairo_surface_pattern_equal (const cairo_pattern_t *A, + const cairo_pattern_t *B) +{ + /* XXX requires cow-snapshots */ + return FALSE; +} + +cairo_bool_t +_cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b) +{ + if (a->status || b->status) + return FALSE; + + if (a->type != b->type) + return FALSE; + + if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t))) + return FALSE; + + if (a->filter != b->filter) + return FALSE; + + if (a->extend != b->extend) + return FALSE; + + switch (a->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_solid_pattern_equal (a, b); + case CAIRO_PATTERN_TYPE_LINEAR: + return _cairo_linear_pattern_equal (a, b); + case CAIRO_PATTERN_TYPE_RADIAL: + return _cairo_radial_pattern_equal (a, b); + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_surface_pattern_equal (a, b); + default: + ASSERT_NOT_REACHED; + return FALSE; + } +} + /** * cairo_pattern_get_rgba * @pattern: a #cairo_pattern_t diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c index 819318a1..f335a37e 100644 --- a/src/cairo-pdf-operators.c +++ b/src/cairo-pdf-operators.c @@ -300,6 +300,7 @@ _word_wrap_stream_create (cairo_output_stream_t *output, int max_column) _cairo_output_stream_init (&stream->base, _word_wrap_stream_write, + NULL, _word_wrap_stream_close); stream->output = output; stream->max_column = max_column; diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index 99166510..f4431fa0 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -1740,6 +1740,7 @@ _string_array_stream_create (cairo_output_stream_t *output) _cairo_output_stream_init (&stream->base, _string_array_stream_write, + NULL, _string_array_stream_close); stream->output = output; stream->column = 0; @@ -1766,6 +1767,7 @@ _base85_array_stream_create (cairo_output_stream_t *output) _cairo_output_stream_init (&stream->base, _string_array_stream_write, + NULL, _string_array_stream_close); stream->output = output; stream->column = 0; diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h index c1d87ae0..86a50bbb 100644 --- a/src/cairo-scaled-font-private.h +++ b/src/cairo-scaled-font-private.h @@ -94,10 +94,11 @@ struct _cairo_scaled_font { cairo_bool_t finished; /* "live" scaled_font members */ - cairo_matrix_t scale; /* font space => device space */ - cairo_matrix_t scale_inverse; /* device space => font space */ - double max_scale; /* maximum x/y expansion of scale */ - cairo_font_extents_t extents; /* user space */ + cairo_matrix_t scale; /* font space => device space */ + cairo_matrix_t scale_inverse; /* device space => font space */ + double max_scale; /* maximum x/y expansion of scale */ + cairo_font_extents_t extents; /* user space */ + cairo_font_extents_t fs_extents; /* font space */ /* The mutex protects modification to all subsequent fields. */ cairo_mutex_t mutex; diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c index aa1a2c8a..8b749940 100644 --- a/src/cairo-scaled-font.c +++ b/src/cairo-scaled-font.c @@ -201,6 +201,7 @@ static const cairo_scaled_font_t _cairo_scaled_font_nil = { { 1., 0., 0., 1., 0, 0}, /* scale_inverse */ 1., /* max_scale */ { 0., 0., 0., 0., 0. }, /* extents */ + { 0., 0., 0., 0., 0. }, /* fs_extents */ CAIRO_MUTEX_NIL_INITIALIZER,/* mutex */ NULL, /* glyphs */ NULL, /* surface_backend */ @@ -691,6 +692,8 @@ _cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, cairo_status_t status; double font_scale_x, font_scale_y; + scaled_font->fs_extents = *fs_metrics; + status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->font_matrix, &font_scale_x, &font_scale_y, 1); @@ -2210,6 +2213,8 @@ _cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph, double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0; double device_x_advance, device_y_advance; + scaled_glyph->fs_metrics = *fs_metrics; + for (hm = 0.0; hm <= 1.0; hm += 1.0) for (wm = 0.0; wm <= 1.0; wm += 1.0) { double x, y; diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c new file mode 100644 index 00000000..9118d66d --- /dev/null +++ b/src/cairo-script-surface.c @@ -0,0 +1,2598 @@ +/* -*- 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 meta-surface, but as there is no impedance mismatch + * between Cairo and CairoScript, we can generate output immediately + * without having to copy and hold the data in memory. + */ + +#include "cairoint.h" + +#include "cairo-script.h" + +#include "cairo-analysis-surface-private.h" +#include "cairo-ft-private.h" +#include "cairo-meta-surface-private.h" +#include "cairo-output-stream-private.h" + +#define _cairo_output_stream_puts(S, STR) \ + _cairo_output_stream_write ((S), (STR), strlen (STR)) + +#define static cairo_warn static + +typedef struct _cairo_script_vmcontext cairo_script_vmcontext_t; +typedef struct _cairo_script_surface cairo_script_surface_t; +typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t; +typedef struct _cairo_script_surface_font_private cairo_script_surface_font_private_t; + +struct _cairo_script_vmcontext { + int ref; + + 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_script_surface_t *current_target; + + cairo_script_surface_font_private_t *fonts; +}; + +struct _cairo_script_surface_font_private { + cairo_script_vmcontext_t *ctx; + cairo_bool_t has_sfnt; + unsigned long id; + unsigned long subset_glyph_index; + cairo_script_surface_font_private_t *prev, *next; + 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_t *current_source; + cairo_matrix_t current_ctm; + 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; +}; + +struct _cairo_script_surface { + cairo_surface_t base; + + cairo_script_vmcontext_t *ctx; + + unsigned long id; + + 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_vmcontext_t *ctx, + double width, + double height); + +static void +_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +static void +_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr); + +static void +_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 (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 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 */ + }; + assert (op < ARRAY_LENGTH (names)); + return names[op]; +} + +static const char * +_extend_to_string (cairo_extend_t extend) +{ + static const char *names[] = { + "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ + "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */ + "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */ + "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ + }; + assert (extend < ARRAY_LENGTH (names)); + return names[extend]; +} + +static const char * +_filter_to_string (cairo_filter_t filter) +{ + static const char *names[] = { + "FILTER_FAST", /* CAIRO_FILTER_FAST */ + "FILTER_GOOD", /* CAIRO_FILTER_GOOD */ + "FILTER_BEST", /* CAIRO_FILTER_BEST */ + "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ + "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ + "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ + }; + assert (filter < ARRAY_LENGTH (names)); + return names[filter]; +} + +static const char * +_fill_rule_to_string (cairo_fill_rule_t rule) +{ + static const char *names[] = { + "WINDING", /* CAIRO_FILL_RULE_WINDING */ + "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ + }; + assert (rule < ARRAY_LENGTH (names)); + return names[rule]; +} + +static const char * +_antialias_to_string (cairo_antialias_t antialias) +{ + static const char *names[] = { + "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ + "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ + "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ + "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */ + }; + assert (antialias < ARRAY_LENGTH (names)); + return names[antialias]; +} + +static const char * +_line_cap_to_string (cairo_line_cap_t line_cap) +{ + static const char *names[] = { + "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ + "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ + "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ + }; + assert (line_cap < ARRAY_LENGTH (names)); + return names[line_cap]; +} + +static const char * +_line_join_to_string (cairo_line_join_t line_join) +{ + static const char *names[] = { + "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ + "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ + "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ + }; + assert (line_join < ARRAY_LENGTH (names)); + return names[line_join]; +} + +static cairo_bool_t +_cairo_script_surface_owns_context (cairo_script_surface_t *surface) +{ + return surface->ctx->current_target == surface; +} + +static cairo_status_t +_emit_context (cairo_script_surface_t *surface) +{ + if (_cairo_script_surface_owns_context (surface)) + return CAIRO_STATUS_SUCCESS; + + if (surface->ctx->current_target != NULL) + _cairo_output_stream_puts (surface->ctx->stream, "pop\n"); + + surface->ctx->current_target = surface; + + if (surface->id == (unsigned long) -1) { + cairo_status_t status; + + status = _bitmap_next_id (&surface->ctx->surface_id, + &surface->id); + if (status) + return status; + + _cairo_output_stream_printf (surface->ctx->stream, + "dict\n" + " /width %f set\n" + " /height %f set\n", + 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 (surface->ctx->stream, + " /fallback-resolution [%f %f] set\n", + 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. */ + _cairo_output_stream_printf (surface->ctx->stream, + " %%/device-offset [%f %f] set\n", + surface->base.device_transform.x0, + surface->base.device_transform.y0); + } + _cairo_output_stream_printf (surface->ctx->stream, + " surface dup /s%lu exch def\n" + "context dup /c%lu exch def\n", + surface->id, + surface->id); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + "c%lu\n", + surface->id); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_operator (cairo_script_surface_t *surface, + cairo_operator_t op) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (surface->cr.current_operator == op) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_operator = op; + + _cairo_output_stream_printf (surface->ctx->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 (_cairo_script_surface_owns_context (surface)); + + if (surface->cr.current_fill_rule == fill_rule) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_fill_rule = fill_rule; + + _cairo_output_stream_printf (surface->ctx->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 (_cairo_script_surface_owns_context (surface)); + + if (! force && surface->cr.current_tolerance == tolerance) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_tolerance = tolerance; + + _cairo_output_stream_printf (surface->ctx->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 (_cairo_script_surface_owns_context (surface)); + + if (surface->cr.current_antialias == antialias) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_antialias = antialias; + + _cairo_output_stream_printf (surface->ctx->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 (_cairo_script_surface_owns_context (surface)); + + if (! force && surface->cr.current_style.line_width == line_width) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_style.line_width = line_width; + + _cairo_output_stream_printf (surface->ctx->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 (_cairo_script_surface_owns_context (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 (surface->ctx->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 (_cairo_script_surface_owns_context (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 (surface->ctx->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 (_cairo_script_surface_owns_context (surface)); + + if (! force && surface->cr.current_style.miter_limit == miter_limit) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_style.miter_limit = miter_limit; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f set_miter_limit\n", + miter_limit); + return CAIRO_STATUS_SUCCESS; +} + +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 (_cairo_script_surface_owns_context (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 || + (surface->cr.current_style.dash_offset == offset && + memcmp (surface->cr.current_style.dash, dash, + sizeof (double) * 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)); + memcpy (surface->cr.current_style.dash, dash, + sizeof (double) * num_dashes); + } else { + if (surface->cr.current_style.dash != NULL) { + free (surface->cr.current_style.dash); + surface->cr.current_style.dash = NULL; + } + } + + surface->cr.current_style.num_dashes = num_dashes; + surface->cr.current_style.dash_offset = offset; + + _cairo_output_stream_printf (surface->ctx->stream, "["); + for (n = 0; n < num_dashes; n++) { + _cairo_output_stream_printf (surface->ctx->stream, "%f", dash[n]); + if (n < num_dashes-1) + _cairo_output_stream_puts (surface->ctx->stream, " "); + } + _cairo_output_stream_printf (surface->ctx->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 (_cairo_script_surface_owns_context (surface)); + + status = _emit_line_width (surface, style->line_width, force); + if (status) + return status; + + status = _emit_line_cap (surface, style->line_cap); + if (status) + return status; + + status = _emit_line_join (surface, style->line_join); + if (status) + return status; + + status = _emit_miter_limit (surface, style->miter_limit, force); + if (status) + return status; + + status = _emit_dash (surface, + style->dash, style->num_dashes, style->dash_offset, + force); + if (status) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static const char * +_format_to_string (cairo_format_t format) +{ + static const char *names[] = { + "ARGB32", /* CAIRO_FORMAT_ARGB32 */ + "RGB24", /* CAIRO_FORMAT_RGB24 */ + "A8", /* CAIRO_FORMAT_A8 */ + "A1" /* CAIRO_FORMAT_A1 */ + }; + assert (format < ARRAY_LENGTH (names)); + return names[format]; +} + +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; + + if (solid->content & CAIRO_CONTENT_ALPHA && + ! CAIRO_COLOR_IS_OPAQUE (&solid->color)) + { + if (! (solid->content & CAIRO_CONTENT_COLOR) || + (solid->color.red_short == 0 && + solid->color.green_short == 0 && + solid->color.blue_short == 0)) + { + _cairo_output_stream_printf (surface->ctx->stream, + "%f a", + solid->color.alpha); + } + else + { + _cairo_output_stream_printf (surface->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 (surface->ctx->stream, + "%f g", + solid->color.red); + } + else + { + _cairo_output_stream_printf (surface->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, + " %f %f %f %f %f add_color_stop\n ", + 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_linear_pattern_t *linear; + + linear = (cairo_linear_pattern_t *) pattern; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f %f %f %f linear\n ", + _cairo_fixed_to_double (linear->p1.x), + _cairo_fixed_to_double (linear->p1.y), + _cairo_fixed_to_double (linear->p2.x), + _cairo_fixed_to_double (linear->p2.y)); + return _emit_gradient_color_stops (&linear->base, surface->ctx->stream); +} + +static cairo_status_t +_emit_radial_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_radial_pattern_t *radial; + + radial = (cairo_radial_pattern_t *) pattern; + + _cairo_output_stream_printf (surface->ctx->stream, + "%f %f %f %f %f %f radial\n ", + _cairo_fixed_to_double (radial->c1.x), + _cairo_fixed_to_double (radial->c1.y), + _cairo_fixed_to_double (radial->r1), + _cairo_fixed_to_double (radial->c2.x), + _cairo_fixed_to_double (radial->c2.y), + _cairo_fixed_to_double (radial->r2)); + return _emit_gradient_color_stops (&radial->base, surface->ctx->stream); +} + +static cairo_status_t +_emit_meta_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_pattern_t *surface_pattern; + cairo_surface_t *source; + cairo_surface_t *null_surface; + cairo_surface_t *analysis_surface; + cairo_surface_t *similar; + cairo_status_t status; + cairo_box_t bbox; + + surface_pattern = (cairo_surface_pattern_t *) pattern; + source = surface_pattern->surface; + + /* first measure the extents */ + null_surface = _cairo_null_surface_create (source->content); + analysis_surface = _cairo_analysis_surface_create (null_surface, -1, -1); + cairo_surface_destroy (null_surface); + + status = analysis_surface->status; + if (status) + return status; + + status = _cairo_meta_surface_replay (source, analysis_surface); + _cairo_analysis_surface_get_bounding_box (analysis_surface, &bbox); + cairo_surface_destroy (analysis_surface); + if (status) + return status; + + similar = cairo_surface_create_similar (&surface->base, + source->content, + _cairo_fixed_to_double (bbox.p2.x-bbox.p1.x), + _cairo_fixed_to_double (bbox.p2.y-bbox.p1.y)); + if (similar->status) + return similar->status; + + status = _cairo_meta_surface_replay (source, similar); + if (status) { + cairo_surface_destroy (similar); + return status; + } + + status = _emit_context (surface); + if (status) { + cairo_surface_destroy (similar); + return status; + } + + _cairo_output_stream_printf (surface->ctx->stream, + "s%lu pattern\n ", + ((cairo_script_surface_t *) similar)->id); + cairo_surface_destroy (similar); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_script_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_pattern_t *surface_pattern; + cairo_script_surface_t *source; + + surface_pattern = (cairo_surface_pattern_t *) pattern; + source = (cairo_script_surface_t *) surface_pattern->surface; + + _cairo_output_stream_printf (surface->ctx->stream, + "s%lu pattern\n ", source->id); + + 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_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; + default: + ASSERT_NOT_REACHED; + break; + } +#else + if (stride > ARRAY_LENGTH (row_stack)) { + rowdata = malloc (stride); + if (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_RGB24: + for (row = image->height; row--; ) { + uint8_t *src = data; + int col; + for (col = 0; col < width; col++) { + rowdata[3*col+2] = *src++; + rowdata[3*col+1] = *src++; + rowdata[3*col+0] = *src++; + src++; + } + _cairo_output_stream_write (output, rowdata, 3*width); + data += stride; + } + break; + case CAIRO_FORMAT_ARGB32: + for (row = image->height; row--; ) { + uint32_t *src = (uint32_t *) data; + uint32_t *dst = (uint32_t *) rowdata; + int col; + for (col = 0; col < width; col++) + dst[col] = bswap_32 (src[col]); + _cairo_output_stream_write (output, rowdata, 4*width); + data += stride; + } + break; + 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_output_stream_t *base85_stream; + cairo_status_t status; + const uint8_t *mime_data; + unsigned int 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 (surface->ctx->stream, + "dict\n" + " /width %d set\n" + " /height %d set\n" + " /format //%s set\n" + " /mime-type (image/png) set\n" + " /source <~", + image->width, image->height, + _format_to_string (image->format)); + + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (status) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + " set\n image"); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_image_surface (cairo_script_surface_t *surface, + cairo_image_surface_t *image) +{ + cairo_output_stream_t *base85_stream; + cairo_output_stream_t *zlib_stream; + cairo_status_t status, status2; + const uint8_t *mime_data; + unsigned int mime_data_length; + + status = _emit_png_surface (surface, image); + if (_cairo_status_is_error (status)) { + return status; + } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + _cairo_output_stream_printf (surface->ctx->stream, + "dict\n" + " /width %d set\n" + " /height %d set\n" + " /format //%s set\n" + " /source <~", + image->width, image->height, + _format_to_string (image->format)); + + if (image->width * image->height > 8) { + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + zlib_stream = _cairo_deflate_stream_create (base85_stream); + + status = _write_image_surface (zlib_stream, image); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + if (status) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + " /deflate filter set\n image"); + } else { + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + status = _write_image_surface (base85_stream, image); + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + _cairo_output_stream_puts (surface->ctx->stream, + " set\n image"); + } + } + + cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (mime_data != NULL) { + _cairo_output_stream_printf (surface->ctx->stream, + "\n (%s) <~", + CAIRO_MIME_TYPE_JPEG); + + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (status) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + " set_mime_data\n "); + } + + _cairo_output_stream_puts (surface->ctx->stream, + " pattern\n "); + + return status; +} + +static cairo_status_t +_emit_image_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_pattern_t *surface_pattern; + cairo_surface_t *source; + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + + surface_pattern = (cairo_surface_pattern_t *) pattern; + source = surface_pattern->surface; + + /* XXX snapshot-cow */ + status = _cairo_surface_acquire_source_image (source, &image, &image_extra); + if (status) + return status; + + status = _emit_image_surface (surface, image); + + _cairo_surface_release_source_image (source, image, image_extra); + + return status; +} + +static cairo_status_t +_emit_surface_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_surface_pattern_t *surface_pattern; + cairo_surface_t *source; + + surface_pattern = (cairo_surface_pattern_t *) pattern; + source = surface_pattern->surface; + + switch ((int) source->type) { + case CAIRO_INTERNAL_SURFACE_TYPE_META: + return _emit_meta_surface_pattern (surface, pattern); + case CAIRO_SURFACE_TYPE_SCRIPT: + return _emit_script_surface_pattern (surface, pattern); + default: + return _emit_image_surface_pattern (surface, pattern); + } +} + +static cairo_status_t +_emit_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_status_t status; + + 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); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + status = _emit_radial_pattern (surface, pattern); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + status = _emit_surface_pattern (surface, pattern); + break; + + default: + ASSERT_NOT_REACHED; + status = CAIRO_INT_STATUS_UNSUPPORTED; + } + if (status) + return status; + + if (! _cairo_matrix_is_identity (&pattern->matrix)) { + _cairo_output_stream_printf (surface->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); + } + + _cairo_output_stream_printf (surface->ctx->stream, + " //%s set_extend\n " + " //%s set_filter\n ", + _extend_to_string (pattern->extend), + _filter_to_string (pattern->filter)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_identity (cairo_script_surface_t *surface, + cairo_bool_t *matrix_updated) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) + return CAIRO_STATUS_SUCCESS; + + _cairo_output_stream_puts (surface->ctx->stream, + "identity set_matrix\n"); + + *matrix_updated = TRUE; + cairo_matrix_init_identity (&surface->cr.current_ctm); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_source (cairo_script_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source) +{ + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + assert (_cairo_script_surface_owns_context (surface)); + + if (op == CAIRO_OPERATOR_CLEAR) { + /* the source is ignored, so don't change it */ + return CAIRO_STATUS_SUCCESS; + } + + if (surface->cr.current_source == source) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_pattern_equal (surface->cr.current_source, source)) + return CAIRO_STATUS_SUCCESS; + + cairo_pattern_destroy (surface->cr.current_source); + status = _cairo_pattern_create_copy (&surface->cr.current_source, + source); + if (status) + return status; + + status = _emit_identity (surface, &matrix_updated); + if (status) + return status; + + status = _emit_pattern (surface, source); + if (status) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + " set_source\n"); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_move_to (void *closure, 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, 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, + cairo_point_t *p1, + cairo_point_t *p2, + cairo_point_t *p3) +{ + _cairo_output_stream_printf (closure, + " %f %f %f %f %f %f c", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y), + _cairo_fixed_to_double (p2->x), + _cairo_fixed_to_double (p2->y), + _cairo_fixed_to_double (p3->x), + _cairo_fixed_to_double (p3->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_close (void *closure) +{ + _cairo_output_stream_printf (closure, + " h"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_path (cairo_script_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_box_t box; + cairo_status_t status; + + assert (_cairo_script_surface_owns_context (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 (surface->ctx->stream, "n"); + + if (path == NULL) { + _cairo_path_fixed_init (&surface->cr.current_path); + } else 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); + + status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); + if (status) + return status; + + _cairo_output_stream_printf (surface->ctx->stream, + " %f %f %f %f rectangle", + x1, y1, x2 - x1, y2 - y1); + } else { + cairo_status_t status; + + status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path); + if (status) + return status; + + status = _cairo_path_fixed_interpret (path, + CAIRO_DIRECTION_FORWARD, + _path_move_to, + _path_line_to, + _path_curve_to, + _path_close, + surface->ctx->stream); + if (status) + return status; + } + + _cairo_output_stream_puts (surface->ctx->stream, "\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_matrix (cairo_script_surface_t *surface, + const cairo_matrix_t *ctm, + cairo_bool_t *matrix_updated) +{ + assert (_cairo_script_surface_owns_context (surface)); + + if (memcmp (&surface->cr.current_ctm, ctm, sizeof (cairo_matrix_t)) == 0) + return CAIRO_STATUS_SUCCESS; + + *matrix_updated = TRUE; + surface->cr.current_ctm = *ctm; + + if (_cairo_matrix_is_identity (ctm)) { + _cairo_output_stream_puts (surface->ctx->stream, + "identity set_matrix\n"); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + "[%f %f %f %f %f %f] set_matrix\n", + ctm->xx, ctm->yx, + ctm->xy, ctm->yy, + ctm->x0, ctm->y0); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_font_matrix (cairo_script_surface_t *surface, + const cairo_matrix_t *font_matrix) +{ + assert (_cairo_script_surface_owns_context (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 (surface->ctx->stream, + "identity set_font_matrix\n"); + } else { + _cairo_output_stream_printf (surface->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 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_surface_t * +_cairo_script_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_script_surface_t *surface, *other; + cairo_script_vmcontext_t *ctx; + cairo_status_t status; + + other = abstract_surface; + ctx = other->ctx; + + if (other->id == (unsigned long) -1) { + cairo_status_t status; + + status = _bitmap_next_id (&ctx->surface_id, + &other->id); + if (status) + return _cairo_surface_create_in_error (status); + + _cairo_output_stream_printf (ctx->stream, + "dict\n" + " /width %f set\n" + " /height %f set\n" + " surface dup /s%lu exch def\n" + "context /c%lu exch def\n", + other->width, + other->height, + other->id, + other->id); + } + + + surface = _cairo_script_surface_create_internal (ctx, width, height); + if (surface->base.status) + return &surface->base; + + status = _bitmap_next_id (&ctx->surface_id, + &surface->id); + if (status) { + cairo_surface_destroy (&surface->base); + return _cairo_surface_create_in_error (status); + } + + if (ctx->current_target != NULL) + _cairo_output_stream_printf (ctx->stream, "pop\n"); + + _cairo_output_stream_printf (ctx->stream, + "s%lu %u %u //%s similar dup /s%lu exch def\n" + "context dup /c%lu exch def\n", + other->id, width, height, + _content_to_string (content), + surface->id, + surface->id); + + ctx->current_target = surface; + + return &surface->base; +} + +static cairo_status_t +_vmcontext_destroy (cairo_script_vmcontext_t *ctx) +{ + cairo_status_t status; + + if (--ctx->ref) + return _cairo_output_stream_flush (ctx->stream); + + while (ctx->fonts != NULL ){ + cairo_script_surface_font_private_t *font = ctx->fonts; + ctx->fonts = font->next; + _cairo_script_surface_scaled_font_fini (font->parent); + } + + status = _cairo_output_stream_destroy (ctx->stream); + + free (ctx); + + return status; +} + +static cairo_status_t +_cairo_script_surface_finish (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + cairo_pattern_destroy (surface->cr.current_source); + _cairo_path_fixed_fini (&surface->cr.current_path); + + if (surface->ctx->current_target == surface) { + _cairo_output_stream_printf (surface->ctx->stream, + "pop\n"); + surface->ctx->current_target = NULL; + } + + _cairo_output_stream_printf (surface->ctx->stream, + "/c%lu undef\n" + "/s%lu undef\n", + surface->id, + surface->id); + + _bitmap_release_id (&surface->ctx->surface_id, surface->id); + + status = _vmcontext_destroy (surface->ctx); + + 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 = _emit_context (surface); + if (status) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "copy_page\n"); + + return CAIRO_STATUS_SUCCESS; +} + +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 = _emit_context (surface); + if (status) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "show_page\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_intersect_clip_path (void *abstract_surface, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + status = _emit_context (surface); + if (status) + return status; + + if (path == NULL) { + _cairo_output_stream_puts (surface->ctx->stream, "reset_clip\n"); + return CAIRO_STATUS_SUCCESS; + } + + status = _emit_identity (surface, &matrix_updated); + if (status) + return status; + + status = _emit_fill_rule (surface, fill_rule); + if (status) + return status; + + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (status) + return status; + + status = _emit_antialias (surface, antialias); + if (status) + return status; + + status = _emit_path (surface, path); + if (status) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "clip+\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_rectangle_int_t *extents) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = _emit_context (surface); + if (status) + return status; + + status = _emit_operator (surface, op); + if (status) + return status; + + status = _emit_source (surface, op, source); + if (status) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + "paint\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + cairo_rectangle_int_t *extents) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = _emit_context (surface); + if (status) + return status; + + status = _emit_operator (surface, op); + if (status) + return status; + + status = _emit_source (surface, op, source); + if (status) + return status; + + status = _emit_pattern (surface, mask); + if (status) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, + " mask\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + status = _emit_context (surface); + if (status) + return status; + + status = _emit_identity (surface, &matrix_updated); + if (status) + return status; + + status = _emit_path (surface, path); + if (status) + return status; + + status = _emit_source (surface, op, source); + if (status) + return status; + + status = _emit_matrix (surface, ctm, &matrix_updated); + if (status) + return status; + + status = _emit_operator (surface, op); + if (status) + return status; + + status = _emit_stroke_style (surface, style, matrix_updated); + if (status) + return status; + + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (status) + return status; + + status = _emit_antialias (surface, antialias); + if (status) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "stroke+\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_rectangle_int_t *extents) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + status = _emit_context (surface); + if (status) + return status; + + status = _emit_operator (surface, op); + if (status) + return status; + + status = _emit_identity (surface, &matrix_updated); + if (status) + return status; + + status = _emit_source (surface, op, source); + if (status) + return status; + + status = _emit_fill_rule (surface, fill_rule); + if (status) + return status; + + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (status) + return status; + + status = _emit_antialias (surface, antialias); + if (status) + return status; + + status = _emit_path (surface, path); + if (status) + return status; + + _cairo_output_stream_puts (surface->ctx->stream, "fill+\n"); + + return CAIRO_STATUS_SUCCESS; +} + +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) +{ + if (cairo_font_options_equal (&surface->cr.current_font_options, + font_options)) + { + return CAIRO_STATUS_SUCCESS; + } + + _cairo_output_stream_printf (surface->ctx->stream, "dict\n"); + + if (font_options->antialias != surface->cr.current_font_options.antialias) { + _cairo_output_stream_printf (surface->ctx->stream, + " /antialias //%s set\n", + _antialias_to_string (font_options->antialias)); + } + + if (font_options->subpixel_order != + surface->cr.current_font_options.subpixel_order) + { + _cairo_output_stream_printf (surface->ctx->stream, + " /subpixel-order //%s set\n", + _subpixel_order_to_string (font_options->subpixel_order)); + } + + if (font_options->hint_style != + surface->cr.current_font_options.hint_style) + { + _cairo_output_stream_printf (surface->ctx->stream, + " /hint-style //%s set\n", + _hint_style_to_string (font_options->hint_style)); + } + + if (font_options->hint_metrics != + surface->cr.current_font_options.hint_metrics) + { + _cairo_output_stream_printf (surface->ctx->stream, + " /hint-metrics //%s set\n", + _hint_metrics_to_string (font_options->hint_metrics)); + } + + _cairo_output_stream_printf (surface->ctx->stream, + " set_font_options\n"); + + surface->cr.current_font_options = *font_options; + + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + cairo_script_surface_font_private_t *font_private; + + font_private = scaled_font->surface_private; + if (font_private != NULL) { + _cairo_output_stream_printf (font_private->ctx->stream, + "/f%lu undef\n", + font_private->id); + + _bitmap_release_id (&font_private->ctx->font_id, font_private->id); + + if (font_private->prev != NULL) + font_private->prev = font_private->next; + else + font_private->ctx->fonts = font_private->next; + + if (font_private->next != NULL) + font_private->next = font_private->prev; + + free (font_private); + + scaled_font->surface_private = NULL; + } +} + +static cairo_status_t +_emit_type42_font (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + const cairo_scaled_font_backend_t *backend; + cairo_script_surface_font_private_t *font_private; + cairo_output_stream_t *base85_stream; + cairo_output_stream_t *zlib_stream; + cairo_status_t status, status2; + unsigned long size; + unsigned int load_flags; + 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 (status) + return status; + + buf = malloc (size); + if (buf == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, 0, 0, buf, NULL); + if (status) { + free (buf); + return status; + } + + load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font); + _cairo_output_stream_printf (surface->ctx->stream, + "dict\n" + " /type 42 set\n" + " /size %lu set\n" + " /index 0 set\n" + " /flags %d set\n" + " /source <~", + size, load_flags); + + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + zlib_stream = _cairo_deflate_stream_create (base85_stream); + + _cairo_output_stream_write (zlib_stream, buf, size); + free (buf); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + font_private = scaled_font->surface_private; + _cairo_output_stream_printf (surface->ctx->stream, + " /deflate filter set\n" + " font dup /f%lu exch def set_font_face\n", + font_private->id); + + return status; +} + +static cairo_status_t +_emit_scaled_font_init (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_surface_font_private_t *font_private; + cairo_status_t status; + + font_private = malloc (sizeof (cairo_script_surface_font_private_t)); + if (font_private == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font_private->ctx = surface->ctx; + font_private->parent = scaled_font; + font_private->subset_glyph_index = 0; + font_private->has_sfnt = TRUE; + + font_private->next = font_private->ctx->fonts; + font_private->prev = NULL; + if (font_private->ctx->fonts != NULL) + font_private->ctx->fonts->prev = font_private; + font_private->ctx->fonts = font_private; + + status = _bitmap_next_id (&surface->ctx->font_id, + &font_private->id); + if (status) { + free (font_private); + return status; + } + + scaled_font->surface_private = font_private; + scaled_font->surface_backend = &_cairo_script_surface_backend; + + status = _emit_context (surface); + if (status) + return status; + + status = _emit_type42_font (surface, scaled_font); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + font_private->has_sfnt = FALSE; + _cairo_output_stream_printf (surface->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\n", + scaled_font->fs_extents.ascent, + scaled_font->fs_extents.descent, + scaled_font->fs_extents.height, + scaled_font->fs_extents.max_x_advance, + scaled_font->fs_extents.max_y_advance, + font_private->id); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_font (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_matrix_t matrix; + cairo_font_options_t options; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + cairo_script_surface_font_private_t *font_private; + + cairo_scaled_font_get_ctm (scaled_font, &matrix); + status = _emit_matrix (surface, &matrix, &matrix_updated); + if (status) + return status; + + if (! matrix_updated && surface->cr.current_scaled_font == scaled_font) + return CAIRO_STATUS_SUCCESS; + + cairo_scaled_font_get_font_matrix (scaled_font, &matrix); + status = _emit_font_matrix (surface, &matrix); + if (status) + return status; + + cairo_scaled_font_get_font_options (scaled_font, &options); + status = _emit_font_options (surface, &options); + if (status) + return status; + + surface->cr.current_scaled_font = scaled_font; + + assert (scaled_font->surface_backend == NULL || + scaled_font->surface_backend == &_cairo_script_surface_backend); + + font_private = scaled_font->surface_private; + if (font_private == NULL) { + status = _emit_scaled_font_init (surface, scaled_font); + if (status) + return status; + } else { + status = _emit_context (surface); + if (status) + return status; + + _cairo_output_stream_printf (surface->ctx->stream, + "f%lu set_font_face\n", + font_private->id); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyph_vector (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_script_surface_font_private_t *font_private; + cairo_script_implicit_context_t old_cr; + cairo_status_t status; + unsigned long index; + + font_private = scaled_font->surface_private; + index = ++font_private->subset_glyph_index; + scaled_glyph->surface_private = (void *) index; + + _cairo_output_stream_printf (surface->ctx->stream, + "%lu dict\n" + " /metrics [%f %f %f %f %f %f] set\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 (surface->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_meta_surface_replay (scaled_glyph->meta_surface, + &surface->base); + surface->cr = old_cr; + + _cairo_output_stream_puts (surface->ctx->stream, "} set set\n"); + + return status; +} + +static cairo_status_t +_emit_scaled_glyph_bitmap (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_script_surface_font_private_t *font_private; + cairo_status_t status; + unsigned long index; + + font_private = scaled_font->surface_private; + index = ++font_private->subset_glyph_index; + scaled_glyph->surface_private = (void *) index; + + _cairo_output_stream_printf (surface->ctx->stream, + "%lu dict\n" + " /metrics [%f %f %f %f %f %f] set\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 (status) + return status; + + if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) { + _cairo_output_stream_printf (surface->ctx->stream, + " [%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 (surface->ctx->stream, + "mask\n} set set\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyph_prologue (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_surface_font_private_t *font_private; + + assert (scaled_font->surface_backend == &_cairo_script_surface_backend); + + font_private = scaled_font->surface_private; + + _cairo_output_stream_printf (surface->ctx->stream, + "f%lu /glyphs get\n", + font_private->id); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyphs (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + unsigned int num_glyphs) +{ + cairo_script_surface_font_private_t *font_private; + cairo_status_t status; + unsigned int n; + cairo_bool_t have_glyph_prologue = FALSE; + + if (num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + + font_private = scaled_font->surface_private; + if (font_private->has_sfnt) + return CAIRO_STATUS_SUCCESS; + + _cairo_scaled_font_freeze_cache (scaled_font); + for (n = 0; n < num_glyphs; n++) { + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (status) + break; + + if (scaled_glyph->surface_private != NULL) + continue; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_META_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 (status) + break; + + have_glyph_prologue = TRUE; + } + + status = _emit_scaled_glyph_vector (surface, + scaled_font, + scaled_glyph); + if (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 (status) + break; + + have_glyph_prologue = TRUE; + } + + status = _emit_scaled_glyph_bitmap (surface, + scaled_font, + scaled_glyph); + if (status) + break; + + continue; + } + } + _cairo_scaled_font_thaw_cache (scaled_font); + + if (have_glyph_prologue) { + _cairo_output_stream_puts (surface->ctx->stream, "pop pop\n"); + } + + return status; +} + +static cairo_int_status_t +_cairo_script_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t backward, + cairo_scaled_font_t *scaled_font, + cairo_rectangle_int_t *extents) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_script_surface_font_private_t *font_private; + cairo_scaled_glyph_t *scaled_glyph; + cairo_matrix_t matrix; + cairo_status_t status; + double x, y, ix, iy; + int n; + cairo_output_stream_t *base85_stream = NULL; + + status = _emit_context (surface); + if (status) + return status; + + status = _emit_operator (surface, op); + if (status) + return status; + + status = _emit_source (surface, op, source); + if (status) + return status; + + status = _emit_scaled_font (surface, scaled_font); + if (status) + return status; + + status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs); + if (status) + return status; + + /* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */ + /* [cx cy [glyphs]] show_glyphs */ + + if (utf8 != NULL && clusters != NULL) { + _cairo_output_stream_printf (surface->ctx->stream, + "(%s) ", + utf8); + } + + matrix = surface->cr.current_ctm; + status = cairo_matrix_invert (&matrix); + assert (status == CAIRO_STATUS_SUCCESS); + + ix = x = glyphs[0].x; + iy = y = glyphs[0].y; + cairo_matrix_transform_point (&matrix, &ix, &iy); + ix -= scaled_font->font_matrix.x0; + iy -= scaled_font->font_matrix.y0; + + _cairo_scaled_font_freeze_cache (scaled_font); + font_private = scaled_font->surface_private; + + _cairo_output_stream_printf (surface->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 (status) { + _cairo_scaled_font_thaw_cache (scaled_font); + return status; + } + } + } + + if (n == num_glyphs) { + _cairo_output_stream_puts (surface->ctx->stream, "<~"); + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + } else + _cairo_output_stream_puts (surface->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 (status) + break; + + if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) { + 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 (status) { + base85_stream = NULL; + break; + } + + _cairo_output_stream_printf (surface->ctx->stream, + " %f %f <~", + ix, iy); + base85_stream = _cairo_base85_stream_create (surface->ctx->stream); + } else { + _cairo_output_stream_printf (surface->ctx->stream, + " ] %f %f [ ", + ix, iy); + } + } + if (base85_stream != NULL) { + uint8_t c; + + if (font_private->has_sfnt) + c = glyphs[n].index; + else + c = (uint8_t) (long unsigned) scaled_glyph->surface_private; + + _cairo_output_stream_write (base85_stream, &c, 1); + } else { + if (font_private->has_sfnt) + _cairo_output_stream_printf (surface->ctx->stream, " %lu", + glyphs[n].index); + else + _cairo_output_stream_printf (surface->ctx->stream, " %lu", + (long unsigned) scaled_glyph->surface_private); + } + + dx = scaled_glyph->metrics.x_advance; + dy = scaled_glyph->metrics.y_advance; + cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy); + x += dx; + y += dy; + } + _cairo_scaled_font_thaw_cache (scaled_font); + + if (base85_stream != NULL) { + cairo_status_t status2; + + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } else { + _cairo_output_stream_puts (surface->ctx->stream, " ]"); + } + if (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 (surface->ctx->stream, "] [ "); + for (n = 0; n < num_clusters; n++) { + _cairo_output_stream_printf (surface->ctx->stream, + "%d %d ", + clusters[n].num_bytes, + clusters[n].num_glyphs); + } + _cairo_output_stream_puts (surface->ctx->stream, "]"); + } + else + { + _cairo_output_stream_puts (surface->ctx->stream, "] <~"); + base85_stream = _cairo_base85_stream_create (surface->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 (status) + return status; + } + + _cairo_output_stream_printf (surface->ctx->stream, + " //%s show_text_glyphs\n", + _direction_to_string (backward)); + } else { + _cairo_output_stream_puts (surface->ctx->stream, + "] show_glyphs\n"); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_script_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_script_surface_t *surface = abstract_surface; + + if (surface->width < 0 || surface->height < 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t +_cairo_script_surface_backend = { + CAIRO_SURFACE_TYPE_SCRIPT, + _cairo_script_surface_create_similar, + _cairo_script_surface_finish, + NULL, //_cairo_script_surface_acquire_source_image, + NULL, //_cairo_script_surface_release_source_image, + NULL, /* acquire_dest_image */ + NULL, /* release_dest_image */ + NULL, /* clone_similar */ + NULL, /* composite */ + NULL, /* fill_rectangles */ + NULL, /* composite_trapezoids */ + _cairo_script_surface_copy_page, + _cairo_script_surface_show_page, + NULL, /* set_clip_region */ + _cairo_script_surface_intersect_clip_path, + _cairo_script_surface_get_extents, + NULL, /* old_show_glyphs */ + NULL, /* get_font_options */ + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + _cairo_script_surface_scaled_font_fini, + NULL, /* scaled_glyph_fini */ + + /* The 5 high level operations */ + _cairo_script_surface_paint, + _cairo_script_surface_mask, + _cairo_script_surface_stroke, + _cairo_script_surface_fill, + NULL, + + NULL, //_cairo_script_surface_snapshot, + + NULL, /* is_similar */ + NULL, /* reset */ + NULL, /* fill_stroke */ + NULL, /* create_solid_pattern_surface */ + + /* The alternate high-level text operation */ + _cairo_script_surface_has_show_text_glyphs, + _cairo_script_surface_show_text_glyphs +}; + +static cairo_bool_t +_cairo_surface_is_script (cairo_surface_t *surface) +{ + return surface->backend == &_cairo_script_surface_backend; +} + +static cairo_script_vmcontext_t * +_cairo_script_vmcontext_create (cairo_output_stream_t *stream) +{ + cairo_script_vmcontext_t *ctx; + + ctx = malloc (sizeof (cairo_script_vmcontext_t)); + if (ctx == NULL) + return NULL; + + memset (ctx, 0, sizeof (cairo_script_vmcontext_t)); + + ctx->stream = stream; + ctx->mode = CAIRO_SCRIPT_MODE_ASCII; + + return ctx; +} + +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); + cr->current_source = (cairo_pattern_t *) &_cairo_pattern_black.base; + _cairo_path_fixed_init (&cr->current_path); + cairo_matrix_init_identity (&cr->current_ctm); + cairo_matrix_init_identity (&cr->current_font_matrix); + _cairo_font_options_init_default (&cr->current_font_options); + cr->current_scaled_font = NULL; +} + +static cairo_script_surface_t * +_cairo_script_surface_create_internal (cairo_script_vmcontext_t *ctx, + double width, + double height) +{ + cairo_script_surface_t *surface; + + if (ctx == NULL) + return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface = malloc (sizeof (cairo_script_surface_t)); + if (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, + CAIRO_CONTENT_COLOR_ALPHA); + + surface->ctx = ctx; + ctx->ref++; + + surface->width = width; + surface->height = height; + + surface->id = (unsigned long) -1; + + _cairo_script_implicit_context_init (&surface->cr); + + return surface; +} + +cairo_surface_t * +cairo_script_surface_create (const char *filename, + double width, + double height) +{ + cairo_output_stream_t *stream; + cairo_script_surface_t *surface; + + stream = _cairo_output_stream_create_for_filename (filename); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + + surface = _cairo_script_surface_create_internal + (_cairo_script_vmcontext_create (stream), width, height); + if (surface->base.status) + return &surface->base; + + _cairo_output_stream_puts (surface->ctx->stream, "%!CairoScript\n"); + return &surface->base; +} + +cairo_surface_t * +cairo_script_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + return &_cairo_script_surface_create_internal + (_cairo_script_vmcontext_create (stream), width, height)->base; +} + +void +cairo_script_surface_set_mode (cairo_surface_t *abstract_surface, + cairo_script_mode_t mode) +{ + cairo_script_surface_t *surface; + cairo_status_t status_ignored; + + if (! _cairo_surface_is_script (abstract_surface)) { + status_ignored = _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + if (abstract_surface->status) + return; + + surface = (cairo_script_surface_t *) abstract_surface; + surface->ctx->mode = mode; +} + +cairo_script_mode_t +cairo_script_surface_get_mode (cairo_surface_t *abstract_surface) +{ + cairo_script_surface_t *surface; + + if (! _cairo_surface_is_script (abstract_surface) || + abstract_surface->status) + { + return CAIRO_SCRIPT_MODE_ASCII; + } + + surface = (cairo_script_surface_t *) abstract_surface; + return surface->ctx->mode; +} diff --git a/src/cairo-script.h b/src/cairo-script.h new file mode 100644 index 00000000..9c428e3b --- /dev/null +++ b/src/cairo-script.h @@ -0,0 +1,74 @@ +/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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> + */ + +#ifndef CAIRO_SCRIPT_H +#define CAIRO_SCRIPT_H + +#include "cairo.h" + +#if CAIRO_HAS_SCRIPT_SURFACE + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_script_surface_create (const char *filename, + double width, + double height); + +cairo_public cairo_surface_t * +cairo_script_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height); + +typedef enum { + CAIRO_SCRIPT_MODE_BINARY, + CAIRO_SCRIPT_MODE_ASCII +} cairo_script_mode_t; + +cairo_public void +cairo_script_surface_set_mode (cairo_surface_t *surface, + cairo_script_mode_t mode); + +cairo_public cairo_script_mode_t +cairo_script_surface_get_mode (cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else /*CAIRO_HAS_SCRIPT_SURFACE*/ +# error Cairo was not compiled with support for the CairoScript backend +#endif /*CAIRO_HAS_SCRIPT_SURFACE*/ + +#endif /*CAIRO_SCRIPT_H*/ diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h index 77f8184b..905bc401 100644 --- a/src/cairo-types-private.h +++ b/src/cairo-types-private.h @@ -44,6 +44,7 @@ #include "cairo-reference-count-private.h" typedef struct _cairo_array cairo_array_t; +typedef struct _cairo_backend cairo_backend_t; typedef struct _cairo_cache cairo_cache_t; typedef struct _cairo_clip cairo_clip_t; typedef struct _cairo_clip_path cairo_clip_path_t; diff --git a/src/cairo.h b/src/cairo.h index 27d56f5a..33655232 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -1876,6 +1876,7 @@ cairo_surface_status (cairo_surface_t *surface); * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image * @CAIRO_SURFACE_TYPE_SDL: The surface is of type SDL, since 1.10 + * @CAIRO_SURFACE_TYPE_SCRIPT: The surface is of type script, since 1.10 * * #cairo_surface_type_t is used to describe the type of a given * surface. The surface types are also known as "backends" or "surface @@ -1915,7 +1916,8 @@ typedef enum _cairo_surface_type { CAIRO_SURFACE_TYPE_OS2, CAIRO_SURFACE_TYPE_WIN32_PRINTING, CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, - CAIRO_SURFACE_TYPE_SDL + CAIRO_SURFACE_TYPE_SDL, + CAIRO_SURFACE_TYPE_SCRIPT } cairo_surface_type_t; cairo_public cairo_surface_type_t diff --git a/src/cairoint.h b/src/cairoint.h index 6101ea55..940e33c5 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -322,9 +322,16 @@ _cairo_user_data_array_set_data (cairo_user_data_array_t *array, void *user_data, cairo_destroy_func_t destroy); +#define _CAIRO_HASH_INIT_VALUE 5381 + cairo_private unsigned long _cairo_hash_string (const char *c); +cairo_private unsigned long +_cairo_hash_bytes (unsigned long hash, + const void *bytes, + unsigned int length); + /* * A #cairo_unscaled_font_t is just an opaque handle we use in the * glyph cache. @@ -339,6 +346,7 @@ typedef struct _cairo_scaled_glyph { cairo_cache_entry_t cache_entry; /* hash is glyph index */ cairo_scaled_font_t *scaled_font; /* font the glyph lives in */ cairo_text_extents_t metrics; /* user-space metrics */ + cairo_text_extents_t fs_metrics; /* font-space metrics */ cairo_box_t bbox; /* device-space bounds */ int16_t x_advance; /* device-space rounded X advance */ int16_t y_advance; /* device-space rounded Y advance */ @@ -2386,6 +2394,16 @@ cairo_private cairo_status_t _cairo_pattern_get_extents (const cairo_pattern_t *pattern, cairo_rectangle_int_t *extents); +cairo_private unsigned long +_cairo_pattern_hash (const cairo_pattern_t *pattern); + +cairo_private unsigned long +_cairo_pattern_size (const cairo_pattern_t *pattern); + +cairo_private cairo_bool_t +_cairo_pattern_equal (const cairo_pattern_t *a, + const cairo_pattern_t *b); + cairo_private void _cairo_pattern_reset_static_data (void); |