diff --git a/boilerplate/Makefile.sources b/boilerplate/Makefile.sources index 44eeb39b1..7be446726 100644 --- a/boilerplate/Makefile.sources +++ b/boilerplate/Makefile.sources @@ -36,6 +36,9 @@ cairo_boilerplate_ps_sources = cairo-boilerplate-ps.c cairo_boilerplate_quartz_private = cairo-boilerplate-quartz-private.h cairo_boilerplate_quartz_sources = cairo-boilerplate-quartz.c +cairo_boilerplate_script_private = cairo-boilerplate-script-private.h +cairo_boilerplate_script_sources = cairo-boilerplate-script.c + cairo_boilerplate_sdl_private = cairo-boilerplate-sdl-private.h cairo_boilerplate_sdl_sources = cairo-boilerplate-sdl.c diff --git a/boilerplate/cairo-boilerplate-script-private.h b/boilerplate/cairo-boilerplate-script-private.h new file mode 100644 index 000000000..480e4221a --- /dev/null +++ b/boilerplate/cairo-boilerplate-script-private.h @@ -0,0 +1,57 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* + * Copyright © 2008 Chris Wilson + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Red Hat, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Red Hat, Inc. makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Chris Wilson + */ + +#ifndef _CAIRO_BOILERPLATE_SCRIPT_PRIVATE_H_ +#define _CAIRO_BOILERPLATE_SCRIPT_PRIVATE_H_ + +cairo_surface_t * +_cairo_boilerplate_script_create_surface (const char *name, + cairo_content_t content, + int width, + int height, + int max_width, + int max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure); + +cairo_status_t +_cairo_boilerplate_script_finish_surface (cairo_surface_t *surface); + +cairo_status_t +_cairo_boilerplate_script_surface_write_to_png (cairo_surface_t *surface, + const char *filename); + +cairo_surface_t * +_cairo_boilerplate_script_get_image_surface (cairo_surface_t *surface, + int page, + int width, + int height); + +void +_cairo_boilerplate_script_cleanup (void *closure); + +#endif diff --git a/boilerplate/cairo-boilerplate-script.c b/boilerplate/cairo-boilerplate-script.c new file mode 100644 index 000000000..ae08cbc5d --- /dev/null +++ b/boilerplate/cairo-boilerplate-script.c @@ -0,0 +1,125 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* + * Copyright © Chris Wilson + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Chris Wilson not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Chris Wilson makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Chris Wilson + */ + +#include "cairo-boilerplate.h" +#include "cairo-boilerplate-script-private.h" + +#include "cairo-script.h" + +cairo_user_data_key_t script_closure_key; + +typedef struct _script_target_closure { + char *filename; + int width; + int height; +} script_target_closure_t; + +cairo_surface_t * +_cairo_boilerplate_script_create_surface (const char *name, + cairo_content_t content, + int width, + int height, + int max_width, + int max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) +{ + script_target_closure_t *ptc; + cairo_surface_t *surface; + cairo_status_t status; + + *closure = ptc = xmalloc (sizeof (script_target_closure_t)); + + ptc->width = width; + ptc->height = height; + + xasprintf (&ptc->filename, "%s.out.cs", name); + xunlink (ptc->filename); + + surface = cairo_script_surface_create (ptc->filename, width, height); + + status = cairo_surface_set_user_data (surface, + &script_closure_key, ptc, NULL); + if (status == CAIRO_STATUS_SUCCESS) + return surface; + + cairo_surface_destroy (surface); + surface = cairo_boilerplate_surface_create_in_error (status); + + free (ptc->filename); + free (ptc); + return surface; +} + +cairo_status_t +_cairo_boilerplate_script_finish_surface (cairo_surface_t *surface) +{ + cairo_surface_finish (surface); + return cairo_surface_status (surface); +} + +cairo_status_t +_cairo_boilerplate_script_surface_write_to_png (cairo_surface_t *surface, + const char *filename) +{ + return CAIRO_STATUS_WRITE_ERROR; +} + +static cairo_surface_t * +_cairo_boilerplate_script_convert_to_image (cairo_surface_t *surface, + int page) +{ + script_target_closure_t *ptc = cairo_surface_get_user_data (surface, + &script_closure_key); + return cairo_boilerplate_convert_to_image (ptc->filename, page); +} + +cairo_surface_t * +_cairo_boilerplate_script_get_image_surface (cairo_surface_t *surface, + int page, + int width, + int height) +{ + cairo_surface_t *image; + + image = _cairo_boilerplate_script_convert_to_image (surface, page); + cairo_surface_set_device_offset (image, + cairo_image_surface_get_width (image) - width, + cairo_image_surface_get_height (image) - height); + surface = _cairo_boilerplate_get_image_surface (image, 0, width, height); + cairo_surface_destroy (image); + + return surface; +} + +void +_cairo_boilerplate_script_cleanup (void *closure) +{ + script_target_closure_t *ptc = closure; + free (ptc->filename); + free (ptc); +} diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c index b12b6d6a1..f6f68f9c5 100644 --- a/boilerplate/cairo-boilerplate.c +++ b/boilerplate/cairo-boilerplate.c @@ -47,6 +47,9 @@ #if CAIRO_HAS_QUARTZ_SURFACE #include "cairo-boilerplate-quartz-private.h" #endif +#if CAIRO_HAS_SCRIPT_SURFACE +#include "cairo-boilerplate-script-private.h" +#endif #if CAIRO_HAS_SDL_SURFACE #include "cairo-boilerplate-sdl-private.h" #endif @@ -603,6 +606,19 @@ static cairo_boilerplate_target_t targets[] = NULL, TRUE, TRUE }, #endif +#if CAIRO_HAS_SCRIPT_SURFACE + { + "script", "script", ".cs", + CAIRO_SURFACE_TYPE_SCRIPT, CAIRO_CONTENT_COLOR_ALPHA, 0, + _cairo_boilerplate_script_create_surface, + NULL, + _cairo_boilerplate_script_finish_surface, + _cairo_boilerplate_script_get_image_surface, + _cairo_boilerplate_script_surface_write_to_png, + _cairo_boilerplate_script_cleanup, + NULL, FALSE + }, +#endif #if CAIRO_HAS_SVG_SURFACE && CAIRO_CAN_TEST_SVG_SURFACE /* It seems we should be able to round-trip SVG content perfectly * through librsvg and cairo, but for some mysterious reason, some diff --git a/build/configure.ac.features b/build/configure.ac.features index 9fa7086ae..9970fde08 100644 --- a/build/configure.ac.features +++ b/build/configure.ac.features @@ -367,6 +367,7 @@ AC_DEFUN([CAIRO_REPORT], echo " XCB: $use_xcb" echo " Win32: $use_win32" echo " OS2: $use_os2" + echo " CairoScript: $use_script" echo " PostScript: $use_ps" echo " PDF: $use_pdf" echo " SVG: $use_svg" @@ -389,6 +390,7 @@ AC_DEFUN([CAIRO_REPORT], echo " test surfaces: $use_test_surfaces" echo " ps testing: $test_ps" echo " pdf testing: $test_pdf" + echo " cs testing: $test_script" echo " svg testing: $test_svg" if test x"$use_win32" = "xyes"; then echo " win32 printing testing: $test_win32_printing" diff --git a/configure.ac b/configure.ac index b13f34ff3..a307493ce 100644 --- a/configure.ac +++ b/configure.ac @@ -236,6 +236,22 @@ CAIRO_ENABLE_SURFACE_BACKEND(directfb, directfb, no, [ dnl =========================================================================== +CAIRO_ENABLE_SURFACE_BACKEND(script, script, no, [ + test_script="yes" + csi_CFLAGS= + csi_LIBS=-lcairo-script-interpreter + if test "x$test_script" = "xyes"; then + AC_DEFINE([CAIRO_CAN_TEST_SCRIPT_SURFACE], 1, + [Define to 1 if the CairoScript backend can be tested]) + else + AC_MSG_WARN([CairoScript backend will not be tested]) + fi + AC_SUBST(csi_CFLAGS) + AC_SUBST(csi_LIBS) +]) + +dnl =========================================================================== + # We use pkg-config to look for freetype2, but fall back to # freetype-config if it fails. We prefer pkg-config, since we can # then just put freetype2 >= $FREETYPE_MIN_VERSION in diff --git a/doc/public/tmpl/cairo-surface.sgml b/doc/public/tmpl/cairo-surface.sgml index 3a6cd8d98..93bfe083c 100644 --- a/doc/public/tmpl/cairo-surface.sgml +++ b/doc/public/tmpl/cairo-surface.sgml @@ -194,6 +194,7 @@ cairo_backend_surface_create(). @CAIRO_SURFACE_TYPE_WIN32_PRINTING: @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: @CAIRO_SURFACE_TYPE_SDL: +@CAIRO_SURFACE_TYPE_SCRIPT: diff --git a/src/Makefile.sources b/src/Makefile.sources index 0b44bbf28..58781dd9b 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 889740392..4d5a46520 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 6a9b8b8d8..5ac8cc8b0 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 01e5713de..f5caba4ba 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 bf2784a20..3bb884ca6 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 7cae0467f..4245448ec 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 3e7d3de02..00f7f77cb 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 1d532c71e..33880d840 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 6f10483f7..2b3d584ec 100644 --- a/src/cairo-output-stream-private.h +++ b/src/cairo-output-stream-private.h @@ -43,14 +43,20 @@ #include #include -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 c31c2da53..9a58aac22 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,12 +178,36 @@ _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) { @@ -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 4a5990d48..43c33c18a 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 90861198b..027ebedf8 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 610ba61bc..c78317eb1 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 819318a19..f335a37ee 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 991665106..f4431fa0d 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 c1d87ae02..86a50bbbd 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 aa1a2c8a7..8b7499409 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 000000000..9118d66d8 --- /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 + */ + +/* 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; mmap[0])*CHAR_BIT; m++, bit<<=1) { + if ((b->map[n] & bit) == 0) { + b->map[n] |= bit; + b->count++; + *id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min; + return CAIRO_STATUS_SUCCESS; + } + } + } + } + min += sizeof (b->map) * CHAR_BIT; + + prev = &b->next; + b = b->next; + } while (b != NULL); + + 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 000000000..9c428e3b8 --- /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 + */ + +#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 77f8184bf..905bc401b 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 27d56f5af..33655232f 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 6101ea555..940e33c50 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); diff --git a/test/Makefile.am b/test/Makefile.am index 131ed6034..498f61616 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1170,10 +1170,10 @@ png_flatten_LDADD = $(top_builddir)/src/libcairo.la $(CAIRO_LDADD) if BUILD_ANY2PPM check_PROGRAMS += any2ppm -any2ppm_CFLAGS = $(POPPLER_CFLAGS) $(LIBRSVG_CFLAGS) $(LIBSPECTRE_CFLAGS) +any2ppm_CFLAGS = $(POPPLER_CFLAGS) $(LIBRSVG_CFLAGS) $(LIBSPECTRE_CFLAGS) $(csi_CFLAGS) # add LDADD, so poppler/librsvg uses "our" cairo any2ppm_LDFLAGS = $(CAIRO_TEST_UNDEFINED_LDFLAGS) -any2ppm_LDADD = $(top_builddir)/src/libcairo.la $(CAIRO_LDADD) $(POPPLER_LIBS) $(LIBRSVG_LIBS) $(LIBSPECTRE_LIBS) +any2ppm_LDADD = $(top_builddir)/src/libcairo.la $(CAIRO_LDADD) $(POPPLER_LIBS) $(LIBRSVG_LIBS) $(LIBSPECTRE_LIBS) $(csi_LIBS) endif if CAIRO_CAN_TEST_PDF_SURFACE diff --git a/test/any2ppm.c b/test/any2ppm.c index 85c1bdbb6..1bd428587 100644 --- a/test/any2ppm.c +++ b/test/any2ppm.c @@ -63,6 +63,10 @@ #include +#if CAIRO_CAN_TEST_SCRIPT_SURFACE +#include +#endif + #if CAIRO_CAN_TEST_PDF_SURFACE #include #endif @@ -160,7 +164,21 @@ write_ppm (cairo_surface_t *surface, int fd) int width, height, stride; int i, j; + data = cairo_image_surface_get_data (surface); + height = cairo_image_surface_get_height (surface); + width = cairo_image_surface_get_width (surface); + stride = cairo_image_surface_get_stride (surface); format = cairo_image_surface_get_format (surface); + if (format == CAIRO_FORMAT_ARGB32) { + /* see if we can convert to a standard ppm type and trim a few bytes */ + cairo_bool_t uses_alpha = FALSE; + const unsigned char *alpha = data; + for (j = height * stride; j-- && uses_alpha == FALSE; alpha += 4) + uses_alpha = *alpha != 0xff; + if (! uses_alpha) + format = CAIRO_FORMAT_RGB24; + } + switch (format) { case CAIRO_FORMAT_ARGB32: /* XXX need true alpha for svg */ @@ -177,11 +195,6 @@ write_ppm (cairo_surface_t *surface, int fd) return "unhandled image format"; } - data = cairo_image_surface_get_data (surface); - height = cairo_image_surface_get_height (surface); - width = cairo_image_surface_get_width (surface); - stride = cairo_image_surface_get_stride (surface); - len = sprintf (buf, "%s %d %d 255\n", format_str, width, height); for (j = 0; j < height; j++) { const unsigned int *row = (unsigned int *) (data + stride * j); @@ -220,6 +233,69 @@ write_ppm (cairo_surface_t *surface, int fd) return NULL; } +#if CAIRO_CAN_TEST_SCRIPT_SURFACE +static cairo_surface_t * +_create_image (void *closure, + double width, double height, + csi_object_t *dictionary) +{ + cairo_surface_t **out = closure; + cairo_surface_t *surface; + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + return *out = cairo_surface_reference (surface); +} + +static const char * +_cairo_script_render_page (const char *filename, + cairo_surface_t **surface_out) +{ + cairo_script_interpreter_t *csi; + cairo_surface_t *surface = NULL; + cairo_status_t status; + + csi = csi_create (); + csi_set_surface_create_function (csi, _create_image, &surface); + csi_run (csi, filename); + status = csi_destroy (csi); + if (status || surface == NULL) { + cairo_surface_destroy (surface); + return "cairo-script interpreter failed"; + } + + status = cairo_surface_status (surface); + if (status) { + cairo_surface_destroy (surface); + return cairo_status_to_string (status); + } + + *surface_out = surface; + return NULL; +} + +static const char * +cs_convert (char **argv, int fd) +{ + const char *err; + cairo_surface_t *surface = NULL; /* silence compiler warning */ + + err = _cairo_script_render_page (argv[0], &surface); + if (err != NULL) + return err; + + err = write_ppm (surface, fd); + cairo_surface_destroy (surface); + + return err; +} +#else +static const char * +cs_convert (char **argv, int fd) +{ + return "compiled without CairoScript support."; +} +#endif + #if CAIRO_CAN_TEST_PDF_SURFACE /* adapted from pdf2png.c */ static const char * @@ -453,6 +529,7 @@ convert (char **argv, int fd) const char *type; const char *(*func) (char **, int); } converters[] = { + { "cs", cs_convert }, { "pdf", pdf_convert }, { "ps", ps_convert }, { "svg", svg_convert }, diff --git a/test/mime-data.script.ref.png b/test/mime-data.script.ref.png new file mode 100644 index 000000000..a236b0444 Binary files /dev/null and b/test/mime-data.script.ref.png differ