diff options
Diffstat (limited to 'src')
81 files changed, 16912 insertions, 8229 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index d4155e817..3f76d2726 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,53 +1,56 @@ -cairoincludedir = $(includedir)/cairo -cairoinclude_HEADERS = \ - cairo.h \ - cairo-atsui.h \ - cairo-features.h\ - cairo-ft.h \ - cairo-glitz.h \ - cairo-pdf.h \ - cairo-png.h \ - cairo-ps.h \ - cairo-quartz.h \ - cairo-xcb.h \ - cairo-xlib.h - -lib_LTLIBRARIES = libcairo.la if CAIRO_HAS_PS_SURFACE -libcairo_ps_sources = cairo_ps_surface.c cairo-ps.h +libcairo_ps_headers = cairo-ps.h +libcairo_ps_sources = cairo_ps_surface.c endif if CAIRO_HAS_PDF_SURFACE -libcairo_pdf_sources = cairo_pdf_surface.c cairo-pdf.h +libcairo_pdf_headers = cairo-pdf.h +libcairo_pdf_sources = cairo_pdf_surface.c endif if CAIRO_HAS_PNG_SURFACE -libcairo_png_sources = cairo_png_surface.c cairo-png.h +libcairo_png_headers = cairo-png.h +libcairo_png_sources = cairo_png_surface.c endif if CAIRO_HAS_XLIB_SURFACE -libcairo_xlib_sources = cairo_xlib_surface.c cairo-xlib.h +libcairo_xlib_headers = cairo-xlib.h +libcairo_xlib_sources = cairo_xlib_surface.c endif if CAIRO_HAS_QUARTZ_SURFACE -libcairo_quartz_sources = cairo_quartz_surface.c cairo-quartz.h +libcairo_quartz_headers = cairo-quartz.h +libcairo_quartz_sources = cairo_quartz_surface.c endif if CAIRO_HAS_XCB_SURFACE -libcairo_xcb_sources = cairo_xcb_surface.c cairo-xcb.h +libcairo_xcb_headers = cairo-xcb.h +libcairo_xcb_sources = cairo_xcb_surface.c +endif + +libcairo_win32_sources = +if CAIRO_HAS_WIN32_SURFACE +libcairo_win32_headers = cairo-win32.h +libcairo_win32_sources += cairo_win32_surface.c cairo-win32-private.h +endif +if CAIRO_HAS_WIN32_FONT +libcairo_win32_sources += cairo_win32_font.c endif if CAIRO_HAS_GLITZ_SURFACE -libcairo_glitz_sources = cairo_glitz_surface.c cairo-glitz.h +libcairo_glitz_headers = cairo-glitz.h +libcairo_glitz_sources = cairo_glitz_surface.c endif if CAIRO_HAS_ATSUI_FONT -libcairo_atsui_sources = cairo_atsui_font.c cairo-atsui.h +libcairo_atsui_headers = cairo-atsui.h +libcairo_atsui_sources = cairo_atsui_font.c endif if CAIRO_HAS_FT_FONT -libcairo_ft_sources = cairo_ft_font.c cairo-ft.h +libcairo_ft_headers = cairo-ft.h +libcairo_ft_sources = cairo_ft_font.c cairo-ft-private.h endif # These names match automake style variable definition conventions so @@ -57,6 +60,23 @@ endif FONTCONFIG_LIBS=@FONTCONFIG_LIBS@ XRENDER_LIBS=@XRENDER_LIBS@ +cairoincludedir = $(includedir)/cairo +cairoinclude_HEADERS = \ + cairo.h \ + cairo-features.h \ + $(libcairo_atsui_headers) \ + $(libcairo_ft_headers) \ + $(libcairo_glitz_headers) \ + $(libcairo_pdf_headers) \ + $(libcairo_png_headers) \ + $(libcairo_ps_headers) \ + $(libcairo_quartz_headers) \ + $(libcairo_win32_headers) \ + $(libcairo_xcb_headers) \ + $(libcairo_xlib_headers) + +lib_LTLIBRARIES = libcairo.la + libcairo_la_SOURCES = \ cairo.c \ cairo.h \ @@ -80,6 +100,7 @@ libcairo_la_SOURCES = \ cairo_surface.c \ cairo_traps.c \ cairo_pattern.c \ + cairo_unicode.c \ cairo_wideint.c \ cairo-wideint.h \ $(libcairo_atsui_sources)\ @@ -91,6 +112,8 @@ libcairo_la_SOURCES = \ $(libcairo_quartz_sources)\ $(libcairo_xcb_sources) \ $(libcairo_glitz_sources)\ + $(libcairo_win32_sources)\ + $(libcairo_freetype_sources) \ cairoint.h libcairo_la_LDFLAGS = -version-info @VERSION_INFO@ -no-undefined @@ -98,3 +121,13 @@ libcairo_la_LDFLAGS = -version-info @VERSION_INFO@ -no-undefined INCLUDES = -I$(srcdir) $(CAIRO_CFLAGS) libcairo_la_LIBADD = $(CAIRO_LIBS) + +install-data-local: + @if test -f $(includedir)/cairo.h || test -f $(includedir)/cairo-features.h ; then \ + echo "****************************************************************" ; \ + echo "*** Error: Old headers found. You should remove the following" ; \ + echo "*** files and then type 'make install' again." ; \ + ls $(includedir)/cairo*.h ; \ + echo "****************************************************************" ; \ + false ; \ + fi diff --git a/src/cairo-atsui-font.c b/src/cairo-atsui-font.c index 52cfc6bd8..cb4b1c5d7 100644 --- a/src/cairo-atsui-font.c +++ b/src/cairo-atsui-font.c @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2004 Calum Robinson + * Copyright © 2004 Calum Robinson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public diff --git a/src/cairo-atsui.h b/src/cairo-atsui.h index 94b30432a..a5b7308f8 100644 --- a/src/cairo-atsui.h +++ b/src/cairo-atsui.h @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2004 Calum Robinson + * Copyright © 2004 Calum Robinson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -33,18 +33,23 @@ * Calum Robinson <calumr@mac.com> */ -#include <cairo.h> - #ifndef CAIRO_ATSUI_H #define CAIRO_ATSUI_H + +#include <cairo.h> + #ifdef CAIRO_HAS_ATSUI_FONT /* ATSUI platform-specific font interface */ #include <Carbon/Carbon.h> +CAIRO_BEGIN_DECLS + cairo_font_t * cairo_atsui_font_create(ATSUStyle style); +CAIRO_END_DECLS + #endif /* CAIRO_HAS_ATSUI_FONT */ #endif /* CAIRO_ATSUI_H */ diff --git a/src/cairo-cache.c b/src/cairo-cache.c index b097b609b..d1ad5a4e2 100644 --- a/src/cairo-cache.c +++ b/src/cairo-cache.c @@ -94,9 +94,9 @@ static const cairo_cache_arrangement_t cache_arrangements [] = { * a mostly-dead table. * * Generally you do not need to worry about freeing cache entries; the - * cache will expire entries randomly as it experiences memory pressure. - * There is currently no explicit entry-removing call, though one can be - * added easily. + * cache will expire entries randomly as it experiences memory pressure. + * If max_memory is set, entries are not expired, and must be explicitely + * removed. * * This table is open-addressed with double hashing. Each table size is a * prime chosen to be a little more than double the high water mark for a @@ -282,17 +282,51 @@ _load_factor (cairo_cache_t *cache) } #endif -static unsigned long -_random_live_entry (cairo_cache_t *cache) -{ - unsigned long idx; - assert(cache != NULL); - do { - idx = rand () % cache->arrangement->size; - } while (! LIVE_ENTRY_P(cache, idx)); - return idx; -} +/* Find a random in the cache matching the given predicate. We use the + * same algorithm as the probing algorithm to walk over the entries in + * the hash table in a pseudo-random order. Walking linearly would + * favor entries following gaps in the hash table. We could also + * call rand() repeatedly, which works well for almost-full tables, + * but degrades when the table is almost empty, or predicate + * returns false for most entries. + */ +static cairo_cache_entry_base_t ** +_random_entry (cairo_cache_t *cache, + int (*predicate)(void*)) +{ + cairo_cache_entry_base_t **probe; + unsigned long hash; + unsigned long table_size, i, idx, step; + + _cache_sane_state (cache); + + table_size = cache->arrangement->size; + hash = rand (); + idx = hash % table_size; + step = 0; + + for (i = 0; i < table_size; ++i) + { + assert(idx < table_size); + probe = cache->entries + idx; + if (LIVE_ENTRY_P(cache, idx) + && (!predicate || predicate (*probe))) + return probe; + + if (step == 0) { + step = hash % cache->arrangement->rehash; + if (step == 0) + step = 1; + } + + idx += step; + if (idx >= table_size) + idx -= table_size; + } + + return NULL; +} /* public API follows */ @@ -356,8 +390,9 @@ _cairo_cache_destroy (cairo_cache_t *cache) cairo_status_t _cairo_cache_lookup (cairo_cache_t *cache, - void *key, - void **entry_return) + void *key, + void **entry_return, + int *created_entry) { unsigned long idx; @@ -392,6 +427,8 @@ _cairo_cache_lookup (cairo_cache_t *cache, cache->hits++; #endif *entry_return = *slot; + if (created_entry) + *created_entry = 0; return status; } @@ -401,19 +438,18 @@ _cairo_cache_lookup (cairo_cache_t *cache, /* Build the new entry. */ status = cache->backend->create_entry (cache, key, - entry_return); + (void **)&new_entry); if (status != CAIRO_STATUS_SUCCESS) return status; - new_entry = (cairo_cache_entry_base_t *) (*entry_return); - /* Store the hash value in case the backend forgot. */ new_entry->hashcode = cache->backend->hash (cache, key); /* Make some entries die if we're under memory pressure. */ while (cache->live_entries > 0 && + cache->max_memory > 0 && ((cache->max_memory - cache->used_memory) < new_entry->memory)) { - idx = _random_live_entry (cache); + idx = _random_entry (cache, NULL) - cache->entries; assert (idx < cache->arrangement->size); _entry_destroy (cache, idx); } @@ -425,7 +461,6 @@ _cairo_cache_lookup (cairo_cache_t *cache, status = _resize_cache (cache, cache->live_entries + 1); if (status != CAIRO_STATUS_SUCCESS) { cache->backend->destroy_entry (cache, new_entry); - *entry_return = NULL; return status; } @@ -439,9 +474,38 @@ _cairo_cache_lookup (cairo_cache_t *cache, _cache_sane_state (cache); + *entry_return = new_entry; + if (created_entry) + *created_entry = 1; + return status; } +cairo_status_t +_cairo_cache_remove (cairo_cache_t *cache, + void *key) +{ + cairo_cache_entry_base_t **slot; + + _cache_sane_state (cache); + + /* See if we have an entry in the table already. */ + slot = _find_exact_live_entry_for (cache, key); + if (slot != NULL) + _entry_destroy (cache, slot - cache->entries); + + return CAIRO_STATUS_SUCCESS; +} + +void * +_cairo_cache_random_entry (cairo_cache_t *cache, + int (*predicate)(void*)) +{ + cairo_cache_entry_base_t **slot = _random_entry (cache, predicate); + + return slot ? *slot : NULL; +} + unsigned long _cairo_hash_string (const char *c) { diff --git a/src/cairo-color.c b/src/cairo-color.c index 899b1e3d5..f203d96cc 100644 --- a/src/cairo-color.c +++ b/src/cairo-color.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" @@ -67,7 +67,8 @@ _cairo_color_set_rgb (cairo_color_t *color, double red, double green, double blu } void -_cairo_color_get_rgb (cairo_color_t *color, double *red, double *green, double *blue) +_cairo_color_get_rgb (const cairo_color_t *color, + double *red, double *green, double *blue) { if (red) *red = color->red; diff --git a/src/cairo-features.h.in b/src/cairo-features.h.in index e2a62ba66..a13250d97 100644 --- a/src/cairo-features.h.in +++ b/src/cairo-features.h.in @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl Worth <cworth@east.isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_FEATURES_H @@ -49,10 +49,14 @@ #define @XCB_SURFACE_FEATURE@ +#define @WIN32_SURFACE_FEATURE@ + #define @GLITZ_SURFACE_FEATURE@ #define @FT_FONT_FEATURE@ +#define @WIN32_FONT_FEATURE@ + #define @ATSUI_FONT_FEATURE@ #define @SANITY_CHECKING_FEATURE@ diff --git a/src/cairo-fixed.c b/src/cairo-fixed.c index ee31718ef..a4faa1708 100644 --- a/src/cairo-fixed.c +++ b/src/cairo-fixed.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo-font.c b/src/cairo-font.c index f5fc0e981..529c1c7c3 100644 --- a/src/cairo-font.c +++ b/src/cairo-font.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -31,293 +32,129 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> + * Graydon Hoare <graydon@redhat.com> + * Owen Taylor <otaylor@redhat.com> */ #include "cairoint.h" -/* First we implement a global font cache for named fonts. */ - -typedef struct { - cairo_cache_entry_base_t base; - const char *family; - cairo_font_slant_t slant; - cairo_font_weight_t weight; -} cairo_font_cache_key_t; - -typedef struct { - cairo_font_cache_key_t key; - cairo_unscaled_font_t *unscaled; -} cairo_font_cache_entry_t; - -static unsigned long -_font_cache_hash (void *cache, void *key) -{ - unsigned long hash; - cairo_font_cache_key_t *in; - in = (cairo_font_cache_key_t *) key; - - /* 1607 and 1451 are just a couple random primes. */ - hash = _cairo_hash_string (in->family); - hash += ((unsigned long) in->slant) * 1607; - hash += ((unsigned long) in->weight) * 1451; - return hash; -} - - -static int -_font_cache_keys_equal (void *cache, - void *k1, - void *k2) -{ - cairo_font_cache_key_t *a, *b; - a = (cairo_font_cache_key_t *) k1; - b = (cairo_font_cache_key_t *) k2; - - return (strcmp (a->family, b->family) == 0) - && (a->weight == b->weight) - && (a->slant == b->slant); -} - - -static cairo_status_t -_font_cache_create_entry (void *cache, - void *key, - void **return_value) -{ - const cairo_font_backend_t *backend = CAIRO_FONT_BACKEND_DEFAULT; - cairo_font_cache_key_t *k; - cairo_font_cache_entry_t *entry; - k = (cairo_font_cache_key_t *) key; - - /* XXX: The current freetype backend may return NULL, (for example - * if no fonts are installed), but I would like to guarantee that - * the toy API always returns at least *some* font, so I would - * like to build in some sort fo font here, (even a really lame, - * ugly one if necessary). */ - - entry = malloc (sizeof (cairo_font_cache_entry_t)); - if (entry == NULL) - goto FAIL; - - entry->key.slant = k->slant; - entry->key.weight = k->weight; - entry->key.family = strdup(k->family); - if (entry->key.family == NULL) - goto FREE_ENTRY; - - entry->unscaled = backend->create (k->family, k->slant, k->weight); - if (entry->unscaled == NULL) - goto FREE_FAMILY; - - /* Not sure how to measure backend font mem; use a simple count for now.*/ - entry->key.base.memory = 1; - *return_value = entry; - return CAIRO_STATUS_SUCCESS; - - FREE_FAMILY: - free ((void *) entry->key.family); - - FREE_ENTRY: - free (entry); - - FAIL: - return CAIRO_STATUS_NO_MEMORY; -} - -static void -_font_cache_destroy_entry (void *cache, - void *entry) -{ - cairo_font_cache_entry_t *e; - - e = (cairo_font_cache_entry_t *) entry; - _cairo_unscaled_font_destroy (e->unscaled); - free ((void *) e->key.family); - free (e); -} - -static void -_font_cache_destroy_cache (void *cache) -{ - free (cache); -} - -static const cairo_cache_backend_t cairo_font_cache_backend = { - _font_cache_hash, - _font_cache_keys_equal, - _font_cache_create_entry, - _font_cache_destroy_entry, - _font_cache_destroy_cache -}; - -static void -_lock_global_font_cache (void) -{ - /* FIXME: implement locking. */ -} - -static void -_unlock_global_font_cache (void) -{ - /* FIXME: implement locking. */ -} - -static cairo_cache_t * -_global_font_cache = NULL; - -static cairo_cache_t * -_get_global_font_cache (void) -{ - if (_global_font_cache == NULL) { - _global_font_cache = malloc (sizeof (cairo_cache_t)); - - if (_global_font_cache == NULL) - goto FAIL; - - if (_cairo_cache_init (_global_font_cache, - &cairo_font_cache_backend, - CAIRO_FONT_CACHE_NUM_FONTS_DEFAULT)) - goto FAIL; - } - - return _global_font_cache; - - FAIL: - if (_global_font_cache) - free (_global_font_cache); - _global_font_cache = NULL; - return NULL; -} - - /* Now the internal "unscaled + scale" font API */ -cairo_unscaled_font_t * -_cairo_unscaled_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) +cairo_private cairo_status_t +_cairo_font_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight, + cairo_font_scale_t *sc, + cairo_font_t **font) { - cairo_cache_t * cache; - cairo_font_cache_key_t key; - cairo_font_cache_entry_t *font; - cairo_status_t status; - - _lock_global_font_cache (); - cache = _get_global_font_cache (); - if (cache == NULL) { - _unlock_global_font_cache (); - return NULL; - } - - key.family = family; - key.slant = slant; - key.weight = weight; - - status = _cairo_cache_lookup (cache, &key, (void **) &font); - if (status) { - _unlock_global_font_cache (); - return NULL; - } + const cairo_font_backend_t *backend = CAIRO_FONT_BACKEND_DEFAULT; - _cairo_unscaled_font_reference (font->unscaled); - _unlock_global_font_cache (); - return font->unscaled; + return backend->create (family, slant, weight, sc, font); } void -_cairo_font_init (cairo_font_t *scaled, - cairo_font_scale_t *scale, - cairo_unscaled_font_t *unscaled) +_cairo_font_init (cairo_font_t *font, + cairo_font_scale_t *scale, + const cairo_font_backend_t *backend) { - scaled->scale = *scale; - scaled->unscaled = unscaled; - scaled->refcount = 1; + font->scale = *scale; + font->refcount = 1; + font->backend = backend; } -cairo_status_t -_cairo_unscaled_font_init (cairo_unscaled_font_t *font, - const cairo_font_backend_t *backend) +void +_cairo_unscaled_font_init (cairo_unscaled_font_t *font, + const cairo_font_backend_t *backend) { font->refcount = 1; font->backend = backend; - return CAIRO_STATUS_SUCCESS; } - cairo_status_t -_cairo_unscaled_font_text_to_glyphs (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs) +_cairo_font_text_to_glyphs (cairo_font_t *font, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) { - return font->backend->text_to_glyphs (font, scale, utf8, glyphs, num_glyphs); + return font->backend->text_to_glyphs (font, utf8, glyphs, num_glyphs); } cairo_status_t -_cairo_unscaled_font_glyph_extents (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) +_cairo_font_glyph_extents (cairo_font_t *font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) { - return font->backend->glyph_extents(font, scale, glyphs, num_glyphs, extents); + return font->backend->glyph_extents(font, glyphs, num_glyphs, extents); } cairo_status_t -_cairo_unscaled_font_glyph_bbox (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) +_cairo_font_glyph_bbox (cairo_font_t *font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) { - return font->backend->glyph_bbox (font, scale, glyphs, num_glyphs, bbox); + return font->backend->glyph_bbox (font, glyphs, num_glyphs, bbox); } cairo_status_t -_cairo_unscaled_font_show_glyphs (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_font_show_glyphs (cairo_font_t *font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs) { cairo_status_t status; if (surface->backend->show_glyphs != NULL) { - status = surface->backend->show_glyphs (font, scale, operator, source, - surface, source_x, source_y, + status = surface->backend->show_glyphs (font, operator, pattern, + surface, + source_x, source_y, + dest_x, dest_y, + width, height, glyphs, num_glyphs); if (status == CAIRO_STATUS_SUCCESS) return status; } /* Surface display routine either does not exist or failed. */ - return font->backend->show_glyphs (font, scale, operator, source, - surface, source_x, source_y, + return font->backend->show_glyphs (font, operator, pattern, + surface, + source_x, source_y, + dest_x, dest_y, + width, height, glyphs, num_glyphs); } cairo_status_t -_cairo_unscaled_font_glyph_path (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) +_cairo_font_glyph_path (cairo_font_t *font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path) +{ + return font->backend->glyph_path (font, glyphs, num_glyphs, path); +} + +void +_cairo_font_get_glyph_cache_key (cairo_font_t *font, + cairo_glyph_cache_key_t *key) { - return font->backend->glyph_path (font, scale, glyphs, num_glyphs, path); + font->backend->get_glyph_cache_key (font, key); } cairo_status_t -_cairo_unscaled_font_font_extents (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - cairo_font_extents_t *extents) +_cairo_font_font_extents (cairo_font_t *font, + cairo_font_extents_t *extents) { - return font->backend->font_extents(font, scale, extents); + return font->backend->font_extents (font, extents); } void @@ -332,8 +169,7 @@ _cairo_unscaled_font_destroy (cairo_unscaled_font_t *font) if (--(font->refcount) > 0) return; - if (font->backend) - font->backend->destroy (font); + font->backend->destroy_unscaled_font (font); } @@ -352,37 +188,154 @@ cairo_font_destroy (cairo_font_t *font) if (--(font->refcount) > 0) return; - if (font->unscaled) - _cairo_unscaled_font_destroy (font->unscaled); - - free (font); + font->backend->destroy_font (font); } -void -cairo_font_set_transform (cairo_font_t *font, - cairo_matrix_t *matrix) +/** + * cairo_font_extents: + * @font: a #cairo_font_t + * @font_matrix: the font transformation for which this font was + * created. (See cairo_transform_font()). This is needed + * properly convert the metrics from the font into user space. + * @extents: a #cairo_font_extents_t which to store the retrieved extents. + * + * Gets the metrics for a #cairo_font_t. + * + * Return value: %CAIRO_STATUS_SUCCESS on success. Otherwise, an + * error such as %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_status_t +cairo_font_extents (cairo_font_t *font, + cairo_matrix_t *font_matrix, + cairo_font_extents_t *extents) { - double dummy; - cairo_matrix_get_affine (matrix, - &font->scale.matrix[0][0], - &font->scale.matrix[0][1], - &font->scale.matrix[1][0], - &font->scale.matrix[1][1], - &dummy, &dummy); + cairo_int_status_t status; + double font_scale_x, font_scale_y; + + status = _cairo_font_font_extents (font, extents); + + if (!CAIRO_OK (status)) + return status; + + _cairo_matrix_compute_scale_factors (font_matrix, + &font_scale_x, &font_scale_y, + /* XXX */ 1); + + /* + * The font responded in unscaled units, scale by the font + * matrix scale factors to get to user space + */ + + extents->ascent *= font_scale_y; + extents->descent *= font_scale_y; + extents->height *= font_scale_y; + extents->max_x_advance *= font_scale_x; + extents->max_y_advance *= font_scale_y; + + return status; } +/** + * cairo_font_glyph_extents: + * @font: a #cairo_font_t + * @font_matrix: the font transformation for which this font was + * created. (See cairo_transform_font()). This is needed + * properly convert the metrics from the font into user space. + * @glyphs: an array of glyph IDs with X and Y offsets. + * @num_glyphs: the number of glyphs in the @glyphs array + * @extents: a #cairo_text_extents_t which to store the retrieved extents. + * + * cairo_font_glyph_extents() gets the overall metrics for a string of + * glyphs. The X and Y offsets in @glyphs are taken from an origin of 0,0. + **/ void -cairo_font_current_transform (cairo_font_t *font, - cairo_matrix_t *matrix) +cairo_font_glyph_extents (cairo_font_t *font, + cairo_matrix_t *font_matrix, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) { - cairo_matrix_set_affine (matrix, - font->scale.matrix[0][0], - font->scale.matrix[0][1], - font->scale.matrix[1][0], - font->scale.matrix[1][1], - 0, 0); -} + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_glyph_t origin_glyph; + cairo_text_extents_t origin_extents; + int i; + double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0; + double x_pos = 0.0, y_pos = 0.0; + int set = 0; + + if (!num_glyphs) + { + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; + + return; + } + for (i = 0; i < num_glyphs; i++) + { + double x, y; + double wm, hm; + + origin_glyph = glyphs[i]; + origin_glyph.x = 0.0; + origin_glyph.y = 0.0; + status = _cairo_font_glyph_extents (font, + &origin_glyph, 1, + &origin_extents); + + /* + * Transform font space metrics into user space metrics + * by running the corners through the font matrix and + * expanding the bounding box as necessary + */ + x = origin_extents.x_bearing; + y = origin_extents.y_bearing; + cairo_matrix_transform_point (font_matrix, + &x, &y); + + for (hm = 0.0; hm <= 1.0; hm += 1.0) + for (wm = 0.0; wm <= 1.0; wm += 1.0) + { + x = origin_extents.x_bearing + origin_extents.width * wm; + y = origin_extents.y_bearing + origin_extents.height * hm; + cairo_matrix_transform_point (font_matrix, + &x, &y); + x += glyphs[i].x; + y += glyphs[i].y; + if (!set) + { + min_x = max_x = x; + min_y = max_y = y; + set = 1; + } + else + { + if (x < min_x) min_x = x; + if (x > max_x) max_x = x; + if (y < min_y) min_y = y; + if (y > max_y) max_y = y; + } + } + + x = origin_extents.x_advance; + y = origin_extents.y_advance; + cairo_matrix_transform_point (font_matrix, + &x, &y); + x_pos = glyphs[i].x + x; + y_pos = glyphs[i].y + y; + } + + extents->x_bearing = min_x - glyphs[0].x; + extents->y_bearing = min_y - glyphs[0].y; + extents->width = max_x - min_x; + extents->height = max_y - min_y; + extents->x_advance = x_pos - glyphs[0].x; + extents->y_advance = y_pos - glyphs[0].y; +} /* Now we implement functions to access a default global image & metrics * cache. @@ -398,7 +351,8 @@ _cairo_glyph_cache_hash (void *cache, void *key) ^ ((unsigned long) in->scale.matrix[0][0]) ^ ((unsigned long) in->scale.matrix[0][1]) ^ ((unsigned long) in->scale.matrix[1][0]) - ^ ((unsigned long) in->scale.matrix[1][1]) + ^ ((unsigned long) in->scale.matrix[1][1]) + ^ (in->flags * 1451) /* 1451 is just an abitrary prime */ ^ in->index; } @@ -412,6 +366,7 @@ _cairo_glyph_cache_keys_equal (void *cache, b = (cairo_glyph_cache_key_t *) k2; return (a->index == b->index) && (a->unscaled == b->unscaled) + && (a->flags == b->flags) && (a->scale.matrix[0][0] == b->scale.matrix[0][0]) && (a->scale.matrix[0][1] == b->scale.matrix[0][1]) && (a->scale.matrix[1][0] == b->scale.matrix[1][0]) diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index b928b04fc..44e1b0e84 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -1,29 +1,40 @@ -/* - * Copyright © 2003 Red Hat Inc. +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * 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 * - * 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. + * 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/ * - * 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. + * 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. * - * Author: Graydon Hoare <graydon@redhat.com> + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Graydon Hoare <graydon@redhat.com> + * Owen Taylor <otaylor@redhat.com> */ -#include "cairoint.h" -#include "cairo-ft.h" +#include "cairo-ft-private.h" #include <fontconfig/fontconfig.h> #include <fontconfig/fcfreetype.h> @@ -38,19 +49,9 @@ #define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) #define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) -/* - * First we make a private, sharable implementation object which can be - * stored both in a private cache and in public font objects (including - * those connected to fonts we don't own) +/* This is the max number of FT_face objects we keep open at once */ - -typedef struct { - int refcount; - - FT_Face face; - int owns_face; - -} ft_font_val_t; +#define MAX_OPEN_FACES 10 /* * The simple 2x2 matrix is converted into separate scale and shape @@ -62,141 +63,126 @@ typedef struct { double shape[2][2]; } ft_font_transform_t; -static ft_font_val_t * -_create_from_face (FT_Face face, int owns_face) -{ - ft_font_val_t *tmp = malloc (sizeof(ft_font_val_t)); - if (tmp) { - tmp->refcount = 1; - tmp->face = face; - tmp->owns_face = owns_face; - FT_Set_Char_Size (face, - DOUBLE_TO_26_6 (1.0), - DOUBLE_TO_26_6 (1.0), - 0, 0); - } - return tmp; -} +/* + * We create an object that corresponds to a single font on the disk; + * (identified by a filename/id pair) these are shared between all + * fonts using that file. For cairo_ft_font_create_for_ft_face(), we + * just create a one-off version with a permanent face value. + */ -static void -_reference_font_val (ft_font_val_t *f) -{ - f->refcount++; -} +typedef struct { + cairo_unscaled_font_t base; -static void -_destroy_font_val (ft_font_val_t *f) -{ - if (--(f->refcount) > 0) - return; + int from_face; /* from cairo_ft_font_create_for_ft_face()? */ + FT_Face face; /* provided or cached face */ - if (f->owns_face) - FT_Done_Face (f->face); + /* only set if from_face is false */ + FT_Library library; + char *filename; + int id; - free (f); -} + /* We temporarily scale the unscaled font as neede */ + int have_scale; + cairo_font_scale_t current_scale; + double x_scale; /* Extracted X scale factor */ + double y_scale; /* Extracted Y scale factor */ + + int lock; /* count of how many times this font has been locked */ +} ft_unscaled_font_t; -static ft_font_val_t * -_create_from_library_and_pattern (FT_Library ft_library, FcPattern *pattern) -{ - ft_font_val_t *f = NULL; - char *filename = NULL; - int owns_face = 0; - FT_Face face = NULL; - FcPattern *resolved = NULL; - FcResult result = FcResultMatch; +const cairo_font_backend_t cairo_ft_font_backend; - if (pattern == NULL) - goto FAIL; +static ft_unscaled_font_t * +_ft_unscaled_font_create_from_face (FT_Face face) +{ + ft_unscaled_font_t *unscaled = malloc (sizeof(ft_unscaled_font_t)); + if (!unscaled) + return NULL; + + unscaled->from_face = 1; + unscaled->face = face; - FcConfigSubstitute (0, pattern, FcMatchPattern); - FcDefaultSubstitute (pattern); + unscaled->library = NULL; + unscaled->filename = NULL; + unscaled->id = 0; - resolved = FcFontMatch (0, pattern, &result); - if (!resolved) - goto FAIL; + unscaled->have_scale = 0; + unscaled->lock = 0; - if (result != FcResultMatch) - goto FREE_RESOLVED; + _cairo_unscaled_font_init ((cairo_unscaled_font_t *)unscaled, + &cairo_ft_font_backend); + return unscaled; +} + +static ft_unscaled_font_t * +_ft_unscaled_font_create_from_filename (FT_Library library, + const char *filename, + int id) +{ + ft_unscaled_font_t *unscaled; + char *new_filename; - /* If the pattern has an FT_Face object, use that. */ - if (FcPatternGetFTFace (resolved, FC_FT_FACE, 0, &face) != FcResultMatch - || face == NULL) - { - /* otherwise it had better have a filename */ - result = FcPatternGetString (resolved, FC_FILE, 0, (FcChar8 **)(&filename)); - - if (result == FcResultMatch) - if (FT_New_Face (ft_library, filename, 0, &face)) - goto FREE_RESOLVED; - - if (face == NULL) - goto FREE_RESOLVED; - - owns_face = 1; + new_filename = strdup (filename); + if (!new_filename) + return NULL; + + unscaled = malloc (sizeof (ft_unscaled_font_t)); + if (!unscaled) { + free (new_filename); + return NULL; } + + unscaled->from_face = 0; + unscaled->face = NULL; - f = _create_from_face (face, owns_face); - - FcPatternDestroy (resolved); - return f; + unscaled->library = library; + unscaled->filename = new_filename; + unscaled->id = id; - FREE_RESOLVED: - if (resolved) - FcPatternDestroy (resolved); - - FAIL: - return NULL; + unscaled->have_scale = 0; + unscaled->lock = 0; + + _cairo_unscaled_font_init ((cairo_unscaled_font_t *)unscaled, + &cairo_ft_font_backend); + return unscaled; } - -/* - * We then make the user-exposed structure out of one of these impls, such - * that it is reasonably cheap to copy and/or destroy. Unfortunately this - * duplicates a certain amount of the caching machinery in the font cache, - * but that's unavoidable as we also provide an FcPattern resolution API, - * which is not part of cairo's generic font finding system. - */ - -typedef struct { - cairo_unscaled_font_t base; - FcPattern *pattern; - ft_font_val_t *val; -} cairo_ft_font_t; - -/* - * We then make a key and entry type which are compatible with the generic - * cache system. This cache serves to share single ft_font_val_t instances - * between fonts (or between font lifecycles). +/* + * We keep a global cache from [file/id] => [ft_unscaled_font_t]. This + * hash isn't limited in size. However, we limit the number of + * FT_Face objects we keep around; when we've exceeeded that + * limit and need to create a new FT_Face, we dump the FT_Face from + * a random ft_unscaled_font_t. */ typedef struct { cairo_cache_entry_base_t base; - FcPattern *pattern; + char *filename; + int id; } cairo_ft_cache_key_t; typedef struct { cairo_ft_cache_key_t key; - ft_font_val_t *val; + ft_unscaled_font_t *unscaled; } cairo_ft_cache_entry_t; -/* - * Then we create a cache which maps FcPattern keys to the refcounted - * ft_font_val_t values. - */ - typedef struct { cairo_cache_t base; FT_Library lib; + int n_faces; /* Number of open FT_Face objects */ } ft_cache_t; - static unsigned long _ft_font_cache_hash (void *cache, void *key) { - cairo_ft_cache_key_t *in; - in = (cairo_ft_cache_key_t *) key; - return FcPatternHash (in->pattern); + cairo_ft_cache_key_t *in = (cairo_ft_cache_key_t *) key; + unsigned long hash; + + /* 1607 is just a random prime. */ + hash = _cairo_hash_string (in->filename); + hash += ((unsigned long) in->id) * 1607; + + return hash; } static int @@ -208,10 +194,10 @@ _ft_font_cache_keys_equal (void *cache, cairo_ft_cache_key_t *b; a = (cairo_ft_cache_key_t *) k1; b = (cairo_ft_cache_key_t *) k2; - - return FcPatternEqual (a->pattern, b->pattern); -} + return strcmp (a->filename, b->filename) == 0 && + a->id == b->id; +} static cairo_status_t _ft_font_cache_create_entry (void *cache, @@ -226,27 +212,33 @@ _ft_font_cache_create_entry (void *cache, if (entry == NULL) return CAIRO_STATUS_NO_MEMORY; - entry->key.pattern = FcPatternDuplicate (k->pattern); - if (!entry->key.pattern) { + entry->unscaled = _ft_unscaled_font_create_from_filename (ftcache->lib, + k->filename, + k->id); + if (!entry->unscaled) { free (entry); return CAIRO_STATUS_NO_MEMORY; } - - entry->val = _create_from_library_and_pattern (ftcache->lib, entry->key.pattern); - entry->key.base.memory = 1; - + + entry->key.base.memory = 0; + entry->key.filename = entry->unscaled->filename; + entry->key.id = entry->unscaled->id; + *return_entry = entry; return CAIRO_STATUS_SUCCESS; } +/* Entries are never spontaneously destroyed; but only when + * we remove them from the cache specifically. We free entry->unscaled + * in the code that removes the entry from the cache + */ static void _ft_font_cache_destroy_entry (void *cache, void *entry) { cairo_ft_cache_entry_t *e = (cairo_ft_cache_entry_t *) entry; - FcPatternDestroy (e->key.pattern); - _destroy_font_val (e->val); + free (e); } @@ -291,11 +283,12 @@ _get_global_ft_cache (void) if (_cairo_cache_init (&_global_ft_cache->base, &_ft_font_cache_backend, - CAIRO_FT_CACHE_NUM_FONTS_DEFAULT)) + 0)) /* No memory limit */ goto FAIL; if (FT_Init_FreeType (&_global_ft_cache->lib)) goto FAIL; + _global_ft_cache->n_faces = 0; } return &_global_ft_cache->base; @@ -306,30 +299,304 @@ _get_global_ft_cache (void) return NULL; } -/* implement the backend interface */ +/* Finds or creates a ft_unscaled_font for the filename/id from pattern. + * Returns a new reference to the unscaled font. + */ +static ft_unscaled_font_t * +_ft_unscaled_font_get_for_pattern (FcPattern *pattern) +{ + cairo_ft_cache_entry_t *entry; + cairo_ft_cache_key_t key; + cairo_cache_t *cache; + cairo_status_t status; + FcChar8 *filename; + int created_entry; + + if (FcPatternGetString (pattern, FC_FILE, 0, &filename) != FcResultMatch) + return NULL; + key.filename = (char *)filename; + + if (FcPatternGetInteger (pattern, FC_INDEX, 0, &key.id) != FcResultMatch) + return NULL; + + _lock_global_ft_cache (); + cache = _get_global_ft_cache (); + if (cache == NULL) { + _unlock_global_ft_cache (); + return NULL; + } + + status = _cairo_cache_lookup (cache, &key, (void **) &entry, &created_entry); + _unlock_global_ft_cache (); + if (status) + return NULL; + + if (!created_entry) + _cairo_unscaled_font_reference ((cairo_unscaled_font_t *)entry->unscaled); + + return entry->unscaled; +} + +static int +_has_unlocked_face (void *entry) +{ + cairo_ft_cache_entry_t *e = entry; -const cairo_font_backend_t cairo_ft_font_backend; + return (e->unscaled->lock == 0 && e->unscaled->face); +} + +/* Ensures that an unscaled font has a face object. If we exceed + * MAX_OPEN_FACES, try to close some. + */ +static FT_Face +_ft_unscaled_font_lock_face (ft_unscaled_font_t *unscaled) +{ + ft_cache_t *ftcache; + FT_Face face = NULL; + + if (unscaled->face) { + unscaled->lock++; + return unscaled->face; + } -static cairo_unscaled_font_t * -_cairo_ft_font_create (const char *family, + assert (!unscaled->from_face); + + _lock_global_ft_cache (); + ftcache = (ft_cache_t *) _get_global_ft_cache (); + assert (ftcache != NULL); + + while (ftcache->n_faces >= MAX_OPEN_FACES) { + cairo_ft_cache_entry_t *entry; + + entry = _cairo_cache_random_entry ((cairo_cache_t *)ftcache, _has_unlocked_face); + if (entry) { + FT_Done_Face (entry->unscaled->face); + entry->unscaled->face = NULL; + entry->unscaled->have_scale = 0; + ftcache->n_faces--; + } else { + break; + } + } + + if (FT_New_Face (ftcache->lib, + unscaled->filename, + unscaled->id, + &face) != FT_Err_Ok) + goto FAIL; + + unscaled->face = face; + unscaled->lock++; + ftcache->n_faces++; + + FAIL: + _unlock_global_ft_cache (); + return face; +} + +/* Unlock unscaled font locked with _ft_unscaled_font_lock_face + */ +static void +_ft_unscaled_font_unlock_face (ft_unscaled_font_t *unscaled) +{ + assert (unscaled->lock > 0); + + unscaled->lock--; +} + +static void +_compute_transform (ft_font_transform_t *sf, + cairo_font_scale_t *sc) +{ + cairo_matrix_t normalized; + double tx, ty; + + /* The font matrix has x and y "scale" components which we extract and + * use as character scale values. These influence the way freetype + * chooses hints, as well as selecting different bitmaps in + * hand-rendered fonts. We also copy the normalized matrix to + * freetype's transformation. + */ + + cairo_matrix_set_affine (&normalized, + sc->matrix[0][0], + sc->matrix[0][1], + sc->matrix[1][0], + sc->matrix[1][1], + 0, 0); + + _cairo_matrix_compute_scale_factors (&normalized, + &sf->x_scale, &sf->y_scale, + /* XXX */ 1); + cairo_matrix_scale (&normalized, 1.0 / sf->x_scale, 1.0 / sf->y_scale); + cairo_matrix_get_affine (&normalized, + &sf->shape[0][0], &sf->shape[0][1], + &sf->shape[1][0], &sf->shape[1][1], + &tx, &ty); +} + +/* Temporarily scales an unscaled font to the give scale. We catch + * scaling to the same size, since changing a FT_Face is expensive. + */ +static void +_ft_unscaled_font_set_scale (ft_unscaled_font_t *unscaled, + cairo_font_scale_t *scale) +{ + ft_font_transform_t sf; + FT_Matrix mat; + + assert (unscaled->face != NULL); + + if (unscaled->have_scale && + scale->matrix[0][0] == unscaled->current_scale.matrix[0][0] && + scale->matrix[0][1] == unscaled->current_scale.matrix[0][1] && + scale->matrix[1][0] == unscaled->current_scale.matrix[1][0] && + scale->matrix[1][1] == unscaled->current_scale.matrix[1][1]) + return; + + unscaled->have_scale = 1; + unscaled->current_scale = *scale; + + _compute_transform (&sf, scale); + + unscaled->x_scale = sf.x_scale; + unscaled->y_scale = sf.y_scale; + + mat.xx = DOUBLE_TO_16_16(sf.shape[0][0]); + mat.yx = - DOUBLE_TO_16_16(sf.shape[0][1]); + mat.xy = - DOUBLE_TO_16_16(sf.shape[1][0]); + mat.yy = DOUBLE_TO_16_16(sf.shape[1][1]); + + FT_Set_Transform(unscaled->face, &mat, NULL); + + FT_Set_Pixel_Sizes(unscaled->face, + (FT_UInt) sf.x_scale, + (FT_UInt) sf.y_scale); +} + +/* implement the font backend interface */ + +typedef struct { + cairo_font_t base; + FcPattern *pattern; + int load_flags; + ft_unscaled_font_t *unscaled; +} cairo_ft_font_t; + +/* for compatibility with older freetype versions */ +#ifndef FT_LOAD_TARGET_MONO +#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME +#endif + +/* The load flags passed to FT_Load_Glyph control aspects like hinting and + * antialiasing. Here we compute them from the fields of a FcPattern. + */ +static int +_get_load_flags (FcPattern *pattern) +{ + FcBool antialias, hinting, autohint; +#ifdef FC_HINT_STYLE + int hintstyle; +#endif + int load_flags = 0; + + /* disable antialiasing if requested */ + if (FcPatternGetBool (pattern, + FC_ANTIALIAS, 0, &antialias) != FcResultMatch) + antialias = FcTrue; + + if (antialias) + load_flags |= FT_LOAD_NO_BITMAP; + else + load_flags |= FT_LOAD_TARGET_MONO; + + /* disable hinting if requested */ + if (FcPatternGetBool (pattern, + FC_HINTING, 0, &hinting) != FcResultMatch) + hinting = FcTrue; + +#ifdef FC_HINT_STYLE + if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch) + hintstyle = FC_HINT_FULL; + + if (!hinting || hintstyle == FC_HINT_NONE) + load_flags |= FT_LOAD_NO_HINTING; + + switch (hintstyle) { + case FC_HINT_SLIGHT: + case FC_HINT_MEDIUM: + load_flags |= FT_LOAD_TARGET_LIGHT; + break; + default: + load_flags |= FT_LOAD_TARGET_NORMAL; + break; + } +#else /* !FC_HINT_STYLE */ + if (!hinting) + load_flags |= FT_LOAD_NO_HINTING; +#endif /* FC_FHINT_STYLE */ + + /* force autohinting if requested */ + if (FcPatternGetBool (pattern, + FC_AUTOHINT, 0, &autohint) != FcResultMatch) + autohint = FcFalse; + + if (autohint) + load_flags |= FT_LOAD_FORCE_AUTOHINT; + + return load_flags; +} + +/* Like the public cairo_ft_font_create, but takes a cairo_font_scale_t, + * rather than a cairo_font_t + */ +static cairo_font_t * +_ft_font_create (FcPattern *pattern, + cairo_font_scale_t *scale) +{ + cairo_ft_font_t *f = NULL; + ft_unscaled_font_t *unscaled = NULL; + + unscaled = _ft_unscaled_font_get_for_pattern (pattern); + if (unscaled == NULL) + return NULL; + + f = malloc (sizeof(cairo_ft_font_t)); + if (f == NULL) + goto FREE_UNSCALED; + + f->unscaled = unscaled; + f->pattern = pattern; + FcPatternReference (pattern); + f->load_flags = _get_load_flags (pattern); + + _cairo_font_init ((cairo_font_t *)f, scale, &cairo_ft_font_backend); + + return (cairo_font_t *)f; + + FREE_UNSCALED: + _cairo_unscaled_font_destroy ((cairo_unscaled_font_t *)unscaled); + + return NULL; +} + +static cairo_status_t +_cairo_ft_font_create (const char *family, cairo_font_slant_t slant, - cairo_font_weight_t weight) + cairo_font_weight_t weight, + cairo_font_scale_t *scale, + cairo_font_t **font) { - cairo_status_t status; - cairo_ft_font_t *font = NULL; + FcPattern *pattern, *resolved; + cairo_font_t *new_font; + FcResult result; int fcslant; int fcweight; - cairo_cache_t *cache; - cairo_ft_cache_entry_t *entry; - cairo_ft_cache_key_t key; - - key.pattern = FcPatternCreate (); - if (key.pattern == NULL) - goto FAIL; + ft_font_transform_t sf; - font = malloc (sizeof (cairo_ft_font_t)); - if (font == NULL) - goto FREE_PATTERN; + pattern = FcPatternCreate (); + if (!pattern) + return CAIRO_STATUS_NO_MEMORY; switch (weight) { @@ -356,46 +623,44 @@ _cairo_ft_font_create (const char *family, break; } - FcPatternAddString (key.pattern, FC_FAMILY, family); - FcPatternAddInteger (key.pattern, FC_SLANT, fcslant); - FcPatternAddInteger (key.pattern, FC_WEIGHT, fcweight); - - if (_cairo_unscaled_font_init (&font->base, &cairo_ft_font_backend)) + if (!FcPatternAddString (pattern, FC_FAMILY, family)) goto FREE_PATTERN; - - _lock_global_ft_cache (); - cache = _get_global_ft_cache (); - if (cache == NULL) { - _unlock_global_ft_cache (); + if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) + goto FREE_PATTERN; + if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) goto FREE_PATTERN; - } - status = _cairo_cache_lookup (cache, &key, (void **) &entry); - _unlock_global_ft_cache (); + _compute_transform (&sf, scale); - if (status) - goto FREE_PATTERN; + FcPatternAddInteger (pattern, FC_PIXEL_SIZE, sf.y_scale); - font->pattern = FcPatternDuplicate (entry->key.pattern); - if (font->pattern == NULL) + FcConfigSubstitute (NULL, pattern, FcMatchPattern); + FcDefaultSubstitute (pattern); + + resolved = FcFontMatch (NULL, pattern, &result); + if (!resolved) goto FREE_PATTERN; - font->val = entry->val; - _reference_font_val (font->val); - - return &font->base; + new_font = _ft_font_create (resolved, scale); - FREE_PATTERN: - FcPatternDestroy (key.pattern); + FcPatternDestroy (resolved); + FcPatternDestroy (pattern); - FAIL: - return NULL; + if (new_font) { + *font = new_font; + return CAIRO_STATUS_SUCCESS; + } else { + return CAIRO_STATUS_NO_MEMORY; /* A guess */ + } -} + FREE_PATTERN: + FcPatternDestroy (pattern); + return CAIRO_STATUS_NO_MEMORY; +} static void -_cairo_ft_font_destroy (void *abstract_font) +_cairo_ft_font_destroy_font (void *abstract_font) { cairo_ft_font_t * font = abstract_font; @@ -405,179 +670,94 @@ _cairo_ft_font_destroy (void *abstract_font) if (font->pattern != NULL) FcPatternDestroy (font->pattern); - _destroy_font_val (font->val); + _cairo_unscaled_font_destroy ((cairo_unscaled_font_t *)font->unscaled); free (font); } static void -_utf8_to_ucs4 (char const *utf8, - FT_ULong **ucs4, - int *nchars) +_cairo_ft_font_destroy_unscaled_font (void *abstract_font) { - int len = 0, step = 0; - int n = 0, alloc = 0; - FcChar32 u = 0; + ft_unscaled_font_t *unscaled = abstract_font; - if (utf8 == NULL || ucs4 == NULL || nchars == NULL) - return; + if (!unscaled->from_face) { + cairo_cache_t *cache; + cairo_ft_cache_key_t key; + + _lock_global_ft_cache (); + cache = _get_global_ft_cache (); + assert (cache); - len = strlen (utf8); - alloc = len; - *ucs4 = malloc (sizeof (FT_ULong) * alloc); - if (*ucs4 == NULL) - return; - - while (len && (step = FcUtf8ToUcs4(utf8, &u, len)) > 0) - { - if (n == alloc) - { - alloc *= 2; - *ucs4 = realloc (*ucs4, sizeof (FT_ULong) * alloc); - if (*ucs4 == NULL) - return; - } - (*ucs4)[n++] = u; - len -= step; - utf8 += step; + key.filename = unscaled->filename; + key.id = unscaled->id; + + _cairo_cache_remove (cache, &key); + + _unlock_global_ft_cache (); } - *nchars = n; -} - -/* - * Split a matrix into the component pieces of scale and shape - */ - -static void -_cairo_ft_font_compute_transform (ft_font_transform_t *sf, cairo_font_scale_t *sc) -{ - cairo_matrix_t normalized; - double tx, ty; - /* The font matrix has x and y "scale" components which we extract and - * use as character scale values. These influence the way freetype - * chooses hints, as well as selecting different bitmaps in - * hand-rendered fonts. We also copy the normalized matrix to - * freetype's transformation. - */ - - cairo_matrix_set_affine (&normalized, - sc->matrix[0][0], - sc->matrix[0][1], - sc->matrix[1][0], - sc->matrix[1][1], - 0, 0); - - _cairo_matrix_compute_scale_factors (&normalized, - &sf->x_scale, &sf->y_scale, - /* XXX */ 1); - cairo_matrix_scale (&normalized, 1.0 / sf->x_scale, 1.0 / sf->y_scale); - cairo_matrix_get_affine (&normalized, - &sf->shape[0][0], &sf->shape[0][1], - &sf->shape[1][0], &sf->shape[1][1], - &tx, &ty); -} - -/* - * Set the font transformation - */ - -static void -_cairo_ft_font_install_transform (ft_font_transform_t *sf, FT_Face face) -{ - FT_Matrix mat; - - mat.xx = DOUBLE_TO_16_16(sf->shape[0][0]); - mat.yx = -DOUBLE_TO_16_16(sf->shape[0][1]); - mat.xy = -DOUBLE_TO_16_16(sf->shape[1][0]); - mat.yy = DOUBLE_TO_16_16(sf->shape[1][1]); + if (unscaled == NULL) + return; - FT_Set_Transform(face, &mat, NULL); + if (!unscaled->from_face && unscaled->face) + FT_Done_Face (unscaled->face); - FT_Set_Char_Size(face, - (FT_F26Dot6) (sf->x_scale * 64.0), - (FT_F26Dot6) (sf->y_scale * 64.0), - 0, 0); + if (unscaled->filename) + free (unscaled->filename); + + free (unscaled); } static void -_install_font_scale (cairo_font_scale_t *sc, FT_Face face) +_cairo_ft_font_get_glyph_cache_key (void *abstract_font, + cairo_glyph_cache_key_t *key) { - cairo_matrix_t normalized; - double x_scale, y_scale; - double xx, xy, yx, yy, tx, ty; - FT_Matrix mat; - - /* The font matrix has x and y "scale" components which we extract and - * use as character scale values. These influence the way freetype - * chooses hints, as well as selecting different bitmaps in - * hand-rendered fonts. We also copy the normalized matrix to - * freetype's transformation. - */ - - cairo_matrix_set_affine (&normalized, - sc->matrix[0][0], - sc->matrix[0][1], - sc->matrix[1][0], - sc->matrix[1][1], - 0, 0); - - _cairo_matrix_compute_scale_factors (&normalized, &x_scale, &y_scale, - /* XXX */ 1); - cairo_matrix_scale (&normalized, 1.0 / x_scale, 1.0 / y_scale); - cairo_matrix_get_affine (&normalized, - &xx /* 00 */ , &yx /* 01 */, - &xy /* 10 */, &yy /* 11 */, - &tx, &ty); - - mat.xx = DOUBLE_TO_16_16(xx); - mat.xy = -DOUBLE_TO_16_16(xy); - mat.yx = -DOUBLE_TO_16_16(yx); - mat.yy = DOUBLE_TO_16_16(yy); - - FT_Set_Transform(face, &mat, NULL); + cairo_ft_font_t *font = abstract_font; - FT_Set_Pixel_Sizes(face, - (FT_UInt) x_scale, - (FT_UInt) y_scale); + key->unscaled = (cairo_unscaled_font_t *)font->unscaled; + key->scale = font->base.scale; + key->flags = font->load_flags; } static cairo_status_t _cairo_ft_font_text_to_glyphs (void *abstract_font, - cairo_font_scale_t *sc, const unsigned char *utf8, cairo_glyph_t **glyphs, int *nglyphs) { double x = 0., y = 0.; size_t i; - FT_ULong *ucs4 = NULL; + uint32_t *ucs4 = NULL; cairo_ft_font_t *font = abstract_font; - FT_Face face = font->val->face; + FT_Face face; cairo_glyph_cache_key_t key; cairo_image_glyph_cache_entry_t *val; - cairo_cache_t *cache; + cairo_cache_t *cache = NULL; + cairo_status_t status = CAIRO_STATUS_SUCCESS; - key.unscaled = &font->base; - key.scale = *sc; + _cairo_ft_font_get_glyph_cache_key (font, &key); - _utf8_to_ucs4 (utf8, &ucs4, nglyphs); - - if (ucs4 == NULL) - return CAIRO_STATUS_NO_MEMORY; + status = _cairo_utf8_to_ucs4 (utf8, -1, &ucs4, nglyphs); + if (!CAIRO_OK (status)) + return status; - *glyphs = (cairo_glyph_t *) malloc ((*nglyphs) * (sizeof (cairo_glyph_t))); - if (*glyphs == NULL) - { - free (ucs4); - return CAIRO_STATUS_NO_MEMORY; + face = cairo_ft_font_lock_face ((cairo_font_t *)font); + if (!face) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL1; } _cairo_lock_global_image_glyph_cache (); cache = _cairo_get_global_image_glyph_cache (); if (cache == NULL) { - _cairo_unlock_global_image_glyph_cache (); - return CAIRO_STATUS_NO_MEMORY; + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL2; + } + + *glyphs = (cairo_glyph_t *) malloc ((*nglyphs) * (sizeof (cairo_glyph_t))); + if (*glyphs == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL2; } for (i = 0; i < *nglyphs; i++) @@ -589,51 +769,62 @@ _cairo_ft_font_text_to_glyphs (void *abstract_font, val = NULL; key.index = (*glyphs)[i].index; - if (_cairo_cache_lookup (cache, &key, (void **) &val) + if (_cairo_cache_lookup (cache, &key, (void **) &val, NULL) != CAIRO_STATUS_SUCCESS || val == NULL) continue; x += val->extents.x_advance; y += val->extents.y_advance; } - _cairo_unlock_global_image_glyph_cache (); + FAIL2: + if (cache) + _cairo_unlock_global_image_glyph_cache (); + + cairo_ft_font_unlock_face ((cairo_font_t *)font); + + FAIL1: free (ucs4); - return CAIRO_STATUS_SUCCESS; + + return status; } static cairo_status_t _cairo_ft_font_font_extents (void *abstract_font, - cairo_font_scale_t *sc, cairo_font_extents_t *extents) { cairo_ft_font_t *font = abstract_font; - FT_Face face = font->val->face; - FT_Size_Metrics *metrics = &face->size->metrics; - ft_font_transform_t sf; + FT_Face face; + FT_Size_Metrics *metrics; + + face = _ft_unscaled_font_lock_face (font->unscaled); + if (!face) + return CAIRO_STATUS_NO_MEMORY; - _cairo_ft_font_compute_transform (&sf, sc); - _cairo_ft_font_install_transform (&sf, face); + metrics = &face->size->metrics; + _ft_unscaled_font_set_scale (font->unscaled, &font->base.scale); + /* * Get to unscaled metrics so that the upper level can get back to * user space */ - extents->ascent = DOUBLE_FROM_26_6(metrics->ascender) / sf.y_scale; - extents->descent = DOUBLE_FROM_26_6(metrics->descender) / sf.y_scale; - extents->height = DOUBLE_FROM_26_6(metrics->height) / sf.y_scale; - extents->max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) / sf.x_scale; + extents->ascent = DOUBLE_FROM_26_6(metrics->ascender) / font->unscaled->y_scale; + extents->descent = DOUBLE_FROM_26_6(metrics->descender) / font->unscaled->y_scale; + extents->height = DOUBLE_FROM_26_6(metrics->height) / font->unscaled->y_scale; + extents->max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) / font->unscaled->x_scale; /* FIXME: this doesn't do vertical layout atm. */ extents->max_y_advance = 0.0; + _ft_unscaled_font_unlock_face (font->unscaled); + return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_ft_font_glyph_extents (void *abstract_font, - cairo_font_scale_t *sc, cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents) @@ -670,14 +861,13 @@ _cairo_ft_font_glyph_extents (void *abstract_font, return CAIRO_STATUS_NO_MEMORY; } - key.unscaled = &font->base; - key.scale = *sc; + _cairo_ft_font_get_glyph_cache_key (font, &key); for (i = 0; i < num_glyphs; i++) { img = NULL; key.index = glyphs[i].index; - if (_cairo_cache_lookup (cache, &key, (void **) &img) + if (_cairo_cache_lookup (cache, &key, (void **) &img, NULL) != CAIRO_STATUS_SUCCESS || img == NULL) continue; @@ -721,7 +911,6 @@ _cairo_ft_font_glyph_extents (void *abstract_font, static cairo_status_t _cairo_ft_font_glyph_bbox (void *abstract_font, - cairo_font_scale_t *sc, const cairo_glyph_t *glyphs, int num_glyphs, cairo_box_t *bbox) @@ -747,16 +936,15 @@ _cairo_ft_font_glyph_bbox (void *abstract_font, return CAIRO_STATUS_NO_MEMORY; } - key.unscaled = &font->base; - key.scale = *sc; - + _cairo_ft_font_get_glyph_cache_key (font, &key); + for (i = 0; i < num_glyphs; i++) { img = NULL; key.index = glyphs[i].index; - if (_cairo_cache_lookup (cache, &key, (void **) &img) + if (_cairo_cache_lookup (cache, &key, (void **) &img, NULL) != CAIRO_STATUS_SUCCESS || img == NULL) continue; @@ -785,12 +973,15 @@ _cairo_ft_font_glyph_bbox (void *abstract_font, static cairo_status_t _cairo_ft_font_show_glyphs (void *abstract_font, - cairo_font_scale_t *sc, cairo_operator_t operator, - cairo_surface_t *source, + cairo_pattern_t *pattern, cairo_surface_t *surface, int source_x, int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, const cairo_glyph_t *glyphs, int num_glyphs) { @@ -798,9 +989,9 @@ _cairo_ft_font_show_glyphs (void *abstract_font, cairo_cache_t *cache; cairo_glyph_cache_key_t key; cairo_ft_font_t *font = abstract_font; + cairo_surface_pattern_t glyph_pattern; cairo_status_t status; - - double x, y; + int x, y; int i; _cairo_lock_global_image_glyph_cache (); @@ -808,47 +999,54 @@ _cairo_ft_font_show_glyphs (void *abstract_font, if (cache == NULL || font == NULL - || source == NULL + || pattern == NULL || surface == NULL || glyphs == NULL) { _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_NO_MEMORY; } - key.unscaled = &font->base; - key.scale = *sc; + key.unscaled = (cairo_unscaled_font_t *)font->unscaled; + key.scale = font->base.scale; + key.flags = font->load_flags; for (i = 0; i < num_glyphs; i++) { img = NULL; key.index = glyphs[i].index; - if (_cairo_cache_lookup (cache, &key, (void **) &img) + if (_cairo_cache_lookup (cache, &key, (void **) &img, NULL) != CAIRO_STATUS_SUCCESS || img == NULL || img->image == NULL) continue; - x = glyphs[i].x; - y = glyphs[i].y; + x = (int) floor (glyphs[i].x + 0.5); + y = (int) floor (glyphs[i].y + 0.5); + + _cairo_pattern_init_for_surface (&glyph_pattern, &(img->image->base)); - status = _cairo_surface_composite (operator, source, - &(img->image->base), + status = _cairo_surface_composite (operator, pattern, + &glyph_pattern.base, surface, - source_x + x + img->size.x, - source_y + y + img->size.y, + x + img->size.x, + y + img->size.y, 0, 0, x + img->size.x, y + img->size.y, (double) img->size.width, (double) img->size.height); + _cairo_pattern_fini (&glyph_pattern.base); + if (status) { - _cairo_unlock_global_image_glyph_cache (); + _cairo_unlock_global_image_glyph_cache (); return status; } } + _cairo_unlock_global_image_glyph_cache (); + return CAIRO_STATUS_SUCCESS; } @@ -932,7 +1130,6 @@ _cubic_to (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *closur static cairo_status_t _cairo_ft_font_glyph_path (void *abstract_font, - cairo_font_scale_t *sc, cairo_glyph_t *glyphs, int num_glyphs, cairo_path_t *path) @@ -940,6 +1137,7 @@ _cairo_ft_font_glyph_path (void *abstract_font, int i; cairo_ft_font_t *font = abstract_font; FT_GlyphSlot glyph; + FT_Face face; FT_Error error; FT_Outline_Funcs outline_funcs = { _move_to, @@ -949,10 +1147,12 @@ _cairo_ft_font_glyph_path (void *abstract_font, 0, /* shift */ 0, /* delta */ }; + + face = cairo_ft_font_lock_face (abstract_font); + if (!face) + return CAIRO_STATUS_NO_MEMORY; - glyph = font->val->face->glyph; - - _install_font_scale (sc, font->val->face); + glyph = face->glyph; for (i = 0; i < num_glyphs; i++) { @@ -961,7 +1161,7 @@ _cairo_ft_font_glyph_path (void *abstract_font, 0, DOUBLE_TO_16_16 (-1.0), }; - error = FT_Load_Glyph (font->val->face, glyphs[i].index, FT_LOAD_DEFAULT); + error = FT_Load_Glyph (font->unscaled->face, glyphs[i].index, font->load_flags | FT_LOAD_NO_BITMAP); /* XXX: What to do in this error case? */ if (error) continue; @@ -977,32 +1177,39 @@ _cairo_ft_font_glyph_path (void *abstract_font, FT_Outline_Decompose (&glyph->outline, &outline_funcs, path); } _cairo_path_close_path (path); + + cairo_ft_font_unlock_face (abstract_font); return CAIRO_STATUS_SUCCESS; } - static cairo_status_t -_cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) +_cairo_ft_font_create_glyph (cairo_image_glyph_cache_entry_t *val) { - cairo_ft_font_t *font = (cairo_ft_font_t *)val->key.unscaled; + ft_unscaled_font_t *unscaled = (ft_unscaled_font_t *)val->key.unscaled; FT_GlyphSlot glyphslot; unsigned int width, height, stride; + FT_Face face; FT_Outline *outline; FT_BBox cbox; FT_Bitmap bitmap; FT_Glyph_Metrics *metrics; - ft_font_transform_t sf; + cairo_status_t status = CAIRO_STATUS_SUCCESS; - glyphslot = font->val->face->glyph; + glyphslot = unscaled->face->glyph; metrics = &glyphslot->metrics; - _cairo_ft_font_compute_transform (&sf, &val->key.scale); - _cairo_ft_font_install_transform (&sf, font->val->face); - - if (FT_Load_Glyph (font->val->face, val->key.index, FT_LOAD_DEFAULT) != 0) + face = _ft_unscaled_font_lock_face (unscaled); + if (!face) return CAIRO_STATUS_NO_MEMORY; + _ft_unscaled_font_set_scale (unscaled, &val->key.scale); + + if (FT_Load_Glyph (face, val->key.index, val->key.flags) != 0) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + /* * Note: the font's coordinate system is upside down from ours, so the * Y coordinates of the bearing and advance need to be negated. @@ -1011,11 +1218,11 @@ _cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) * by FreeType */ - val->extents.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) / sf.x_scale; - val->extents.y_bearing = -DOUBLE_FROM_26_6 (metrics->horiBearingY) / sf.y_scale; + val->extents.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) / unscaled->x_scale; + val->extents.y_bearing = -DOUBLE_FROM_26_6 (metrics->horiBearingY) / unscaled->y_scale; - val->extents.width = DOUBLE_FROM_26_6 (metrics->width) / sf.x_scale; - val->extents.height = DOUBLE_FROM_26_6 (metrics->height) / sf.y_scale; + val->extents.width = DOUBLE_FROM_26_6 (metrics->width) / unscaled->x_scale; + val->extents.height = DOUBLE_FROM_26_6 (metrics->height) / unscaled->y_scale; /* * use untransformed advance values @@ -1023,8 +1230,8 @@ _cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) should provide FT_LOAD_VERTICAL_LAYOUT */ - val->extents.x_advance = DOUBLE_FROM_26_6 (font->val->face->glyph->metrics.horiAdvance) / sf.x_scale; - val->extents.y_advance = 0 / sf.y_scale; + val->extents.x_advance = DOUBLE_FROM_26_6 (face->glyph->metrics.horiAdvance) / unscaled->x_scale; + val->extents.y_advance = 0 / unscaled->y_scale; outline = &glyphslot->outline; @@ -1052,14 +1259,16 @@ _cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) bitmap.buffer = calloc (1, stride * height); if (bitmap.buffer == NULL) { - return CAIRO_STATUS_NO_MEMORY; - }; + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } FT_Outline_Translate (outline, -cbox.xMin, -cbox.yMin); if (FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap) != 0) { free (bitmap.buffer); - return CAIRO_STATUS_NO_MEMORY; + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; } val->image = (cairo_image_surface_t *) @@ -1068,7 +1277,8 @@ _cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) width, height, stride); if (val->image == NULL) { free (bitmap.buffer); - return CAIRO_STATUS_NO_MEMORY; + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; } _cairo_image_surface_assume_ownership_of_data (val->image); @@ -1084,138 +1294,245 @@ _cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) val->size.x = (short) (cbox.xMin >> 6); val->size.y = - (short) (cbox.yMax >> 6); - return CAIRO_STATUS_SUCCESS; + FAIL: + _ft_unscaled_font_unlock_face (unscaled); + + return status; } const cairo_font_backend_t cairo_ft_font_backend = { _cairo_ft_font_create, - _cairo_ft_font_destroy, + _cairo_ft_font_destroy_font, + _cairo_ft_font_destroy_unscaled_font, _cairo_ft_font_font_extents, _cairo_ft_font_text_to_glyphs, _cairo_ft_font_glyph_extents, _cairo_ft_font_glyph_bbox, _cairo_ft_font_show_glyphs, _cairo_ft_font_glyph_path, + _cairo_ft_font_get_glyph_cache_key, _cairo_ft_font_create_glyph }; - /* implement the platform-specific interface */ +/** + * cairo_ft_font_create: + * @pattern: A fully resolved fontconfig + * pattern. A pattern can be resolved, by, among other things, calling + * FcConfigSubstitute(), FcDefaultSubstitute(), then + * FcFontMatch(). Cairo will call FcPatternReference() on this + * pattern, so you should not further modify the pattern, but you can + * release your reference to the pattern with FcPatternDestroy() if + * you no longer need to access it. + * @scale: The scale at which this font will be used. The + * scale is given by multiplying the font matrix (see + * cairo_transform_font()) by the current transformation matrix. + * The translation elements of the resulting matrix are ignored. + * + * Creates a new font for the FreeType font backend based on a + * fontconfig pattern. This font can then be used with + * cairo_set_font(), cairo_font_glyph_extents(), or FreeType backend + * specific functions like cairo_ft_font_lock_face(). + * + * Return value: a newly created #cairo_font_t. Free with + * cairo_font_destroy() when you are done using it. + **/ cairo_font_t * -cairo_ft_font_create (FT_Library ft_library, FcPattern *pattern) +cairo_ft_font_create (FcPattern *pattern, + cairo_matrix_t *scale) { - cairo_font_scale_t scale; - cairo_font_t *scaled; - cairo_ft_font_t *f = NULL; - ft_font_val_t *v = NULL; - FcPattern *dup; - - scale.matrix[0][0] = 1.; - scale.matrix[0][1] = 0.; - scale.matrix[1][0] = 0.; - scale.matrix[1][1] = 1.; - - scaled = malloc (sizeof (cairo_font_t)); - if (scaled == NULL) - goto FAIL; - - dup = FcPatternDuplicate(pattern); - if (dup == NULL) - goto FREE_SCALED; - - v = _create_from_library_and_pattern (ft_library, pattern); - if (v == NULL) - goto FREE_PATTERN; - - f = malloc (sizeof(cairo_ft_font_t)); - if (f == NULL) - goto FREE_VAL; - - if (_cairo_unscaled_font_init (&f->base, &cairo_ft_font_backend)) - goto FREE_VAL; - - f->pattern = dup; - f->val = v; - - _cairo_font_init (scaled, &scale, &f->base); - - return scaled; - - FREE_VAL: - _destroy_font_val (v); - - FREE_PATTERN: - FcPatternDestroy (dup); + cairo_font_scale_t sc; + double tx, ty; - FREE_SCALED: - free (scaled); + cairo_matrix_get_affine (scale, + &sc.matrix[0][0], &sc.matrix[0][1], + &sc.matrix[1][0], &sc.matrix[1][1], + &tx, &ty); - FAIL: - return NULL; + return _ft_font_create (pattern, &sc); } +/** + * cairo_ft_font_create_for_ft_face: + * @face: A FreeType face object, already opened. This must + * be kept around until the font object's refcount drops to + * zero and it is freed. The font object can be kept alive by + * internal caching, so it's safest to keep the face object + * around forever. + * @load_flags: The flags to pass to FT_Load_Glyph when loading + * glyphs from the font. These flags control aspects of + * rendering such as hinting and antialiasing. See the FreeType + * docs for full information. + * @scale: The scale at which this font will be used. The + * scale is given by multiplying the font matrix (see + * cairo_transform_font()) by the current transformation matrix. + * The translation elements of the resulting matrix are ignored. + * + * Creates a new font forthe FreeType font backend from a pre-opened + * FreeType face. This font can then be used with cairo_set_font(), + * cairo_font_glyph_extents(), or FreeType backend specific + * functions like cairo_ft_font_lock_face() Cairo will determine the + * pixel size and transformation from the @scale parameter and call + * FT_Set_Transform() and FT_Set_Pixel_Sizes(). + * + * Return value: a newly created #cairo_font_t. Free with + * cairo_font_destroy() when you are done using it. + **/ cairo_font_t * -cairo_ft_font_create_for_ft_face (FT_Face face) +cairo_ft_font_create_for_ft_face (FT_Face face, + int load_flags, + cairo_matrix_t *scale) { - cairo_font_scale_t scale; - cairo_font_t *scaled; cairo_ft_font_t *f = NULL; - ft_font_val_t *v = NULL; - - scale.matrix[0][0] = 1.; - scale.matrix[0][1] = 0.; - scale.matrix[1][0] = 0.; - scale.matrix[1][1] = 1.; - - scaled = malloc (sizeof (cairo_font_t)); - if (scaled == NULL) - goto FAIL; + ft_unscaled_font_t *unscaled = NULL; + cairo_font_scale_t sc; + double tx, ty; - v = _create_from_face (face, 0); - if (v == NULL) - goto FREE_SCALED; + unscaled = _ft_unscaled_font_create_from_face (face); + if (unscaled == NULL) + return NULL; f = malloc (sizeof(cairo_ft_font_t)); if (f == NULL) - goto FREE_VAL; + goto FREE_UNSCALED; - _cairo_unscaled_font_init (&f->base, &cairo_ft_font_backend); + f->unscaled = unscaled; f->pattern = NULL; - f->val = v; + f->load_flags = load_flags; - _cairo_font_init (scaled, &scale, &f->base); + cairo_matrix_get_affine (scale, + &sc.matrix[0][0], &sc.matrix[0][1], + &sc.matrix[1][0], &sc.matrix[1][1], + &tx, &ty); - return scaled; + _cairo_font_init ((cairo_font_t *)f, &sc, &cairo_ft_font_backend); - FREE_VAL: - _destroy_font_val (v); + return (cairo_font_t *)f; - FREE_SCALED: - free (scaled); + FREE_UNSCALED: + _cairo_unscaled_font_destroy ((cairo_unscaled_font_t *)unscaled); - FAIL: return NULL; } + +/** + * cairo_ft_font_lock_face: + * @ft_font: A #cairo_font_t from the FreeType font backend. Such an + * object can be created with cairo_ft_font_create() or + * cairo_ft_font_create_for_ft_face(). On some platforms the font from + * cairo_current_font() will also be a FreeType font, but using this + * functionality with fonts you don't create yourself is not + * recommended. + * + * cairo_ft_font_lock_face() gets the #FT_Face object from a FreeType + * backend font and scales it appropriately for the font. You must + * release the face with cairo_ft_font_unlock_face() + * when you are done using it. Since the #FT_Face object can be + * shared between multiple #cairo_font_t objects, you must not + * lock any other font objects until you unlock this one. A count is + * kept of the number of times cairo_ft_font_lock_face() is + * called. cairo_ft_font_unlock_face() must be called the same number + * of times. + * + * You must be careful when using this function in a library or in a + * threaded application, because other threads may lock faces that + * share the same #FT_Face object. For this reason, you must call + * cairo_ft_lock() before locking any face objects, and + * cairo_ft_unlock() after you are done. (These functions are not yet + * implemented, so this function cannot be currently safely used in a + * threaded application.) + + * Return value: The #FT_Face object for @font, scaled appropriately. + **/ FT_Face -cairo_ft_font_face (cairo_font_t *abstract_font) +cairo_ft_font_lock_face (cairo_font_t *abstract_font) { - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font->unscaled; + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + FT_Face face; - if (font == NULL || font->val == NULL) - return NULL; + face = _ft_unscaled_font_lock_face (font->unscaled); + if (!face) + return NULL; + + _ft_unscaled_font_set_scale (font->unscaled, &font->base.scale); + + return face; +} - return font->val->face; +/** + * cairo_ft_font_unlock_face: + * @ft_font: A #cairo_font_t from the FreeType font backend. Such an + * object can be created with cairo_ft_font_create() or + * cairo_ft_font_create_for_ft_face(). On some platforms the font from + * cairo_current_font() will also be a FreeType font, but using this + * functionality with fonts you don't create yourself is not + * recommended. + * + * Releases a face obtained with cairo_ft_font_lock_face(). See the + * documentation for that function for full details. + **/ +void +cairo_ft_font_unlock_face (cairo_font_t *abstract_font) +{ + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + + _ft_unscaled_font_unlock_face (font->unscaled); } +/** + * cairo_ft_font_get_pattern: + * @ft_font: A #cairo_font_t from the FreeType font backend. Such an + * object can be created with cairo_ft_font_create() or + * cairo_ft_font_create_for_ft_face(). On some platforms the font from + * cairo_current_font() will also be a FreeType font, but using this + * functionality with fonts you don't create yourself is not + * recommended. + * + * cairo_ft_font_get_pattern() gets the #FcPattern for a FreeType + * backend font. + + * Return value: The #FcPattenr for @font. The return value is owned + * by the font, so you must not modify it, and must call + * FcPatternReference() to keep a persistant reference to the + * pattern. If the font was created with cairo_ft_font_create_for_ft_face() + * returns %NULL. + **/ FcPattern * -cairo_ft_font_pattern (cairo_font_t *abstract_font) +cairo_ft_font_get_pattern (cairo_font_t *abstract_font) { - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font->unscaled; + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; if (font == NULL) return NULL; return font->pattern; } + +/* We expose our unscaled font implementation internally for the the + * PDF backend, which needs to keep track of the the different + * fonts-on-disk used by a document, so it can embed them. + */ +cairo_unscaled_font_t * +_cairo_ft_font_get_unscaled_font (cairo_font_t *abstract_font) +{ + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + + return (cairo_unscaled_font_t *)font->unscaled; +} + +/* This differs from _cairo_ft_scaled_font_lock_face in that it doesn't + * set the scale on the face, but just returns it at the last scale. + */ +FT_Face +_cairo_ft_unscaled_font_lock_face (cairo_unscaled_font_t *unscaled_font) +{ + return _ft_unscaled_font_lock_face ((ft_unscaled_font_t *)unscaled_font); +} + +void +_cairo_ft_unscaled_font_unlock_face (cairo_unscaled_font_t *unscaled_font) +{ + _ft_unscaled_font_unlock_face ((ft_unscaled_font_t *)unscaled_font); +} diff --git a/src/cairo-ft-private.h b/src/cairo-ft-private.h new file mode 100644 index 000000000..37a6feecc --- /dev/null +++ b/src/cairo-ft-private.h @@ -0,0 +1,63 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * 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 Red Hat, Inc. + * + * Contributor(s): + * Graydon Hoare <graydon@redhat.com> + * Owen Taylor <otaylor@redhat.com> + */ + +#ifndef CAIRO_FT_PRIVATE_H +#define CAIRO_FT_PRIVATE_H + +#include <cairo-ft.h> +#include <cairoint.h> + +#ifdef CAIRO_HAS_FT_FONT + +CAIRO_BEGIN_DECLS + +/* These functions are needed by the PDF backend, which needs to keep track of the + * the different fonts-on-disk used by a document, so it can embed them + */ +cairo_private cairo_unscaled_font_t * +_cairo_ft_font_get_unscaled_font (cairo_font_t *font); + +cairo_private FT_Face +_cairo_ft_unscaled_font_lock_face (cairo_unscaled_font_t *unscaled_font); + +cairo_private void +_cairo_ft_unscaled_font_unlock_face (cairo_unscaled_font_t *unscaled_font); + +CAIRO_END_DECLS + +#endif /* CAIRO_HAS_FT_FONT */ + +#endif /* CAIRO_FT_PRIVATE_H */ diff --git a/src/cairo-ft.h b/src/cairo-ft.h index 57d439ab2..f10c67d80 100644 --- a/src/cairo-ft.h +++ b/src/cairo-ft.h @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -27,17 +27,18 @@ * * The Original Code is the cairo graphics library. * - * The Initial Developer of the Original Code is University of Southern - * California. + * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Graydon Hoare <graydon@redhat.com> + * Owen Taylor <otaylor@redhat.com> */ -#include <cairo.h> - #ifndef CAIRO_FT_H #define CAIRO_FT_H + +#include <cairo.h> + #ifdef CAIRO_HAS_FT_FONT /* Fontconfig/Freetype platform-specific font interface */ @@ -46,17 +47,27 @@ #include <ft2build.h> #include FT_FREETYPE_H +CAIRO_BEGIN_DECLS + cairo_font_t * -cairo_ft_font_create (FT_Library ft_library, FcPattern *pattern); +cairo_ft_font_create (FcPattern *pattern, + cairo_matrix_t *scale); cairo_font_t * -cairo_ft_font_create_for_ft_face (FT_Face face); +cairo_ft_font_create_for_ft_face (FT_Face face, + int load_flags, + cairo_matrix_t *scale); FT_Face -cairo_ft_font_face (cairo_font_t *ft_font); +cairo_ft_font_lock_face (cairo_font_t *ft_font); + +void +cairo_ft_font_unlock_face (cairo_font_t *ft_font); FcPattern * -cairo_ft_font_pattern (cairo_font_t *ft_font); +cairo_ft_font_get_pattern (cairo_font_t *ft_font); + +CAIRO_END_DECLS #endif /* CAIRO_HAS_FT_FONT */ #endif /* CAIRO_FT_H */ diff --git a/src/cairo-glitz-surface.c b/src/cairo-glitz-surface.c index 69fc82f2e..ee664e1cc 100644 --- a/src/cairo-glitz-surface.c +++ b/src/cairo-glitz-surface.c @@ -21,30 +21,12 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * Author: David Reveman <c99drn@cs.umu.se> + * Author: David Reveman <davidr@novell.com> */ #include "cairoint.h" #include "cairo-glitz.h" -#define GLITZ_FIXED_TO_FLOAT(f) \ - (((glitz_float_t) (f)) / 65536) - -#define GLITZ_FIXED_LINE_X_TO_FLOAT(line, v) \ - (((glitz_float_t) \ - ((line).p1.x + (cairo_fixed_16_16_t) \ - (((cairo_fixed_32_32_t) ((v) - (line).p1.y) * \ - ((line).p2.x - (line).p1.x)) / \ - ((line).p2.y - (line).p1.y)))) / 65536) - -#define GLITZ_FIXED_LINE_X_CEIL_TO_FLOAT(line, v) \ - (((glitz_float_t) \ - ((line).p1.x + (cairo_fixed_16_16_t) \ - (((((line).p2.y - (line).p1.y) - 1) + \ - ((cairo_fixed_32_32_t) ((v) - (line).p1.y) * \ - ((line).p2.x - (line).p1.x))) / \ - ((line).p2.y - (line).p1.y)))) / 65536) - void cairo_set_target_glitz (cairo_t *cr, glitz_surface_t *surface) { @@ -65,13 +47,11 @@ cairo_set_target_glitz (cairo_t *cr, glitz_surface_t *surface) } typedef struct _cairo_glitz_surface { - cairo_surface_t base; - - glitz_surface_t *surface; - glitz_format_t *format; + cairo_surface_t base; - cairo_pattern_t pattern; - cairo_box_t pattern_box; + glitz_surface_t *surface; + glitz_format_t *format; + pixman_region16_t *clip; } cairo_glitz_surface_t; static void @@ -79,11 +59,60 @@ _cairo_glitz_surface_destroy (void *abstract_surface) { cairo_glitz_surface_t *surface = abstract_surface; + if (surface->clip) + { + glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); + pixman_region_destroy (surface->clip); + } + glitz_surface_destroy (surface->surface); + free (surface); +} - _cairo_pattern_fini (&surface->pattern); +static glitz_format_name_t +_glitz_format (cairo_format_t format) +{ + switch (format) { + default: + case CAIRO_FORMAT_ARGB32: + return GLITZ_STANDARD_ARGB32; + case CAIRO_FORMAT_RGB24: + return GLITZ_STANDARD_RGB24; + case CAIRO_FORMAT_A8: + return GLITZ_STANDARD_A8; + case CAIRO_FORMAT_A1: + return GLITZ_STANDARD_A1; + } +} - free (surface); +static cairo_surface_t * +_cairo_glitz_surface_create_similar (void *abstract_src, + cairo_format_t format, + int draw, + int width, + int height) +{ + cairo_glitz_surface_t *src = abstract_src; + cairo_surface_t *crsurface; + glitz_drawable_t *drawable; + glitz_surface_t *surface; + glitz_format_t *gformat; + + drawable = glitz_surface_get_drawable (src->surface); + + gformat = glitz_find_standard_format (drawable, _glitz_format (format)); + if (!gformat) + return NULL; + + surface = glitz_surface_create (drawable, gformat, width, height, 0, NULL); + if (!surface) + return NULL; + + crsurface = cairo_glitz_surface_create (surface); + + glitz_surface_destroy (surface); + + return crsurface; } static double @@ -92,31 +121,54 @@ _cairo_glitz_surface_pixels_per_inch (void *abstract_surface) return 96.0; } -static cairo_image_surface_t * -_cairo_glitz_surface_get_image (void *abstract_surface) +static cairo_status_t +_cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, + cairo_rectangle_t *interest, + cairo_image_surface_t **image_out, + cairo_rectangle_t *rect_out) { - cairo_glitz_surface_t *surface = abstract_surface; cairo_image_surface_t *image; - char *pixels; - int width, height; - cairo_format_masks_t format; - glitz_buffer_t *buffer; - glitz_pixel_format_t pf; - - if (surface->pattern.type != CAIRO_PATTERN_SURFACE) { - cairo_box_t box; - - box.p1.x = box.p1.y = 0; - box.p2.x = surface->pattern_box.p2.x; - box.p2.y = surface->pattern_box.p2.y; - - return _cairo_pattern_get_image (&surface->pattern, &box); + int x1, y1, x2, y2; + int width, height; + char *pixels; + cairo_format_masks_t format; + glitz_buffer_t *buffer; + glitz_pixel_format_t pf; + + x1 = 0; + y1 = 0; + x2 = glitz_surface_get_width (surface->surface); + y2 = glitz_surface_get_height (surface->surface); + + if (interest) + { + if (interest->x > x1) + x1 = interest->x; + if (interest->y > y1) + y1 = interest->y; + if (interest->x + interest->width < x2) + x2 = interest->x + interest->width; + if (interest->y + interest->height < y2) + y2 = interest->y + interest->height; + + if (x1 >= x2 || y1 >= y2) + { + *image_out = NULL; + return CAIRO_STATUS_SUCCESS; + } } + width = x2 - x1; + height = y2 - y1; - width = glitz_surface_get_width (surface->surface); - height = glitz_surface_get_height (surface->surface); - + if (rect_out) + { + rect_out->x = x1; + rect_out->y = y1; + rect_out->width = width; + rect_out->height = height; + } + if (surface->format->type == GLITZ_FORMAT_TYPE_COLOR) { if (surface->format->color.red_size > 0) { format.bpp = 32; @@ -149,21 +201,24 @@ _cairo_glitz_surface_get_image (void *abstract_surface) pf.masks.blue_mask = format.blue_mask; pf.xoffset = 0; pf.skip_lines = 0; + + /* XXX: we should eventually return images with negative stride, + need to verify that libpixman have no problem with this first. */ pf.bytes_per_line = (((width * format.bpp) / 8) + 3) & -4; pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; pixels = malloc (height * pf.bytes_per_line); if (!pixels) - return NULL; + return CAIRO_STATUS_NO_MEMORY; buffer = glitz_buffer_create_for_data (pixels); if (!buffer) { free (pixels); - return NULL; + return CAIRO_STATUS_NO_MEMORY; } glitz_get_pixels (surface->surface, - 0, 0, + x1, y1, width, height, &pf, buffer); @@ -175,27 +230,38 @@ _cairo_glitz_surface_get_image (void *abstract_surface) &format, width, height, pf.bytes_per_line); - + + if (!image) + { + free (pixels); + return CAIRO_STATUS_NO_MEMORY; + } + _cairo_image_surface_assume_ownership_of_data (image); _cairo_image_surface_set_repeat (image, surface->base.repeat); _cairo_image_surface_set_matrix (image, &(surface->base.matrix)); - return image; + *image_out = image; + + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_glitz_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +_cairo_glitz_surface_set_image (void *abstract_surface, + cairo_image_surface_t *image, + int x_dst, + int y_dst) { cairo_glitz_surface_t *surface = abstract_surface; - glitz_buffer_t *buffer; - glitz_pixel_format_t pf; - pixman_format_t *format; - int am, rm, gm, bm; + glitz_buffer_t *buffer; + glitz_pixel_format_t pf; + pixman_format_t *format; + int am, rm, gm, bm; + char *data; format = pixman_image_get_format (image->pixman_image); - if (format == NULL) + if (!format) return CAIRO_STATUS_NO_MEMORY; pixman_format_get_masks (format, &pf.masks.bpp, &am, &rm, &gm, &bm); @@ -206,15 +272,27 @@ _cairo_glitz_surface_set_image (void *abstract_surface, pf.masks.blue_mask = bm; pf.xoffset = 0; pf.skip_lines = 0; - pf.bytes_per_line = image->stride; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; - buffer = glitz_buffer_create_for_data (image->data); + /* check for negative stride */ + if (image->stride < 0) + { + pf.bytes_per_line = -image->stride; + pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; + data = (char *) image->data + image->stride * (image->height - 1); + } + else + { + pf.bytes_per_line = image->stride; + pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; + data = (char *) image->data; + } + + buffer = glitz_buffer_create_for_data (data); if (!buffer) return CAIRO_STATUS_NO_MEMORY; glitz_set_pixels (surface->surface, - 0, 0, + x_dst, y_dst, image->width, image->height, &pf, buffer); @@ -225,63 +303,118 @@ _cairo_glitz_surface_set_image (void *abstract_surface, } static cairo_status_t -_cairo_glitz_surface_set_matrix (void *abstract_surface, - cairo_matrix_t *matrix) +_cairo_glitz_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) { cairo_glitz_surface_t *surface = abstract_surface; - glitz_transform_t transform; - transform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); - transform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); - transform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); + *image_extra = NULL; + + return _cairo_glitz_surface_get_image (surface, NULL, image_out, NULL); +} - transform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); - transform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); - transform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); +static void +_cairo_glitz_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} - transform.matrix[2][0] = 0; - transform.matrix[2][1] = 0; - transform.matrix[2][2] = 1 << 16; +static cairo_status_t +_cairo_glitz_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect_out, + void **image_extra) +{ + cairo_glitz_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; - glitz_surface_set_transform (surface->surface, &transform); + status = _cairo_glitz_surface_get_image (surface, interest_rect, &image, + image_rect_out); + if (status) + return status; - return CAIRO_STATUS_SUCCESS; + *image_out = image; + *image_extra = NULL; + + return status; } +static void +_cairo_glitz_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ + cairo_glitz_surface_t *surface = abstract_surface; + + _cairo_glitz_surface_set_image (surface, image, + image_rect->x, image_rect->y); + + cairo_surface_destroy (&image->base); +} + + static cairo_status_t -_cairo_glitz_surface_set_filter (void *abstract_surface, cairo_filter_t filter) +_cairo_glitz_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { cairo_glitz_surface_t *surface = abstract_surface; - glitz_filter_t glitz_filter; + cairo_glitz_surface_t *clone; - switch (filter) { - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - glitz_filter = GLITZ_FILTER_NEAREST; - break; - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - default: - glitz_filter = GLITZ_FILTER_BILINEAR; - break; + if (src->backend == surface->base.backend) + { + *clone_out = src; + cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; } + else if (_cairo_surface_is_image (src)) + { + cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; + + clone = (cairo_glitz_surface_t *) + _cairo_glitz_surface_create_similar (surface, image_src->format, 0, + image_src->width, + image_src->height); + if (!clone) + return CAIRO_STATUS_NO_MEMORY; - glitz_surface_set_filter (surface->surface, glitz_filter, NULL, 0); + _cairo_glitz_surface_set_image (clone, image_src, 0, 0); + + *clone_out = &clone->base; - return CAIRO_STATUS_SUCCESS; + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; } -static cairo_status_t -_cairo_glitz_surface_set_repeat (void *abstract_surface, int repeat) +static void +_cairo_glitz_surface_set_matrix (cairo_glitz_surface_t *surface, + cairo_matrix_t *matrix) { - cairo_glitz_surface_t *surface = abstract_surface; + glitz_transform_t transform; + + transform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); + transform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); + transform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); - glitz_surface_set_fill (surface->surface, - (repeat)? GLITZ_FILL_REPEAT: - GLITZ_FILL_TRANSPARENT); + transform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); + transform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); + transform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); - return CAIRO_STATUS_SUCCESS; + transform.matrix[2][0] = 0; + transform.matrix[2][1] = 0; + transform.matrix[2][2] = 1 << 16; + + glitz_surface_set_transform (surface->surface, &transform); } static glitz_operator_t @@ -318,32 +451,6 @@ _glitz_operator (cairo_operator_t op) } } -static glitz_surface_t * -_glitz_surface_create_solid (glitz_surface_t *other, - glitz_format_name_t format_name, - glitz_color_t *color) -{ - glitz_drawable_t *drawable; - glitz_format_t *format; - glitz_surface_t *surface; - - drawable = glitz_surface_get_drawable (other); - - format = glitz_find_standard_format (drawable, format_name); - if (format == NULL) - return NULL; - - surface = glitz_surface_create (drawable, format, 1, 1); - if (surface == NULL) - return NULL; - - glitz_set_rectangle (surface, color, 0, 0, 1, 1); - - glitz_surface_set_fill (surface, GLITZ_FILL_REPEAT); - - return surface; -} - static glitz_status_t _glitz_ensure_target (glitz_surface_t *surface) { @@ -355,7 +462,6 @@ _glitz_ensure_target (glitz_surface_t *surface) glitz_drawable_format_t templ; glitz_format_t *format; glitz_drawable_t *pbuffer; - glitz_pbuffer_attributes_t attributes; unsigned long mask; int i; @@ -397,21 +503,13 @@ _glitz_ensure_target (glitz_surface_t *surface) if (!dformat) return CAIRO_INT_STATUS_UNSUPPORTED; - attributes.width = glitz_surface_get_width (surface); - attributes.height = glitz_surface_get_height (surface); - mask = GLITZ_PBUFFER_WIDTH_MASK | GLITZ_PBUFFER_HEIGHT_MASK; - - pbuffer = glitz_create_pbuffer_drawable (drawable, dformat, - &attributes, mask); + pbuffer = + glitz_create_pbuffer_drawable (drawable, dformat, + glitz_surface_get_width (surface), + glitz_surface_get_height (surface)); if (!pbuffer) return CAIRO_INT_STATUS_UNSUPPORTED; - if (glitz_drawable_get_width (pbuffer) < attributes.width || - glitz_drawable_get_height (pbuffer) < attributes.height) { - glitz_drawable_destroy (pbuffer); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - glitz_surface_attach (surface, pbuffer, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR, 0, 0); @@ -422,388 +520,711 @@ _glitz_ensure_target (glitz_surface_t *surface) return CAIRO_STATUS_SUCCESS; } -static glitz_format_name_t -_glitz_format (cairo_format_t format) +typedef struct _cairo_glitz_surface_attributes { + cairo_surface_attributes_t base; + + glitz_fill_t fill; + glitz_filter_t filter; + glitz_fixed16_16_t *params; + int n_params; + cairo_bool_t acquired; +} cairo_glitz_surface_attributes_t; + +static cairo_int_status_t +_cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, + cairo_glitz_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_glitz_surface_t **surface_out, + cairo_glitz_surface_attributes_t *attr) { - switch (format) { + cairo_glitz_surface_t *src = NULL; + + attr->acquired = FALSE; + + switch (pattern->type) { + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: { + cairo_gradient_pattern_t *gradient = + (cairo_gradient_pattern_t *) pattern; + glitz_drawable_t *drawable; + glitz_fixed16_16_t *params; + int n_params; + int i; + unsigned short alpha; + + /* XXX: the current color gradient acceleration provided by glitz is + * experimental, it's been proven inappropriate in a number of ways, + * most importantly, it's currently implemented as filters and + * gradients are not filters. eventually, it will be replaced with + * something more appropriate. + */ + + if (gradient->n_stops < 2) + break; + + /* glitz doesn't support inner and outer circle with different + center points. */ + if (pattern->type == CAIRO_PATTERN_RADIAL) + { + cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern; + + if (grad->center0.x != grad->center1.x || + grad->center0.y != grad->center1.y) + break; + } + + drawable = glitz_surface_get_drawable (dst->surface); + if (!(glitz_drawable_get_features (drawable) & + GLITZ_FEATURE_FRAGMENT_PROGRAM_MASK)) + break; + + if (pattern->filter != CAIRO_FILTER_BILINEAR && + pattern->filter != CAIRO_FILTER_GOOD && + pattern->filter != CAIRO_FILTER_BEST) + break; + + alpha = (gradient->stops[0].color.alpha * pattern->alpha) * 0xffff; + for (i = 1; i < gradient->n_stops; i++) + { + unsigned short a; + + a = (gradient->stops[i].color.alpha * pattern->alpha) * 0xffff; + if (a != alpha) + break; + } + + /* we can't have color stops with different alpha as gradient color + interpolation should be done to unpremultiplied colors. */ + if (i < gradient->n_stops) + break; + + n_params = gradient->n_stops * 3 + 4; + + params = malloc (sizeof (glitz_fixed16_16_t) * n_params); + if (!params) + return CAIRO_STATUS_NO_MEMORY; + + src = (cairo_glitz_surface_t *) + _cairo_surface_create_similar_scratch (&dst->base, + CAIRO_FORMAT_ARGB32, 0, + gradient->n_stops, 1); + if (!src) + { + free (params); + return CAIRO_STATUS_NO_MEMORY; + } + + for (i = 0; i < gradient->n_stops; i++) { + glitz_color_t color; + + color.red = gradient->stops[i].color.red * alpha; + color.green = gradient->stops[i].color.green * alpha; + color.blue = gradient->stops[i].color.blue * alpha; + color.alpha = alpha; + + glitz_set_rectangle (src->surface, &color, i, 0, 1, 1); + + params[4 + 3 * i] = gradient->stops[i].offset; + params[5 + 3 * i] = i << 16; + params[6 + 3 * i] = 0; + } + + if (pattern->type == CAIRO_PATTERN_LINEAR) + { + cairo_linear_pattern_t *grad = (cairo_linear_pattern_t *) pattern; + + params[0] = _cairo_fixed_from_double (grad->point0.x); + params[1] = _cairo_fixed_from_double (grad->point0.y); + params[2] = _cairo_fixed_from_double (grad->point1.x); + params[3] = _cairo_fixed_from_double (grad->point1.y); + attr->filter = GLITZ_FILTER_LINEAR_GRADIENT; + } + else + { + cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern; + + params[0] = _cairo_fixed_from_double (grad->center0.x); + params[1] = _cairo_fixed_from_double (grad->center0.y); + params[2] = _cairo_fixed_from_double (grad->radius0); + params[3] = _cairo_fixed_from_double (grad->radius1); + attr->filter = GLITZ_FILTER_RADIAL_GRADIENT; + } + + switch (pattern->extend) { + case CAIRO_EXTEND_NONE: + attr->fill = GLITZ_FILL_NEAREST; + break; + case CAIRO_EXTEND_REPEAT: + attr->fill = GLITZ_FILL_REPEAT; + break; + case CAIRO_EXTEND_REFLECT: + attr->fill = GLITZ_FILL_REFLECT; + break; + } + + attr->params = params; + attr->n_params = n_params; + attr->base.matrix = pattern->matrix; + attr->base.x_offset = 0; + attr->base.y_offset = 0; + } break; default: - case CAIRO_FORMAT_ARGB32: - return GLITZ_STANDARD_ARGB32; - case CAIRO_FORMAT_RGB24: - return GLITZ_STANDARD_RGB24; - case CAIRO_FORMAT_A8: - return GLITZ_STANDARD_A8; - case CAIRO_FORMAT_A1: - return GLITZ_STANDARD_A1; + break; } -} -static cairo_surface_t * -_cairo_glitz_surface_create_similar (void *abstract_src, - cairo_format_t format, - int draw, - int width, - int height) -{ - cairo_glitz_surface_t *src = abstract_src; - cairo_surface_t *crsurface; - glitz_drawable_t *drawable; - glitz_surface_t *surface; - glitz_format_t *gformat; + if (!src) + { + cairo_int_status_t status; - drawable = glitz_surface_get_drawable (src->surface); - - gformat = glitz_find_standard_format (drawable, _glitz_format (format)); - if (gformat == NULL) - return NULL; - - surface = glitz_surface_create (drawable, gformat, width, height); - if (surface == NULL) - return NULL; + status = _cairo_pattern_acquire_surface (pattern, &dst->base, + x, y, width, height, + (cairo_surface_t **) &src, + &attr->base); + if (status) + return status; + + if (src) + { + switch (attr->base.extend) { + case CAIRO_EXTEND_NONE: + attr->fill = GLITZ_FILL_TRANSPARENT; + break; + case CAIRO_EXTEND_REPEAT: + attr->fill = GLITZ_FILL_REPEAT; + break; + case CAIRO_EXTEND_REFLECT: + attr->fill = GLITZ_FILL_REFLECT; + break; + } - crsurface = cairo_glitz_surface_create (surface); - - glitz_surface_destroy (surface); + switch (attr->base.filter) { + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + attr->filter = GLITZ_FILTER_NEAREST; + break; + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + default: + attr->filter = GLITZ_FILTER_BILINEAR; + break; + } + + attr->params = NULL; + attr->n_params = 0; + attr->acquired = TRUE; + } + } - return crsurface; + *surface_out = src; + + return CAIRO_STATUS_SUCCESS; } -static cairo_glitz_surface_t * -_cairo_glitz_surface_clone_similar (cairo_glitz_surface_t *templ, - cairo_surface_t *src, - cairo_format_t format) +static void +_cairo_glitz_pattern_release_surface (cairo_glitz_surface_t *dst, + cairo_glitz_surface_t *surface, + cairo_glitz_surface_attributes_t *attr) { - cairo_glitz_surface_t *clone; - cairo_image_surface_t *src_image; + if (attr->acquired) + _cairo_pattern_release_surface (&dst->base, &surface->base, + &attr->base); + else + _cairo_glitz_surface_destroy (surface); +} - src_image = _cairo_surface_get_image (src); +static cairo_int_status_t +_cairo_glitz_pattern_acquire_surfaces (cairo_pattern_t *src, + cairo_pattern_t *mask, + cairo_glitz_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + unsigned int width, + unsigned int height, + cairo_glitz_surface_t **src_out, + cairo_glitz_surface_t **mask_out, + cairo_glitz_surface_attributes_t *sattr, + cairo_glitz_surface_attributes_t *mattr) +{ + cairo_int_status_t status; + cairo_pattern_union_t tmp; + cairo_bool_t src_opaque, mask_opaque; + double src_alpha, mask_alpha; - clone = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (templ, format, 0, - src_image->width, - src_image->height); - if (clone == NULL) - return NULL; - - _cairo_glitz_surface_set_filter (clone, cairo_surface_get_filter (src)); + src_opaque = _cairo_pattern_is_opaque (src); + mask_opaque = !mask || _cairo_pattern_is_opaque (mask); - _cairo_glitz_surface_set_image (clone, src_image); - - _cairo_glitz_surface_set_matrix (clone, &(src_image->base.matrix)); + /* For surface patterns, we move any translucency from src->alpha + * to mask->alpha so we can use the source unchanged. Otherwise we + * move the translucency from mask->alpha to src->alpha so that + * we can drop the mask if possible. + */ + if (src->type == CAIRO_PATTERN_SURFACE) + { + if (mask) { + mask_opaque = mask_opaque && src_opaque; + mask_alpha = mask->alpha * src->alpha; + } else { + mask_opaque = src_opaque; + mask_alpha = src->alpha; + } + + src_alpha = 1.0; + src_opaque = TRUE; + } + else + { + if (mask) + { + src_opaque = mask_opaque && src_opaque; + src_alpha = mask->alpha * src->alpha; + /* FIXME: This needs changing when we support RENDER + * style 4-channel masks. + */ + if (mask->type == CAIRO_PATTERN_SOLID) + mask = NULL; + } else + src_alpha = src->alpha; + + mask_alpha = 1.0; + mask_opaque = TRUE; + } + + _cairo_pattern_init_copy (&tmp.base, src); + _cairo_pattern_set_alpha (&tmp.base, src_alpha); + + status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, + src_x, src_y, + width, height, + src_out, sattr); - cairo_surface_destroy (&src_image->base); + _cairo_pattern_fini (&tmp.base); - return clone; -} + if (status) + return status; -static cairo_int_status_t -_glitz_composite (glitz_operator_t op, - glitz_surface_t *src, - glitz_surface_t *mask, - glitz_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - int width, - int height, - glitz_buffer_t *geometry, - glitz_geometry_format_t *format) -{ - if (_glitz_ensure_target (dst)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (mask || !mask_opaque) + { + if (mask) + _cairo_pattern_init_copy (&tmp.base, mask); + else + _cairo_pattern_init_solid (&tmp.solid, 0.0, 0.0, 0.0); - if (glitz_surface_get_status (dst)) - return CAIRO_STATUS_NO_TARGET_SURFACE; - - glitz_set_geometry (dst, - 0, 0, - format, geometry); - - glitz_composite (op, - src, - mask, - dst, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); - - glitz_set_geometry (dst, 0, 0, NULL, NULL); + _cairo_pattern_set_alpha (&tmp.base, mask_alpha); + + status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, + mask_x, mask_y, + width, height, + mask_out, mattr); + + _cairo_pattern_fini (&tmp.base); - if (glitz_surface_get_status (dst) == GLITZ_STATUS_NOT_SUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (status) + { + _cairo_glitz_pattern_release_surface (dst, *src_out, sattr); + return status; + } + } + else + { + *mask_out = NULL; + } return CAIRO_STATUS_SUCCESS; } +static void +_cairo_glitz_surface_set_attributes (cairo_glitz_surface_t *surface, + cairo_glitz_surface_attributes_t *a) +{ + _cairo_glitz_surface_set_matrix (surface, &a->base.matrix); + glitz_surface_set_fill (surface->surface, a->fill); + glitz_surface_set_filter (surface->surface, a->filter, + a->params, a->n_params); +} + static cairo_int_status_t _cairo_glitz_surface_composite (cairo_operator_t op, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) { - cairo_glitz_surface_t *dst = abstract_dst; - cairo_glitz_surface_t *src = (cairo_glitz_surface_t *) generic_src; - cairo_glitz_surface_t *mask = (cairo_glitz_surface_t *) generic_mask; - cairo_glitz_surface_t *src_clone = NULL; - cairo_glitz_surface_t *mask_clone = NULL; - cairo_int_status_t status; + cairo_glitz_surface_attributes_t src_attr, mask_attr; + cairo_glitz_surface_t *dst = abstract_dst; + cairo_glitz_surface_t *src; + cairo_glitz_surface_t *mask; + cairo_int_status_t status; if (op == CAIRO_OPERATOR_SATURATE) return CAIRO_INT_STATUS_UNSUPPORTED; - if (generic_src->backend != dst->base.backend) { - src_clone = _cairo_glitz_surface_clone_similar (dst, generic_src, - CAIRO_FORMAT_ARGB32); - if (!src_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (_glitz_ensure_target (dst->surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_glitz_pattern_acquire_surfaces (src_pattern, mask_pattern, + dst, + src_x, src_y, + mask_x, mask_y, + width, height, + &src, &mask, + &src_attr, &mask_attr); + if (status) + return status; + + _cairo_glitz_surface_set_attributes (src, &src_attr); + if (mask) + { + _cairo_glitz_surface_set_attributes (mask, &mask_attr); + glitz_composite (_glitz_operator (op), + src->surface, + mask->surface, + dst->surface, + src_x + src_attr.base.x_offset, + src_y + src_attr.base.y_offset, + mask_x + mask_attr.base.x_offset, + mask_y + mask_attr.base.y_offset, + dst_x, dst_y, + width, height); - src = src_clone; - } - - if (generic_mask && (generic_mask->backend != dst->base.backend)) { - mask_clone = _cairo_glitz_surface_clone_similar (dst, generic_mask, - CAIRO_FORMAT_A8); - if (!mask_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (mask_attr.n_params) + free (mask_attr.params); - mask = mask_clone; + _cairo_glitz_pattern_release_surface (dst, mask, &mask_attr); + } + else + { + glitz_composite (_glitz_operator (op), + src->surface, + NULL, + dst->surface, + src_x + src_attr.base.x_offset, + src_y + src_attr.base.y_offset, + 0, 0, + dst_x, dst_y, + width, height); } - status = _glitz_composite (_glitz_operator (op), - src->surface, - (mask)? mask->surface: NULL, - dst->surface, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height, - NULL, NULL); - - if (src_clone) - cairo_surface_destroy (&src_clone->base); - - if (mask_clone) - cairo_surface_destroy (&mask_clone->base); + if (src_attr.n_params) + free (src_attr.params); - return status; + _cairo_glitz_pattern_release_surface (dst, src, &src_attr); + + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t -_cairo_glitz_surface_fill_rectangles (void *abstract_dst, - cairo_operator_t op, +_cairo_glitz_surface_fill_rectangles (void *abstract_dst, + cairo_operator_t op, const cairo_color_t *color, - cairo_rectangle_t *rects, - int n_rects) + cairo_rectangle_t *rects, + int n_rects) { cairo_glitz_surface_t *dst = abstract_dst; - glitz_color_t glitz_color; - if (op == CAIRO_OPERATOR_SATURATE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - glitz_color.red = color->red_short; - glitz_color.green = color->green_short; - glitz_color.blue = color->blue_short; - glitz_color.alpha = color->alpha_short; + if (op == CAIRO_OPERATOR_SRC) + { + glitz_color_t glitz_color; + + glitz_color.red = color->red_short; + glitz_color.green = color->green_short; + glitz_color.blue = color->blue_short; + glitz_color.alpha = color->alpha_short; + + if (glitz_surface_get_width (dst->surface) != 1 || + glitz_surface_get_height (dst->surface) != 1) + _glitz_ensure_target (dst->surface); - if (op != CAIRO_OPERATOR_SRC) { - glitz_surface_t *solid; - glitz_float_t *vertices; - glitz_buffer_t *buffer; - glitz_geometry_format_t gf; - cairo_int_status_t status; - int width, height; - void *data; + glitz_set_rectangles (dst->surface, &glitz_color, + (glitz_rectangle_t *) rects, n_rects); + } + else + { + cairo_glitz_surface_t *src; - gf.mode = GLITZ_GEOMETRY_MODE_DIRECT; - gf.edge_hint = GLITZ_GEOMETRY_EDGE_HINT_SHARP; - gf.primitive = GLITZ_GEOMETRY_PRIMITIVE_QUADS; - gf.type = GLITZ_DATA_TYPE_FLOAT; - gf.first = 0; - gf.count = n_rects * 4; - - data = malloc (n_rects * 8 * sizeof (glitz_float_t)); - if (!data) - return CAIRO_STATUS_NO_MEMORY; - - buffer = glitz_buffer_create_for_data (data); - if (buffer == NULL) { - free (data); - return CAIRO_STATUS_NO_MEMORY; - } + if (op == CAIRO_OPERATOR_SATURATE) + return CAIRO_INT_STATUS_UNSUPPORTED; - width = height = 0; - vertices = glitz_buffer_map (buffer, GLITZ_BUFFER_ACCESS_WRITE_ONLY); - for (; n_rects; rects++, n_rects--) { - *vertices++ = (glitz_float_t) rects->x; - *vertices++ = (glitz_float_t) rects->y; - *vertices++ = (glitz_float_t) (rects->x + rects->width); - *vertices++ = (glitz_float_t) rects->y; - *vertices++ = (glitz_float_t) (rects->x + rects->width); - *vertices++ = (glitz_float_t) (rects->y + rects->height); - *vertices++ = (glitz_float_t) rects->x; - *vertices++ = (glitz_float_t) (rects->y + rects->height); - - if ((rects->x + rects->width) > width) - width = rects->x + rects->width; - - if ((rects->y + rects->height) > height) - height = rects->y + rects->height; - } - glitz_buffer_unmap (buffer); + if (_glitz_ensure_target (dst->surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; - solid = _glitz_surface_create_solid (dst->surface, - GLITZ_STANDARD_ARGB32, - &glitz_color); - if (solid == NULL) + src = (cairo_glitz_surface_t *) + _cairo_surface_create_similar_solid (&dst->base, + CAIRO_FORMAT_ARGB32, 1, 1, + (cairo_color_t *) color); + if (!src) return CAIRO_STATUS_NO_MEMORY; - - status = _glitz_composite (_glitz_operator (op), - solid, - NULL, - dst->surface, - 0, 0, - 0, 0, - 0, 0, - width, height, - buffer, &gf); - - glitz_surface_destroy (solid); - glitz_buffer_destroy (buffer); - free (data); - - return status; - } else { - if (glitz_surface_get_width (dst->surface) != 1 || - glitz_surface_get_height (dst->surface) != 1) { - if (_glitz_ensure_target (dst->surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; + + while (n_rects--) + { + glitz_composite (_glitz_operator (op), + src->surface, + NULL, + dst->surface, + 0, 0, + 0, 0, + rects->x, rects->y, + rects->width, rects->height); + rects++; } - glitz_set_rectangles (dst->surface, &glitz_color, - (glitz_rectangle_t *) rects, n_rects); + cairo_surface_destroy (&src->base); } + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) + return CAIRO_INT_STATUS_UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t -_cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, - cairo_surface_t *generic_src, - void *abstract_dst, - int x_src, - int y_src, +_cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, + cairo_pattern_t *pattern, + void *abstract_dst, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, - int n_traps) + int n_traps) { - cairo_glitz_surface_t *dst = abstract_dst; - cairo_glitz_surface_t *src = (cairo_glitz_surface_t *) generic_src; - glitz_surface_t *mask = NULL; - glitz_float_t *vertices; - glitz_buffer_t *buffer; - glitz_geometry_format_t gf; - cairo_int_status_t status; - int x_dst, y_dst, x_rel, y_rel, width, height; - void *data; + cairo_glitz_surface_attributes_t attributes; + cairo_glitz_surface_t *dst = abstract_dst; + cairo_glitz_surface_t *src; + cairo_glitz_surface_t *mask = NULL; + glitz_buffer_t *buffer = NULL; + void *data = NULL; + cairo_int_status_t status; + unsigned short alpha; if (op == CAIRO_OPERATOR_SATURATE) return CAIRO_INT_STATUS_UNSUPPORTED; - if (generic_src->backend != dst->base.backend) + if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; - gf.mode = GLITZ_GEOMETRY_MODE_DIRECT; - gf.edge_hint = GLITZ_GEOMETRY_EDGE_HINT_GOOD_SMOOTH; - gf.primitive = GLITZ_GEOMETRY_PRIMITIVE_QUADS; - gf.type = GLITZ_DATA_TYPE_FLOAT; - gf.first = 0; - gf.count = n_traps * 4; + if (pattern->type == CAIRO_PATTERN_SURFACE) + { + cairo_pattern_union_t tmp; - data = malloc (n_traps * 8 * sizeof (glitz_float_t)); - if (!data) - return CAIRO_STATUS_NO_MEMORY; - - buffer = glitz_buffer_create_for_data (data); - if (buffer == NULL) { - free (data); - return CAIRO_STATUS_NO_MEMORY; - } - - x_dst = traps[0].left.p1.x >> 16; - y_dst = traps[0].left.p1.y >> 16; + _cairo_pattern_init_copy (&tmp.base, pattern); + _cairo_pattern_set_alpha (&tmp.base, 1.0); - vertices = glitz_buffer_map (buffer, GLITZ_BUFFER_ACCESS_WRITE_ONLY); - for (; n_traps; traps++, n_traps--) { - glitz_float_t top, bottom; + status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, + src_x, src_y, + width, height, + &src, &attributes); - top = GLITZ_FIXED_TO_FLOAT (traps->top); - bottom = GLITZ_FIXED_TO_FLOAT (traps->bottom); + _cairo_pattern_fini (&tmp.base); - *vertices++ = GLITZ_FIXED_LINE_X_TO_FLOAT (traps->left, traps->top); - *vertices++ = top; - *vertices++ = - GLITZ_FIXED_LINE_X_CEIL_TO_FLOAT (traps->right, traps->top); - *vertices++ = top; - *vertices++ = - GLITZ_FIXED_LINE_X_CEIL_TO_FLOAT (traps->right, traps->bottom); - *vertices++ = bottom; - *vertices++ = GLITZ_FIXED_LINE_X_TO_FLOAT (traps->left, traps->bottom); - *vertices++ = bottom; + alpha = pattern->alpha * 0xffff; } - glitz_buffer_unmap (buffer); + else + { + status = _cairo_glitz_pattern_acquire_surface (pattern, dst, + src_x, src_y, + width, height, + &src, &attributes); + alpha = 0xffff; + } + + if (status) + return status; + + if (op == CAIRO_OPERATOR_ADD || n_traps <= 1) + { + static glitz_color_t clear_black = { 0, 0, 0, 0 }; + glitz_color_t color; + glitz_geometry_format_t format; + int n_trap_added; + int offset = 0; + int data_size = 0; + int size = 30 * n_traps; /* just a guess */ + + format.vertex.primitive = GLITZ_PRIMITIVE_QUADS; + format.vertex.type = GLITZ_DATA_TYPE_FLOAT; + format.vertex.bytes_per_vertex = 3 * sizeof (glitz_float_t); + format.vertex.attributes = GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK; + format.vertex.mask.type = GLITZ_DATA_TYPE_FLOAT; + format.vertex.mask.size = GLITZ_COORDINATE_SIZE_X; + format.vertex.mask.offset = 2 * sizeof (glitz_float_t); + + mask = (cairo_glitz_surface_t *) + _cairo_glitz_surface_create_similar (&dst->base, + CAIRO_FORMAT_A8, 0, + 2, 1); + if (!mask) + { + _cairo_glitz_pattern_release_surface (dst, src, &attributes); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + color.red = color.green = color.blue = color.alpha = alpha; - if ((src->pattern.type == CAIRO_PATTERN_SURFACE) && - (src->pattern.color.alpha != 1.0)) { - glitz_color_t color; + glitz_set_rectangle (mask->surface, &clear_black, 0, 0, 1, 1); + glitz_set_rectangle (mask->surface, &color, 1, 0, 1, 1); + + glitz_surface_set_fill (mask->surface, GLITZ_FILL_NEAREST); + glitz_surface_set_filter (mask->surface, + GLITZ_FILTER_BILINEAR, + NULL, 0); + + size *= format.vertex.bytes_per_vertex; - color.red = color.green = color.blue = 0; - color.alpha = src->pattern.color.alpha_short; + while (n_traps) + { + if (data_size < size) + { + data_size = size; + data = realloc (data, data_size); + if (!data) + { + _cairo_glitz_pattern_release_surface (dst, src, + &attributes); + return CAIRO_STATUS_NO_MEMORY; + } + + if (buffer) + glitz_buffer_destroy (buffer); + + buffer = glitz_buffer_create_for_data (data); + if (!buffer) { + free (data); + _cairo_glitz_pattern_release_surface (dst, src, + &attributes); + return CAIRO_STATUS_NO_MEMORY; + } + } - mask = _glitz_surface_create_solid (dst->surface, - GLITZ_STANDARD_A8, - &color); + offset += + glitz_add_trapezoids (buffer, + offset, size - offset, + format.vertex.type, mask->surface, + (glitz_trapezoid_t *) traps, n_traps, + &n_trap_added); + + n_traps -= n_trap_added; + traps += n_trap_added; + size *= 2; + } + + glitz_set_geometry (dst->surface, + GLITZ_GEOMETRY_TYPE_VERTEX, + &format, buffer); + glitz_set_array (dst->surface, 0, 3, + offset / format.vertex.bytes_per_vertex, + 0, 0); } + else + { + cairo_image_surface_t *image; + char *ptr; + int stride; + + stride = (width + 3) & -4; + data = malloc (stride * height); + if (!data) + { + _cairo_glitz_pattern_release_surface (dst, src, &attributes); + return CAIRO_STATUS_NO_MEMORY; + } + + memset (data, 0, stride * height); - x_rel = (src->pattern_box.p1.x >> 16) + x_src - x_dst; - y_rel = (src->pattern_box.p1.y >> 16) + y_src - y_dst; + /* using negative stride */ + ptr = (char *) data + stride * (height - 1); + + image = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (ptr, + CAIRO_FORMAT_A8, + width, height, + -stride); + if (!image) + { + cairo_surface_destroy (&src->base); + free (data); + return CAIRO_STATUS_NO_MEMORY; + } - x_dst = src->pattern_box.p1.x >> 16; - y_dst = src->pattern_box.p1.y >> 16; + pixman_add_trapezoids (image->pixman_image, -dst_x, -dst_y, + (pixman_trapezoid_t *) traps, n_traps); + + if (alpha != 0xffff) + { + pixman_color_t color; + + color.red = color.green = color.blue = color.alpha = alpha; + + pixman_fill_rectangle (PIXMAN_OPERATOR_IN, + image->pixman_image, + &color, + 0, 0, width, height); + } + + mask = (cairo_glitz_surface_t *) + _cairo_surface_create_similar_scratch (&dst->base, + CAIRO_FORMAT_A8, 0, + width, height); + if (!mask) + { + _cairo_glitz_pattern_release_surface (dst, src, &attributes); + free (data); + cairo_surface_destroy (&image->base); + return CAIRO_STATUS_NO_MEMORY; + } + + _cairo_glitz_surface_set_image (mask, image, 0, 0); + } + + _cairo_glitz_surface_set_attributes (src, &attributes); - width = ((src->pattern_box.p2.x + 65535) >> 16) - - (src->pattern_box.p1.x >> 16); - height = ((src->pattern_box.p2.y + 65535) >> 16) - - (src->pattern_box.p1.y >> 16); + glitz_composite (_glitz_operator (op), + src->surface, + mask->surface, + dst->surface, + src_x + attributes.base.x_offset, + src_y + attributes.base.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + + if (attributes.n_params) + free (attributes.params); + + glitz_set_geometry (dst->surface, + GLITZ_GEOMETRY_TYPE_NONE, + NULL, NULL); + + if (buffer) + glitz_buffer_destroy (buffer); - status = _glitz_composite (_glitz_operator (op), - src->surface, - mask, - dst->surface, - x_rel, y_rel, - 0, 0, - x_dst, y_dst, - width, height, - buffer, &gf); + free (data); + _cairo_glitz_pattern_release_surface (dst, src, &attributes); if (mask) - glitz_surface_destroy (mask); + cairo_surface_destroy (&mask->base); - glitz_buffer_destroy (buffer); - free (data); + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) + return CAIRO_INT_STATUS_UNSUPPORTED; - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t @@ -819,173 +1240,56 @@ _cairo_glitz_surface_show_page (void *abstract_surface) } static cairo_int_status_t -_cairo_glitz_surface_create_pattern (void *abstract_dst, - cairo_pattern_t *pattern, - cairo_box_t *box) +_cairo_glitz_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) { - cairo_glitz_surface_t *dst = abstract_dst; - cairo_surface_t *generic_src = NULL; - cairo_image_surface_t *image = NULL; - cairo_glitz_surface_t *src; - - switch (pattern->type) { - case CAIRO_PATTERN_SOLID: - generic_src = - _cairo_surface_create_similar_solid (abstract_dst, - CAIRO_FORMAT_ARGB32, - 1, 1, - &pattern->color); - if (generic_src) - cairo_surface_set_repeat (generic_src, 1); - break; - case CAIRO_PATTERN_RADIAL: - /* glitz doesn't support inner and outer circle with different - center points. */ - if (pattern->u.radial.center0.x != pattern->u.radial.center1.x || - pattern->u.radial.center0.y != pattern->u.radial.center1.y) - break; - /* fall-through */ - case CAIRO_PATTERN_LINEAR: { - glitz_drawable_t *drawable; - glitz_fixed16_16_t *params; - int i, n_params; - - drawable = glitz_surface_get_drawable (dst->surface); - if (!(glitz_drawable_get_features (drawable) & - GLITZ_FEATURE_FRAGMENT_PROGRAM_MASK)) - break; - - if (pattern->filter != CAIRO_FILTER_BILINEAR) - break; - - n_params = pattern->n_stops * 3 + 4; - - params = malloc (sizeof (glitz_fixed16_16_t) * n_params); - if (params == NULL) - return CAIRO_STATUS_NO_MEMORY; - - generic_src = - _cairo_glitz_surface_create_similar (abstract_dst, - CAIRO_FORMAT_ARGB32, 0, - pattern->n_stops, 1); - if (generic_src == NULL) { - free (params); - return CAIRO_STATUS_NO_MEMORY; - } - - src = (cairo_glitz_surface_t *) generic_src; - - for (i = 0; i < pattern->n_stops; i++) { - glitz_color_t color; + cairo_glitz_surface_t *surface = abstract_surface; - color.alpha = pattern->stops[i].color_char[3]; - color.red = pattern->stops[i].color_char[0] * color.alpha; - color.green = pattern->stops[i].color_char[1] * color.alpha; - color.blue = pattern->stops[i].color_char[2] * color.alpha; - color.alpha *= 256; + if (region) + { + glitz_box_t *box; + int n; - glitz_set_rectangle (src->surface, &color, i, 0, 1, 1); - - params[4 + 3 * i] = pattern->stops[i].offset; - params[5 + 3 * i] = i << 16; - params[6 + 3 * i] = 0; - } - - if (pattern->type == CAIRO_PATTERN_LINEAR) { - params[0] = _cairo_fixed_from_double (pattern->u.linear.point0.x); - params[1] = _cairo_fixed_from_double (pattern->u.linear.point0.y); - params[2] = _cairo_fixed_from_double (pattern->u.linear.point1.x); - params[3] = _cairo_fixed_from_double (pattern->u.linear.point1.y); - - glitz_surface_set_filter (src->surface, - GLITZ_FILTER_LINEAR_GRADIENT, - params, n_params); - } else { - params[0] = _cairo_fixed_from_double (pattern->u.radial.center0.x); - params[1] = _cairo_fixed_from_double (pattern->u.radial.center0.y); - params[2] = _cairo_fixed_from_double (pattern->u.radial.radius0); - params[3] = _cairo_fixed_from_double (pattern->u.radial.radius1); - - glitz_surface_set_filter (src->surface, - GLITZ_FILTER_RADIAL_GRADIENT, - params, n_params); - } - - switch (pattern->extend) { - case CAIRO_EXTEND_REPEAT: - glitz_surface_set_fill (src->surface, GLITZ_FILL_REPEAT); - break; - case CAIRO_EXTEND_REFLECT: - glitz_surface_set_fill (src->surface, GLITZ_FILL_REFLECT); - break; - case CAIRO_EXTEND_NONE: - default: - glitz_surface_set_fill (src->surface, GLITZ_FILL_NEAREST); - break; + if (!surface->clip) + { + surface->clip = pixman_region_create (); + if (!surface->clip) + return CAIRO_STATUS_NO_MEMORY; } + pixman_region_copy (surface->clip, region); - cairo_surface_set_matrix (&src->base, &pattern->matrix); - - free (params); - } break; - case CAIRO_PATTERN_SURFACE: - generic_src = pattern->u.surface.surface; - cairo_surface_reference (generic_src); - break; - } - - if (generic_src == NULL) { - image = _cairo_pattern_get_image (pattern, box); - if (image == NULL) - return CAIRO_STATUS_NO_MEMORY; - - generic_src = &image->base; + box = (glitz_box_t *) pixman_region_rects (surface->clip); + n = pixman_region_num_rects (surface->clip); + glitz_surface_set_clip_region (surface->surface, 0, 0, box, n); } + else + { + glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); - if (generic_src->backend != dst->base.backend) { - src = _cairo_glitz_surface_clone_similar (dst, generic_src, - CAIRO_FORMAT_ARGB32); - if (src == NULL) - return CAIRO_STATUS_NO_MEMORY; - - cairo_surface_set_repeat (&src->base, generic_src->repeat); - } else - src = (cairo_glitz_surface_t *) generic_src; + if (surface->clip) + pixman_region_destroy (surface->clip); - if (image) - cairo_surface_destroy (&image->base); - - _cairo_pattern_init_copy (&src->pattern, pattern); - src->pattern_box = *box; - - pattern->source = &src->base; + surface->clip = NULL; + } return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_glitz_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - static const cairo_surface_backend_t cairo_glitz_surface_backend = { _cairo_glitz_surface_create_similar, _cairo_glitz_surface_destroy, _cairo_glitz_surface_pixels_per_inch, - _cairo_glitz_surface_get_image, - _cairo_glitz_surface_set_image, - _cairo_glitz_surface_set_matrix, - _cairo_glitz_surface_set_filter, - _cairo_glitz_surface_set_repeat, + _cairo_glitz_surface_acquire_source_image, + _cairo_glitz_surface_release_source_image, + _cairo_glitz_surface_acquire_dest_image, + _cairo_glitz_surface_release_dest_image, + _cairo_glitz_surface_clone_similar, _cairo_glitz_surface_composite, _cairo_glitz_surface_fill_rectangles, _cairo_glitz_surface_composite_trapezoids, _cairo_glitz_surface_copy_page, _cairo_glitz_surface_show_page, _cairo_glitz_surface_set_clip_region, - _cairo_glitz_surface_create_pattern, NULL /* show_glyphs */ }; @@ -1004,12 +1308,10 @@ cairo_glitz_surface_create (glitz_surface_t *surface) _cairo_surface_init (&crsurface->base, &cairo_glitz_surface_backend); glitz_surface_reference (surface); - crsurface->surface = surface; - crsurface->format = glitz_surface_get_format (surface); - _cairo_pattern_init (&crsurface->pattern); - crsurface->pattern.type = CAIRO_PATTERN_SURFACE; - crsurface->pattern.u.surface.surface = NULL; + crsurface->surface = surface; + crsurface->format = glitz_surface_get_format (surface); + crsurface->clip = NULL; return (cairo_surface_t *) crsurface; } diff --git a/src/cairo-glitz.h b/src/cairo-glitz.h index 350d10233..f1917eb28 100644 --- a/src/cairo-glitz.h +++ b/src/cairo-glitz.h @@ -31,17 +31,20 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ -#include <cairo.h> - #ifndef CAIRO_GLITZ_H #define CAIRO_GLITZ_H + +#include <cairo.h> + #ifdef CAIRO_HAS_GLITZ_SURFACE #include <glitz.h> +CAIRO_BEGIN_DECLS + void cairo_set_target_glitz (cairo_t *cr, glitz_surface_t *surface); @@ -49,5 +52,7 @@ cairo_set_target_glitz (cairo_t *cr, cairo_surface_t * cairo_glitz_surface_create (glitz_surface_t *surface); +CAIRO_END_DECLS + #endif /* CAIRO_HAS_GLITZ_SURFACE */ #endif /* CAIRO_GLITZ_H */ diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index e855a7a66..d6db560a3 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include <stdlib.h> @@ -46,20 +46,33 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, cairo_surface_t *dst, cairo_traps_t *traps); +static cairo_status_t +_cairo_gstate_ensure_font (cairo_gstate_t *gstate); + +static void +_cairo_gstate_unset_font (cairo_gstate_t *gstate); + cairo_gstate_t * _cairo_gstate_create () { + cairo_status_t status; cairo_gstate_t *gstate; gstate = malloc (sizeof (cairo_gstate_t)); if (gstate) - _cairo_gstate_init (gstate); + { + status = _cairo_gstate_init (gstate); + if (status) { + free (gstate); + return NULL; + } + } return gstate; } -void +cairo_status_t _cairo_gstate_init (cairo_gstate_t *gstate) { gstate->operator = CAIRO_GSTATE_OPERATOR_DEFAULT; @@ -77,9 +90,11 @@ _cairo_gstate_init (cairo_gstate_t *gstate) gstate->num_dashes = 0; gstate->dash_offset = 0.0; - gstate->font = _cairo_unscaled_font_create (CAIRO_FONT_FAMILY_DEFAULT, - CAIRO_FONT_SLANT_DEFAULT, - CAIRO_FONT_WEIGHT_DEFAULT); + gstate->font_family = NULL; + gstate->font_slant = CAIRO_FONT_SLANT_DEFAULT; + gstate->font_weight = CAIRO_FONT_WEIGHT_DEFAULT; + + gstate->font = NULL; gstate->surface = NULL; @@ -87,6 +102,9 @@ _cairo_gstate_init (cairo_gstate_t *gstate) gstate->clip.surface = NULL; gstate->pattern = _cairo_pattern_create_solid (0.0, 0.0, 0.0); + if (!gstate->pattern) + return CAIRO_STATUS_NO_MEMORY; + gstate->alpha = 1.0; gstate->pixels_per_inch = CAIRO_GSTATE_PIXELS_PER_INCH_DEFAULT; @@ -97,6 +115,8 @@ _cairo_gstate_init (cairo_gstate_t *gstate) _cairo_pen_init_empty (&gstate->pen_regular); gstate->next = NULL; + + return CAIRO_STATUS_SUCCESS; } cairo_status_t @@ -118,9 +138,15 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) memcpy (gstate->dash, other->dash, other->num_dashes * sizeof (double)); } + if (other->font_family) { + gstate->font_family = strdup (other->font_family); + if (!gstate->font_family) + goto CLEANUP_DASH; + } + if (other->font) { gstate->font = other->font; - _cairo_unscaled_font_reference (gstate->font); + cairo_font_reference (gstate->font); } if (other->clip.region) @@ -148,18 +174,29 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) _cairo_path_fini (&gstate->path); CLEANUP_FONT: - _cairo_unscaled_font_destroy (gstate->font); + cairo_font_destroy (gstate->font); + gstate->font = NULL; + + if (gstate->font_family) { + free (gstate->font_family); + gstate->font_family = NULL; + } + CLEANUP_DASH: free (gstate->dash); gstate->dash = NULL; - return status; + return CAIRO_STATUS_NO_MEMORY; } void _cairo_gstate_fini (cairo_gstate_t *gstate) { - _cairo_unscaled_font_destroy (gstate->font); + if (gstate->font_family) + free (gstate->font_family); + + if (gstate->font) + cairo_font_destroy (gstate->font); if (gstate->surface) cairo_surface_destroy (gstate->surface); @@ -323,6 +360,8 @@ _cairo_gstate_set_target_surface (cairo_gstate_t *gstate, cairo_surface_t *surfa { double scale; + _cairo_gstate_unset_font (gstate); + if (gstate->surface) cairo_surface_destroy (gstate->surface); @@ -365,11 +404,9 @@ _cairo_gstate_set_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern) if (pattern == NULL) return CAIRO_STATUS_NULL_POINTER; - if (gstate->pattern) - cairo_pattern_destroy (gstate->pattern); - - gstate->pattern = pattern; cairo_pattern_reference (pattern); + cairo_pattern_destroy (gstate->pattern); + gstate->pattern = pattern; return CAIRO_STATUS_SUCCESS; } @@ -407,6 +444,8 @@ _cairo_gstate_set_rgb_color (cairo_gstate_t *gstate, double red, double green, d cairo_pattern_destroy (gstate->pattern); gstate->pattern = _cairo_pattern_create_solid (red, green, blue); + if (!gstate->pattern) + return CAIRO_STATUS_NO_MEMORY; return CAIRO_STATUS_SUCCESS; } @@ -549,6 +588,8 @@ _cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty) { cairo_matrix_t tmp; + _cairo_gstate_unset_font (gstate); + _cairo_matrix_set_translate (&tmp, tx, ty); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); @@ -566,6 +607,8 @@ _cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy) if (sx == 0 || sy == 0) return CAIRO_STATUS_INVALID_MATRIX; + _cairo_gstate_unset_font (gstate); + _cairo_matrix_set_scale (&tmp, sx, sy); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); @@ -580,6 +623,8 @@ _cairo_gstate_rotate (cairo_gstate_t *gstate, double angle) { cairo_matrix_t tmp; + _cairo_gstate_unset_font (gstate); + _cairo_matrix_set_rotate (&tmp, angle); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); @@ -595,6 +640,8 @@ _cairo_gstate_concat_matrix (cairo_gstate_t *gstate, { cairo_matrix_t tmp; + _cairo_gstate_unset_font (gstate); + cairo_matrix_copy (&tmp, matrix); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); @@ -610,6 +657,8 @@ _cairo_gstate_set_matrix (cairo_gstate_t *gstate, { cairo_status_t status; + _cairo_gstate_unset_font (gstate); + cairo_matrix_copy (&gstate->ctm, matrix); cairo_matrix_copy (&gstate->ctm_inverse, matrix); @@ -627,6 +676,8 @@ _cairo_gstate_default_matrix (cairo_gstate_t *gstate) if (scale == 0) scale = 1; + _cairo_gstate_unset_font (gstate); + cairo_matrix_set_identity (&gstate->font_matrix); cairo_matrix_set_identity (&gstate->ctm); @@ -640,6 +691,8 @@ _cairo_gstate_default_matrix (cairo_gstate_t *gstate) cairo_status_t _cairo_gstate_identity_matrix (cairo_gstate_t *gstate) { + _cairo_gstate_unset_font (gstate); + cairo_matrix_set_identity (&gstate->ctm); cairo_matrix_set_identity (&gstate->ctm_inverse); @@ -1256,54 +1309,17 @@ _cairo_gstate_interpret_path (cairo_gstate_t *gstate, &gpi); } -/* This function modifies the pattern and the state of the pattern surface it - may contain. The pattern surface will be restored to its orignal state - when the pattern is destroyed. The appropriate way is to pass a copy of - the original pattern to this function just before the pattern should be - used and destroy the copy when done. */ -static cairo_status_t -_cairo_gstate_create_pattern (cairo_gstate_t *gstate, - cairo_pattern_t *pattern, - cairo_box_t *extents) +/* XXX: gstate->alpha will be going away before too long, and when it + * does, it may make sense for this function to just disappear. + */ +static void +_cairo_gstate_pattern_init_copy (cairo_gstate_t *gstate, + cairo_pattern_union_t *pattern, + cairo_pattern_t *src) { - cairo_int_status_t status; - - if (gstate->surface == NULL) { - _cairo_pattern_fini (pattern); - return CAIRO_STATUS_NO_TARGET_SURFACE; - } - - if (pattern->type == CAIRO_PATTERN_LINEAR || - pattern->type == CAIRO_PATTERN_RADIAL) { - if (pattern->n_stops < 2) { - pattern->type = CAIRO_PATTERN_SOLID; - - if (pattern->n_stops) { - cairo_color_stop_t *stop = pattern->stops; - - _cairo_color_set_rgb (&pattern->color, - (double) stop->color_char[0] / 0xff, - (double) stop->color_char[1] / 0xff, - (double) stop->color_char[2] / 0xff); - _cairo_color_set_alpha (&pattern->color, - (double) stop->color_char[3] / 0xff); - } - } - } - - _cairo_pattern_set_alpha (pattern, gstate->alpha); - _cairo_pattern_transform (pattern, &gstate->ctm_inverse); - - status = _cairo_surface_create_pattern (gstate->surface, pattern, extents); - if (status) { - _cairo_pattern_fini (pattern); - return status; - } - - if (pattern->type == CAIRO_PATTERN_SURFACE) - _cairo_pattern_prepare_surface (pattern); - - return CAIRO_STATUS_SUCCESS; + _cairo_pattern_init_copy (&pattern->base, src); + _cairo_pattern_transform (&pattern->base, &gstate->ctm_inverse); + _cairo_pattern_set_alpha (&pattern->base, gstate->alpha); } cairo_status_t @@ -1342,7 +1358,7 @@ cairo_status_t _cairo_gstate_in_stroke (cairo_gstate_t *gstate, double x, double y, - int *inside_ret) + cairo_bool_t *inside_ret) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_traps_t traps; @@ -1365,51 +1381,85 @@ BAIL: return status; } -static cairo_status_t -_calculate_region_for_intermediate_clip_surface (pixman_region16_t *out, - cairo_box_t *extents, - cairo_clip_rec_t *clip_rect) +/* XXX We currently have a confusing mix of boxes and rectangles as + * exemplified by this function. A cairo_box_t is a rectangular area + * represented by the coordinates of the upper left and lower right + * corners, expressed in fixed point numbers. A cairo_rectangle_t is + * also a rectangular area, but represented by the upper left corner + * and the width and the height, as integer numbers. + * + * This function converts a cairo_box_t to a cairo_rectangle_t by + * increasing the area to the nearest integer coordinates. We should + * standardize on cairo_rectangle_t and cairo_rectangle_fixed_t, and + * this function could be renamed to the more reasonable + * _cairo_rectangle_fixed_round. + */ + +static void +_cairo_box_round_to_rectangle (cairo_box_t *box, cairo_rectangle_t *rectangle) { - cairo_status_t status; - pixman_region16_t *extents_region, *clip_region; - pixman_box16_t clip_box, pixman_extents; - - pixman_extents.x1 = _cairo_fixed_integer_floor (extents->p1.x); - pixman_extents.y1 = _cairo_fixed_integer_floor (extents->p1.y); - pixman_extents.x2 = _cairo_fixed_integer_ceil (extents->p2.x); - pixman_extents.y2 = _cairo_fixed_integer_ceil (extents->p2.y); - extents_region = pixman_region_create_simple (&pixman_extents); - if (extents_region == NULL) - { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL0; - } + rectangle->x = _cairo_fixed_integer_floor (box->p1.x); + rectangle->y = _cairo_fixed_integer_floor (box->p1.y); + rectangle->width = _cairo_fixed_integer_ceil (box->p2.x) - rectangle->x; + rectangle->height = _cairo_fixed_integer_ceil (box->p2.y) - rectangle->y; +} - clip_box.x1 = clip_rect->x; - clip_box.y1 = clip_rect->y; - clip_box.x2 = clip_rect->x + clip_rect->width; - clip_box.y2 = clip_rect->y + clip_rect->height; - clip_region = pixman_region_create_simple (&clip_box); - if (clip_region == NULL) - { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL1; - } +static void +_cairo_rectangle_intersect (cairo_rectangle_t *dest, cairo_rectangle_t *src) +{ + int x1, y1, x2, y2; - if (pixman_region_intersect (out, - extents_region, - clip_region) - == PIXMAN_REGION_STATUS_FAILURE) - status = CAIRO_STATUS_NO_MEMORY; - else - status = CAIRO_STATUS_SUCCESS; - - pixman_region_destroy (extents_region); - BAIL1: - pixman_region_destroy (clip_region); - - BAIL0: - return status; + x1 = MAX (dest->x, src->x); + y1 = MAX (dest->y, src->y); + x2 = MIN (dest->x + dest->width, src->x + src->width); + y2 = MIN (dest->y + dest->height, src->y + src->height); + + if (x1 >= x2 || y1 >= y2) { + dest->x = 0; + dest->y = 0; + dest->width = 0; + dest->height = 0; + } else { + dest->x = x1; + dest->y = y1; + dest->width = x2 - x1; + dest->height = y2 - y1; + } +} + +static int +_cairo_rectangle_empty (cairo_rectangle_t *rect) +{ + return rect->width == 0 || rect->height == 0; +} + +static void +translate_traps (cairo_traps_t *traps, int x, int y) +{ + cairo_fixed_t xoff, yoff; + cairo_trapezoid_t *t; + int i; + + /* Ugh. The cairo_composite/(Render) interface doesn't allow + an offset for the trapezoids. Need to manually shift all + the coordinates to align with the offset origin of the + intermediate surface. */ + + xoff = _cairo_fixed_from_int (x); + yoff = _cairo_fixed_from_int (y); + + for (i = 0, t = traps->traps; i < traps->num_traps; i++, t++) { + t->top += yoff; + t->bottom += yoff; + t->left.p1.x += xoff; + t->left.p1.y += yoff; + t->left.p2.x += xoff; + t->left.p2.y += yoff; + t->right.p1.x += xoff; + t->right.p1.y += yoff; + t->right.p2.x += xoff; + t->right.p2.y += yoff; + } } @@ -1422,173 +1472,148 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, cairo_traps_t *traps) { cairo_status_t status; - cairo_pattern_t pattern; - cairo_box_t extents; - int x_src, y_src; + cairo_pattern_union_t pattern; + cairo_rectangle_t extents; + cairo_box_t trap_extents; if (traps->num_traps == 0) return CAIRO_STATUS_SUCCESS; + if (gstate->surface == NULL) + return CAIRO_STATUS_NO_TARGET_SURFACE; + + _cairo_traps_extents (traps, &trap_extents); + _cairo_box_round_to_rectangle (&trap_extents, &extents); + if (gstate->clip.surface) { - cairo_fixed_t xoff, yoff; - cairo_trapezoid_t *t; - int i; cairo_surface_t *intermediate; + cairo_surface_pattern_t intermediate_pattern; cairo_color_t empty_color; - pixman_box16_t *draw_extents; - pixman_region16_t *draw_region; - draw_region = pixman_region_create (); - if (draw_region == NULL) - { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL0; - } - - _cairo_traps_extents (traps, &extents); - - status = _calculate_region_for_intermediate_clip_surface (draw_region, - &extents, - &gstate->clip); - if (status) - goto BAIL1; + _cairo_rectangle_intersect (&extents, &gstate->clip.rect); - /* Shortcut if empty */ - if (!pixman_region_not_empty (draw_region)) { + if (_cairo_rectangle_empty (&extents)) { status = CAIRO_STATUS_SUCCESS; goto BAIL1; } - draw_extents = pixman_region_extents (draw_region); - - /* Ugh. The cairo_composite/(Render) interface doesn't allow - an offset for the trapezoids. Need to manually shift all - the coordinates to align with the offset origin of the - intermediate surface. */ - xoff = _cairo_fixed_from_int (draw_extents->x1); - yoff = _cairo_fixed_from_int (draw_extents->y1); - for (i=0, t=traps->traps; i < traps->num_traps; i++, t++) { - t->top -= yoff; - t->bottom -= yoff; - t->left.p1.x -= xoff; - t->left.p1.y -= yoff; - t->left.p2.x -= xoff; - t->left.p2.y -= yoff; - t->right.p1.x -= xoff; - t->right.p1.y -= yoff; - t->right.p2.x -= xoff; - t->right.p2.y -= yoff; - } - - if (traps->traps[0].left.p1.y < traps->traps[0].left.p2.y) { - x_src = _cairo_fixed_to_double (traps->traps[0].left.p1.x); - y_src = _cairo_fixed_to_double (traps->traps[0].left.p1.y); - } else { - x_src = _cairo_fixed_to_double (traps->traps[0].left.p2.x); - y_src = _cairo_fixed_to_double (traps->traps[0].left.p2.y); - } - - _cairo_pattern_init_solid (&pattern, 1.0, 1.0, 1.0); - _cairo_pattern_set_alpha (&pattern, 1.0); - - status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); - if (status) - goto BAIL1; + translate_traps (traps, -extents.x, -extents.y); _cairo_color_init (&empty_color); _cairo_color_set_alpha (&empty_color, 0.); intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, CAIRO_FORMAT_A8, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1, + extents.width, + extents.height, &empty_color); if (intermediate == NULL) { status = CAIRO_STATUS_NO_MEMORY; - goto BAIL2; + goto BAIL1; } + _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0); + status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD, - pattern.source, intermediate, - x_src, - y_src, + &pattern.base, + intermediate, + extents.x, extents.y, + 0, 0, + extents.width, + extents.height, traps->traps, traps->num_traps); + _cairo_pattern_fini (&pattern.base); + if (status) - goto BAIL3; + goto BAIL2; + + + _cairo_pattern_init_for_surface (&pattern.surface, + gstate->clip.surface); status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - gstate->clip.surface, + &pattern.base, NULL, intermediate, - draw_extents->x1 - gstate->clip.x, - draw_extents->y1 - gstate->clip.y, + extents.x - gstate->clip.rect.x, + extents.y - gstate->clip.rect.y, 0, 0, 0, 0, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1); - if (status) - goto BAIL3; - - _cairo_pattern_fini (&pattern); + extents.width, extents.height); + _cairo_pattern_fini (&pattern.base); - _cairo_pattern_init_copy (&pattern, src); - - extents.p1.x = _cairo_fixed_from_int (draw_extents->x1); - extents.p1.y = _cairo_fixed_from_int (draw_extents->y1); - extents.p2.x = _cairo_fixed_from_int (draw_extents->x2); - extents.p2.y = _cairo_fixed_from_int (draw_extents->y2); - status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); if (status) - goto BAIL3; + goto BAIL2; - if (dst == gstate->clip.surface) - xoff = yoff = 0; + _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); + _cairo_gstate_pattern_init_copy (gstate, &pattern, src); status = _cairo_surface_composite (operator, - pattern.source, intermediate, dst, - 0, 0, + &pattern.base, + &intermediate_pattern.base, + dst, + extents.x, extents.y, 0, 0, - xoff >> 16, - yoff >> 16, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1); + extents.x, extents.y, + extents.width, extents.height); + _cairo_pattern_fini (&pattern.base); + _cairo_pattern_fini (&intermediate_pattern.base); - BAIL3: - cairo_surface_destroy (intermediate); BAIL2: - _cairo_pattern_fini (&pattern); + cairo_surface_destroy (intermediate); BAIL1: - pixman_region_destroy (draw_region); - BAIL0: if (status) return status; } else { - if (traps->traps[0].left.p1.y < traps->traps[0].left.p2.y) { - x_src = _cairo_fixed_to_double (traps->traps[0].left.p1.x); - y_src = _cairo_fixed_to_double (traps->traps[0].left.p1.y); - } else { - x_src = _cairo_fixed_to_double (traps->traps[0].left.p2.x); - y_src = _cairo_fixed_to_double (traps->traps[0].left.p2.y); + if (gstate->clip.region) { + pixman_box16_t box; + pixman_box16_t *intersection_extents; + pixman_region16_t *rect, *intersection; + + box.x1 = _cairo_fixed_integer_floor (trap_extents.p1.x); + box.y1 = _cairo_fixed_integer_floor (trap_extents.p1.y); + box.x2 = _cairo_fixed_integer_ceil (trap_extents.p2.x); + box.y2 = _cairo_fixed_integer_ceil (trap_extents.p2.y); + + rect = pixman_region_create_simple (&box); + if (rect == NULL) + goto bail1; + intersection = pixman_region_create(); + if (intersection == NULL) + goto bail2; + + if (pixman_region_intersect (intersection, gstate->clip.region, + rect) != PIXMAN_REGION_STATUS_SUCCESS) + goto bail3; + intersection_extents = pixman_region_extents (intersection); + + extents.x = intersection_extents->x1; + extents.y = intersection_extents->y1; + extents.width = intersection_extents->x2 - intersection_extents->x1; + extents.height = intersection_extents->y2 - intersection_extents->y1; + bail3: + pixman_region_destroy (intersection); + bail2: + pixman_region_destroy (rect); + bail1: + ; } - _cairo_pattern_init_copy (&pattern, src); + _cairo_gstate_pattern_init_copy (gstate, &pattern, src); - _cairo_traps_extents (traps, &extents); - status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); - if (status) - return status; - status = _cairo_surface_composite_trapezoids (gstate->operator, - pattern.source, dst, - x_src - pattern.source_offset.x, - y_src - pattern.source_offset.y, + &pattern.base, dst, + extents.x, extents.y, + extents.x, extents.y, + extents.width, + extents.height, traps->traps, traps->num_traps); - _cairo_pattern_fini (&pattern); + _cairo_pattern_fini (&pattern.base); if (status) return status; @@ -1628,7 +1653,7 @@ cairo_status_t _cairo_gstate_in_fill (cairo_gstate_t *gstate, double x, double y, - int *inside_ret) + cairo_bool_t *inside_ret) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_traps_t traps; @@ -1807,9 +1832,10 @@ cairo_status_t _cairo_gstate_clip (cairo_gstate_t *gstate) { cairo_status_t status; - cairo_pattern_t pattern; + cairo_pattern_union_t pattern; cairo_traps_t traps; cairo_color_t white_color; + cairo_box_t extents; pixman_box16_t box; /* Fill the clip region as traps. */ @@ -1871,33 +1897,32 @@ _cairo_gstate_clip (cairo_gstate_t *gstate) _cairo_color_init (&white_color); if (gstate->clip.surface == NULL) { - cairo_box_t extents; - _cairo_traps_extents (&traps, &extents); - gstate->clip.x = extents.p1.x >> 16; - gstate->clip.y = extents.p1.y >> 16; - gstate->clip.width = ((extents.p2.x + 65535) >> 16) - gstate->clip.x; - gstate->clip.height = ((extents.p2.y + 65535) >> 16) - gstate->clip.y; + _cairo_box_round_to_rectangle (&extents, &gstate->clip.rect); gstate->clip.surface = _cairo_surface_create_similar_solid (gstate->surface, CAIRO_FORMAT_A8, - gstate->clip.width, - gstate->clip.height, + gstate->clip.rect.width, + gstate->clip.rect.height, &white_color); if (gstate->clip.surface == NULL) return CAIRO_STATUS_NO_MEMORY; } - _cairo_pattern_init_solid (&pattern, 1.0, 1.0, 1.0); - _cairo_pattern_set_alpha (&pattern, 1.0); - - _cairo_gstate_clip_and_composite_trapezoids (gstate, - &pattern, - CAIRO_OPERATOR_IN, - gstate->clip.surface, - &traps); + translate_traps (&traps, -gstate->clip.rect.x, -gstate->clip.rect.y); + _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0); - _cairo_pattern_fini (&pattern); + status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_IN, + &pattern.base, + gstate->clip.surface, + 0, 0, + 0, 0, + gstate->clip.rect.width, + gstate->clip.rect.height, + traps.traps, + traps.num_traps); + + _cairo_pattern_fini (&pattern.base); _cairo_traps_fini (&traps); @@ -1978,19 +2003,15 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, * */ - cairo_status_t status; - cairo_matrix_t user_to_image, image_to_user; - cairo_matrix_t image_to_device, device_to_image; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_matrix_t image_to_user, image_to_device; double device_x, device_y; double device_width, device_height; - cairo_pattern_t pattern; + cairo_surface_pattern_t pattern; cairo_box_t pattern_extents; + cairo_rectangle_t extents; - cairo_surface_get_matrix (surface, &user_to_image); - cairo_matrix_multiply (&device_to_image, &gstate->ctm_inverse, &user_to_image); - cairo_surface_set_matrix (surface, &device_to_image); - - image_to_user = user_to_image; + cairo_surface_get_matrix (surface, &image_to_user); cairo_matrix_invert (&image_to_user); cairo_matrix_multiply (&image_to_device, &image_to_user, &gstate->ctm); @@ -2001,126 +2022,82 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, &device_x, &device_y, &device_width, &device_height); - _cairo_pattern_init (&pattern); + _cairo_pattern_init_for_surface (&pattern, surface); + + /* inherit surface attributes while surface attribute functions still + exist */ + pattern.base.matrix = surface->matrix; + pattern.base.filter = surface->filter; + if (surface->repeat) + pattern.base.extend = CAIRO_EXTEND_REPEAT; + else + pattern.base.extend = CAIRO_EXTEND_NONE; + + _cairo_pattern_transform (&pattern.base, &gstate->ctm_inverse); + _cairo_pattern_set_alpha (&pattern.base, gstate->alpha); pattern_extents.p1.x = _cairo_fixed_from_double (device_x); pattern_extents.p1.y = _cairo_fixed_from_double (device_y); pattern_extents.p2.x = _cairo_fixed_from_double (device_x + device_width); pattern_extents.p2.y = _cairo_fixed_from_double (device_y + device_height); - - if ((gstate->pattern->type != CAIRO_PATTERN_SOLID) || - (gstate->alpha != 1.0)) { - /* I'm allowing any type of pattern for the mask right now. - Maybe this is bad. Will allow for some cool effects though. */ - _cairo_pattern_init_copy (&pattern, gstate->pattern); - status = _cairo_gstate_create_pattern (gstate, &pattern, &pattern_extents); - if (status) - return status; - } - + _cairo_box_round_to_rectangle (&pattern_extents, &extents); + if (gstate->clip.surface) { - cairo_surface_t *intermediate; - cairo_color_t empty_color; - pixman_box16_t *draw_extents; - pixman_region16_t *draw_region; - - draw_region = pixman_region_create (); - if (draw_region == NULL) - { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL0; + _cairo_rectangle_intersect (&extents, &gstate->clip.rect); + + /* We only need to composite if the rectangle is not empty. */ + if (!_cairo_rectangle_empty (&extents)) { + cairo_surface_pattern_t clip_pattern; + + _cairo_pattern_init_for_surface (&clip_pattern, + gstate->clip.surface); + + status = _cairo_surface_composite (gstate->operator, + &pattern.base, + &clip_pattern.base, + gstate->surface, + extents.x, extents.y, + 0, 0, + extents.x, extents.y, + extents.width, extents.height); + + _cairo_pattern_fini (&clip_pattern.base); } - - status = _calculate_region_for_intermediate_clip_surface (draw_region, - &pattern_extents, - &gstate->clip); - if (status) - goto BAIL1; - - /* Shortcut if empty */ - if (!pixman_region_not_empty (draw_region)) { - status = CAIRO_STATUS_SUCCESS; - goto BAIL1; - } - - draw_extents = pixman_region_extents (draw_region); - - _cairo_color_init (&empty_color); - _cairo_color_set_alpha (&empty_color, .0); - intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, - CAIRO_FORMAT_A8, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1, - &empty_color); - - /* it is not completely clear what the "right" way to combine the - pattern and mask surface is. I will use the the clip as a source - and the pattern as a mask in building up my temporary, because - this is not *totally* bogus and accomodates the case where - pattern's source image is NULL reasonably well. feel free to - correct this if you see a reason. */ - - status = _cairo_surface_composite (CAIRO_OPERATOR_SRC, - gstate->clip.surface, - pattern.source, - intermediate, - draw_extents->x1 - gstate->clip.x, - draw_extents->y1 - gstate->clip.y, - 0, 0, - 0, 0, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1); - - - if (status) - goto BAIL2; - - status = _cairo_surface_composite (gstate->operator, - surface, - intermediate, - gstate->surface, - draw_extents->x1, draw_extents->y1, - 0, 0, - draw_extents->x1, draw_extents->y1, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1); - - BAIL2: - cairo_surface_destroy (intermediate); - BAIL1: - pixman_region_destroy (draw_region); - BAIL0: - ; } else { - - /* XXX: The rendered size is sometimes 1 or 2 pixels short from - what I expect. Need to fix this. */ + /* XXX: The rendered size is sometimes 1 or 2 pixels short + * from what I expect. Need to fix this. + * KRH: I'm guessing this was due to rounding error when + * passing double coordinates for integer arguments. Using + * the extents rectangle should fix this, since it's properly + * rounded. Is this still the case? + */ status = _cairo_surface_composite (gstate->operator, - surface, - pattern.source, + &pattern.base, + NULL, gstate->surface, - device_x, device_y, + extents.x, extents.y, 0, 0, - device_x, device_y, - device_width, - device_height); + extents.x, extents.y, + extents.width, extents.height); } - _cairo_pattern_fini (&pattern); + _cairo_pattern_fini (&pattern.base); - /* restore the matrix originally in the surface */ - cairo_surface_set_matrix (surface, &user_to_image); - - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; + return status; } +static void +_cairo_gstate_unset_font (cairo_gstate_t *gstate) +{ + if (gstate->font) { + cairo_font_destroy (gstate->font); + gstate->font = NULL; + } +} cairo_status_t _cairo_gstate_select_font (cairo_gstate_t *gstate, @@ -2128,12 +2105,17 @@ _cairo_gstate_select_font (cairo_gstate_t *gstate, cairo_font_slant_t slant, cairo_font_weight_t weight) { - if (gstate->font) - _cairo_unscaled_font_destroy (gstate->font); + char *new_family; - gstate->font = _cairo_unscaled_font_create (family, slant, weight); - if (gstate->font == NULL) + new_family = strdup (family); + if (!new_family) return CAIRO_STATUS_NO_MEMORY; + + _cairo_gstate_unset_font (gstate); + + gstate->font_family = new_family; + gstate->font_slant = slant; + gstate->font_weight = weight; cairo_matrix_set_identity (&gstate->font_matrix); @@ -2144,6 +2126,8 @@ cairo_status_t _cairo_gstate_scale_font (cairo_gstate_t *gstate, double scale) { + _cairo_gstate_unset_font (gstate); + return cairo_matrix_scale (&gstate->font_matrix, scale, scale); } @@ -2153,6 +2137,9 @@ _cairo_gstate_transform_font (cairo_gstate_t *gstate, { cairo_matrix_t tmp; double a, b, c, d, tx, ty; + + _cairo_gstate_unset_font (gstate); + cairo_matrix_get_affine (matrix, &a, &b, &c, &d, &tx, &ty); cairo_matrix_set_affine (&tmp, a, b, c, d, 0, 0); return cairo_matrix_multiply (&gstate->font_matrix, &gstate->font_matrix, &tmp); @@ -2160,28 +2147,16 @@ _cairo_gstate_transform_font (cairo_gstate_t *gstate, cairo_status_t -_cairo_gstate_current_font (cairo_gstate_t *gstate, - cairo_font_t **font) +_cairo_gstate_current_font (cairo_gstate_t *gstate, + cairo_font_t **font) { - cairo_font_scale_t scale; - cairo_font_t *scaled; - double dummy; - - scaled = malloc (sizeof (cairo_font_t)); - if (scaled == NULL) - return CAIRO_STATUS_NO_MEMORY; - - cairo_matrix_get_affine (&gstate->font_matrix, - &scale.matrix[0][0], - &scale.matrix[0][1], - &scale.matrix[1][0], - &scale.matrix[1][1], - &dummy, &dummy); - - _cairo_font_init (scaled, &scale, gstate->font); - _cairo_unscaled_font_reference (gstate->font); + cairo_status_t status; - *font = scaled; + status = _cairo_gstate_ensure_font (gstate); + if (status) + return status; + + *font = gstate->font; return CAIRO_STATUS_SUCCESS; } @@ -2190,6 +2165,8 @@ void _cairo_gstate_set_font_transform (cairo_gstate_t *gstate, cairo_matrix_t *matrix) { + _cairo_gstate_unset_font (gstate); + cairo_matrix_copy (&gstate->font_matrix, matrix); } @@ -2214,12 +2191,10 @@ _cairo_gstate_current_font_transform (cairo_gstate_t *gstate, * independently scale the user coordinate system *or* the font matrix, in * order to adjust the rendered size of the font. * - * If the user asks for a permanent reference to "a font", they are given a - * handle to a structure holding a scale matrix and an unscaled font. This - * effectively decouples the font from further changes to user space. Even - * if the user then "sets" the current cairo_t font to the handle they were - * passed, further changes to the cairo_t CTM will not affect externally - * held references to the font. + * The only font type exposed to the user is cairo_font_t which is a + * a font specialized to a particular scale matrix, CTM, and target + * surface. The user is responsible for not using a cairo_font_t + * after changing the parameters; doing so will produce garbled metrics. * * * The font's view @@ -2279,9 +2254,9 @@ _cairo_gstate_current_font_transform (cairo_gstate_t *gstate, * */ -static void -_build_font_scale (cairo_gstate_t *gstate, - cairo_font_scale_t *sc) +void +_cairo_gstate_current_font_scale (cairo_gstate_t *gstate, + cairo_font_scale_t *sc) { cairo_matrix_t tmp; double dummy; @@ -2294,34 +2269,46 @@ _build_font_scale (cairo_gstate_t *gstate, &dummy, &dummy); } -cairo_status_t -_cairo_gstate_current_font_extents (cairo_gstate_t *gstate, - cairo_font_extents_t *extents) +static cairo_status_t +_cairo_gstate_ensure_font (cairo_gstate_t *gstate) { - cairo_int_status_t status; cairo_font_scale_t sc; - double font_scale_x, font_scale_y; + cairo_status_t status; + const char *family; + + if (gstate->font) + return CAIRO_STATUS_SUCCESS; + + _cairo_gstate_current_font_scale (gstate, &sc); - _build_font_scale (gstate, &sc); + if (gstate->font_family) + family = gstate->font_family; + else + family = CAIRO_FONT_FAMILY_DEFAULT; + + status = _cairo_font_create (family, + gstate->font_slant, + gstate->font_weight, + &sc, + &gstate->font); - status = _cairo_unscaled_font_font_extents (gstate->font, &sc, extents); + if (status) + return status; - _cairo_matrix_compute_scale_factors (&gstate->font_matrix, - &font_scale_x, &font_scale_y, - /* XXX */ 1); - - /* - * The font responded in unscaled units, scale by the font - * matrix scale factors to get to user space - */ - - extents->ascent *= font_scale_y; - extents->descent *= font_scale_y; - extents->height *= font_scale_y; - extents->max_x_advance *= font_scale_x; - extents->max_y_advance *= font_scale_y; - - return status; + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_current_font_extents (cairo_gstate_t *gstate, + cairo_font_extents_t *extents) +{ + cairo_status_t status = _cairo_gstate_ensure_font (gstate); + if (status) + return status; + + return cairo_font_extents (gstate->font, + &gstate->font_matrix, + extents); } cairo_status_t @@ -2331,14 +2318,15 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, int *nglyphs) { cairo_status_t status; - cairo_font_scale_t sc; cairo_point_t point; double origin_x, origin_y; int i; - _build_font_scale (gstate, &sc); - + status = _cairo_gstate_ensure_font (gstate); + if (status) + return status; + status = _cairo_path_current_point (&gstate->path, &point); if (status == CAIRO_STATUS_NO_CURRENT_POINT) { origin_x = 0.0; @@ -2350,8 +2338,8 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, &origin_x, &origin_y); } - status = _cairo_unscaled_font_text_to_glyphs (gstate->font, - &sc, utf8, glyphs, nglyphs); + status = _cairo_font_text_to_glyphs (gstate->font, + utf8, glyphs, nglyphs); if (status || !glyphs || !nglyphs || !(*glyphs) || !(nglyphs)) return status; @@ -2373,18 +2361,16 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, cairo_status_t _cairo_gstate_set_font (cairo_gstate_t *gstate, - cairo_font_t *font) -{ - if (gstate->font != NULL) - _cairo_unscaled_font_destroy (gstate->font); - gstate->font = font->unscaled; - _cairo_unscaled_font_reference (gstate->font); - cairo_matrix_set_affine (&gstate->font_matrix, - font->scale.matrix[0][0], - font->scale.matrix[0][1], - font->scale.matrix[1][0], - font->scale.matrix[1][1], - 0, 0); + cairo_font_t *font) +{ + if (font != gstate->font) { + if (gstate->font) + cairo_font_destroy (gstate->font); + gstate->font = font; + if (gstate->font) + cairo_font_reference (gstate->font); + } + return CAIRO_STATUS_SUCCESS; } @@ -2394,90 +2380,18 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, int num_glyphs, cairo_text_extents_t *extents) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_glyph_t origin_glyph; - cairo_text_extents_t origin_extents; - cairo_font_scale_t sc; - int i; - double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0; - double x_pos = 0.0, y_pos = 0.0; - int set = 0; - - if (!num_glyphs) - { - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; - return CAIRO_STATUS_SUCCESS; - } - - _build_font_scale (gstate, &sc); - - for (i = 0; i < num_glyphs; i++) - { - double x, y; - double wm, hm; - - origin_glyph = glyphs[i]; - origin_glyph.x = 0.0; - origin_glyph.y = 0.0; - status = _cairo_unscaled_font_glyph_extents (gstate->font, &sc, - &origin_glyph, 1, - &origin_extents); - - /* - * Transform font space metrics into user space metrics - * by running the corners through the font matrix and - * expanding the bounding box as necessary - */ - x = origin_extents.x_bearing; - y = origin_extents.y_bearing; - cairo_matrix_transform_point (&gstate->font_matrix, - &x, &y); - - for (hm = 0.0; hm <= 1.0; hm += 1.0) - for (wm = 0.0; wm <= 1.0; wm += 1.0) - { - x = origin_extents.x_bearing + origin_extents.width * wm; - y = origin_extents.y_bearing + origin_extents.height * hm; - cairo_matrix_transform_point (&gstate->font_matrix, - &x, &y); - x += glyphs[i].x; - y += glyphs[i].y; - if (!set) - { - min_x = max_x = x; - min_y = max_y = y; - set = 1; - } - else - { - if (x < min_x) min_x = x; - if (x > max_x) max_x = x; - if (y < min_y) min_y = y; - if (y > max_y) max_y = y; - } - } + cairo_status_t status; - x = origin_extents.x_advance; - y = origin_extents.y_advance; - cairo_matrix_transform_point (&gstate->font_matrix, - &x, &y); - x_pos = glyphs[i].x + x; - y_pos = glyphs[i].y + y; - } + status = _cairo_gstate_ensure_font (gstate); + if (status) + return status; - extents->x_bearing = min_x - glyphs[0].x; - extents->y_bearing = min_y - glyphs[0].y; - extents->width = max_x - min_x; - extents->height = max_y - min_y; - extents->x_advance = x_pos - glyphs[0].x; - extents->y_advance = y_pos - glyphs[0].y; + cairo_font_glyph_extents (gstate->font, + &gstate->font_matrix, + glyphs, num_glyphs, + extents); - return status; + return CAIRO_STATUS_SUCCESS; } cairo_status_t @@ -2488,12 +2402,14 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, cairo_status_t status; int i; cairo_glyph_t *transformed_glyphs = NULL; - cairo_font_scale_t sc; - cairo_pattern_t pattern; + cairo_pattern_union_t pattern; cairo_box_t bbox; + cairo_rectangle_t extents; - _build_font_scale (gstate, &sc); - + status = _cairo_gstate_ensure_font (gstate); + if (status) + return status; + transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); if (transformed_glyphs == NULL) return CAIRO_STATUS_NO_MEMORY; @@ -2506,52 +2422,34 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, &transformed_glyphs[i].y); } - _cairo_pattern_init_copy (&pattern, gstate->pattern); - status = _cairo_unscaled_font_glyph_bbox (gstate->font, &sc, - transformed_glyphs, num_glyphs, - &bbox); - if (status) - goto CLEANUP_GLYPHS; + status = _cairo_font_glyph_bbox (gstate->font, + transformed_glyphs, num_glyphs, + &bbox); + _cairo_box_round_to_rectangle (&bbox, &extents); - status = _cairo_gstate_create_pattern (gstate, &pattern, &bbox); if (status) goto CLEANUP_GLYPHS; - + if (gstate->clip.surface) { cairo_surface_t *intermediate; + cairo_surface_pattern_t intermediate_pattern; cairo_color_t empty_color; - pixman_box16_t *draw_extents; - pixman_region16_t *draw_region; - - draw_region = pixman_region_create (); - if (draw_region == NULL) - { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL0; - } - status = _calculate_region_for_intermediate_clip_surface (draw_region, - &bbox, - &gstate->clip); - if (status) { - goto BAIL1; - } + _cairo_rectangle_intersect (&extents, &gstate->clip.rect); /* Shortcut if empty */ - if (!pixman_region_not_empty (draw_region)) { + if (_cairo_rectangle_empty (&extents)) { status = CAIRO_STATUS_SUCCESS; goto BAIL1; } - draw_extents = pixman_region_extents (draw_region); - _cairo_color_init (&empty_color); _cairo_color_set_alpha (&empty_color, .0); intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, CAIRO_FORMAT_A8, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1, + extents.width, + extents.height, &empty_color); if (intermediate == NULL) { status = CAIRO_STATUS_NO_MEMORY; @@ -2561,66 +2459,77 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, /* move the glyphs again, from dev space to intermediate space */ for (i = 0; i < num_glyphs; ++i) { - transformed_glyphs[i].x -= draw_extents->x1; - transformed_glyphs[i].y -= draw_extents->y1; + transformed_glyphs[i].x -= extents.x; + transformed_glyphs[i].y -= extents.y; } - status = _cairo_unscaled_font_show_glyphs (gstate->font, - &sc, - CAIRO_OPERATOR_ADD, - pattern.source, intermediate, - draw_extents->x1 - pattern.source_offset.x, - draw_extents->y1 - pattern.source_offset.y, - transformed_glyphs, num_glyphs); + _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0); + + status = _cairo_font_show_glyphs (gstate->font, + CAIRO_OPERATOR_ADD, + &pattern.base, intermediate, + extents.x, extents.y, + 0, 0, + extents.width, extents.height, + transformed_glyphs, num_glyphs); + + _cairo_pattern_fini (&pattern.base); if (status) goto BAIL2; + _cairo_pattern_init_for_surface (&pattern.surface, + gstate->clip.surface); + status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - gstate->clip.surface, + &pattern.base, NULL, intermediate, - draw_extents->x1 - gstate->clip.x, - draw_extents->y1 - gstate->clip.y, + extents.x - gstate->clip.rect.x, + extents.y - gstate->clip.rect.y, 0, 0, 0, 0, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1); + extents.width, extents.height); + + _cairo_pattern_fini (&pattern.base); if (status) goto BAIL2; + _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); + _cairo_gstate_pattern_init_copy (gstate, &pattern, gstate->pattern); + status = _cairo_surface_composite (gstate->operator, - pattern.source, - intermediate, + &pattern.base, + &intermediate_pattern.base, gstate->surface, - 0, 0, + extents.x, extents.y, 0, 0, - draw_extents->x1, - draw_extents->y1, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1); + extents.x, extents.y, + extents.width, extents.height); + _cairo_pattern_fini (&pattern.base); + _cairo_pattern_fini (&intermediate_pattern.base); BAIL2: cairo_surface_destroy (intermediate); BAIL1: - pixman_region_destroy (draw_region); - BAIL0: ; } else { - status = _cairo_unscaled_font_show_glyphs (gstate->font, - &sc, - gstate->operator, pattern.source, - gstate->surface, - -pattern.source_offset.x, - -pattern.source_offset.y, - transformed_glyphs, num_glyphs); + _cairo_gstate_pattern_init_copy (gstate, &pattern, gstate->pattern); + + status = _cairo_font_show_glyphs (gstate->font, + gstate->operator, &pattern.base, + gstate->surface, + extents.x, extents.y, + extents.x, extents.y, + extents.width, extents.height, + transformed_glyphs, num_glyphs); + + _cairo_pattern_fini (&pattern.base); } - _cairo_pattern_fini (&pattern); - CLEANUP_GLYPHS: free (transformed_glyphs); @@ -2635,9 +2544,6 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, cairo_status_t status; int i; cairo_glyph_t *transformed_glyphs = NULL; - cairo_font_scale_t sc; - - _build_font_scale (gstate, &sc); transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); if (transformed_glyphs == NULL) @@ -2651,9 +2557,9 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, &(transformed_glyphs[i].y)); } - status = _cairo_unscaled_font_glyph_path (gstate->font, &sc, - transformed_glyphs, num_glyphs, - &gstate->path); + status = _cairo_font_glyph_path (gstate->font, + transformed_glyphs, num_glyphs, + &gstate->path); free (transformed_glyphs); return status; diff --git a/src/cairo-hash.c b/src/cairo-hash.c index b097b609b..d1ad5a4e2 100644 --- a/src/cairo-hash.c +++ b/src/cairo-hash.c @@ -94,9 +94,9 @@ static const cairo_cache_arrangement_t cache_arrangements [] = { * a mostly-dead table. * * Generally you do not need to worry about freeing cache entries; the - * cache will expire entries randomly as it experiences memory pressure. - * There is currently no explicit entry-removing call, though one can be - * added easily. + * cache will expire entries randomly as it experiences memory pressure. + * If max_memory is set, entries are not expired, and must be explicitely + * removed. * * This table is open-addressed with double hashing. Each table size is a * prime chosen to be a little more than double the high water mark for a @@ -282,17 +282,51 @@ _load_factor (cairo_cache_t *cache) } #endif -static unsigned long -_random_live_entry (cairo_cache_t *cache) -{ - unsigned long idx; - assert(cache != NULL); - do { - idx = rand () % cache->arrangement->size; - } while (! LIVE_ENTRY_P(cache, idx)); - return idx; -} +/* Find a random in the cache matching the given predicate. We use the + * same algorithm as the probing algorithm to walk over the entries in + * the hash table in a pseudo-random order. Walking linearly would + * favor entries following gaps in the hash table. We could also + * call rand() repeatedly, which works well for almost-full tables, + * but degrades when the table is almost empty, or predicate + * returns false for most entries. + */ +static cairo_cache_entry_base_t ** +_random_entry (cairo_cache_t *cache, + int (*predicate)(void*)) +{ + cairo_cache_entry_base_t **probe; + unsigned long hash; + unsigned long table_size, i, idx, step; + + _cache_sane_state (cache); + + table_size = cache->arrangement->size; + hash = rand (); + idx = hash % table_size; + step = 0; + + for (i = 0; i < table_size; ++i) + { + assert(idx < table_size); + probe = cache->entries + idx; + if (LIVE_ENTRY_P(cache, idx) + && (!predicate || predicate (*probe))) + return probe; + + if (step == 0) { + step = hash % cache->arrangement->rehash; + if (step == 0) + step = 1; + } + + idx += step; + if (idx >= table_size) + idx -= table_size; + } + + return NULL; +} /* public API follows */ @@ -356,8 +390,9 @@ _cairo_cache_destroy (cairo_cache_t *cache) cairo_status_t _cairo_cache_lookup (cairo_cache_t *cache, - void *key, - void **entry_return) + void *key, + void **entry_return, + int *created_entry) { unsigned long idx; @@ -392,6 +427,8 @@ _cairo_cache_lookup (cairo_cache_t *cache, cache->hits++; #endif *entry_return = *slot; + if (created_entry) + *created_entry = 0; return status; } @@ -401,19 +438,18 @@ _cairo_cache_lookup (cairo_cache_t *cache, /* Build the new entry. */ status = cache->backend->create_entry (cache, key, - entry_return); + (void **)&new_entry); if (status != CAIRO_STATUS_SUCCESS) return status; - new_entry = (cairo_cache_entry_base_t *) (*entry_return); - /* Store the hash value in case the backend forgot. */ new_entry->hashcode = cache->backend->hash (cache, key); /* Make some entries die if we're under memory pressure. */ while (cache->live_entries > 0 && + cache->max_memory > 0 && ((cache->max_memory - cache->used_memory) < new_entry->memory)) { - idx = _random_live_entry (cache); + idx = _random_entry (cache, NULL) - cache->entries; assert (idx < cache->arrangement->size); _entry_destroy (cache, idx); } @@ -425,7 +461,6 @@ _cairo_cache_lookup (cairo_cache_t *cache, status = _resize_cache (cache, cache->live_entries + 1); if (status != CAIRO_STATUS_SUCCESS) { cache->backend->destroy_entry (cache, new_entry); - *entry_return = NULL; return status; } @@ -439,9 +474,38 @@ _cairo_cache_lookup (cairo_cache_t *cache, _cache_sane_state (cache); + *entry_return = new_entry; + if (created_entry) + *created_entry = 1; + return status; } +cairo_status_t +_cairo_cache_remove (cairo_cache_t *cache, + void *key) +{ + cairo_cache_entry_base_t **slot; + + _cache_sane_state (cache); + + /* See if we have an entry in the table already. */ + slot = _find_exact_live_entry_for (cache, key); + if (slot != NULL) + _entry_destroy (cache, slot - cache->entries); + + return CAIRO_STATUS_SUCCESS; +} + +void * +_cairo_cache_random_entry (cairo_cache_t *cache, + int (*predicate)(void*)) +{ + cairo_cache_entry_base_t **slot = _random_entry (cache, predicate); + + return slot ? *slot : NULL; +} + unsigned long _cairo_hash_string (const char *c) { diff --git a/src/cairo-hull.c b/src/cairo-hull.c index 99b16d1ae..c93d70625 100644 --- a/src/cairo-hull.c +++ b/src/cairo-hull.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index 14e30f695..9745b3150 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" @@ -54,7 +54,8 @@ _cairo_format_bpp (cairo_format_t format) } static cairo_image_surface_t * -_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image) +_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, + cairo_format_t format) { cairo_image_surface_t *surface; @@ -66,6 +67,7 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image) surface->pixman_image = pixman_image; + surface->format = format; surface->data = (char *) pixman_image_get_data (pixman_image); surface->owns_data = 0; @@ -105,7 +107,8 @@ _cairo_image_surface_create_with_masks (char *data, if (pixman_image == NULL) return NULL; - surface = _cairo_image_surface_create_for_pixman_image (pixman_image); + surface = _cairo_image_surface_create_for_pixman_image (pixman_image, + (cairo_format_t)-1); return surface; } @@ -130,6 +133,20 @@ _create_pixman_format (cairo_format_t format) } } +/** + * cairo_image_surface_create: + * @format: format of pixels in the surface to create + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates an image surface of the specified format and + * dimensions. The initial contents of the surface is undefined; you + * must explicitely clear the buffer, using, for example, + * cairo_rectangle() and cairo_fill() if you want it cleared. + * + * Return value: the newly created surface, or %NULL if it couldn't + * be created because of lack of memory + **/ cairo_surface_t * cairo_image_surface_create (cairo_format_t format, int width, @@ -150,11 +167,33 @@ cairo_image_surface_create (cairo_format_t format, if (pixman_image == NULL) return NULL; - surface = _cairo_image_surface_create_for_pixman_image (pixman_image); + surface = _cairo_image_surface_create_for_pixman_image (pixman_image, format); return &surface->base; } +/** + * cairo_image_surface_create_for_data: + * @data: a pointer to a buffer supplied by the application + * in which to write contents. + * @format: the format of pixels in the buffer + * @width: the width of the image to be stored in the buffer + * @height: the height of the image to be stored in the buffer + * @stride: the number of bytes between the start of rows + * in the buffer. Having this be specified separate from @width + * allows for padding at the end of rows, or for writing + * to a subportion of a larger image. + * + * Creates an image surface for the provided pixel data. The output + * buffer must be kept around until the #cairo_surface_t is destroyed + * or cairo_surface_finish() is called on the surface. The initial + * contents of @buffer will be used as the inital image contents; you + * must explicitely clear the buffer, using, for example, + * cairo_rectangle() and cairo_fill() if you want it cleared. + * + * Return value: the newly created surface, or %NULL if it couldn't + * be created because of lack of memory + **/ cairo_surface_t * cairo_image_surface_create_for_data (char *data, cairo_format_t format, @@ -180,7 +219,7 @@ cairo_image_surface_create_for_data (char *data, if (pixman_image == NULL) return NULL; - surface = _cairo_image_surface_create_for_pixman_image (pixman_image); + surface = _cairo_image_surface_create_for_pixman_image (pixman_image, format); return &surface->base; } @@ -224,33 +263,66 @@ _cairo_image_surface_pixels_per_inch (void *abstract_surface) return 96.0; } -static cairo_image_surface_t * -_cairo_image_surface_get_image (void *abstract_surface) +static cairo_status_t +_cairo_image_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) { - cairo_image_surface_t *surface = abstract_surface; - - cairo_surface_reference (&surface->base); + *image_out = abstract_surface; + + return CAIRO_STATUS_SUCCESS; +} - return surface; +static void +_cairo_image_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ } static cairo_status_t -_cairo_image_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +_cairo_image_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect_out, + void **image_extra) { - if (image == abstract_surface) - return CAIRO_STATUS_SUCCESS; + cairo_image_surface_t *surface = abstract_surface; + + image_rect_out->x = 0; + image_rect_out->y = 0; + image_rect_out->width = surface->width; + image_rect_out->height = surface->height; + + *image_out = surface; - /* XXX: This case has not yet been implemented. We'll lie for now. */ return CAIRO_STATUS_SUCCESS; } +static void +_cairo_image_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ +} + static cairo_status_t -_cairo_image_abstract_surface_set_matrix (void *abstract_surface, - cairo_matrix_t *matrix) +_cairo_image_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { cairo_image_surface_t *surface = abstract_surface; - return _cairo_image_surface_set_matrix (surface, matrix); + + if (src->backend == surface->base.backend) { + *clone_out = src; + cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; } cairo_status_t @@ -276,14 +348,6 @@ _cairo_image_surface_set_matrix (cairo_image_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_image_abstract_surface_set_filter (void *abstract_surface, cairo_filter_t filter) -{ - cairo_image_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_filter (surface, filter); -} - cairo_status_t _cairo_image_surface_set_filter (cairo_image_surface_t *surface, cairo_filter_t filter) { @@ -314,13 +378,6 @@ _cairo_image_surface_set_filter (cairo_image_surface_t *surface, cairo_filter_t return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_image_abstract_surface_set_repeat (void *abstract_surface, int repeat) -{ - cairo_image_surface_t *surface = abstract_surface; - return _cairo_image_surface_set_repeat (surface, repeat); -} - cairo_status_t _cairo_image_surface_set_repeat (cairo_image_surface_t *surface, int repeat) { @@ -329,6 +386,34 @@ _cairo_image_surface_set_repeat (cairo_image_surface_t *surface, int repeat) return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_image_surface_set_attributes (cairo_image_surface_t *surface, + cairo_surface_attributes_t *attributes) +{ + cairo_int_status_t status; + + status = _cairo_image_surface_set_matrix (surface, &attributes->matrix); + if (status) + return status; + + switch (attributes->extend) { + case CAIRO_EXTEND_NONE: + _cairo_image_surface_set_repeat (surface, 0); + break; + case CAIRO_EXTEND_REPEAT: + _cairo_image_surface_set_repeat (surface, 1); + break; + case CAIRO_EXTEND_REFLECT: + /* XXX: Obviously wrong. */ + _cairo_image_surface_set_repeat (surface, 1); + break; + } + + status = _cairo_image_surface_set_filter (surface, attributes->filter); + + return status; +} + static pixman_operator_t _pixman_operator (cairo_operator_t operator) { @@ -368,8 +453,8 @@ _pixman_operator (cairo_operator_t operator) static cairo_int_status_t _cairo_image_surface_composite (cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, @@ -380,26 +465,61 @@ _cairo_image_surface_composite (cairo_operator_t operator, unsigned int width, unsigned int height) { - cairo_image_surface_t *dst = abstract_dst; - cairo_image_surface_t *src = (cairo_image_surface_t *) generic_src; - cairo_image_surface_t *mask = (cairo_image_surface_t *) generic_mask; - - if (generic_src->backend != dst->base.backend || - (generic_mask && (generic_mask->backend != dst->base.backend))) + cairo_surface_attributes_t src_attr, mask_attr; + cairo_image_surface_t *dst = abstract_dst; + cairo_image_surface_t *src; + cairo_image_surface_t *mask; + cairo_int_status_t status; + + status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, + &dst->base, + src_x, src_y, + mask_x, mask_y, + width, height, + (cairo_surface_t **) &src, + (cairo_surface_t **) &mask, + &src_attr, &mask_attr); + if (status) + return status; + + status = _cairo_image_surface_set_attributes (src, &src_attr); + if (CAIRO_OK (status)) { - return CAIRO_INT_STATUS_UNSUPPORTED; + if (mask) + { + status = _cairo_image_surface_set_attributes (mask, &mask_attr); + if (CAIRO_OK (status)) + pixman_composite (_pixman_operator (operator), + src->pixman_image, + mask->pixman_image, + dst->pixman_image, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + mask_x + mask_attr.x_offset, + mask_y + mask_attr.y_offset, + dst_x, dst_y, + width, height); + } + else + { + pixman_composite (_pixman_operator (operator), + src->pixman_image, + NULL, + dst->pixman_image, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + } } - pixman_composite (_pixman_operator (operator), - src->pixman_image, - mask ? mask->pixman_image : NULL, - dst->pixman_image, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); - - return CAIRO_STATUS_SUCCESS; + if (mask) + _cairo_pattern_release_surface (&dst->base, &mask->base, &mask_attr); + + _cairo_pattern_release_surface (&dst->base, &src->base, &src_attr); + + return status; } static cairo_int_status_t @@ -427,24 +547,56 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, static cairo_int_status_t _cairo_image_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *generic_src, + cairo_pattern_t *pattern, void *abstract_dst, - int x_src, - int y_src, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps) { - cairo_image_surface_t *dst = abstract_dst; - cairo_image_surface_t *src = (cairo_image_surface_t *) generic_src; + cairo_surface_attributes_t attributes; + cairo_image_surface_t *dst = abstract_dst; + cairo_image_surface_t *src; + cairo_int_status_t status; + int render_reference_x, render_reference_y; + int render_src_x, render_src_y; + + status = _cairo_pattern_acquire_surface (pattern, &dst->base, + src_x, src_y, width, height, + (cairo_surface_t **) &src, + &attributes); + if (status) + return status; + + if (traps[0].left.p1.y < traps[0].left.p2.y) { + render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); + render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); + } else { + render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); + render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); + } - if (generic_src->backend != dst->base.backend) - return CAIRO_INT_STATUS_UNSUPPORTED; + render_src_x = src_x + render_reference_x - dst_x; + render_src_y = src_y + render_reference_y - dst_y; - /* XXX: The pixman_trapezoid_t cast is evil and needs to go away somehow. */ - pixman_composite_trapezoids (operator, src->pixman_image, dst->pixman_image, - x_src, y_src, (pixman_trapezoid_t *) traps, num_traps); + /* XXX: The pixman_trapezoid_t cast is evil and needs to go away + * somehow. */ + status = _cairo_image_surface_set_attributes (src, &attributes); + if (CAIRO_OK (status)) + pixman_composite_trapezoids (operator, + src->pixman_image, + dst->pixman_image, + render_src_x + attributes.x_offset, + render_src_y + attributes.y_offset, + (pixman_trapezoid_t *) traps, num_traps); - return CAIRO_STATUS_SUCCESS; + _cairo_pattern_release_surface (&dst->base, &src->base, &attributes); + + return status; } static cairo_int_status_t @@ -490,41 +642,34 @@ _cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_image_abstract_surface_create_pattern (void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *box) +/** + * _cairo_surface_is_image: + * @surface: a #cairo_surface_t + * + * Checks if a surface is an #cairo_image_surface_t + * + * Return value: True if the surface is an image surface + **/ +int +_cairo_surface_is_image (cairo_surface_t *surface) { - cairo_image_surface_t *image; - - /* Fall back to general pattern creation for surface patterns. */ - if (pattern->type == CAIRO_PATTERN_SURFACE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - image = _cairo_pattern_get_image (pattern, box); - if (image) { - pattern->source = &image->base; - - return CAIRO_STATUS_SUCCESS; - } else - return CAIRO_STATUS_NO_MEMORY; + return surface->backend == &cairo_image_surface_backend; } - + static const cairo_surface_backend_t cairo_image_surface_backend = { _cairo_image_surface_create_similar, _cairo_image_abstract_surface_destroy, _cairo_image_surface_pixels_per_inch, - _cairo_image_surface_get_image, - _cairo_image_surface_set_image, - _cairo_image_abstract_surface_set_matrix, - _cairo_image_abstract_surface_set_filter, - _cairo_image_abstract_surface_set_repeat, + _cairo_image_surface_acquire_source_image, + _cairo_image_surface_release_source_image, + _cairo_image_surface_acquire_dest_image, + _cairo_image_surface_release_dest_image, + _cairo_image_surface_clone_similar, _cairo_image_surface_composite, _cairo_image_surface_fill_rectangles, _cairo_image_surface_composite_trapezoids, _cairo_image_surface_copy_page, _cairo_image_surface_show_page, _cairo_image_abstract_surface_set_clip_region, - _cairo_image_abstract_surface_create_pattern, NULL /* show_glyphs */ }; diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c index b964b688c..88e536e8a 100644 --- a/src/cairo-matrix.c +++ b/src/cairo-matrix.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #define _GNU_SOURCE @@ -54,6 +54,14 @@ _cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar); static void _cairo_matrix_compute_adjoint (cairo_matrix_t *matrix); +/** + * cairo_matrix_create: + * + * Creates a new identity matrix. + * + * Return value: a newly created matrix; free with cairo_matrix_destroy(), + * or %NULL if memory couldn't be allocated. + **/ cairo_matrix_t * cairo_matrix_create (void) { @@ -80,6 +88,12 @@ _cairo_matrix_fini (cairo_matrix_t *matrix) /* nothing to do here */ } +/** + * cairo_matrix_destroy: + * @matrix: a #cairo_matrix_t + * + * Frees a matrix created with cairo_matrix_create. + **/ void cairo_matrix_destroy (cairo_matrix_t *matrix) { @@ -87,6 +101,15 @@ cairo_matrix_destroy (cairo_matrix_t *matrix) free (matrix); } +/** + * cairo_matrix_copy: + * @matrix: a #cairo_matrix_t + * @other: another #cairo_ + * + * Modifies @matrix to be identical to @other. + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_copy (cairo_matrix_t *matrix, const cairo_matrix_t *other) { @@ -96,6 +119,14 @@ cairo_matrix_copy (cairo_matrix_t *matrix, const cairo_matrix_t *other) } slim_hidden_def(cairo_matrix_copy); +/** + * cairo_matrix_set_identity: + * @matrix: a #cairo_matrix_t + * + * Modifies @matrix to be an identity transformation. + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_set_identity (cairo_matrix_t *matrix) { @@ -105,6 +136,26 @@ cairo_matrix_set_identity (cairo_matrix_t *matrix) } slim_hidden_def(cairo_matrix_set_identity); +/** + * cairo_matrix_set_affine: + * @matrix: a cairo_matrix_t + * @a: a component of the affine transformation + * @b: b component of the affine transformation + * @c: c component of the affine transformation + * @d: d component of the affine transformation + * @tx: X translation component of the affine transformation + * @ty: Y translation component of the affine transformation + * + * Sets @matrix to be the affine transformation given by + * @a, b, @c, @d, @tx, @ty. The transformation is given + * by: + * <programlisting> + * x_new = x * a + y * c + tx; + * y_new = x * b + y * d + ty; + * </programlisting> + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_set_affine (cairo_matrix_t *matrix, double a, double b, @@ -119,6 +170,21 @@ cairo_matrix_set_affine (cairo_matrix_t *matrix, } slim_hidden_def(cairo_matrix_set_affine); +/** + * cairo_matrix_get_affine: + * @matrix: a @cairo_matrix_t + * @a: location to store a component of affine transformation, or %NULL + * @b: location to store b component of affine transformation, or %NULL + * @c: location to store c component of affine transformation, or %NULL + * @d: location to store d component of affine transformation, or %NULL + * @tx: location to store X-translation component of affine transformation, or %NULL + * @ty: location to store Y-translation component of affine transformation, or %NULL + * + * Gets the matrix values for the affine tranformation that @matrix represents. + * See cairo_matrix_set_affine(). + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_get_affine (cairo_matrix_t *matrix, double *a, double *b, @@ -153,6 +219,18 @@ _cairo_matrix_set_translate (cairo_matrix_t *matrix, tx, ty); } +/** + * cairo_matrix_translate: + * @matrix: a cairo_matrix_t + * @tx: amount to rotate in the X direction + * @ty: amount to rotate in the Y direction + * + * Applies a translation by @tx, @ty to the transformation in + * @matrix. The new transformation is given by first translating by + * @tx, @ty then applying the original transformation + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty) { @@ -173,6 +251,18 @@ _cairo_matrix_set_scale (cairo_matrix_t *matrix, 0, 0); } +/** + * cairo_matrix_scale: + * @matrix: a #cairo_matrix_t + * @sx: Scale factor in the X direction + * @sy: Scale factor in the Y direction + * + * Applies scaling by @tx, @ty to the transformation in + * @matrix. The new transformation is given by first scaling by @sx + * and @sy then applying the original transformation + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy) { @@ -202,6 +292,21 @@ _cairo_matrix_set_rotate (cairo_matrix_t *matrix, 0, 0); } +/** + * cairo_matrix_rotate: + * @matrix: a @cairo_matrix_t + * @radians: angle of rotation, in radians. Angles are defined + * so that an angle of 90 degrees (%M_PI radians) rotates the + * positive X axis into the positive Y axis. With the default + * Cairo choice of axis orientation, positive rotations are + * clockwise. + * + * Applies rotation by @radians to the transformation in + * @matrix. The new transformation is given by first rotating by + * @radians then applying the original transformation + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) { @@ -212,6 +317,19 @@ cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) return cairo_matrix_multiply (matrix, &tmp, matrix); } +/** + * cairo_matrix_multiply: + * @result: a @cairo_matrix_t in which to store the result + * @a: a @cairo_matrix_t + * @b: a @cairo_matrix_t + * + * Multiplies the affine transformations in @a and @b together + * and stores the result in @result. The resulting transformation + * is given by first applying the transformation in @b then + * applying the transformation in @a. + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b) { @@ -238,6 +356,27 @@ cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const ca } slim_hidden_def(cairo_matrix_multiply); +/** + * cairo_matrix_transform_distance: + * @matrix: a @cairo_matrix_t + * @dx: a distance in the X direction. An in/out parameter + * @dy: a distance in the Y direction. An in/out parameter + * + * Transforms the vector (@dx,@dy) by @matrix. Translation is + * ignored. In terms of the components of the affine transformation: + * + * <programlisting> + * dx2 = dx1 * a + dy1 * c; + * dy2 = dx1 * b + dy1 * d; + * </programlisting> + * + * Affine transformations are position invariant, so the same vector + * always transforms to the same vector. If (@x1,@y1) transforms + * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to + * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2. + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_transform_distance (cairo_matrix_t *matrix, double *dx, double *dy) { @@ -255,6 +394,16 @@ cairo_matrix_transform_distance (cairo_matrix_t *matrix, double *dx, double *dy) } slim_hidden_def(cairo_matrix_transform_distance); +/** + * cairo_matrix_transform_point: + * @matrix: a @cairo_matrix_t + * @x: X position. An in/out parameter + * @y: Y position. An in/out parameter + * + * Transforms the point (@x, @y) by @matrix. + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_transform_point (cairo_matrix_t *matrix, double *x, double *y) { @@ -351,6 +500,19 @@ _cairo_matrix_compute_adjoint (cairo_matrix_t *matrix) c*ty - d*tx, b*tx - a*ty); } +/** + * cairo_matrix_invert: + * @matrix: a @cairo_matrix_t + * + * Changes @matrix to be the inverse of it's original value. Not + * all transformation matrices have inverses; if the matrix + * collapses points together (it is <firstterm>degenerate</firstterm>), + * then it has no inverse and this function will fail. + * + * Returns: If @matrix has an inverse, modifies @matrix to + * be the inverse matrix and returns %CAIRO_STATUS_SUCCESS. Otherwise, + * returns %CAIRO_STATUS_INVALID_MATRIX. + **/ cairo_status_t cairo_matrix_invert (cairo_matrix_t *matrix) { @@ -458,7 +620,7 @@ _cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double return CAIRO_STATUS_SUCCESS; } -int +cairo_bool_t _cairo_matrix_is_integer_translation(cairo_matrix_t *mat, int *itx, int *ity) { @@ -477,7 +639,7 @@ _cairo_matrix_is_integer_translation(cairo_matrix_t *mat, if (ok) { *itx = _cairo_fixed_integer_part(ttx); *ity = _cairo_fixed_integer_part(tty); - return 1; + return TRUE; } - return 0; + return FALSE; } diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c index cfcdd97ee..7c5772a82 100644 --- a/src/cairo-path-bounds.c +++ b/src/cairo-path-bounds.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c index 6c6ebd976..dc79b6b96 100644 --- a/src/cairo-path-fill.c +++ b/src/cairo-path-fill.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c index ad0220370..08b380902 100644 --- a/src/cairo-path-stroke.c +++ b/src/cairo-path-stroke.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo-path.c b/src/cairo-path.c index 36c25d637..8314f601c 100644 --- a/src/cairo-path.c +++ b/src/cairo-path.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include <stdlib.h> @@ -100,6 +100,7 @@ _cairo_path_init_copy (cairo_path_t *path, cairo_path_t *other) for (other_op = other->op_head; other_op; other_op = other_op->next) { op = _cairo_path_op_buf_create (); if (op == NULL) { + _cairo_path_fini(path); return CAIRO_STATUS_NO_MEMORY; } *op = *other_op; @@ -109,6 +110,7 @@ _cairo_path_init_copy (cairo_path_t *path, cairo_path_t *other) for (other_arg = other->arg_head; other_arg; other_arg = other_arg->next) { arg = _cairo_path_arg_buf_create (); if (arg == NULL) { + _cairo_path_fini(path); return CAIRO_STATUS_NO_MEMORY; } *arg = *other_arg; diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index 6cb981458..283c36dbd 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -21,58 +21,108 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * Author: David Reveman <c99drn@cs.umu.se> + * Author: David Reveman <davidr@novell.com> */ #include "cairoint.h" +typedef void (*cairo_shader_function_t) (unsigned char *color0, + unsigned char *color1, + cairo_fixed_t factor, + uint32_t *pixel); + +typedef struct _cairo_shader_color_stop { + cairo_fixed_t offset; + cairo_fixed_48_16_t scale; + int id; + unsigned char color_char[4]; +} cairo_shader_color_stop_t; + +typedef struct _cairo_shader_op { + cairo_shader_color_stop_t *stops; + int n_stops; + cairo_extend_t extend; + cairo_shader_function_t shader_function; +} cairo_shader_op_t; + #define MULTIPLY_COLORCOMP(c1, c2) \ ((unsigned char) \ ((((unsigned char) (c1)) * (int) ((unsigned char) (c2))) / 0xff)) -void -_cairo_pattern_init (cairo_pattern_t *pattern) +static void +_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) { + pattern->type = type; pattern->ref_count = 1; - - pattern->extend = CAIRO_EXTEND_DEFAULT; - pattern->filter = CAIRO_FILTER_DEFAULT; - - _cairo_color_init (&pattern->color); + pattern->extend = CAIRO_EXTEND_DEFAULT; + pattern->filter = CAIRO_FILTER_DEFAULT; + pattern->alpha = 1.0; _cairo_matrix_init (&pattern->matrix); +} - pattern->stops = NULL; - pattern->n_stops = 0; +static cairo_status_t +_cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, + cairo_gradient_pattern_t *other) +{ + if (other->base.type == CAIRO_PATTERN_LINEAR) + { + cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern; + cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other; + + *dst = *src; + } + else + { + cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern; + cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other; + + *dst = *src; + } - pattern->type = CAIRO_PATTERN_SOLID; + if (other->n_stops) + { + pattern->stops = malloc (other->n_stops * sizeof (cairo_color_stop_t)); + if (!pattern->stops) + return CAIRO_STATUS_NO_MEMORY; + + memcpy (pattern->stops, other->stops, + other->n_stops * sizeof (cairo_color_stop_t)); + } - pattern->source = NULL; - pattern->source_offset.x = 0.0; - pattern->source_offset.y = 0.0; + return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other) { - *pattern = *other; - - pattern->ref_count = 1; + switch (other->type) { + case CAIRO_PATTERN_SOLID: { + cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern; + cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other; - if (pattern->n_stops) { - pattern->stops = - malloc (sizeof (cairo_color_stop_t) * pattern->n_stops); - if (pattern->stops == NULL) - return CAIRO_STATUS_NO_MEMORY; - memcpy (pattern->stops, other->stops, - sizeof (cairo_color_stop_t) * other->n_stops); + *dst = *src; + } break; + case CAIRO_PATTERN_SURFACE: { + cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern; + cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other; + + *dst = *src; + cairo_surface_reference (dst->surface); + } break; + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: { + cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern; + cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other; + cairo_status_t status; + + status = _cairo_gradient_pattern_init_copy (dst, src); + if (status) + return status; + } break; } - - if (pattern->source) - cairo_surface_reference (other->source); - - if (pattern->type == CAIRO_PATTERN_SURFACE) - cairo_surface_reference (other->u.surface.surface); + + pattern->ref_count = 1; return CAIRO_STATUS_SUCCESS; } @@ -80,110 +130,145 @@ _cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other) void _cairo_pattern_fini (cairo_pattern_t *pattern) { - if (pattern->n_stops) - free (pattern->stops); - - if (pattern->type == CAIRO_PATTERN_SURFACE) { - /* show_surface require us to restore surface matrix, repeat - attribute, filter type */ - if (pattern->source) { - cairo_surface_set_matrix (pattern->source, - &pattern->u.surface.save_matrix); - cairo_surface_set_repeat (pattern->source, - pattern->u.surface.save_repeat); - cairo_surface_set_filter (pattern->source, - pattern->u.surface.save_filter); - } - cairo_surface_destroy (pattern->u.surface.surface); + switch (pattern->type) { + case CAIRO_PATTERN_SOLID: + break; + case CAIRO_PATTERN_SURFACE: { + cairo_surface_pattern_t *fini = (cairo_surface_pattern_t *) pattern; + + cairo_surface_destroy (fini->surface); + } break; + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: { + cairo_gradient_pattern_t *fini = (cairo_gradient_pattern_t *) pattern; + + if (fini->n_stops) + free (fini->stops); + } break; } +} + +void +_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, + double red, + double green, + double blue) +{ + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SOLID); - if (pattern->source) - cairo_surface_destroy (pattern->source); + pattern->red = red; + pattern->green = green; + pattern->blue = blue; } void -_cairo_pattern_init_solid (cairo_pattern_t *pattern, - double red, double green, double blue) +_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, + cairo_surface_t *surface) { - _cairo_pattern_init (pattern); + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SURFACE); + + pattern->surface = surface; + cairo_surface_reference (surface); +} - pattern->type = CAIRO_PATTERN_SOLID; - _cairo_color_set_rgb (&pattern->color, red, green, blue); +static void +_cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern, + cairo_pattern_type_t type) +{ + _cairo_pattern_init (&pattern->base, type); + + pattern->stops = 0; + pattern->n_stops = 0; +} + +void +_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern, + double x0, double y0, double x1, double y1) +{ + _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_LINEAR); + + pattern->point0.x = x0; + pattern->point0.y = y0; + pattern->point1.x = x1; + pattern->point1.y = y1; +} + +void +_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, + double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1) +{ + _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_RADIAL); + + pattern->center0.x = cx0; + pattern->center0.y = cy0; + pattern->radius0 = fabs (radius0); + pattern->center1.x = cx1; + pattern->center1.y = cy1; + pattern->radius1 = fabs (radius1); } cairo_pattern_t * _cairo_pattern_create_solid (double red, double green, double blue) { - cairo_pattern_t *pattern; + cairo_solid_pattern_t *pattern; - pattern = malloc (sizeof (cairo_pattern_t)); + pattern = malloc (sizeof (cairo_solid_pattern_t)); if (pattern == NULL) return NULL; _cairo_pattern_init_solid (pattern, red, green, blue); - return pattern; + return &pattern->base; } cairo_pattern_t * cairo_pattern_create_for_surface (cairo_surface_t *surface) { - cairo_pattern_t *pattern; + cairo_surface_pattern_t *pattern; - pattern = malloc (sizeof (cairo_pattern_t)); + pattern = malloc (sizeof (cairo_surface_pattern_t)); if (pattern == NULL) return NULL; - _cairo_pattern_init (pattern); - - pattern->type = CAIRO_PATTERN_SURFACE; - pattern->u.surface.surface = surface; - cairo_surface_reference (surface); + _cairo_pattern_init_for_surface (pattern, surface); + + /* this will go away when we completely remove the surface attributes */ + if (surface->repeat) + pattern->base.extend = CAIRO_EXTEND_REPEAT; + else + pattern->base.extend = CAIRO_EXTEND_DEFAULT; - return pattern; + return &pattern->base; } cairo_pattern_t * cairo_pattern_create_linear (double x0, double y0, double x1, double y1) { - cairo_pattern_t *pattern; + cairo_linear_pattern_t *pattern; - pattern = malloc (sizeof (cairo_pattern_t)); + pattern = malloc (sizeof (cairo_linear_pattern_t)); if (pattern == NULL) return NULL; - _cairo_pattern_init (pattern); - - pattern->type = CAIRO_PATTERN_LINEAR; - pattern->u.linear.point0.x = x0; - pattern->u.linear.point0.y = y0; - pattern->u.linear.point1.x = x1; - pattern->u.linear.point1.y = y1; + _cairo_pattern_init_linear (pattern, x0, y0, x1, y1); - return pattern; + return &pattern->base.base; } cairo_pattern_t * cairo_pattern_create_radial (double cx0, double cy0, double radius0, double cx1, double cy1, double radius1) { - cairo_pattern_t *pattern; + cairo_radial_pattern_t *pattern; - pattern = malloc (sizeof (cairo_pattern_t)); + pattern = malloc (sizeof (cairo_radial_pattern_t)); if (pattern == NULL) return NULL; - _cairo_pattern_init (pattern); - - pattern->type = CAIRO_PATTERN_RADIAL; - pattern->u.radial.center0.x = cx0; - pattern->u.radial.center0.y = cy0; - pattern->u.radial.radius0 = fabs (radius0); - pattern->u.radial.center1.x = cx1; - pattern->u.radial.center1.y = cy1; - pattern->u.radial.radius1 = fabs (radius1); + _cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1); - return pattern; + return &pattern->base.base; } void @@ -209,37 +294,19 @@ cairo_pattern_destroy (cairo_pattern_t *pattern) free (pattern); } -static int -_cairo_pattern_stop_compare (const void *elem1, const void *elem2) -{ - return - (((cairo_color_stop_t *) elem1)->offset == - ((cairo_color_stop_t *) elem2)->offset) ? - /* equal offsets, sort on id */ - ((((cairo_color_stop_t *) elem1)->id < - ((cairo_color_stop_t *) elem2)->id) ? -1 : 1) : - /* sort on offset */ - ((((cairo_color_stop_t *) elem1)->offset < - ((cairo_color_stop_t *) elem2)->offset) ? -1 : 1); -} - -cairo_status_t -cairo_pattern_add_color_stop (cairo_pattern_t *pattern, - double offset, - double red, double green, double blue, - double alpha) +static cairo_status_t +_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, + double offset, + double red, + double green, + double blue, + double alpha) { cairo_color_stop_t *stop; - int i; - - _cairo_restrict_value (&offset, 0.0, 1.0); - _cairo_restrict_value (&red, 0.0, 1.0); - _cairo_restrict_value (&green, 0.0, 1.0); - _cairo_restrict_value (&blue, 0.0, 1.0); pattern->n_stops++; pattern->stops = realloc (pattern->stops, - sizeof (cairo_color_stop_t) * pattern->n_stops); + pattern->n_stops * sizeof (cairo_color_stop_t)); if (pattern->stops == NULL) { pattern->n_stops = 0; @@ -249,41 +316,51 @@ cairo_pattern_add_color_stop (cairo_pattern_t *pattern, stop = &pattern->stops[pattern->n_stops - 1]; stop->offset = _cairo_fixed_from_double (offset); - stop->id = pattern->n_stops; - stop->color_char[0] = red * 0xff; - stop->color_char[1] = green * 0xff; - stop->color_char[2] = blue * 0xff; - stop->color_char[3] = alpha * 0xff; + _cairo_color_init (&stop->color); + _cairo_color_set_rgb (&stop->color, red, green, blue); + _cairo_color_set_alpha (&stop->color, alpha); - /* sort stops in ascending order */ - qsort (pattern->stops, pattern->n_stops, sizeof (cairo_color_stop_t), - _cairo_pattern_stop_compare); - - for (i = 0; i < pattern->n_stops - 1; i++) { - pattern->stops[i + 1].scale = - pattern->stops[i + 1].offset - pattern->stops[i].offset; - if (pattern->stops[i + 1].scale == 65536) - pattern->stops[i + 1].scale = 0; + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +cairo_pattern_add_color_stop (cairo_pattern_t *pattern, + double offset, + double red, + double green, + double blue, + double alpha) +{ + if (pattern->type != CAIRO_PATTERN_LINEAR && + pattern->type != CAIRO_PATTERN_RADIAL) + { + /* XXX: CAIRO_STATUS_INVALID_PATTERN? */ + return CAIRO_STATUS_SUCCESS; } - return CAIRO_STATUS_SUCCESS; + _cairo_restrict_value (&offset, 0.0, 1.0); + _cairo_restrict_value (&red, 0.0, 1.0); + _cairo_restrict_value (&green, 0.0, 1.0); + _cairo_restrict_value (&blue, 0.0, 1.0); + _cairo_restrict_value (&alpha, 0.0, 1.0); + + return _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, + offset, + red, green, blue, + alpha); } cairo_status_t cairo_pattern_set_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) { - cairo_matrix_copy (&pattern->matrix, matrix); - - return CAIRO_STATUS_SUCCESS; + return cairo_matrix_copy (&pattern->matrix, matrix); } cairo_status_t cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) { - cairo_matrix_copy (matrix, &pattern->matrix); - - return CAIRO_STATUS_SUCCESS; + return cairo_matrix_copy (matrix, &pattern->matrix); } cairo_status_t @@ -316,9 +393,20 @@ cairo_pattern_get_extend (cairo_pattern_t *pattern) cairo_status_t _cairo_pattern_get_rgb (cairo_pattern_t *pattern, - double *red, double *green, double *blue) + double *red, + double *green, + double *blue) { - _cairo_color_get_rgb (&pattern->color, red, green, blue); + + if (pattern->type == CAIRO_PATTERN_SOLID) + { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + + *red = solid->red; + *green = solid->green; + *blue = solid->blue; + } else + *red = *green = *blue = 1.0; return CAIRO_STATUS_SUCCESS; } @@ -326,63 +414,16 @@ _cairo_pattern_get_rgb (cairo_pattern_t *pattern, void _cairo_pattern_set_alpha (cairo_pattern_t *pattern, double alpha) { - int i; - - _cairo_color_set_alpha (&pattern->color, alpha); - - for (i = 0; i < pattern->n_stops; i++) - pattern->stops[i].color_char[3] = - MULTIPLY_COLORCOMP (pattern->stops[i].color_char[3], alpha * 0xff); -} - -void -_cairo_pattern_set_source_offset (cairo_pattern_t *pattern, - double x, double y) -{ - pattern->source_offset.x = x; - pattern->source_offset.y = y; + pattern->alpha = alpha; } void _cairo_pattern_transform (cairo_pattern_t *pattern, - cairo_matrix_t *ctm_inverse) + cairo_matrix_t *ctm_inverse) { cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix); } -void -_cairo_pattern_prepare_surface (cairo_pattern_t *pattern) -{ - cairo_matrix_t device_to_source; - cairo_matrix_t user_to_source; - - /* should the surface matrix interface be remove from the API? - for now we multiple the surface matrix with the pattern matrix */ - cairo_surface_get_matrix (pattern->u.surface.surface, &user_to_source); - cairo_matrix_multiply (&device_to_source, &pattern->matrix, - &user_to_source); - cairo_surface_set_matrix (pattern->source, &device_to_source); - - /* storing original surface matrix in pattern */ - pattern->u.surface.save_matrix = user_to_source; - - /* storing original surface repeat mode in pattern */ - pattern->u.surface.save_repeat = pattern->source->repeat; - - /* what do we do with extend types pad and reflect? */ - if (pattern->extend == CAIRO_EXTEND_REPEAT - || pattern->source->repeat == 1) - cairo_surface_set_repeat (pattern->source, 1); - else - cairo_surface_set_repeat (pattern->source, 0); - - /* storing original surface filter in pattern */ - pattern->u.surface.save_filter = - cairo_surface_get_filter (pattern->source); - - cairo_surface_set_filter (pattern->source, pattern->filter); -} - #define INTERPOLATE_COLOR_NEAREST(c1, c2, factor) \ ((factor < 32768)? c1: c2) @@ -390,7 +431,7 @@ static void _cairo_pattern_shader_nearest (unsigned char *color0, unsigned char *color1, cairo_fixed_t factor, - int *pixel) + uint32_t *pixel) { *pixel = ((INTERPOLATE_COLOR_NEAREST (color0[3], color1[3], factor) << 24) | @@ -408,7 +449,7 @@ static void _cairo_pattern_shader_linear (unsigned char *color0, unsigned char *color1, cairo_fixed_t factor, - int *pixel) + uint32_t *pixel) { *pixel = ((INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor) << 24) | (INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor) << 16) | @@ -422,7 +463,7 @@ static void _cairo_pattern_shader_gaussian (unsigned char *color0, unsigned char *color1, cairo_fixed_t factor, - int *pixel) + uint32_t *pixel) { double f = ((double) factor) / 65536.0; @@ -436,17 +477,59 @@ _cairo_pattern_shader_gaussian (unsigned char *color0, #undef INTERPOLATE_COLOR_LINEAR -void -_cairo_pattern_shader_init (cairo_pattern_t *pattern, - cairo_shader_op_t *op) -{ - op->stops = pattern->stops; - op->n_stops = pattern->n_stops - 1; - op->min_offset = pattern->stops[0].offset; - op->max_offset = pattern->stops[op->n_stops].offset; - op->extend = pattern->extend; - - switch (pattern->filter) { +static int +_cairo_shader_color_stop_compare (const void *elem1, const void *elem2) +{ + cairo_shader_color_stop_t *s1 = (cairo_shader_color_stop_t *) elem1; + cairo_shader_color_stop_t *s2 = (cairo_shader_color_stop_t *) elem2; + + return + (s1->offset == s2->offset) ? + /* equal offsets, sort on id */ + ((s1->id < s2->id) ? -1 : 1) : + /* sort on offset */ + ((s1->offset < s2->offset) ? -1 : 1); +} + +static cairo_status_t +_cairo_pattern_shader_init (cairo_gradient_pattern_t *pattern, + cairo_shader_op_t *op) +{ + int i; + + op->stops = malloc (pattern->n_stops * sizeof (cairo_shader_color_stop_t)); + if (!op->stops) + return CAIRO_STATUS_NO_MEMORY; + + for (i = 0; i < pattern->n_stops; i++) + { + op->stops[i].color_char[0] = pattern->stops[i].color.red * 0xff; + op->stops[i].color_char[1] = pattern->stops[i].color.green * 0xff; + op->stops[i].color_char[2] = pattern->stops[i].color.blue * 0xff; + op->stops[i].color_char[3] = pattern->stops[i].color.alpha * + pattern->base.alpha * 0xff; + op->stops[i].offset = pattern->stops[i].offset; + op->stops[i].id = i; + } + + /* sort stops in ascending order */ + qsort (op->stops, pattern->n_stops, sizeof (cairo_shader_color_stop_t), + _cairo_shader_color_stop_compare); + + for (i = 0; i < pattern->n_stops - 1; i++) + { + op->stops[i + 1].scale = op->stops[i + 1].offset - op->stops[i].offset; + if (op->stops[i + 1].scale == 65536) + op->stops[i + 1].scale = 0; + } + + op->n_stops = pattern->n_stops; + op->extend = pattern->base.extend; + + /* XXX: this is wrong, the filter should not be used for selecting + color stop interpolation function. function should always be 'linear' + and filter should be used for computing pixels. */ + switch (pattern->base.filter) { case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: op->shader_function = _cairo_pattern_shader_nearest; @@ -460,15 +543,56 @@ _cairo_pattern_shader_init (cairo_pattern_t *pattern, op->shader_function = _cairo_pattern_shader_linear; break; } + + return CAIRO_STATUS_SUCCESS; } -void -_cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op, - cairo_fixed_t factor, - int *pixel) +static void +_cairo_pattern_shader_fini (cairo_shader_op_t *op) +{ + if (op->stops) + free (op->stops); +} + +/* Find two color stops bounding the given offset. If the given offset + * is before the first or after the last stop offset, the nearest + * offset is returned twice. + */ +static void +_cairo_shader_op_find_color_stops (cairo_shader_op_t *op, + cairo_fixed_t offset, + cairo_shader_color_stop_t *stops[2]) { int i; - + + /* Before first stop. */ + if (offset <= op->stops[0].offset) { + stops[0] = &op->stops[0]; + stops[1] = &op->stops[0]; + return; + } + + /* Between two stops. */ + for (i = 0; i < op->n_stops - 1; i++) { + if (offset <= op->stops[i + 1].offset) { + stops[0] = &op->stops[i]; + stops[1] = &op->stops[i + 1]; + return; + } + } + + /* After last stop. */ + stops[0] = &op->stops[op->n_stops - 1]; + stops[1] = &op->stops[op->n_stops - 1]; +} + +static void +_cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op, + cairo_fixed_t factor, + uint32_t *pixel) +{ + cairo_shader_color_stop_t *stops[2]; + switch (op->extend) { case CAIRO_EXTEND_REPEAT: factor -= factor & 0xffff0000; @@ -485,96 +609,158 @@ _cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op, break; } - if (factor < op->min_offset) - factor = op->min_offset; - else if (factor > op->max_offset) - factor = op->max_offset; - - for (i = 0; i < op->n_stops; i++) { - if (factor <= op->stops[i + 1].offset) { - - /* take offset as new 0 of coordinate system */ - factor -= op->stops[i].offset; - - /* difference between two offsets == 0, abrubt change */ - if (op->stops[i + 1].scale) - factor = ((cairo_fixed_48_16_t) factor << 16) / - op->stops[i + 1].scale; + _cairo_shader_op_find_color_stops (op, factor, stops); + + /* take offset as new 0 of coordinate system */ + factor -= stops[0]->offset; - op->shader_function (op->stops[i].color_char, - op->stops[i + 1].color_char, - factor, pixel); + /* difference between two offsets == 0, abrubt change */ + if (stops[1]->scale) + factor = ((cairo_fixed_48_16_t) factor << 16) / + stops[1]->scale; + + op->shader_function (stops[0]->color_char, + stops[1]->color_char, + factor, pixel); - /* multiply alpha */ - if (((unsigned char) (*pixel >> 24)) != 0xff) { - *pixel = (*pixel & 0xff000000) | - (MULTIPLY_COLORCOMP (*pixel >> 16, *pixel >> 24) << 16) | - (MULTIPLY_COLORCOMP (*pixel >> 8, *pixel >> 24) << 8) | - (MULTIPLY_COLORCOMP (*pixel >> 0, *pixel >> 24) << 0); - } - break; - } + /* multiply alpha */ + if (((unsigned char) (*pixel >> 24)) != 0xff) { + *pixel = (*pixel & 0xff000000) | + (MULTIPLY_COLORCOMP (*pixel >> 16, *pixel >> 24) << 16) | + (MULTIPLY_COLORCOMP (*pixel >> 8, *pixel >> 24) << 8) | + (MULTIPLY_COLORCOMP (*pixel >> 0, *pixel >> 24) << 0); } } -static void -_cairo_image_data_set_linear (cairo_pattern_t *pattern, - double offset_x, - double offset_y, - int *pixels, - int width, - int height) +static cairo_status_t +_cairo_image_data_set_linear (cairo_linear_pattern_t *pattern, + double offset_x, + double offset_y, + uint32_t *pixels, + int width, + int height) { int x, y; cairo_point_double_t point0, point1; - double px, py, ex, ey; double a, b, c, d, tx, ty; - double length, start, angle, fx, fy, factor; + double scale, start, dx, dy, factor; cairo_shader_op_t op; - - _cairo_pattern_shader_init (pattern, &op); - - point0.x = pattern->u.linear.point0.x; - point0.y = pattern->u.linear.point0.y; - point1.x = pattern->u.linear.point1.x; - point1.y = pattern->u.linear.point1.y; - - cairo_matrix_get_affine (&pattern->matrix, &a, &b, &c, &d, &tx, &ty); - - length = sqrt ((point1.x - point0.x) * (point1.x - point0.x) + - (point1.y - point0.y) * (point1.y - point0.y)); - length = (length) ? 1.0 / length : CAIRO_MAXSHORT; - - angle = -atan2 (point1.y - point0.y, point1.x - point0.x); - fx = cos (angle); - fy = -sin (angle); - - start = fx * point0.x; - start += fy * point0.y; + cairo_status_t status; + + status = _cairo_pattern_shader_init (&pattern->base, &op); + if (status) + return status; + + /* We compute the position in the linear gradient for + * a point q as: + * + * [q . (p1 - p0) - p0 . (p1 - p0)] / (p1 - p0) ^ 2 + * + * The computation is done in pattern space. The + * calculation could be heavily optimized by using the + * fact that 'factor' increases linearly in both + * directions. + */ + point0.x = pattern->point0.x; + point0.y = pattern->point0.y; + point1.x = pattern->point1.x; + point1.y = pattern->point1.y; + + cairo_matrix_get_affine (&pattern->base.base.matrix, + &a, &b, &c, &d, &tx, &ty); + + dx = point1.x - point0.x; + dy = point1.y - point0.y; + scale = dx * dx + dy * dy; + scale = (scale) ? 1.0 / scale : 1.0; + + start = dx * point0.x + dy * point0.y; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - px = x + offset_x; - py = y + offset_y; + double qx_device = x + offset_x; + double qy_device = y + offset_y; - /* transform fragment */ - ex = a * px + c * py + tx; - ey = b * px + d * py + ty; + /* transform fragment into pattern space */ + double qx = a * qx_device + c * qy_device + tx; + double qy = b * qx_device + d * qy_device + ty; - factor = ((fx * ex + fy * ey) - start) * length; + factor = ((dx * qx + dy * qy) - start) * scale; _cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++); } } + + _cairo_pattern_shader_fini (&op); + + return CAIRO_STATUS_SUCCESS; } static void -_cairo_image_data_set_radial (cairo_pattern_t *pattern, - double offset_x, - double offset_y, - int *pixels, - int width, - int height) +_cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern, + double offset_x, + double offset_y, + int width, + int height, + cairo_bool_t *is_horizontal, + cairo_bool_t *is_vertical) +{ + cairo_point_double_t point0, point1; + double a, b, c, d, tx, ty; + double scale, start, dx, dy; + cairo_fixed_t factors[3]; + int i; + + /* To classidy a pattern as horizontal or vertical, we first + * compute the (fixed point) factors at the corners of the + * pattern. We actually only need 3/4 corners, so we skip the + * fourth. + */ + point0.x = pattern->point0.x; + point0.y = pattern->point0.y; + point1.x = pattern->point1.x; + point1.y = pattern->point1.y; + + cairo_matrix_get_affine (&pattern->base.base.matrix, + &a, &b, &c, &d, &tx, &ty); + + dx = point1.x - point0.x; + dy = point1.y - point0.y; + scale = dx * dx + dy * dy; + scale = (scale) ? 1.0 / scale : 1.0; + + start = dx * point0.x + dy * point0.y; + + for (i = 0; i < 3; i++) { + double qx_device = (i % 2) * (width - 1) + offset_x; + double qy_device = (i / 2) * (height - 1) + offset_y; + + /* transform fragment into pattern space */ + double qx = a * qx_device + c * qy_device + tx; + double qy = b * qx_device + d * qy_device + ty; + + factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale); + } + + /* We consider a pattern to be vertical if the fixed point factor + * at the two upper corners is the same. We could accept a small + * change, but determining what change is acceptable would require + * sorting the stops in the pattern and looking at the differences. + * + * Horizontal works the same way with the two left corners. + */ + + *is_vertical = factors[1] == factors[0]; + *is_horizontal = factors[2] == factors[0]; +} + +static cairo_status_t +_cairo_image_data_set_radial (cairo_radial_pattern_t *pattern, + double offset_x, + double offset_y, + uint32_t *pixels, + int width, + int height) { int x, y, aligned_circles; cairo_point_double_t c0, c1; @@ -584,15 +770,18 @@ _cairo_image_data_set_radial (cairo_pattern_t *pattern, c0_c1_x, c0_c1_y, c0_c1, angle_c0, c1_y, y_x, c0_y, c0_x, r1_2, denumerator, fraction, factor; cairo_shader_op_t op; + cairo_status_t status; - _cairo_pattern_shader_init (pattern, &op); + status = _cairo_pattern_shader_init (&pattern->base, &op); + if (status) + return status; - c0.x = pattern->u.radial.center0.x; - c0.y = pattern->u.radial.center0.y; - r0 = pattern->u.radial.radius0; - c1.x = pattern->u.radial.center1.x; - c1.y = pattern->u.radial.center1.y; - r1 = pattern->u.radial.radius1; + c0.x = pattern->center0.x; + c0.y = pattern->center0.y; + r0 = pattern->radius0; + c1.x = pattern->center1.x; + c1.y = pattern->center1.y; + r1 = pattern->radius1; if (c0.x != c1.x || c0.y != c1.y) { aligned_circles = 0; @@ -606,7 +795,8 @@ _cairo_image_data_set_radial (cairo_pattern_t *pattern, r1_2 = c0_c1 = 0.0; /* shut up compiler */ } - cairo_matrix_get_affine (&pattern->matrix, &a, &b, &c, &d, &tx, &ty); + cairo_matrix_get_affine (&pattern->base.base.matrix, + &a, &b, &c, &d, &tx, &ty); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { @@ -682,70 +872,454 @@ _cairo_image_data_set_radial (cairo_pattern_t *pattern, _cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++); } } + + _cairo_pattern_shader_fini (&op); + + return CAIRO_STATUS_SUCCESS; } -cairo_image_surface_t * -_cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box) +static cairo_int_status_t +_cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_surface_t **out, + cairo_surface_attributes_t *attr) { - cairo_surface_t *surface; + cairo_image_surface_t *image; + cairo_status_t status; + uint32_t *data; + cairo_bool_t repeat = FALSE; + + if (pattern->base.type == CAIRO_PATTERN_LINEAR) { + cairo_bool_t is_horizontal; + cairo_bool_t is_vertical; + + _cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern, + x, y, width, height, + &is_horizontal, &is_vertical); + if (is_horizontal) { + height = 1; + repeat = TRUE; + } + if (is_vertical) { + width = 1; + repeat = TRUE; + } + } + + data = malloc (width * height * 4); + if (!data) + return CAIRO_STATUS_NO_MEMORY; + + if (pattern->base.type == CAIRO_PATTERN_LINEAR) + { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; + + status = _cairo_image_data_set_linear (linear, x, y, data, + width, height); + } + else + { + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; + + status = _cairo_image_data_set_radial (radial, x, y, data, + width, height); + } - switch (pattern->type) { - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: { - char *data; - double x = box->p1.x >> 16; - double y = box->p1.y >> 16; - int width = ((box->p2.x + 65535) >> 16) - (box->p1.x >> 16); - int height = ((box->p2.y + 65535) >> 16) - (box->p1.y >> 16); + if (status) { + free (data); + return status; + } + + image = (cairo_image_surface_t *) + cairo_image_surface_create_for_data ((char *) data, + CAIRO_FORMAT_ARGB32, + width, height, + width * 4); + + if (image == NULL) { + free (data); + return CAIRO_STATUS_NO_MEMORY; + } + + _cairo_image_surface_assume_ownership_of_data (image); + + status = _cairo_surface_clone_similar (dst, &image->base, out); + + cairo_surface_destroy (&image->base); + + attr->x_offset = -x; + attr->y_offset = -y; + cairo_matrix_set_identity (&attr->matrix); + attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE; + attr->filter = CAIRO_FILTER_NEAREST; + attr->acquired = FALSE; + + return status; +} + +static cairo_int_status_t +_cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_surface_t **out, + cairo_surface_attributes_t *attribs) +{ + cairo_color_t color; + + _cairo_color_init (&color); + _cairo_color_set_rgb (&color, pattern->red, pattern->green, pattern->blue); + _cairo_color_set_alpha (&color, pattern->base.alpha); + + *out = _cairo_surface_create_similar_solid (dst, + CAIRO_FORMAT_ARGB32, + 1, 1, + &color); + + if (*out == NULL) + return CAIRO_STATUS_NO_MEMORY; + + attribs->x_offset = attribs->y_offset = 0; + cairo_matrix_set_identity (&attribs->matrix); + attribs->extend = CAIRO_EXTEND_REPEAT; + attribs->filter = CAIRO_FILTER_NEAREST; + attribs->acquired = FALSE; + + return CAIRO_STATUS_SUCCESS; +} + + +/** + * _cairo_pattern_is_opaque + * + * Convenience function to determine whether a pattern has an opaque + * alpha value. This is done by testing whether the pattern's alpha + * value when converted to a byte is 255, so if a backend actually + * supported deep alpha channels this function might not do the right + * thing. + * + * Note that for a gradient or surface pattern, the overall resulting + * alpha for the pattern can be non-opaque even this function returns + * %TRUE, since the resulting alpha is the multiplication of the + * alpha of the gradient or surface with the pattern's alpha. In + * the future, alpha will be moved from the base pattern to the + * solid pattern subtype, at which point this function should + * probably be renamed to _cairo_pattern_is_opaque_solid() + * + * Return value: %TRUE if the pattern is opaque + **/ +cairo_bool_t +_cairo_pattern_is_opaque (cairo_pattern_t *pattern) +{ + return (pattern->alpha >= ((double)0xff00 / (double)0xffff)); +} + +static cairo_int_status_t +_cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_surface_t **out, + cairo_surface_attributes_t *attr) +{ + cairo_int_status_t status; + + attr->acquired = FALSE; + + /* handle pattern opacity */ + if (!_cairo_pattern_is_opaque (&pattern->base)) + { + cairo_surface_pattern_t tmp; + cairo_color_t color; + + _cairo_color_init (&color); + _cairo_color_set_alpha (&color, pattern->base.alpha); + + *out = _cairo_surface_create_similar_solid (dst, + CAIRO_FORMAT_ARGB32, + width, height, + &color); + if (*out == NULL) + return CAIRO_STATUS_NO_MEMORY; + + status = _cairo_pattern_init_copy (&tmp.base, &pattern->base); + if (CAIRO_OK (status)) + { + tmp.base.alpha = 1.0; + status = _cairo_surface_composite (CAIRO_OPERATOR_IN, + &tmp.base, + NULL, + *out, + x, y, 0, 0, 0, 0, + width, height); + + _cairo_pattern_fini (&tmp.base); + } + + if (status) { + cairo_surface_destroy (*out); + return status; + } - data = malloc (width * height * 4); - if (!data) - return NULL; + attr->x_offset = -x; + attr->y_offset = -y; + attr->extend = CAIRO_EXTEND_NONE; + attr->filter = CAIRO_FILTER_NEAREST; + + cairo_matrix_set_identity (&attr->matrix); + } + else + { + int tx, ty; + + if (_cairo_surface_is_image (dst)) + { + cairo_image_surface_t *image; + + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, + &attr->extra); + if (CAIRO_OK (status)) + *out = &image->base; + + attr->acquired = TRUE; + } + else + status = _cairo_surface_clone_similar (dst, pattern->surface, out); - if (pattern->type == CAIRO_PATTERN_RADIAL) - _cairo_image_data_set_radial (pattern, x, y, (int *) data, - width, height); + attr->extend = pattern->base.extend; + attr->filter = pattern->base.filter; + if (_cairo_matrix_is_integer_translation (&pattern->base.matrix, + &tx, &ty)) + { + cairo_matrix_set_identity (&attr->matrix); + attr->x_offset = tx; + attr->y_offset = ty; + } else - _cairo_image_data_set_linear (pattern, x, y, (int *) data, - width, height); + { + attr->matrix = pattern->base.matrix; + attr->x_offset = attr->y_offset = 0; + } + } + + return status; +} - _cairo_pattern_set_source_offset (pattern, x, y); +/** + * _cairo_pattern_acquire_surface: + * @pattern: a #cairo_pattern_t + * @dst: destination surface + * @x: X coordinate in source corresponding to left side of destination area + * @y: Y coordinate in source corresponding to top side of destination area + * @width: width of destination area + * @height: height of destination area + * @surface_out: location to store a pointer to a surface + * @attributes: surface attributes that destination backend should apply to + * the returned surface + * + * A convenience function to obtain a surface to use as the source for + * drawing on @dst. + * + * Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out. + **/ +cairo_int_status_t +_cairo_pattern_acquire_surface (cairo_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_surface_t **surface_out, + cairo_surface_attributes_t *attributes) +{ + switch (pattern->type) { + case CAIRO_PATTERN_SOLID: { + cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) pattern; + + return _cairo_pattern_acquire_surface_for_solid (src, dst, + x, y, width, height, + surface_out, + attributes); + } break; + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: { + cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) pattern; + + /* fast path for gradients with less than 2 color stops */ + if (src->n_stops < 2) + { + cairo_solid_pattern_t solid; + + if (src->n_stops) + { + _cairo_pattern_init_solid (&solid, + src->stops->color.red, + src->stops->color.green, + src->stops->color.blue); + _cairo_pattern_set_alpha (&solid.base, + src->stops->color.alpha); + } + else + { + _cairo_pattern_init_solid (&solid, 0.0, 0.0, 0.0); + _cairo_pattern_set_alpha (&solid.base, 0.0); + } - surface = cairo_image_surface_create_for_data (data, - CAIRO_FORMAT_ARGB32, - width, height, - width * 4); + return _cairo_pattern_acquire_surface_for_solid (&solid, dst, + x, y, + width, height, + surface_out, + attributes); + } + else + return _cairo_pattern_acquire_surface_for_gradient (src, dst, + x, y, + width, height, + surface_out, + attributes); + } break; + case CAIRO_PATTERN_SURFACE: { + cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) pattern; - if (surface) - _cairo_image_surface_assume_ownership_of_data ( - (cairo_image_surface_t *) surface); + return _cairo_pattern_acquire_surface_for_surface (src, dst, + x, y, width, height, + surface_out, + attributes); + } break; } - break; - case CAIRO_PATTERN_SOLID: - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); - if (surface) { - _cairo_surface_fill_rectangle (surface, - CAIRO_OPERATOR_SRC, - &pattern->color, 0, 0, 1, 1); - cairo_surface_set_repeat (surface, 1); + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +/** + * _cairo_pattern_release_surface: + * @pattern: a #cairo_pattern_t + * @info: pointer to #cairo_surface_attributes_t filled in by + * _cairo_pattern_acquire_surface + * + * Releases resources obtained by _cairo_pattern_acquire_surface. + **/ +void +_cairo_pattern_release_surface (cairo_surface_t *dst, + cairo_surface_t *surface, + cairo_surface_attributes_t *attributes) +{ + if (attributes->acquired) + _cairo_surface_release_source_image (dst, + (cairo_image_surface_t *) surface, + attributes->extra); + else + cairo_surface_destroy (surface); +} + +cairo_int_status_t +_cairo_pattern_acquire_surfaces (cairo_pattern_t *src, + cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + unsigned int width, + unsigned int height, + cairo_surface_t **src_out, + cairo_surface_t **mask_out, + cairo_surface_attributes_t *src_attributes, + cairo_surface_attributes_t *mask_attributes) +{ + cairo_int_status_t status; + + cairo_pattern_union_t tmp; + cairo_bool_t src_opaque, mask_opaque; + double src_alpha, mask_alpha; + + src_opaque = _cairo_pattern_is_opaque (src); + mask_opaque = !mask || _cairo_pattern_is_opaque (mask); + + /* For surface patterns, we move any translucency from src->alpha + * to mask->alpha so we can use the source unchanged. Otherwise we + * move the translucency from mask->alpha to src->alpha so that + * we can drop the mask if possible. + */ + if (src->type == CAIRO_PATTERN_SURFACE) + { + if (mask) { + mask_opaque = mask_opaque && src_opaque; + mask_alpha = mask->alpha * src->alpha; + } else { + mask_opaque = src_opaque; + mask_alpha = src->alpha; } - break; - case CAIRO_PATTERN_SURFACE: { - cairo_image_surface_t *image; + + src_alpha = 1.0; + src_opaque = TRUE; + } + else + { + if (mask) + { + src_opaque = mask_opaque && src_opaque; + src_alpha = mask->alpha * src->alpha; + /* FIXME: This needs changing when we support RENDER + * style 4-channel masks. + */ + if (mask->type == CAIRO_PATTERN_SOLID) + mask = NULL; + } else + src_alpha = src->alpha; + + mask_alpha = 1.0; + mask_opaque = TRUE; + } - image = _cairo_surface_get_image (pattern->u.surface.surface); - if (image) - surface = &image->base; + _cairo_pattern_init_copy (&tmp.base, src); + _cairo_pattern_set_alpha (&tmp.base, src_alpha); + + status = _cairo_pattern_acquire_surface (&tmp.base, dst, + src_x, src_y, + width, height, + src_out, src_attributes); + + _cairo_pattern_fini (&tmp.base); + + if (status) + return status; + + if (mask || !mask_opaque) + { + if (mask) + _cairo_pattern_init_copy (&tmp.base, mask); else - surface = NULL; + _cairo_pattern_init_solid (&tmp.solid, 0.0, 0.0, 0.0); + + _cairo_pattern_set_alpha (&tmp.base, mask_alpha); + + status = _cairo_pattern_acquire_surface (&tmp.base, dst, + mask_x, mask_y, + width, height, + mask_out, mask_attributes); + _cairo_pattern_fini (&tmp.base); + + if (status) + { + _cairo_pattern_release_surface (dst, *src_out, src_attributes); + return status; + } } - break; - default: - surface = NULL; - break; + else + { + *mask_out = NULL; } - - return (cairo_image_surface_t *) surface; + + return CAIRO_STATUS_SUCCESS; } - diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 23230aa74..fee918355 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -36,9 +36,8 @@ #include "cairoint.h" #include "cairo-pdf.h" -/* XXX: This seems broken to me. What about users without freetype - * that want to use a cairo PDF surface? */ -#include "cairo-ft.h" +/* XXX: Eventually, we need to handle other font backends */ +#include "cairo-ft-private.h" #include <ft2build.h> #include FT_FREETYPE_H @@ -54,10 +53,6 @@ * - Why doesn't pages inherit /alpha%d GS dictionaries from the Pages * object? * - * - Why isn't the pattern passed to composite traps instead of - * pattern->source? If composite traps needs an image or a surface it - * can call create_pattern(). - * * - We embed an image in the stream each time it's composited. We * could add generation counters to surfaces and remember the stream * ID for a particular generation for a particular surface. @@ -183,9 +178,6 @@ struct cairo_pdf_surface { double width_inches; double height_inches; - /* HACK: Non-null if this surface was created for a pattern. */ - cairo_pattern_t *pattern; - cairo_pdf_document_t *document; cairo_pdf_stream_t *current_stream; @@ -240,8 +232,6 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend; #define ARRAY_LENGTH(a) ( (sizeof (a)) / (sizeof ((a)[0])) ) #define SFNT_VERSION 0x00010000 -#define OFFSET_TABLE_SIZE 12 -#define TABLE_DIRECTORY_ENTRY_SIZE 16 #ifdef WORDS_BIGENDIAN @@ -300,19 +290,15 @@ cairo_pdf_font_destroy (cairo_pdf_font_t *font) } static cairo_pdf_font_t * -cairo_pdf_ft_font_create (cairo_pdf_document_t *document, - cairo_unscaled_font_t *unscaled_font, - cairo_font_scale_t *scale) +cairo_pdf_ft_font_create (cairo_pdf_document_t *document, + cairo_unscaled_font_t *unscaled_font) { - cairo_font_t scaled_font; FT_Face face; cairo_pdf_ft_font_t *font; unsigned long size; int i, j; - /* FIXME: Why do I have to pass a scaled font to get the FT_Face? */ - _cairo_font_init (&scaled_font, scale, unscaled_font); - face = cairo_ft_font_face (&scaled_font); + face = _cairo_ft_unscaled_font_lock_face (unscaled_font); /* We currently only support freetype truetype fonts. */ size = 0; @@ -333,7 +319,8 @@ cairo_pdf_ft_font_create (cairo_pdf_document_t *document, if (_cairo_array_grow_by (&font->output, 4096) != CAIRO_STATUS_SUCCESS) goto fail1; - font->face = face; + font->base.unscaled_font = unscaled_font; + _cairo_unscaled_font_reference (unscaled_font); font->glyphs = calloc (face->num_glyphs + 1, sizeof (ft_subset_glyph_t)); if (font->glyphs == NULL) goto fail2; @@ -364,6 +351,8 @@ cairo_pdf_ft_font_create (cairo_pdf_document_t *document, if (font->base.widths == NULL) goto fail5; + _cairo_ft_unscaled_font_unlock_face (unscaled_font); + font->status = CAIRO_STATUS_SUCCESS; return &font->base; @@ -447,7 +436,7 @@ cairo_pdf_ft_font_write_cmap_table (cairo_pdf_ft_font_t *font, unsigned long tag cairo_pdf_ft_font_write_be16 (font, 0); cairo_pdf_ft_font_write_be16 (font, 1); - cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 1); cairo_pdf_ft_font_write_be16 (font, 0); cairo_pdf_ft_font_write_be32 (font, 12); @@ -764,12 +753,15 @@ cairo_pdf_ft_font_generate (void *abstract_font, unsigned long start, end, next, checksum; int i; + font->face = _cairo_ft_unscaled_font_lock_face (font->base.unscaled_font); + if (cairo_pdf_ft_font_write_offset_table (font)) - return font->status; + goto fail; start = cairo_pdf_ft_font_align_output (font); end = start; + end = 0; for (i = 0; i < ARRAY_LENGTH (truetype_tables); i++) { if (truetype_tables[i].write (font, truetype_tables[i].tag)) goto fail; @@ -789,6 +781,9 @@ cairo_pdf_ft_font_generate (void *abstract_font, *length = _cairo_array_num_elements (&font->output); fail: + _cairo_ft_unscaled_font_unlock_face (font->base.unscaled_font); + font->face = NULL; + return font->status; } @@ -947,14 +942,13 @@ _cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document, surface->width_inches = width_inches; surface->height_inches = height_inches; - surface->pattern = NULL; _cairo_pdf_document_reference (document); surface->document = document; _cairo_array_init (&surface->streams, sizeof (cairo_pdf_stream_t *)); _cairo_array_init (&surface->patterns, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&surface->xobjects, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&surface->alphas, sizeof (double)); - _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t)); + _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_resource_t)); return &surface->base; } @@ -1102,38 +1096,46 @@ _cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface) } } -static cairo_image_surface_t * -_cairo_pdf_surface_get_image (void *abstract_surface) +static cairo_status_t +_cairo_pdf_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) { - return NULL; + return CAIRO_INT_STATUS_UNSUPPORTED; } -static cairo_status_t -_cairo_pdf_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +static void +_cairo_pdf_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) { - return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t -_cairo_pdf_surface_set_matrix (void *abstract_surface, - cairo_matrix_t *matrix) +_cairo_pdf_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect, + void **image_extra) { - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_UNSUPPORTED; } -static cairo_status_t -_cairo_pdf_surface_set_filter (void *abstract_surface, - cairo_filter_t filter) +static void +_cairo_pdf_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) { - return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_pdf_surface_set_repeat (void *abstract_surface, - int repeat) +_cairo_pdf_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_UNSUPPORTED; } static void * @@ -1210,23 +1212,34 @@ emit_image_data (cairo_pdf_document_t *document, } static cairo_int_status_t -_cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst, - cairo_image_surface_t *image) +_cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst, + cairo_surface_pattern_t *pattern) { cairo_pdf_document_t *document = dst->document; FILE *file = document->file; unsigned id; cairo_matrix_t i2u; + cairo_status_t status; + cairo_image_surface_t *image; + cairo_surface_t *src; + void *image_extra; + + src = pattern->surface; + status = _cairo_surface_acquire_source_image (src, &image, &image_extra); + if (!CAIRO_OK (status)) + return status; id = emit_image_data (dst->document, image); - if (id == 0) - return CAIRO_STATUS_NO_MEMORY; + if (id == 0) { + status = CAIRO_STATUS_NO_MEMORY; + goto bail; + } _cairo_pdf_surface_add_xobject (dst, id); _cairo_pdf_surface_ensure_stream (dst); - cairo_matrix_copy (&i2u, &image->base.matrix); + cairo_matrix_copy (&i2u, &pattern->base.matrix); cairo_matrix_invert (&i2u); cairo_matrix_translate (&i2u, 0, image->height); cairo_matrix_scale (&i2u, image->width, -image->height); @@ -1238,7 +1251,10 @@ _cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst, i2u.m[2][0], i2u.m[2][1], id); - return CAIRO_STATUS_SUCCESS; + bail: + _cairo_surface_release_source_image (src, image, image_extra); + + return status; } /* The contents of the surface is already transformed into PDF units, @@ -1253,20 +1269,19 @@ _cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst, static cairo_int_status_t _cairo_pdf_surface_composite_pdf (cairo_pdf_surface_t *dst, - cairo_pdf_surface_t *src, - int width, int height) + cairo_surface_pattern_t *pattern) { cairo_pdf_document_t *document = dst->document; FILE *file = document->file; cairo_matrix_t i2u; cairo_pdf_stream_t *stream; int num_streams, i; - - if (src->pattern != NULL) - return CAIRO_STATUS_SUCCESS; + cairo_pdf_surface_t *src; _cairo_pdf_surface_ensure_stream (dst); + src = (cairo_pdf_surface_t *) pattern->surface; + cairo_matrix_copy (&i2u, &src->base.matrix); cairo_matrix_invert (&i2u); cairo_matrix_scale (&i2u, @@ -1297,8 +1312,8 @@ _cairo_pdf_surface_composite_pdf (cairo_pdf_surface_t *dst, static cairo_int_status_t _cairo_pdf_surface_composite (cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, @@ -1310,17 +1325,18 @@ _cairo_pdf_surface_composite (cairo_operator_t operator, unsigned int height) { cairo_pdf_surface_t *dst = abstract_dst; - cairo_pdf_surface_t *src; - cairo_image_surface_t *image; + cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) src_pattern; - if (generic_src->backend == &cairo_pdf_surface_backend) { - src = (cairo_pdf_surface_t *) generic_src; - return _cairo_pdf_surface_composite_pdf (dst, src, width, height); - } - else { - image = _cairo_surface_get_image (generic_src); - return _cairo_pdf_surface_composite_image (dst, image); - } + if (mask_pattern) + return CAIRO_STATUS_SUCCESS; + + if (src_pattern->type != CAIRO_PATTERN_SURFACE) + return CAIRO_STATUS_SUCCESS; + + if (src->surface->backend == &cairo_pdf_surface_backend) + return _cairo_pdf_surface_composite_pdf (dst, src); + else + return _cairo_pdf_surface_composite_image (dst, src); } static cairo_int_status_t @@ -1335,9 +1351,6 @@ _cairo_pdf_surface_fill_rectangles (void *abstract_surface, FILE *file = document->file; int i; - if (surface->pattern != NULL) - return CAIRO_STATUS_SUCCESS; - _cairo_pdf_surface_ensure_stream (surface); fprintf (file, @@ -1355,23 +1368,44 @@ _cairo_pdf_surface_fill_rectangles (void *abstract_surface, } static void -emit_tiling_pattern (cairo_operator_t operator, - cairo_pdf_surface_t *dst, - cairo_pattern_t *pattern) +emit_solid_pattern (cairo_pdf_surface_t *surface, + cairo_solid_pattern_t *pattern) +{ + cairo_pdf_document_t *document = surface->document; + FILE *file = document->file; + unsigned int alpha; + + alpha = _cairo_pdf_surface_add_alpha (surface, pattern->base.alpha); + _cairo_pdf_surface_ensure_stream (surface); + fprintf (file, + "%f %f %f rg /a%d gs\r\n", + pattern->red, + pattern->green, + pattern->blue, + alpha); +} + +static void +emit_surface_pattern (cairo_pdf_surface_t *dst, + cairo_surface_pattern_t *pattern) { cairo_pdf_document_t *document = dst->document; FILE *file = document->file; cairo_pdf_stream_t *stream; cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; char entries[250]; unsigned int id, alpha; cairo_matrix_t pm; - if (pattern->u.surface.surface->backend == &cairo_pdf_surface_backend) { + if (pattern->surface->backend == &cairo_pdf_surface_backend) { return; } - - image = _cairo_surface_get_image (pattern->u.surface.surface); + + status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); + if (!CAIRO_OK (status)) + return; _cairo_pdf_document_close_stream (document); @@ -1382,7 +1416,7 @@ emit_tiling_pattern (cairo_operator_t operator, cairo_matrix_set_identity (&pm); cairo_matrix_scale (&pm, image->width, image->height); - cairo_matrix_copy (&pm, &pattern->matrix); + cairo_matrix_copy (&pm, &pattern->base.matrix); cairo_matrix_invert (&pm); snprintf (entries, sizeof entries, @@ -1401,6 +1435,8 @@ emit_tiling_pattern (cairo_operator_t operator, stream = _cairo_pdf_document_open_stream (document, entries); + /* FIXME: emit code to show surface here. */ + _cairo_pdf_surface_add_pattern (dst, stream->id); _cairo_pdf_surface_ensure_stream (dst); @@ -1408,10 +1444,12 @@ emit_tiling_pattern (cairo_operator_t operator, fprintf (file, "/Pattern cs /res%d scn /a%d gs\r\n", stream->id, alpha); + + _cairo_surface_release_source_image (pattern->surface, image, image_extra); } static unsigned int -emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) +emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern) { cairo_pdf_document_t *document = surface->document; FILE *file = document->file; @@ -1430,12 +1468,12 @@ emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) "stream\r\n", function_id); - fputc (pattern->stops[0].color_char[0], file); - fputc (pattern->stops[0].color_char[1], file); - fputc (pattern->stops[0].color_char[2], file); - fputc (pattern->stops[1].color_char[0], file); - fputc (pattern->stops[1].color_char[1], file); - fputc (pattern->stops[1].color_char[2], file); + fputc (pattern->stops[0].color.red * 0xff, file); + fputc (pattern->stops[0].color.green * 0xff, file); + fputc (pattern->stops[0].color.blue * 0xff, file); + fputc (pattern->stops[1].color.red * 0xff, file); + fputc (pattern->stops[1].color.green * 0xff, file); + fputc (pattern->stops[1].color.blue * 0xff, file); fprintf (file, "\r\n" @@ -1446,7 +1484,7 @@ emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) } static void -emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) +emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_linear_pattern_t *pattern) { cairo_pdf_document_t *document = surface->document; FILE *file = document->file; @@ -1456,16 +1494,16 @@ emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) _cairo_pdf_document_close_stream (document); - function_id = emit_pattern_stops (surface, pattern); + function_id = emit_pattern_stops (surface, &pattern->base); - cairo_matrix_copy (&p2u, &pattern->matrix); + cairo_matrix_copy (&p2u, &pattern->base.base.matrix); cairo_matrix_invert (&p2u); - x0 = pattern->u.linear.point0.x; - y0 = pattern->u.linear.point0.y; + x0 = pattern->point0.x; + y0 = pattern->point0.y; cairo_matrix_transform_point (&p2u, &x0, &y0); - x1 = pattern->u.linear.point1.x; - y1 = pattern->u.linear.point1.y; + x1 = pattern->point1.x; + y1 = pattern->point1.y; cairo_matrix_transform_point (&p2u, &x1, &y1); pattern_id = _cairo_pdf_document_new_object (document); @@ -1479,16 +1517,14 @@ emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) " /ColorSpace /DeviceRGB\r\n" " /Coords [ %f %f %f %f ]\r\n" " /Function %d 0 R\r\n" - " /Extend [ %s %s ]\r\n" + " /Extend [ true true ]\r\n" " >>\r\n" ">>\r\n" "endobj\r\n", pattern_id, document->height_inches * document->y_ppi, x0, y0, x1, y1, - function_id, - (1 || pattern->extend) ? "true" : "false", - (1 || pattern->extend) ? "true" : "false"); + function_id); _cairo_pdf_surface_add_pattern (surface, pattern_id); @@ -1502,7 +1538,7 @@ emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) } static void -emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) +emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_radial_pattern_t *pattern) { cairo_pdf_document_t *document = surface->document; FILE *file = document->file; @@ -1512,24 +1548,31 @@ emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) _cairo_pdf_document_close_stream (document); - function_id = emit_pattern_stops (surface, pattern); + function_id = emit_pattern_stops (surface, &pattern->base); - cairo_matrix_copy (&p2u, &pattern->matrix); + cairo_matrix_copy (&p2u, &pattern->base.base.matrix); cairo_matrix_invert (&p2u); - x0 = pattern->u.radial.center0.x; - y0 = pattern->u.radial.center0.y; - r0 = pattern->u.radial.radius0; + x0 = pattern->center0.x; + y0 = pattern->center0.y; + r0 = pattern->radius0; cairo_matrix_transform_point (&p2u, &x0, &y0); - x1 = pattern->u.radial.center1.x; - y1 = pattern->u.radial.center1.y; - r1 = pattern->u.radial.radius1; + x1 = pattern->center1.x; + y1 = pattern->center1.y; + r1 = pattern->radius1; cairo_matrix_transform_point (&p2u, &x1, &y1); /* FIXME: This is surely crack, but how should you scale a radius * in a non-orthogonal coordinate system? */ cairo_matrix_transform_distance (&p2u, &r0, &r1); + /* FIXME: There is a difference between the cairo gradient extend + * semantics and PDF extend semantics. PDFs extend=false means + * that nothing is painted outside the gradient boundaries, + * whereas cairo takes this to mean that the end color is padded + * to infinity. Setting extend=true in PDF gives the cairo default + * behavoir, not yet sure how to implement the cairo mirror and + * repeat behaviour. */ pattern_id = _cairo_pdf_document_new_object (document); fprintf (file, "%d 0 obj\r\n" @@ -1541,16 +1584,14 @@ emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) " /ColorSpace /DeviceRGB\r\n" " /Coords [ %f %f %f %f %f %f ]\r\n" " /Function %d 0 R\r\n" - " /Extend [ %s %s ]\r\n" + " /Extend [ true true ]\r\n" " >>\r\n" ">>\r\n" "endobj\r\n", pattern_id, document->height_inches * document->y_ppi, x0, y0, r0, x1, y1, r1, - function_id, - (1 || pattern->extend) ? "true" : "false", - (1 || pattern->extend) ? "true" : "false"); + function_id); _cairo_pdf_surface_add_pattern (surface, pattern_id); @@ -1563,6 +1604,28 @@ emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) pattern_id, alpha); } +static void +emit_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) +{ + switch (pattern->type) { + case CAIRO_PATTERN_SOLID: + emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_SURFACE: + emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_LINEAR: + emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_RADIAL: + emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern); + break; + } +} + static double intersect (cairo_line_t *line, cairo_fixed_t y) { @@ -1574,60 +1637,23 @@ intersect (cairo_line_t *line, cairo_fixed_t y) static cairo_int_status_t _cairo_pdf_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *generic_src, + cairo_pattern_t *pattern, void *abstract_dst, int x_src, int y_src, + int x_dst, + int y_dst, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps) { cairo_pdf_surface_t *surface = abstract_dst; - cairo_pdf_surface_t *source = (cairo_pdf_surface_t *) generic_src; cairo_pdf_document_t *document = surface->document; - cairo_pattern_t *pattern; FILE *file = document->file; int i; - unsigned int alpha; - /* FIXME: we really just want the original pattern here, not a - * source surface. */ - pattern = source->pattern; - - if (source->base.backend != &cairo_pdf_surface_backend) { - printf ("_cairo_pdf_surface_composite_trapezoids: not a pdf source\r"); - return CAIRO_STATUS_SUCCESS; - } - - if (pattern == NULL) { - printf ("_cairo_pdf_surface_composite_trapezoids: " - "non-pattern pdf source\r"); - return CAIRO_STATUS_SUCCESS; - } - - switch (pattern->type) { - case CAIRO_PATTERN_SOLID: - alpha = _cairo_pdf_surface_add_alpha (surface, pattern->color.alpha); - _cairo_pdf_surface_ensure_stream (surface); - fprintf (file, - "%f %f %f rg /a%d gs\r\n", - pattern->color.red, - pattern->color.green, - pattern->color.blue, - alpha); - break; - - case CAIRO_PATTERN_SURFACE: - emit_tiling_pattern (operator, surface, pattern); - break; - - case CAIRO_PATTERN_LINEAR: - emit_linear_pattern (surface, pattern); - break; - - case CAIRO_PATTERN_RADIAL: - emit_radial_pattern (surface, pattern ); - break; - } + emit_pattern (surface, pattern); /* After the above switch the current stream should belong to this * surface, so no need to _cairo_pdf_surface_ensure_stream() */ @@ -1686,59 +1712,48 @@ _cairo_pdf_surface_set_clip_region (void *abstract_surface, return CAIRO_INT_STATUS_UNSUPPORTED; } -static cairo_int_status_t -_cairo_pdf_surface_create_pattern (void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *extents) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_surface_t *source; - - source = (cairo_pdf_surface_t *) - _cairo_pdf_surface_create_for_document (surface->document, 0, 0); - source->pattern = pattern; - pattern->source = &source->base; - - return CAIRO_STATUS_SUCCESS; -} - static cairo_pdf_font_t * _cairo_pdf_document_get_font (cairo_pdf_document_t *document, - cairo_unscaled_font_t *unscaled_font, - cairo_font_scale_t *scale) + cairo_font_t *font) { - cairo_pdf_font_t *font; + cairo_unscaled_font_t *unscaled_font; + cairo_pdf_font_t *pdf_font; unsigned int num_fonts, i; + unscaled_font = _cairo_ft_font_get_unscaled_font (font); + num_fonts = _cairo_array_num_elements (&document->fonts); for (i = 0; i < num_fonts; i++) { - _cairo_array_copy_element (&document->fonts, i, &font); - if (font->unscaled_font == unscaled_font) - return font; + _cairo_array_copy_element (&document->fonts, i, &pdf_font); + if (pdf_font->unscaled_font == unscaled_font) + return pdf_font; } /* FIXME: Figure out here which font backend is in use and call * the appropriate constructor. */ - font = cairo_pdf_ft_font_create (document, unscaled_font, scale); - if (font == NULL) + pdf_font = cairo_pdf_ft_font_create (document, unscaled_font); + if (pdf_font == NULL) return NULL; - if (_cairo_array_append (&document->fonts, &font, 1) == NULL) { - cairo_pdf_font_destroy (font); + if (_cairo_array_append (&document->fonts, &pdf_font, 1) == NULL) { + cairo_pdf_font_destroy (pdf_font); return NULL; } - return font; + return pdf_font; } static cairo_status_t -_cairo_pdf_surface_show_glyphs (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, +_cairo_pdf_surface_show_glyphs (cairo_font_t *font, cairo_operator_t operator, - cairo_surface_t *source, + cairo_pattern_t *pattern, void *abstract_surface, int source_x, int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, const cairo_glyph_t *glyphs, int num_glyphs) { @@ -1748,23 +1763,23 @@ _cairo_pdf_surface_show_glyphs (cairo_unscaled_font_t *font, cairo_pdf_font_t *pdf_font; int i, index; - pdf_font = _cairo_pdf_document_get_font (document, font, scale); + pdf_font = _cairo_pdf_document_get_font (document, font); if (pdf_font == NULL) return CAIRO_STATUS_NO_MEMORY; - _cairo_pdf_surface_ensure_stream (surface); + emit_pattern (surface, pattern); - fprintf (file, "0 0 0 rg BT /res%u 1 Tf", pdf_font->font_id); + fprintf (file, "BT /res%u 1 Tf", pdf_font->font_id); for (i = 0; i < num_glyphs; i++) { index = cairo_pdf_font_use_glyph (pdf_font, glyphs[i].index); fprintf (file, - " %f %f %f %f %f %f Tm (%c) Tj", - scale->matrix[0][0], - scale->matrix[0][1], - scale->matrix[1][0], - -scale->matrix[1][1], + " %f %f %f %f %f %f Tm (\\%o) Tj", + font->scale.matrix[0][0], + font->scale.matrix[0][1], + font->scale.matrix[1][0], + -font->scale.matrix[1][1], glyphs[i].x, glyphs[i].y, index); @@ -1780,18 +1795,17 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { _cairo_pdf_surface_create_similar, _cairo_pdf_surface_destroy, _cairo_pdf_surface_pixels_per_inch, - _cairo_pdf_surface_get_image, - _cairo_pdf_surface_set_image, - _cairo_pdf_surface_set_matrix, - _cairo_pdf_surface_set_filter, - _cairo_pdf_surface_set_repeat, + _cairo_pdf_surface_acquire_source_image, + _cairo_pdf_surface_release_source_image, + _cairo_pdf_surface_acquire_dest_image, + _cairo_pdf_surface_release_dest_image, + _cairo_pdf_surface_clone_similar, _cairo_pdf_surface_composite, _cairo_pdf_surface_fill_rectangles, _cairo_pdf_surface_composite_trapezoids, _cairo_pdf_surface_copy_page, _cairo_pdf_surface_show_page, _cairo_pdf_surface_set_clip_region, - _cairo_pdf_surface_create_pattern, _cairo_pdf_surface_show_glyphs }; @@ -1930,8 +1944,8 @@ _cairo_pdf_document_write_fonts (cairo_pdf_document_t *document) fprintf (file, "%d 0 obj\r\n" "<< /Type /FontDescriptor\r\n" - " /FontName /%s\r\n" - " /Flags 32\r\n" + " /FontName /7%s\r\n" + " /Flags 4\r\n" " /FontBBox [ %ld %ld %ld %ld ]\r\n" " /ItalicAngle 0\r\n" " /Ascent %ld\r\n" diff --git a/src/cairo-pdf.h b/src/cairo-pdf.h index 0f624af31..701a7b4a7 100644 --- a/src/cairo-pdf.h +++ b/src/cairo-pdf.h @@ -31,17 +31,20 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ -#include <cairo.h> - #ifndef CAIRO_PDF_H #define CAIRO_PDF_H + +#include <cairo.h> + #ifdef CAIRO_HAS_PDF_SURFACE #include <stdio.h> +CAIRO_BEGIN_DECLS + void cairo_set_target_pdf (cairo_t *cr, FILE *file, @@ -58,5 +61,7 @@ cairo_pdf_surface_create (FILE *file, double x_pixels_per_inch, double y_pixels_per_inch); +CAIRO_END_DECLS + #endif /* CAIRO_HAS_PDF_SURFACE */ #endif /* CAIRO_PDF_H */ diff --git a/src/cairo-pen.c b/src/cairo-pen.c index f365091dc..6ecaa00b3 100644 --- a/src/cairo-pen.c +++ b/src/cairo-pen.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo-png.h b/src/cairo-png.h index 766d6f91f..3e86210b0 100644 --- a/src/cairo-png.h +++ b/src/cairo-png.h @@ -31,17 +31,20 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ -#include <cairo.h> - #ifndef CAIRO_PNG_H #define CAIRO_PNG_H -#ifdef CAIRO_HAS_PNG_SURFACE + +#include <cairo.h> + +#ifdef CAIRO_HAS_PNG_SURFACE #include <stdio.h> +CAIRO_BEGIN_DECLS + void cairo_set_target_png (cairo_t *cr, FILE *file, @@ -55,5 +58,7 @@ cairo_png_surface_create (FILE *file, int width, int height); +CAIRO_END_DECLS + #endif /* CAIRO_HAS_PNG_SURFACE */ #endif /* CAIRO_PNG_H */ diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c index e85858033..59c615da2 100644 --- a/src/cairo-polygon.c +++ b/src/cairo-polygon.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include <stdlib.h> diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index 4da8162c7..4a45fc679 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" @@ -42,6 +42,22 @@ static const cairo_surface_backend_t cairo_ps_surface_backend; +/** + * cairo_set_target_ps: + * @cr: a #cairo_t + * @file: an open, writeable file + * @width_inches: width of the output page, in inches + * @height_inches: height of the output page, in inches + * @x_pixels_per_inch: X resolution to use for image fallbacks; + * not all Cairo drawing can be represented in a postscript + * file, so Cairo will write out images for some portions + * of the output. + * @y_pixels_per_inch: Y resolution to use for image fallbacks. + * + * Directs output for a #cairo_t to a postscript file. The file must + * be kept open until the #cairo_t is destroyed or set to have a + * different target, and then must be closed by the application. + **/ void cairo_set_target_ps (cairo_t *cr, FILE *file, @@ -192,62 +208,65 @@ _cairo_ps_surface_pixels_per_inch (void *abstract_surface) return surface->y_ppi; } -static cairo_image_surface_t * -_cairo_ps_surface_get_image (void *abstract_surface) +static cairo_status_t +_cairo_ps_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) { cairo_ps_surface_t *surface = abstract_surface; + + *image_out = surface->image; - cairo_surface_reference (&surface->image->base); - - return surface->image; + return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_ps_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +static void +_cairo_ps_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) { - cairo_ps_surface_t *surface = abstract_surface; - - if (image == surface->image) - return CAIRO_STATUS_SUCCESS; - - /* XXX: Need to call _cairo_image_surface_set_image here, but it's - not implemented yet. */ - - return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t -_cairo_ps_surface_set_matrix (void *abstract_surface, - cairo_matrix_t *matrix) +_cairo_ps_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect, + void **image_extra) { cairo_ps_surface_t *surface = abstract_surface; + + image_rect->x = 0; + image_rect->y = 0; + image_rect->width = surface->image->width; + image_rect->height = surface->image->height; + + *image_out = surface->image; - return _cairo_image_surface_set_matrix (surface->image, matrix); + return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_ps_surface_set_filter (void *abstract_surface, - cairo_filter_t filter) +static void +_cairo_ps_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) { - cairo_ps_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_filter (surface->image, filter); } static cairo_status_t -_cairo_ps_surface_set_repeat (void *abstract_surface, - int repeat) +_cairo_ps_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { - cairo_ps_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_repeat (surface->image, repeat); + return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_int_status_t _cairo_ps_surface_composite (cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, + cairo_pattern_t *src, + cairo_pattern_t *mask, void *abstract_dst, int src_x, int src_y, @@ -273,10 +292,14 @@ _cairo_ps_surface_fill_rectangles (void *abstract_surface, static cairo_int_status_t _cairo_ps_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *generic_src, + cairo_pattern_t *generic_src, void *abstract_dst, int x_src, int y_src, + int x_dst, + int y_dst, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps) { @@ -294,12 +317,10 @@ _cairo_ps_surface_copy_page (void *abstract_surface) int i, x, y; - cairo_surface_t *white_surface; + cairo_solid_pattern_t white_pattern; char *rgb, *compressed; long rgb_size, compressed_size; - cairo_color_t white; - rgb_size = 3 * width * height; rgb = malloc (rgb_size); if (rgb == NULL) { @@ -316,26 +337,19 @@ _cairo_ps_surface_copy_page (void *abstract_surface) /* PostScript can not represent the alpha channel, so we blend the current image over a white RGB surface to eliminate it. */ - white_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 1, 1); - if (white_surface == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL2; - } - _cairo_color_init (&white); - _cairo_surface_fill_rectangle (white_surface, - CAIRO_OPERATOR_SRC, - &white, - 0, 0, 1, 1); - cairo_surface_set_repeat (white_surface, 1); + _cairo_pattern_init_solid (&white_pattern, 1.0, 1.0, 1.0); + _cairo_surface_composite (CAIRO_OPERATOR_OVER_REVERSE, - white_surface, + &white_pattern.base, NULL, &surface->image->base, 0, 0, 0, 0, 0, 0, width, height); + + _cairo_pattern_fini (&white_pattern.base); i = 0; for (y = 0; y < height; y++) { @@ -379,8 +393,6 @@ _cairo_ps_surface_copy_page (void *abstract_surface) /* Page footer */ fprintf (file, "%%%%EndPage\n"); - cairo_surface_destroy (white_surface); - BAIL2: free (compressed); BAIL1: free (rgb); @@ -412,29 +424,20 @@ _cairo_ps_surface_set_clip_region (void *abstract_surface, return _cairo_image_surface_set_clip_region (surface->image, region); } -static cairo_int_status_t -_cairo_ps_surface_create_pattern (void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *extents) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - static const cairo_surface_backend_t cairo_ps_surface_backend = { _cairo_ps_surface_create_similar, _cairo_ps_surface_destroy, _cairo_ps_surface_pixels_per_inch, - _cairo_ps_surface_get_image, - _cairo_ps_surface_set_image, - _cairo_ps_surface_set_matrix, - _cairo_ps_surface_set_filter, - _cairo_ps_surface_set_repeat, + _cairo_ps_surface_acquire_source_image, + _cairo_ps_surface_release_source_image, + _cairo_ps_surface_acquire_dest_image, + _cairo_ps_surface_release_dest_image, + _cairo_ps_surface_clone_similar, _cairo_ps_surface_composite, _cairo_ps_surface_fill_rectangles, _cairo_ps_surface_composite_trapezoids, _cairo_ps_surface_copy_page, _cairo_ps_surface_show_page, _cairo_ps_surface_set_clip_region, - _cairo_ps_surface_create_pattern, NULL /* show_glyphs */ }; diff --git a/src/cairo-ps.h b/src/cairo-ps.h index ae8e72192..88382920e 100644 --- a/src/cairo-ps.h +++ b/src/cairo-ps.h @@ -31,17 +31,20 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ -#include <cairo.h> - #ifndef CAIRO_PS_H #define CAIRO_PS_H + +#include <cairo.h> + #ifdef CAIRO_HAS_PS_SURFACE #include <stdio.h> +CAIRO_BEGIN_DECLS + void cairo_set_target_ps (cairo_t *cr, FILE *file, @@ -59,5 +62,7 @@ cairo_ps_surface_create (FILE *file, double x_pixels_per_inch, double y_pixels_per_inch); +CAIRO_END_DECLS + #endif /* CAIRO_HAS_PS_SURFACE */ #endif /* CAIRO_PS_H */ diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index b7103b051..01b345cdc 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2004 Calum Robinson + * Copyright © 2004 Calum Robinson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public diff --git a/src/cairo-quartz.h b/src/cairo-quartz.h index 918bc18d7..5afd46426 100644 --- a/src/cairo-quartz.h +++ b/src/cairo-quartz.h @@ -31,17 +31,20 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ -#include <cairo.h> - #ifndef CAIRO_QUARTZ_H #define CAIRO_QUARTZ_H + +#include <cairo.h> + #ifdef CAIRO_HAS_QUARTZ_SURFACE #include <Carbon/Carbon.h> +CAIRO_BEGIN_DECLS + void cairo_set_target_quartz_context( cairo_t *cr, CGContextRef context, @@ -53,6 +56,8 @@ cairo_quartz_surface_create ( CGContextRef context, int width, int height); +CAIRO_END_DECLS + #endif /* CAIRO_HAS_QUARTZ_SURFACE */ #endif /* CAIRO_QUARTZ_H */ diff --git a/src/cairo-slope.c b/src/cairo-slope.c index 1a1497988..a2edec038 100644 --- a/src/cairo-slope.c +++ b/src/cairo-slope.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo-spline.c b/src/cairo-spline.c index ff290d9dd..5119a8e2b 100644 --- a/src/cairo-spline.c +++ b/src/cairo-spline.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo-surface.c b/src/cairo-surface.c index a457d2062..330d58b1e 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include <stdlib.h> @@ -151,16 +151,151 @@ _cairo_surface_pixels_per_inch (cairo_surface_t *surface) return surface->backend->pixels_per_inch (surface); } -cairo_image_surface_t * -_cairo_surface_get_image (cairo_surface_t *surface) +/** + * _cairo_surface_acquire_source_image: + * @surface: a #cairo_surface_t + * @image_out: location to store a pointer to an image surface that includes at least + * the intersection of @interest_rect with the visible area of @surface. + * This surface could be @surface itself, a surface held internal to @surface, + * or it could be a new surface with a copy of the relevant portion of @surface. + * @image_extra: location to store image specific backend data + * + * Gets an image surface to use when drawing as a fallback when drawing with + * @surface as a source. _cairo_surface_release_source_image() must be called + * when finished. + * + * Return value: %CAIRO_STATUS_SUCCESS if a an image was stored in @image_out. + * %CAIRO_INT_STATUS_UNSUPPORTED if an image cannot be retrieved for the specified + * surface. Or %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_surface_acquire_source_image (cairo_surface_t *surface, + cairo_image_surface_t **image_out, + void **image_extra) { - return surface->backend->get_image (surface); + return surface->backend->acquire_source_image (surface, image_out, image_extra); } +/** + * _cairo_surface_release_source_image: + * @surface: a #cairo_surface_t + * @image_extra: same as return from the matching _cairo_surface_acquire_dest_image() + * + * Releases any resources obtained with _cairo_surface_acquire_source_image() + **/ +cairo_private void +_cairo_surface_release_source_image (cairo_surface_t *surface, + cairo_image_surface_t *image, + void *image_extra) +{ + surface->backend->release_source_image (surface, image, image_extra); +} + +/** + * _cairo_surface_acquire_dest_image: + * @surface: a #cairo_surface_t + * @interest_rect: area of @surface for which fallback drawing is being done. + * A value of %NULL indicates that the entire surface is desired. + * @image_out: location to store a pointer to an image surface that includes at least + * the intersection of @interest_rect with the visible area of @surface. + * This surface could be @surface itself, a surface held internal to @surface, + * or it could be a new surface with a copy of the relevant portion of @surface. + * @image_rect: location to store area of the original surface occupied + * by the surface stored in @image. + * @image_extra: location to store image specific backend data + * + * Retrieves a local image for a surface for implementing a fallback drawing + * operation. After calling this function, the implementation of the fallback + * drawing operation draws the primitive to the surface stored in @image_out + * then calls _cairo_surface_release_dest_fallback(), + * which, if a temporary surface was created, copies the bits back to the + * main surface and frees the temporary surface. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY. + * %CAIRO_INT_STATUS_UNSUPPORTED can be returned but this will mean that + * the backend can't draw with fallbacks. It's possible for the routine + * to store NULL in @local_out and return %CAIRO_STATUS_SUCCESS; + * that indicates that no part of @interest_rect is visible, so no drawing + * is necessary. _cairo_surface_release_dest_fallback() should not be called in that + * case. + **/ +cairo_status_t +_cairo_surface_acquire_dest_image (cairo_surface_t *surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect, + void **image_extra) +{ + return surface->backend->acquire_dest_image (surface, interest_rect, + image_out, image_rect, image_extra); +} + +/** + * _cairo_surface_end_fallback: + * @surface: a #cairo_surface_t + * @interest_rect: same as passed to the matching _cairo_surface_acquire_dest_image() + * @image: same as returned from the matching _cairo_surface_acquire_dest_image() + * @image_rect: same as returned from the matching _cairo_surface_acquire_dest_image() + * @image_extra: same as return from the matching _cairo_surface_acquire_dest_image() + * + * Finishes the operation started with _cairo_surface_acquire_dest_image(), by, if + * necessary, copying the image from @image back to @surface and freeing any + * resources that were allocated. + **/ +void +_cairo_surface_release_dest_image (cairo_surface_t *surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ + surface->backend->release_dest_image (surface, interest_rect, + image, image_rect, image_extra); +} + +/** + * _cairo_surface_clone_similar: + * @surface: a #cairo_surface_t + * @src: the source image + * @clone_out: location to store a surface compatible with @surface + * and with contents identical to @src. The caller must call + * cairo_surface_destroy() on the result. + * + * Creates a surface with contents identical to @src but that + * can be used efficiently with @surface. If @surface and @src are + * already compatible then it may return a new reference to @src. + * + * Return value: %CAIRO_STATUS_SUCCESS if a surface was created and stored + * in @clone_out. Otherwise %CAIRO_INT_STATUS_UNSUPPORTED or another + * error like %CAIRO_STATUS_NO_MEMORY. + **/ cairo_status_t -_cairo_surface_set_image (cairo_surface_t *surface, cairo_image_surface_t *image) +_cairo_surface_clone_similar (cairo_surface_t *surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { - return surface->backend->set_image (surface, image); + cairo_status_t status; + cairo_image_surface_t *image; + void *image_extra; + + status = surface->backend->clone_similar (surface, src, clone_out); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_surface_acquire_source_image (src, &image, &image_extra); + if (status != CAIRO_STATUS_SUCCESS) + return status; + + status = surface->backend->clone_similar (surface, &image->base, clone_out); + + /* If the above failed point, we could implement a full fallback + * using acquire_dest_image, but that's going to be very + * inefficient compared to a backend-specific implementation of + * clone_similar() with an image source. So we don't bother + */ + + _cairo_surface_release_source_image (src, image, image_extra); + return status; } cairo_status_t @@ -169,9 +304,7 @@ cairo_surface_set_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix) if (surface == NULL) return CAIRO_STATUS_NULL_POINTER; - cairo_matrix_copy (&surface->matrix, matrix); - - return surface->backend->set_matrix (surface, matrix); + return cairo_matrix_copy (&surface->matrix, matrix); } slim_hidden_def(cairo_surface_set_matrix); @@ -192,7 +325,7 @@ cairo_surface_set_filter (cairo_surface_t *surface, cairo_filter_t filter) return CAIRO_STATUS_NULL_POINTER; surface->filter = filter; - return surface->backend->set_filter (surface, filter); + return CAIRO_STATUS_SUCCESS; } cairo_filter_t @@ -224,14 +357,81 @@ cairo_surface_set_repeat (cairo_surface_t *surface, int repeat) surface->repeat = repeat; - return surface->backend->set_repeat (surface, repeat); + return CAIRO_STATUS_SUCCESS; } slim_hidden_def(cairo_surface_set_repeat); +typedef struct { + cairo_surface_t *dst; + cairo_rectangle_t extents; + cairo_image_surface_t *image; + cairo_rectangle_t image_rect; + void *image_extra; +} fallback_state_t; + +static cairo_status_t +_fallback_init (fallback_state_t *state, + cairo_surface_t *dst, + int x, + int y, + int width, + int height) +{ + state->extents.x = x; + state->extents.y = y; + state->extents.width = width; + state->extents.height = height; + + state->dst = dst; + + return _cairo_surface_acquire_dest_image (dst, &state->extents, + &state->image, &state->image_rect, &state->image_extra); +} + +static void +_fallback_cleanup (fallback_state_t *state) +{ + _cairo_surface_release_dest_image (state->dst, &state->extents, + state->image, &state->image_rect, state->image_extra); +} + +static cairo_status_t +_fallback_composite (cairo_operator_t operator, + cairo_pattern_t *src, + cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) +{ + fallback_state_t state; + cairo_status_t status; + + status = _fallback_init (&state, dst, dst_x, dst_y, width, height); + if (!CAIRO_OK (status) || !state.image) + return status; + + state.image->base.backend->composite (operator, src, mask, + &state.image->base, + src_x, src_y, mask_x, mask_y, + dst_x - state.image_rect.x, + dst_y - state.image_rect.y, + width, height); + + _fallback_cleanup (&state); + + return status; +} + cairo_status_t _cairo_surface_composite (cairo_operator_t operator, - cairo_surface_t *src, - cairo_surface_t *mask, + cairo_pattern_t *src, + cairo_pattern_t *mask, cairo_surface_t *dst, int src_x, int src_y, @@ -243,7 +443,6 @@ _cairo_surface_composite (cairo_operator_t operator, unsigned int height) { cairo_int_status_t status; - cairo_image_surface_t *src_image, *mask_image = 0, *dst_image; status = dst->backend->composite (operator, src, mask, dst, @@ -254,28 +453,12 @@ _cairo_surface_composite (cairo_operator_t operator, if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - src_image = _cairo_surface_get_image (src); - if (mask) - mask_image = _cairo_surface_get_image (mask); - dst_image = _cairo_surface_get_image (dst); - - dst_image->base.backend->composite (operator, - &src_image->base, - mask ? &mask_image->base : NULL, - dst_image, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); - - status = _cairo_surface_set_image (dst, dst_image); - - cairo_surface_destroy (&src_image->base); - if (mask) - cairo_surface_destroy (&mask_image->base); - cairo_surface_destroy (&dst_image->base); - - return status; + return _fallback_composite (operator, + src, mask, dst, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height); } cairo_status_t @@ -297,6 +480,77 @@ _cairo_surface_fill_rectangle (cairo_surface_t *surface, return _cairo_surface_fill_rectangles (surface, operator, color, &rect, 1); } +static cairo_status_t +_fallback_fill_rectangles (cairo_surface_t *surface, + cairo_operator_t operator, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) +{ + fallback_state_t state; + cairo_rectangle_t *offset_rects = NULL; + cairo_status_t status; + int x1, y1, x2, y2; + int i; + + if (num_rects <= 0) + return CAIRO_STATUS_SUCCESS; + + /* Compute the bounds of the rectangles, so that we know what area of the + * destination surface to fetch + */ + x1 = rects[0].x; + y1 = rects[0].y; + x2 = rects[0].x + rects[0].width; + y2 = rects[0].y + rects[0].height; + + for (i = 1; i < num_rects; i++) { + if (rects[0].x < x1) + x1 = rects[0].x; + if (rects[0].y < y1) + y1 = rects[0].y; + + if (rects[0].x + rects[0].width > x2) + x2 = rects[0].x + rects[0].width; + if (rects[0].y + rects[0].height > y2) + y2 = rects[0].y + rects[0].height; + } + + status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); + if (!CAIRO_OK (status) || !state.image) + return status; + + /* If the fetched image isn't at 0,0, we need to offset the rectangles */ + + if (state.image_rect.x != 0 || state.image_rect.y != 0) { + offset_rects = malloc (sizeof (cairo_rectangle_t) * num_rects); + if (!offset_rects) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + + for (i = 0; i < num_rects; i++) { + offset_rects[i].x = rects[i].x - state.image_rect.x; + offset_rects[i].y = rects[i].y - state.image_rect.y; + offset_rects[i].width = rects[i].width; + offset_rects[i].height = rects[i].height; + } + + rects = offset_rects; + } + + state.image->base.backend->fill_rectangles (&state.image->base, operator, color, + rects, num_rects); + + if (offset_rects) + free (offset_rects); + + FAIL: + _fallback_cleanup (&state); + + return status; +} + cairo_status_t _cairo_surface_fill_rectangles (cairo_surface_t *surface, cairo_operator_t operator, @@ -305,7 +559,6 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, int num_rects) { cairo_int_status_t status; - cairo_image_surface_t *surface_image; if (num_rects == 0) return CAIRO_STATUS_SUCCESS; @@ -317,54 +570,105 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - surface_image = _cairo_surface_get_image (surface); + return _fallback_fill_rectangles (surface, operator, color, rects, num_rects); +} + +static cairo_status_t +_fallback_composite_trapezoids (cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *dst, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps) +{ + fallback_state_t state; + cairo_trapezoid_t *offset_traps = NULL; + cairo_status_t status; + int i; + + status = _fallback_init (&state, dst, dst_x, dst_y, width, height); + if (!CAIRO_OK (status) || !state.image) + return status; - surface_image->base.backend->fill_rectangles (surface_image, - operator, - color, - rects, num_rects); + /* If the destination image isn't at 0,0, we need to offset the trapezoids */ + + if (state.image_rect.x != 0 || state.image_rect.y != 0) { + + cairo_fixed_t xoff = _cairo_fixed_from_int (state.image_rect.x); + cairo_fixed_t yoff = _cairo_fixed_from_int (state.image_rect.y); + + offset_traps = malloc (sizeof (cairo_trapezoid_t) * num_traps); + if (!offset_traps) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } - status = _cairo_surface_set_image (surface, surface_image); + for (i = 0; i < num_traps; i++) { + offset_traps[i].top = traps[i].top - yoff; + offset_traps[i].bottom = traps[i].bottom - yoff; + offset_traps[i].left.p1.x = traps[i].left.p1.x - xoff; + offset_traps[i].left.p1.y = traps[i].left.p1.y - yoff; + offset_traps[i].left.p2.x = traps[i].left.p2.x - xoff; + offset_traps[i].left.p2.y = traps[i].left.p2.y - yoff; + offset_traps[i].right.p1.x = traps[i].right.p1.x - xoff; + offset_traps[i].right.p1.y = traps[i].right.p1.y - yoff; + offset_traps[i].right.p2.x = traps[i].right.p2.x - xoff; + offset_traps[i].right.p2.y = traps[i].right.p2.y - yoff; + } - cairo_surface_destroy (&surface_image->base); + traps = offset_traps; + } + state.image->base.backend->composite_trapezoids (operator, pattern, + &state.image->base, + src_x, src_y, + dst_x - state.image_rect.x, + dst_y - state.image_rect.y, + width, height, traps, num_traps); + if (offset_traps) + free (offset_traps); + + FAIL: + _fallback_cleanup (&state); + return status; } + cairo_status_t _cairo_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *src, + cairo_pattern_t *pattern, cairo_surface_t *dst, - int x_src, - int y_src, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps) { cairo_int_status_t status; - cairo_image_surface_t *src_image, *dst_image; status = dst->backend->composite_trapezoids (operator, - src, dst, - x_src, y_src, + pattern, dst, + src_x, src_y, + dst_x, dst_y, + width, height, traps, num_traps); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - src_image = _cairo_surface_get_image (src); - dst_image = _cairo_surface_get_image (dst); - - dst_image->base.backend->composite_trapezoids (operator, - &src_image->base, - dst_image, - x_src, y_src, - traps, num_traps); - - status = _cairo_surface_set_image (dst, dst_image); - - cairo_surface_destroy (&src_image->base); - cairo_surface_destroy (&dst_image->base); - - return status; + return _fallback_composite_trapezoids (operator, pattern, dst, + src_x, src_y, + dst_x, dst_y, + width, height, + traps, num_traps); } cairo_status_t @@ -402,109 +706,3 @@ _cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *reg { return surface->backend->set_clip_region (surface, region); } - -cairo_status_t -_cairo_surface_create_pattern (cairo_surface_t *surface, - cairo_pattern_t *pattern, - cairo_box_t *box) -{ - cairo_int_status_t status; - - status = surface->backend->create_pattern (surface, pattern, box); - - /* The backend cannot accelerate this pattern, lets create an - unaccelerated source instead. */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - - status = CAIRO_STATUS_SUCCESS; - switch (pattern->type) { - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: { - cairo_image_surface_t *image; - - image = _cairo_pattern_get_image (pattern, box); - if (image) { - pattern->source = &image->base; - - return CAIRO_STATUS_SUCCESS; - } else - return CAIRO_STATUS_NO_MEMORY; - - } break; - case CAIRO_PATTERN_SOLID: - pattern->source = - _cairo_surface_create_similar_solid (surface, - CAIRO_FORMAT_ARGB32, - 1, 1, - &pattern->color); - if (pattern->source) { - cairo_surface_set_repeat (pattern->source, 1); - - return CAIRO_STATUS_SUCCESS; - } else - return CAIRO_STATUS_NO_MEMORY; - break; - case CAIRO_PATTERN_SURFACE: - status = CAIRO_INT_STATUS_UNSUPPORTED; - - /* handle pattern opacity */ - if (pattern->color.alpha != 1.0) { - double x = box->p1.x >> 16; - double y = box->p1.y >> 16; - int width = ((box->p2.x + 65535) >> 16) - (box->p1.x >> 16); - int height = ((box->p2.y + 65535) >> 16) - (box->p1.y >> 16); - cairo_pattern_t alpha; - - pattern->source = - cairo_surface_create_similar (surface, - CAIRO_FORMAT_ARGB32, - width, height); - if (pattern->source) { - _cairo_pattern_init_solid (&alpha, 1.0, 1.0, 1.0); - _cairo_pattern_set_alpha (&alpha, pattern->color.alpha); - - status = _cairo_surface_create_pattern (pattern->source, - &alpha, box); - - if (status == CAIRO_STATUS_SUCCESS) { - int save_repeat = pattern->u.surface.surface->repeat; - - if (pattern->extend == CAIRO_EXTEND_REPEAT || - pattern->u.surface.surface->repeat == 1) - cairo_surface_set_repeat (pattern->u.surface.surface, 1); - else - cairo_surface_set_repeat (pattern->u.surface.surface, 0); - - status = - _cairo_surface_composite (CAIRO_OPERATOR_OVER, - pattern->u.surface.surface, - alpha.source, - pattern->source, - 0, 0, 0, 0, 0, 0, - width, height); - - cairo_surface_set_repeat (pattern->u.surface.surface, - save_repeat); - - if (status == CAIRO_STATUS_SUCCESS) - _cairo_pattern_set_source_offset (pattern, x, y); - else - cairo_surface_destroy (pattern->source); - } - - _cairo_pattern_fini (&alpha); - } - } - - if (status != CAIRO_STATUS_SUCCESS) { - pattern->source = pattern->u.surface.surface; - cairo_surface_reference (pattern->u.surface.surface); - - return CAIRO_STATUS_SUCCESS; - } - break; - } - } - - return status; -} diff --git a/src/cairo-traps.c b/src/cairo-traps.c index d17a27281..79c7e16b6 100644 --- a/src/cairo-traps.c +++ b/src/cairo-traps.c @@ -1,31 +1,42 @@ /* - * Copyright © 2002 Keith Packard + * Copyright © 2002 Keith Packard * - * 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 Keith Packard not be used in - * advertising or publicity pertaining to distribution of the software without - * specific, written prior permission. Keith Packard makes no - * representations about the suitability of this software for any purpose. It - * is provided "as is" without express or implied warranty. + * 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. * - * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL KEITH PACKARD 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. + * 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 Keith Packard + * + * Contributor(s): + * Keith R. Packard <keithp@keithp.com> + * Carl D. Worth <cworth@cworth.org> * * 2002-07-15: Converted from XRenderCompositeDoublePoly to cairo_trap. Carl D. Worth */ #include "cairoint.h" -#define CAIRO_TRAPS_GROWTH_INC 10 - /* private functions */ static cairo_status_t @@ -52,12 +63,6 @@ _compare_cairo_edge_by_slope (const void *av, const void *bv); static cairo_fixed_16_16_t _compute_x (cairo_line_t *line, cairo_fixed_t y); -static double -_compute_inverse_slope (cairo_line_t *l); - -static double -_compute_x_intercept (cairo_line_t *l, double inverse_slope); - static int _line_segs_intersect_ceil (cairo_line_t *left, cairo_line_t *right, cairo_fixed_t *y_ret); @@ -68,6 +73,8 @@ _cairo_traps_init (cairo_traps_t *traps) traps->traps_size = 0; traps->traps = NULL; + traps->extents.p1.x = traps->extents.p1.y = CAIRO_MAXSHORT << 16; + traps->extents.p2.x = traps->extents.p2.y = CAIRO_MINSHORT << 16; } void @@ -93,7 +100,8 @@ _cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bo } if (traps->num_traps >= traps->traps_size) { - status = _cairo_traps_grow_by (traps, CAIRO_TRAPS_GROWTH_INC); + int inc = traps->traps_size ? traps->traps_size : 32; + status = _cairo_traps_grow_by (traps, inc); if (status) return status; } @@ -104,6 +112,28 @@ _cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bo trap->left = *left; trap->right = *right; + if (top < traps->extents.p1.y) + traps->extents.p1.y = top; + if (bottom > traps->extents.p2.y) + traps->extents.p2.y = bottom; + /* + * This isn't generally accurate, but it is close enough for + * this purpose. Assuming that the left and right segments always + * contain the trapezoid vertical extents, these compares will + * yield a containing box. Assuming that the points all come from + * the same figure which will eventually be completely drawn, then + * the compares will yield the correct overall extents + */ + if (left->p1.x < traps->extents.p1.x) + traps->extents.p1.x = left->p1.x; + if (left->p2.x < traps->extents.p1.x) + traps->extents.p1.x = left->p2.x; + + if (right->p1.x > traps->extents.p2.x) + traps->extents.p2.x = right->p1.x; + if (right->p2.x > traps->extents.p2.x) + traps->extents.p2.x = right->p2.x; + traps->num_traps++; return CAIRO_STATUS_SUCCESS; @@ -327,40 +357,132 @@ _compare_cairo_edge_by_current_x_slope (const void *av, const void *bv) sub-computations -- just a bunch of determinants. I haven't looked at complexity, (both are probably similar and it probably doesn't matter much anyway). + */ -static double -_det (double a, double b, double c, double d) +/* XXX: Keith's new intersection code is much cleaner, and uses + * sufficient precision for correctly sorting intersections according + * to the analysis in Hobby's paper. + * + * But, when we enable this code, some things are failing, (eg. the + * stars in test/fill_rule get filled wrong). This could indicate a + * bug in one of tree places: + * + * 1) The new intersection code in this file + * + * 2) cairo_wideint.c (which is only exercised here) + * + * 3) In the current tessellator, (where the old intersection + * code, with its mystic increments could be masking the bug). + * + * It will likely be easier to revisit this when the new tessellation + * code is in place. So, for now, we'll simply disable the new + * intersection code. + */ + +#define CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE 0 + +#if CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE +static const cairo_fixed_32_32_t +_det16_32 (cairo_fixed_16_16_t a, + cairo_fixed_16_16_t b, + cairo_fixed_16_16_t c, + cairo_fixed_16_16_t d) { - return a * d - b * c; + return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d), + _cairo_int32x32_64_mul (b, c)); } -static int -_lines_intersect (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_intersection) +static const cairo_fixed_64_64_t +_det32_64 (cairo_fixed_32_32_t a, + cairo_fixed_32_32_t b, + cairo_fixed_32_32_t c, + cairo_fixed_32_32_t d) { - double dx1 = cairo_fixed_to_double (l1->p1.x - l1->p2.x); - double dy1 = cairo_fixed_to_double (l1->p1.y - l1->p2.y); - - double dx2 = cairo_fixed_to_double (l2->p1.x - l2->p2.x); - double dy2 = cairo_fixed_to_double (l2->p1.y - l2->p2.y); - - double l1_det, l2_det; + return _cairo_int128_sub (_cairo_int64x64_128_mul (a, d), + _cairo_int64x64_128_mul (b, c)); +} - double den_det = _det (dx1, dy1, dx2, dy2); +static const cairo_fixed_32_32_t +_fixed_16_16_to_fixed_32_32 (cairo_fixed_16_16_t a) +{ + return _cairo_int64_lsl (_cairo_int32_to_int64 (a), 16); +} - if (den_det == 0) +static int +_line_segs_intersect_ceil (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_intersection) +{ + cairo_fixed_16_16_t dx1, dx2, dy1, dy2; + cairo_fixed_32_32_t den_det; + cairo_fixed_32_32_t l1_det, l2_det; + cairo_fixed_64_64_t num_det; + cairo_fixed_32_32_t intersect_32_32; + cairo_fixed_48_16_t intersect_48_16; + cairo_fixed_16_16_t intersect_16_16; + cairo_quorem128_t qr; + + dx1 = l1->p1.x - l1->p2.x; + dy1 = l1->p1.y - l1->p2.y; + dx2 = l2->p1.x - l2->p2.x; + dy2 = l2->p1.y - l2->p2.y; + den_det = _det16_32 (dx1, dy1, + dx2, dy2); + + if (_cairo_int64_eq (den_det, _cairo_int32_to_int64(0))) return 0; - l1_det = _det (l1->p1.x, l1->p1.y, - l1->p2.x, l1->p2.y); - l2_det = _det (l2->p1.x, l2->p1.y, - l2->p2.x, l2->p2.y); + l1_det = _det16_32 (l1->p1.x, l1->p1.y, + l1->p2.x, l1->p2.y); + l2_det = _det16_32 (l2->p1.x, l2->p1.y, + l2->p2.x, l2->p2.y); - *y_intersection = _det (l1_det, dy1, - l2_det, dy2) / den_det; + + num_det = _det32_64 (l1_det, _fixed_16_16_to_fixed_32_32 (dy1), + l2_det, _fixed_16_16_to_fixed_32_32 (dy2)); + + /* + * Ok, this one is a bit tricky in fixed point, the denominator + * needs to be left with 32-bits of fraction so that the + * result of the divide ends up with 32-bits of fraction (64 - 32 = 32) + */ + qr = _cairo_int128_divrem (num_det, _cairo_int64_to_int128 (den_det)); + + intersect_32_32 = _cairo_int128_to_int64 (qr.quo); + + /* + * Find the ceiling of the quotient -- divrem returns + * the quotient truncated towards zero, so if the + * quotient should be positive (num_den and den_det have same sign) + * bump the quotient up by one. + */ + + if (_cairo_int128_ne (qr.rem, _cairo_int32_to_int128 (0)) && + (_cairo_int128_ge (num_det, _cairo_int32_to_int128 (0)) == + _cairo_int64_ge (den_det, _cairo_int32_to_int64 (0)))) + { + intersect_32_32 = _cairo_int64_add (intersect_32_32, + _cairo_int32_to_int64 (1)); + } + + /* + * Now convert from 32.32 to 48.16 and take the ceiling; + * this requires adding in 15 1 bits and shifting the result + */ + + intersect_32_32 = _cairo_int64_add (intersect_32_32, + _cairo_int32_to_int64 ((1 << 16) - 1)); + intersect_48_16 = _cairo_int64_rsa (intersect_32_32, 16); + + /* + * And drop the top bits + */ + intersect_16_16 = _cairo_int64_to_int32 (intersect_48_16); + + *y_intersection = intersect_16_16; return 1; } -*/ +#endif /* CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE */ + static cairo_fixed_16_16_t _compute_x (cairo_line_t *line, cairo_fixed_t y) { @@ -371,6 +493,7 @@ _compute_x (cairo_line_t *line, cairo_fixed_t y) return line->p1.x + (ex / dy); } +#if ! CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE static double _compute_inverse_slope (cairo_line_t *l) { @@ -460,6 +583,7 @@ _line_segs_intersect_ceil (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_ return 1; } +#endif /* CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE */ /* The algorithm here is pretty simple: @@ -567,32 +691,32 @@ _cairo_traps_tessellate_polygon (cairo_traps_t *traps, return CAIRO_STATUS_SUCCESS; } -static int +static cairo_bool_t _cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt) { cairo_slope_t slope_left, slope_pt, slope_right; if (t->top > pt->y) - return 0; + return FALSE; if (t->bottom < pt->y) - return 0; + return FALSE; _cairo_slope_init (&slope_left, &t->left.p1, &t->left.p2); _cairo_slope_init (&slope_pt, &t->left.p1, pt); if (_cairo_slope_compare (&slope_left, &slope_pt) < 0) - return 0; + return FALSE; _cairo_slope_init (&slope_right, &t->right.p1, &t->right.p2); _cairo_slope_init (&slope_pt, &t->right.p1, pt); if (_cairo_slope_compare (&slope_pt, &slope_right) < 0) - return 0; + return FALSE; - return 1; + return TRUE; } -int +cairo_bool_t _cairo_traps_contain (cairo_traps_t *traps, double x, double y) { int i; @@ -603,45 +727,14 @@ _cairo_traps_contain (cairo_traps_t *traps, double x, double y) for (i = 0; i < traps->num_traps; i++) { if (_cairo_trap_contains (&traps->traps[i], &point)) - return 1; + return TRUE; } - return 0; -} - -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#define MAX(a,b) ((a) > (b) ? (a) : (b)) - -static void -_cairo_trap_extents (cairo_trapezoid_t *t, cairo_box_t *extents) -{ - cairo_fixed_t x; - - if (t->top < extents->p1.y) - extents->p1.y = t->top; - - if (t->bottom > extents->p2.y) - extents->p2.y = t->bottom; - - x = MIN (_compute_x (&t->left, t->top), - _compute_x (&t->left, t->bottom)); - if (x < extents->p1.x) - extents->p1.x = x; - - x = MAX (_compute_x (&t->right, t->top), - _compute_x (&t->right, t->bottom)); - if (x > extents->p2.x) - extents->p2.x = x; + return FALSE; } void _cairo_traps_extents (cairo_traps_t *traps, cairo_box_t *extents) { - int i; - - extents->p1.x = extents->p1.y = CAIRO_MAXSHORT << 16; - extents->p2.x = extents->p2.y = CAIRO_MINSHORT << 16; - - for (i = 0; i < traps->num_traps; i++) - _cairo_trap_extents (&traps->traps[i], extents); + *extents = traps->extents; } diff --git a/src/cairo-unicode.c b/src/cairo-unicode.c new file mode 100644 index 000000000..92201391a --- /dev/null +++ b/src/cairo-unicode.c @@ -0,0 +1,340 @@ +/* cairo_unicode.c: Unicode conversion routines + * + * The code in this file is derived from GLib's gutf8.c and + * ultimately from libunicode. It is relicensed under the + * dual LGPL/MPL with permission of the original authors. + * + * Copyright © 1999 Tom Tromey + * Copyright © 2005 Red Hat, Inc + * + * 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 cairo_unicode.c as distributed with the + * cairo graphics library. + * + * The Initial Developer of the Original Code is Tom Tromey. + * and Red Hat, Inc. + * + * Contributor(s): + * Owen Taylor <otaylor@redhat.com> + */ + +#include <limits.h> + +#include <cairoint.h> + +#define UTF8_COMPUTE(Char, Mask, Len) \ + if (Char < 128) \ + { \ + Len = 1; \ + Mask = 0x7f; \ + } \ + else if ((Char & 0xe0) == 0xc0) \ + { \ + Len = 2; \ + Mask = 0x1f; \ + } \ + else if ((Char & 0xf0) == 0xe0) \ + { \ + Len = 3; \ + Mask = 0x0f; \ + } \ + else if ((Char & 0xf8) == 0xf0) \ + { \ + Len = 4; \ + Mask = 0x07; \ + } \ + else if ((Char & 0xfc) == 0xf8) \ + { \ + Len = 5; \ + Mask = 0x03; \ + } \ + else if ((Char & 0xfe) == 0xfc) \ + { \ + Len = 6; \ + Mask = 0x01; \ + } \ + else \ + Len = -1; + +#define UTF8_LENGTH(Char) \ + ((Char) < 0x80 ? 1 : \ + ((Char) < 0x800 ? 2 : \ + ((Char) < 0x10000 ? 3 : \ + ((Char) < 0x200000 ? 4 : \ + ((Char) < 0x4000000 ? 5 : 6))))) + + +#define UTF8_GET(Result, Chars, Count, Mask, Len) \ + (Result) = (Chars)[0] & (Mask); \ + for ((Count) = 1; (Count) < (Len); ++(Count)) \ + { \ + if (((Chars)[(Count)] & 0xc0) != 0x80) \ + { \ + (Result) = -1; \ + break; \ + } \ + (Result) <<= 6; \ + (Result) |= ((Chars)[(Count)] & 0x3f); \ + } + +#define UNICODE_VALID(Char) \ + ((Char) < 0x110000 && \ + (((Char) & 0xFFFFF800) != 0xD800) && \ + ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ + ((Char) & 0xFFFE) != 0xFFFE) + + +static const char utf8_skip_data[256] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +#define UTF8_NEXT_CHAR(p) (char *)((p) + utf8_skip_data[*(unsigned char *)(p)]) + +/* Converts a sequence of bytes encoded as UTF-8 to a Unicode character. + * If @p does not point to a valid UTF-8 encoded character, results are + * undefined. + **/ +static uint32_t +_utf8_get_char (const char *p) +{ + int i, mask = 0, len; + uint32_t result; + unsigned char c = (unsigned char) *p; + + UTF8_COMPUTE (c, mask, len); + if (len == -1) + return (uint32_t)-1; + UTF8_GET (result, p, i, mask, len); + + return result; +} + +/* Like _utf8_get_char, but take a maximum length + * and return (uint32_t)-2 on incomplete trailing character + */ +static uint32_t +_utf8_get_char_extended (const char *p, + long max_len) +{ + int i, len; + uint32_t wc = (unsigned char) *p; + + if (wc < 0x80) { + return wc; + } else if (wc < 0xc0) { + return (uint32_t)-1; + } else if (wc < 0xe0) { + len = 2; + wc &= 0x1f; + } else if (wc < 0xf0) { + len = 3; + wc &= 0x0f; + } else if (wc < 0xf8) { + len = 4; + wc &= 0x07; + } else if (wc < 0xfc) { + len = 5; + wc &= 0x03; + } else if (wc < 0xfe) { + len = 6; + wc &= 0x01; + } else { + return (uint32_t)-1; + } + + if (max_len >= 0 && len > max_len) { + for (i = 1; i < max_len; i++) { + if ((((unsigned char *)p)[i] & 0xc0) != 0x80) + return (uint32_t)-1; + } + return (uint32_t)-2; + } + + for (i = 1; i < len; ++i) { + uint32_t ch = ((unsigned char *)p)[i]; + + if ((ch & 0xc0) != 0x80) { + if (ch) + return (uint32_t)-1; + else + return (uint32_t)-2; + } + + wc <<= 6; + wc |= (ch & 0x3f); + } + + if (UTF8_LENGTH(wc) != len) + return (uint32_t)-1; + + return wc; +} + +/** + * _cairo_utf8_to_utf32: + * @str: an UTF-8 string + * @len: length of @str in bytes, or -1 if it is nul-terminated. + * If @len is supplied and the string has an embedded nul + * byte, only the portion before the nul byte is converted. + * @result: location to store a pointer to a newly allocated UTF-32 + * string (always native endian). Free with free(). A 0 + * word will be written after the last character. + * @items_written: location to store number of 32-bit words + * written. (Not including the trailing 0) + * + * Converts a UTF-8 string to UCS-4. UCS-4 is an encoding of Unicode + * with 1 32-bit word per character. The string is validated to + * consist entirely of valid Unicode characters. + * + * Return value: %CAIRO_STATUS_SUCCESS if the entire string was + * succesfully converted. %CAIRO_STATUS_INVALID_STRING if an + * an invalid sequence was found. + **/ +cairo_status_t +_cairo_utf8_to_ucs4 (const char *str, + int len, + uint32_t **result, + int *items_written) +{ + uint32_t *str32 = NULL; + int n_chars, i; + const char *in; + + in = str; + n_chars = 0; + while ((len < 0 || str + len - in > 0) && *in) + { + uint32_t wc = _utf8_get_char_extended (in, str + len - in); + if (wc & 0x80000000 || !UNICODE_VALID (wc)) + return CAIRO_STATUS_INVALID_STRING; + + n_chars++; + if (n_chars == INT_MAX) + return CAIRO_STATUS_INVALID_STRING; + + in = UTF8_NEXT_CHAR (in); + } + + str32 = malloc (sizeof (uint32_t) * (n_chars + 1)); + if (!str32) + return CAIRO_STATUS_NO_MEMORY; + + in = str; + for (i=0; i < n_chars; i++) { + str32[i] = _utf8_get_char (in); + in = UTF8_NEXT_CHAR (in); + } + str32[i] = 0; + + *result = str32; + if (items_written) + *items_written = n_chars; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_utf8_to_utf16: + * @str: an UTF-8 string + * @len: length of @str in bytes, or -1 if it is nul-terminated. + * If @len is supplied and the string has an embedded nul + * byte, only the portion before the nul byte is converted. + * @result: location to store a pointer to a newly allocated UTF-16 + * string (always native endian). Free with free(). A 0 + * word will be written after the last character. + * @items_written: location to store number of 16-bit words + * written. (Not including the trailing 0) + * + * Converts a UTF-8 string to UTF-16. UTF-16 is an encoding of Unicode + * where characters are represented either as a single 16-bit word, or + * as a pair of 16-bit "surrogates". The string is validated to + * consist entirely of valid Unicode characters. + * + * Return value: %CAIRO_STATUS_SUCCESS if the entire string was + * succesfully converted. %CAIRO_STATUS_INVALID_STRING if an + * an invalid sequence was found. + **/ +cairo_status_t +_cairo_utf8_to_utf16 (const char *str, + int len, + uint16_t **result, + int *items_written) +{ + uint16_t *str16 = NULL; + int n16, i; + const char *in; + + in = str; + n16 = 0; + while ((len < 0 || str + len - in > 0) && *in) { + uint32_t wc = _utf8_get_char_extended (in, str + len - in); + if (wc & 0x80000000 || !UNICODE_VALID (wc)) + return CAIRO_STATUS_INVALID_STRING; + + if (wc < 0x10000) + n16 += 1; + else + n16 += 2; + + if (n16 == INT_MAX - 1 || n16 == INT_MAX) + return CAIRO_STATUS_INVALID_STRING; + + in = UTF8_NEXT_CHAR (in); + } + + + str16 = malloc (sizeof (uint16_t) * (n16 + 1)); + if (!str16) + return CAIRO_STATUS_NO_MEMORY; + + in = str; + for (i = 0; i < n16;) { + uint32_t wc = _utf8_get_char (in); + + if (wc < 0x10000) { + str16[i++] = wc; + } else { + str16[i++] = (wc - 0x10000) / 0x400 + 0xd800; + str16[i++] = (wc - 0x10000) % 0x400 + 0xdc00; + } + + in = UTF8_NEXT_CHAR (in); + } + + str16[i] = 0; + + *result = str16; + if (items_written) + *items_written = n16; + + return CAIRO_STATUS_SUCCESS; +} diff --git a/src/cairo-win32-font.c b/src/cairo-win32-font.c new file mode 100644 index 000000000..02f0cffd6 --- /dev/null +++ b/src/cairo-win32-font.c @@ -0,0 +1,1252 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * 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 Red Hat, Inc. + * + * Contributor(s): + */ + +#include <string.h> +#include <stdio.h> + +#include "cairo-win32-private.h" + +#ifndef SPI_GETFONTSMOOTHINGTYPE +#define SPI_GETFONTSMOOTHINGTYPE 0x200a +#endif +#ifndef FE_FONTSMOOTHINGCLEARTYPE +#define FE_FONTSMOOTHINGCLEARTYPE 2 +#endif +#ifndef CLEARTYPE_QUALITY +#define CLEARTYPE_QUALITY 5 +#endif + +const cairo_font_backend_t cairo_win32_font_backend; + +#define LOGICAL_SCALE 32 + +typedef struct { + cairo_font_t base; + + LOGFONTW logfont; + + BYTE quality; + + /* We do drawing and metrics computation in a "logical space" which + * is similar to font space, except that it is scaled by a factor + * of the (desired font size) * (LOGICAL_SCALE). The multiplication + * by LOGICAL_SCALE allows for sub-pixel precision. + */ + double logical_scale; + + /* The size we should actually request the font at from Windows; differs + * from the logical_scale because it is quantized for orthogonal + * transformations + */ + double logical_size; + + /* Transformations from device <=> logical space + */ + cairo_matrix_t logical_to_device; + cairo_matrix_t device_to_logical; + + /* We special case combinations of 90-degree-rotations, scales and + * flips ... that is transformations that take the axes to the + * axes. If preserve_axes is true, then swap_axes/swap_x/swap_y + * encode the 8 possibilities for orientation (4 rotation angles with + * and without a flip), and scale_x, scale_y the scale components. + */ + cairo_bool_t preserve_axes; + cairo_bool_t swap_axes; + cairo_bool_t swap_x; + cairo_bool_t swap_y; + double x_scale; + double y_scale; + + /* The size of the design unit of the font + */ + int em_square; + + HFONT scaled_font; + HFONT unscaled_font; + +} cairo_win32_font_t; + +#define NEARLY_ZERO(d) (fabs(d) < (1. / 65536.)) + +static void +_compute_transform (cairo_win32_font_t *font, + cairo_font_scale_t *sc) +{ + if (NEARLY_ZERO (sc->matrix[0][1]) && NEARLY_ZERO (sc->matrix[1][0])) { + font->preserve_axes = TRUE; + font->x_scale = sc->matrix[0][0]; + font->swap_x = (sc->matrix[0][0] < 0); + font->y_scale = sc->matrix[1][1]; + font->swap_y = (sc->matrix[1][1] < 0); + font->swap_axes = FALSE; + + } else if (NEARLY_ZERO (sc->matrix[0][0]) && NEARLY_ZERO (sc->matrix[1][1])) { + font->preserve_axes = TRUE; + font->x_scale = sc->matrix[0][1]; + font->swap_x = (sc->matrix[0][1] < 0); + font->y_scale = sc->matrix[1][0]; + font->swap_y = (sc->matrix[1][0] < 0); + font->swap_axes = TRUE; + + } else { + font->preserve_axes = FALSE; + font->swap_x = font->swap_y = font->swap_axes = FALSE; + } + + if (font->preserve_axes) { + if (font->swap_x) + font->x_scale = - font->x_scale; + if (font->swap_y) + font->y_scale = - font->y_scale; + + font->logical_scale = LOGICAL_SCALE * font->y_scale; + font->logical_size = LOGICAL_SCALE * floor (font->y_scale + 0.5); + } + + /* The font matrix has x and y "scale" components which we extract and + * use as character scale values. + */ + cairo_matrix_set_affine (&font->logical_to_device, + sc->matrix[0][0], + sc->matrix[0][1], + sc->matrix[1][0], + sc->matrix[1][1], + 0, 0); + + if (!font->preserve_axes) { + _cairo_matrix_compute_scale_factors (&font->logical_to_device, + &font->x_scale, &font->y_scale, + TRUE); /* XXX: Handle vertical text */ + + font->logical_size = floor (LOGICAL_SCALE * font->y_scale + 0.5); + font->logical_scale = LOGICAL_SCALE * font->y_scale; + } + + cairo_matrix_scale (&font->logical_to_device, + 1.0 / font->logical_scale, 1.0 / font->logical_scale); + + font->device_to_logical = font->logical_to_device; + if (!CAIRO_OK (cairo_matrix_invert (&font->device_to_logical))) + cairo_matrix_set_identity (&font->device_to_logical); +} + +static BYTE +_get_system_quality (void) +{ + BOOL font_smoothing; + + if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { + _cairo_win32_print_gdi_error ("_get_system_quality"); + return FALSE; + } + + if (font_smoothing) { + OSVERSIONINFO version_info; + + version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + + if (!GetVersionEx (&version_info)) { + _cairo_win32_print_gdi_error ("_get_system_quality"); + return FALSE; + } + + if (version_info.dwMajorVersion > 5 || + (version_info.dwMajorVersion == 5 && + version_info.dwMinorVersion >= 1)) { /* XP or newer */ + UINT smoothing_type; + + if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE, + 0, &smoothing_type, 0)) { + _cairo_win32_print_gdi_error ("_get_system_quality"); + return FALSE; + } + + if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) + return CLEARTYPE_QUALITY; + } + + return ANTIALIASED_QUALITY; + } else + return DEFAULT_QUALITY; +} + +static cairo_font_t * +_win32_font_create (LOGFONTW *logfont, + cairo_font_scale_t *scale) +{ + cairo_win32_font_t *f; + + f = malloc (sizeof(cairo_win32_font_t)); + if (f == NULL) + return NULL; + + f->logfont = *logfont; + f->quality = _get_system_quality (); + f->em_square = 0; + f->scaled_font = NULL; + f->unscaled_font = NULL; + + _compute_transform (f, scale); + + _cairo_font_init ((cairo_font_t *)f, scale, &cairo_win32_font_backend); + + return (cairo_font_t *)f; +} + +static cairo_status_t +_win32_font_set_world_transform (cairo_win32_font_t *font, + HDC hdc) +{ + XFORM xform; + + xform.eM11 = font->logical_to_device.m[0][0]; + xform.eM21 = font->logical_to_device.m[1][0]; + xform.eM12 = font->logical_to_device.m[0][1]; + xform.eM22 = font->logical_to_device.m[1][1]; + xform.eDx = font->logical_to_device.m[2][0]; + xform.eDy = font->logical_to_device.m[2][1]; + + if (!SetWorldTransform (hdc, &xform)) + return _cairo_win32_print_gdi_error ("_win32_font_set_world_transform"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_win32_font_set_identity_transform (HDC hdc) +{ + if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY)) + return _cairo_win32_print_gdi_error ("_win32_font_set_identity_transform"); + + return CAIRO_STATUS_SUCCESS; +} + +static HDC +_get_global_font_dc (void) +{ + static HDC hdc; + + if (!hdc) { + hdc = CreateCompatibleDC (NULL); + if (!hdc) { + _cairo_win32_print_gdi_error ("_get_global_font_dc"); + return NULL; + } + + if (!SetGraphicsMode (hdc, GM_ADVANCED)) { + _cairo_win32_print_gdi_error ("_get_global_font_dc"); + DeleteDC (hdc); + return NULL; + } + } + + return hdc; +} + +static HFONT +_win32_font_get_scaled_font (cairo_win32_font_t *font) +{ + if (!font->scaled_font) { + LOGFONTW logfont = font->logfont; + logfont.lfHeight = font->logical_size; + logfont.lfWidth = 0; + logfont.lfEscapement = 0; + logfont.lfOrientation = 0; + logfont.lfQuality = font->quality; + + font->scaled_font = CreateFontIndirectW (&logfont); + if (!font->scaled_font) { + _cairo_win32_print_gdi_error ("_win32_font_get_scaled_font"); + return NULL; + } + } + + return font->scaled_font; +} + +static HFONT +_win32_font_get_unscaled_font (cairo_win32_font_t *font, + HDC hdc) +{ + if (!font->unscaled_font) { + OUTLINETEXTMETRIC *otm; + unsigned int otm_size; + HFONT scaled_font; + LOGFONTW logfont; + + scaled_font = _win32_font_get_scaled_font (font); + if (!scaled_font) + return NULL; + + if (!SelectObject (hdc, scaled_font)) { + _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:SelectObject"); + return NULL; + } + + otm_size = GetOutlineTextMetrics (hdc, 0, NULL); + if (!otm_size) { + _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:GetOutlineTextMetrics"); + return NULL; + } + + otm = malloc (otm_size); + if (!otm) + return NULL; + + if (!GetOutlineTextMetrics (hdc, otm_size, otm)) { + _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:GetOutlineTextMetrics"); + free (otm); + return NULL; + } + + font->em_square = otm->otmEMSquare; + free (otm); + + logfont = font->logfont; + logfont.lfHeight = font->em_square; + logfont.lfWidth = 0; + logfont.lfEscapement = 0; + logfont.lfOrientation = 0; + logfont.lfQuality = font->quality; + + font->unscaled_font = CreateFontIndirectW (&logfont); + if (!font->unscaled_font) { + _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:CreateIndirect"); + return NULL; + } + } + + return font->unscaled_font; +} + +static cairo_status_t +_cairo_win32_font_select_unscaled_font (cairo_font_t *font, + HDC hdc) +{ + cairo_status_t status; + HFONT hfont; + HFONT old_hfont = NULL; + + hfont = _win32_font_get_unscaled_font ((cairo_win32_font_t *)font, hdc); + if (!hfont) + return CAIRO_STATUS_NO_MEMORY; + + old_hfont = SelectObject (hdc, hfont); + if (!old_hfont) + return _cairo_win32_print_gdi_error ("_cairo_win32_font_select_unscaled_font"); + + status = _win32_font_set_identity_transform (hdc); + if (!CAIRO_OK (status)) { + SelectObject (hdc, old_hfont); + return status; + } + + SetMapMode (hdc, MM_TEXT); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_font_done_unscaled_font (cairo_font_t *font) +{ +} + +/* implement the font backend interface */ + +static cairo_status_t +_cairo_win32_font_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight, + cairo_font_scale_t *scale, + cairo_font_t **font_out) +{ + LOGFONTW logfont; + cairo_font_t *font; + uint16_t *face_name; + int face_name_len; + cairo_status_t status; + + status = _cairo_utf8_to_utf16 (family, -1, &face_name, &face_name_len); + if (!CAIRO_OK (status)) + return status; + + if (face_name_len > LF_FACESIZE - 1) { + free (face_name); + return CAIRO_STATUS_INVALID_STRING; + } + + memcpy (logfont.lfFaceName, face_name, sizeof (uint16_t) * (face_name_len + 1)); + free (face_name); + + logfont.lfHeight = 0; /* filled in later */ + logfont.lfWidth = 0; /* filled in later */ + logfont.lfEscapement = 0; /* filled in later */ + logfont.lfOrientation = 0; /* filled in later */ + + switch (weight) { + case CAIRO_FONT_WEIGHT_NORMAL: + default: + logfont.lfWeight = FW_NORMAL; + break; + case CAIRO_FONT_WEIGHT_BOLD: + logfont.lfWeight = FW_BOLD; + break; + } + + switch (slant) { + case CAIRO_FONT_SLANT_NORMAL: + default: + logfont.lfItalic = FALSE; + break; + case CAIRO_FONT_SLANT_ITALIC: + case CAIRO_FONT_SLANT_OBLIQUE: + logfont.lfItalic = TRUE; + break; + } + + logfont.lfUnderline = FALSE; + logfont.lfStrikeOut = FALSE; + /* The docs for LOGFONT discourage using this, since the + * interpretation is locale-specific, but it's not clear what + * would be a better alternative. + */ + logfont.lfCharSet = DEFAULT_CHARSET; + logfont.lfOutPrecision = OUT_DEFAULT_PRECIS; + logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; + logfont.lfQuality = DEFAULT_QUALITY; /* filled in later */ + logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + + if (!logfont.lfFaceName) + return CAIRO_STATUS_NO_MEMORY; + + font = _win32_font_create (&logfont, scale); + if (!font) + return CAIRO_STATUS_NO_MEMORY; + + *font_out = font; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_font_destroy_font (void *abstract_font) +{ + cairo_win32_font_t *font = abstract_font; + + if (font->scaled_font) + DeleteObject (font->scaled_font); + + if (font->unscaled_font) + DeleteObject (font->unscaled_font); + + free (font); +} + +static void +_cairo_win32_font_destroy_unscaled_font (void *abstract_font) +{ +} + +static void +_cairo_win32_font_get_glyph_cache_key (void *abstract_font, + cairo_glyph_cache_key_t *key) +{ +} + +static cairo_status_t +_cairo_win32_font_text_to_glyphs (void *abstract_font, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) +{ + cairo_win32_font_t *font = abstract_font; + uint16_t *utf16; + int n16; + GCP_RESULTSW gcp_results; + unsigned int buffer_size, i; + WCHAR *glyph_indices = NULL; + int *dx = NULL; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + double x_pos; + HDC hdc = NULL; + + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); + if (!CAIRO_OK (status)) + return status; + + gcp_results.lStructSize = sizeof (GCP_RESULTS); + gcp_results.lpOutString = NULL; + gcp_results.lpOrder = NULL; + gcp_results.lpCaretPos = NULL; + gcp_results.lpClass = NULL; + + buffer_size = MAX (n16 * 1.2, 16); /* Initially guess number of chars plus a few */ + if (buffer_size > INT_MAX) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL1; + } + + hdc = _get_global_font_dc (); + if (!hdc) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL1; + } + + status = cairo_win32_font_select_font (&font->base, hdc); + if (!CAIRO_OK (status)) + goto FAIL1; + + while (TRUE) { + if (glyph_indices) { + free (glyph_indices); + glyph_indices = NULL; + } + if (dx) { + free (dx); + dx = NULL; + } + + glyph_indices = malloc (sizeof (WCHAR) * buffer_size); + dx = malloc (sizeof (int) * buffer_size); + if (!glyph_indices || !dx) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL2; + } + + gcp_results.nGlyphs = buffer_size; + gcp_results.lpDx = dx; + gcp_results.lpGlyphs = glyph_indices; + + if (!GetCharacterPlacementW (hdc, utf16, n16, + 0, + &gcp_results, + GCP_DIACRITIC | GCP_LIGATE | GCP_GLYPHSHAPE | GCP_REORDER)) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_font_text_to_glyphs"); + goto FAIL2; + } + + if (gcp_results.lpDx && gcp_results.lpGlyphs) + break; + + /* Too small a buffer, try again */ + + buffer_size *= 1.5; + if (buffer_size > INT_MAX) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL2; + } + } + + *num_glyphs = gcp_results.nGlyphs; + *glyphs = malloc (sizeof (cairo_glyph_t) * gcp_results.nGlyphs); + if (!*glyphs) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL2; + } + + x_pos = 0; + for (i = 0; i < gcp_results.nGlyphs; i++) { + (*glyphs)[i].index = glyph_indices[i]; + (*glyphs)[i].x = x_pos ; + (*glyphs)[i].y = 0; + + x_pos += dx[i] / font->logical_scale; + } + + FAIL2: + if (glyph_indices) + free (glyph_indices); + if (dx) + free (dx); + + cairo_win32_font_done_font (&font->base); + + FAIL1: + free (utf16); + + return status; +} + +static cairo_status_t +_cairo_win32_font_font_extents (void *abstract_font, + cairo_font_extents_t *extents) +{ + cairo_win32_font_t *font = abstract_font; + cairo_status_t status; + TEXTMETRIC metrics; + HDC hdc; + + hdc = _get_global_font_dc (); + if (!hdc) + return CAIRO_STATUS_NO_MEMORY; + + if (font->preserve_axes) { + /* For 90-degree rotations (including 0), we get the metrics + * from the GDI in logical space, then convert back to font space + */ + status = cairo_win32_font_select_font (&font->base, hdc); + if (!CAIRO_OK (status)) + return status; + GetTextMetrics (hdc, &metrics); + cairo_win32_font_done_font (&font->base); + + extents->ascent = metrics.tmAscent / font->logical_scale; + extents->descent = metrics.tmDescent / font->logical_scale; + + extents->height = (metrics.tmHeight + metrics.tmExternalLeading) / font->logical_scale; + extents->max_x_advance = metrics.tmMaxCharWidth / font->logical_scale; + extents->max_y_advance = 0; + + } else { + /* For all other transformations, we use the design metrics + * of the font. The GDI results from GetTextMetrics() on a + * transformed font are inexplicably large and we want to + * avoid them. + */ + status = _cairo_win32_font_select_unscaled_font (&font->base, hdc); + if (!CAIRO_OK (status)) + return status; + GetTextMetrics (hdc, &metrics); + _cairo_win32_font_done_unscaled_font (&font->base); + + extents->ascent = (double)metrics.tmAscent / font->em_square; + extents->descent = metrics.tmDescent * font->em_square; + extents->height = (double)(metrics.tmHeight + metrics.tmExternalLeading) / font->em_square; + extents->max_x_advance = (double)(metrics.tmMaxCharWidth) / font->em_square; + extents->max_y_advance = 0; + + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_font_glyph_extents (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + cairo_win32_font_t *font = abstract_font; + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + GLYPHMETRICS metrics; + cairo_status_t status; + HDC hdc; + + hdc = _get_global_font_dc (); + if (!hdc) + return CAIRO_STATUS_NO_MEMORY; + + /* We handle only the case num_glyphs == 1, glyphs[i].x == glyphs[0].y == 0. + * This is all that the calling code triggers, and the backend interface + * will eventually be changed to match + */ + assert (num_glyphs == 1); + + if (font->preserve_axes) { + /* If we aren't rotating / skewing the axes, then we get the metrics + * from the GDI in device space and convert to font space. + */ + status = cairo_win32_font_select_font (&font->base, hdc); + if (!CAIRO_OK (status)) + return status; + GetGlyphOutlineW (hdc, glyphs[0].index, GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + cairo_win32_font_done_font (&font->base); + + if (font->swap_axes) { + extents->x_bearing = - metrics.gmptGlyphOrigin.y / font->y_scale; + extents->y_bearing = metrics.gmptGlyphOrigin.x / font->x_scale; + extents->width = metrics.gmBlackBoxY / font->y_scale; + extents->height = metrics.gmBlackBoxX / font->x_scale; + extents->x_advance = metrics.gmCellIncY / font->x_scale; + extents->y_advance = metrics.gmCellIncX / font->y_scale; + } else { + extents->x_bearing = metrics.gmptGlyphOrigin.x / font->x_scale; + extents->y_bearing = - metrics.gmptGlyphOrigin.y / font->y_scale; + extents->width = metrics.gmBlackBoxX / font->x_scale; + extents->height = metrics.gmBlackBoxY / font->y_scale; + extents->x_advance = metrics.gmCellIncX / font->x_scale; + extents->y_advance = metrics.gmCellIncY / font->y_scale; + } + + if (font->swap_x) { + extents->x_bearing = (- extents->x_bearing - extents->width); + extents->x_advance = - extents->x_advance; + } + + if (font->swap_y) { + extents->y_bearing = (- extents->y_bearing - extents->height); + extents->y_advance = - extents->y_advance; + } + + } else { + /* For all other transformations, we use the design metrics + * of the font. + */ + status = _cairo_win32_font_select_unscaled_font (&font->base, hdc); + GetGlyphOutlineW (hdc, glyphs[0].index, GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + _cairo_win32_font_done_unscaled_font (&font->base); + + extents->x_bearing = (double)metrics.gmptGlyphOrigin.x / font->em_square; + extents->y_bearing = (double)metrics.gmptGlyphOrigin.y / font->em_square; + extents->width = (double)metrics.gmBlackBoxX / font->em_square; + extents->height = (double)metrics.gmBlackBoxY / font->em_square; + extents->x_advance = (double)metrics.gmCellIncX / font->em_square; + extents->y_advance = (double)metrics.gmCellIncY / font->em_square; + } + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +_cairo_win32_font_glyph_bbox (void *abstract_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) +{ + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + cairo_win32_font_t *font = abstract_font; + int x1 = 0, x2 = 0, y1 = 0, y2 = 0; + + if (num_glyphs > 0) { + HDC hdc = _get_global_font_dc (); + GLYPHMETRICS metrics; + cairo_status_t status; + int i; + + if (!hdc) + return CAIRO_STATUS_NO_MEMORY; + + status = cairo_win32_font_select_font (&font->base, hdc); + if (!CAIRO_OK (status)) + return status; + + for (i = 0; i < num_glyphs; i++) { + int x = floor (0.5 + glyphs[i].x); + int y = floor (0.5 + glyphs[i].y); + + GetGlyphOutlineW (hdc, glyphs[i].index, GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + + if (i == 0 || x1 > x + metrics.gmptGlyphOrigin.x) + x1 = x + metrics.gmptGlyphOrigin.x; + if (i == 0 || y1 > y - metrics.gmptGlyphOrigin.y) + y1 = y - metrics.gmptGlyphOrigin.y; + if (i == 0 || x2 < x + metrics.gmptGlyphOrigin.x + metrics.gmBlackBoxX) + x2 = x + metrics.gmptGlyphOrigin.x + metrics.gmBlackBoxX; + if (i == 0 || y2 < y - metrics.gmptGlyphOrigin.y + metrics.gmBlackBoxY) + y2 = y - metrics.gmptGlyphOrigin.y + metrics.gmBlackBoxY; + } + + cairo_win32_font_done_font (&font->base); + } + + bbox->p1.x = _cairo_fixed_from_int (x1); + bbox->p1.y = _cairo_fixed_from_int (y1); + bbox->p2.x = _cairo_fixed_from_int (x2); + bbox->p2.y = _cairo_fixed_from_int (y2); + + return CAIRO_STATUS_SUCCESS; +} + +typedef struct { + cairo_win32_font_t *font; + HDC hdc; + + cairo_array_t glyphs; + cairo_array_t dx; + + int start_x; + int last_x; + int last_y; +} cairo_glyph_state_t; + +static void +_start_glyphs (cairo_glyph_state_t *state, + cairo_win32_font_t *font, + HDC hdc) +{ + state->hdc = hdc; + state->font = font; + + _cairo_array_init (&state->glyphs, sizeof (WCHAR)); + _cairo_array_init (&state->dx, sizeof (int)); +} + +static cairo_status_t +_flush_glyphs (cairo_glyph_state_t *state) +{ + int dx = 0; + if (!_cairo_array_append (&state->dx, &dx, 1)) + return CAIRO_STATUS_NO_MEMORY; + + if (!ExtTextOutW (state->hdc, + state->start_x, state->last_y, + ETO_GLYPH_INDEX, + NULL, + (WCHAR *)state->glyphs.elements, + state->glyphs.num_elements, + (int *)state->dx.elements)) { + return _cairo_win32_print_gdi_error ("_flush_glyphs"); + } + + _cairo_array_truncate (&state->glyphs, 0); + _cairo_array_truncate (&state->dx, 0); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_add_glyph (cairo_glyph_state_t *state, + unsigned long index, + double device_x, + double device_y) +{ + double user_x = device_x; + double user_y = device_y; + WCHAR glyph_index = index; + int logical_x, logical_y; + + cairo_matrix_transform_point (&state->font->device_to_logical, &user_x, &user_y); + + logical_x = floor (user_x + 0.5); + logical_y = floor (user_y + 0.5); + + if (state->glyphs.num_elements > 0) { + int dx; + + if (logical_y != state->last_y) { + cairo_status_t status = _flush_glyphs (state); + if (!CAIRO_OK (status)) + return status; + state->start_x = logical_x; + } + + dx = logical_x - state->last_x; + if (!_cairo_array_append (&state->dx, &dx, 1)) + return CAIRO_STATUS_NO_MEMORY; + } else { + state->start_x = logical_x; + } + + state->last_x = logical_x; + state->last_y = logical_y; + + _cairo_array_append (&state->glyphs, &glyph_index, 1); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_finish_glyphs (cairo_glyph_state_t *state) +{ + _flush_glyphs (state); + + _cairo_array_fini (&state->glyphs); + _cairo_array_fini (&state->dx); +} + +static cairo_status_t +_draw_glyphs_on_surface (cairo_win32_surface_t *surface, + cairo_win32_font_t *font, + COLORREF color, + int x_offset, + int y_offset, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_glyph_state_t state; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int i; + + if (!SaveDC (surface->dc)) + return _cairo_win32_print_gdi_error ("_draw_glyphs_on_surface:SaveDC"); + + status = cairo_win32_font_select_font (&font->base, surface->dc); + if (!CAIRO_OK (status)) + goto FAIL1; + + SetTextColor (surface->dc, color); + SetTextAlign (surface->dc, TA_BASELINE | TA_LEFT); + SetBkMode (surface->dc, TRANSPARENT); + + _start_glyphs (&state, font, surface->dc); + + for (i = 0; i < num_glyphs; i++) { + status = _add_glyph (&state, glyphs[i].index, + glyphs[i].x - x_offset, glyphs[i].y - y_offset); + if (!CAIRO_OK (status)) + goto FAIL2; + } + + FAIL2: + _finish_glyphs (&state); + cairo_win32_font_done_font (&font->base); + FAIL1: + RestoreDC (surface->dc, 1); + + return status; +} + +/* Duplicate the green channel of a 4-channel mask in the alpha channel, then + * invert the whole mask. + */ +static void +_compute_argb32_mask_alpha (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; + int i, j; + + for (i = 0; i < image->height; i++) { + uint32_t *p = (uint32_t *) (image->data + i * image->stride); + for (j = 0; j < image->width; j++) { + *p = 0xffffffff ^ (*p | ((*p & 0x0000ff00) << 16)); + p++; + } + } +} + +/* Invert a mask + */ +static void +_invert_argb32_mask (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; + int i, j; + + for (i = 0; i < image->height; i++) { + uint32_t *p = (uint32_t *) (image->data + i * image->stride); + for (j = 0; j < image->width; j++) { + *p = 0xffffffff ^ *p; + p++; + } + } +} + +/* Compute an alpha-mask from a monochrome RGB24 image + */ +static cairo_surface_t * +_compute_a8_mask (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image; + cairo_image_surface_t *image8; + int i, j; + + image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8, + image24->width, image24->height); + if (!image8) + return NULL; + + for (i = 0; i < image24->height; i++) { + uint32_t *p = (uint32_t *) (image24->data + i * image24->stride); + unsigned char *q = (unsigned char *) (image8->data + i * image8->stride); + + for (j = 0; j < image24->width; j++) { + *q = 255 - ((*p & 0x0000ff00) >> 8); + p++; + q++; + } + } + + return &image8->base; +} + +static cairo_status_t +_cairo_win32_font_show_glyphs (void *abstract_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *generic_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_win32_font_t *font = abstract_font; + cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; + cairo_status_t status; + + if (width == 0 || height == 0) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_surface_is_win32 (generic_surface) && + surface->format == CAIRO_FORMAT_RGB24 && + operator == CAIRO_OPERATOR_OVER && + pattern->type == CAIRO_PATTERN_SOLID && + _cairo_pattern_is_opaque (pattern)) { + + cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern; + + /* When compositing OVER on a GDI-understood surface, with a + * solid opaque color, we can just call ExtTextOut directly. + */ + COLORREF new_color; + + new_color = RGB (((int)(0xffff * solid_pattern->red)) >> 8, + ((int)(0xffff * solid_pattern->green)) >> 8, + ((int)(0xffff * solid_pattern->blue)) >> 8); + + status = _draw_glyphs_on_surface (surface, font, new_color, + 0, 0, + glyphs, num_glyphs); + + return status; + } else { + /* Otherwise, we need to draw using software fallbacks. We create a mask + * surface by drawing the the glyphs onto a DIB, black-on-white then + * inverting. GDI outputs gamma-corrected images so inverted black-on-white + * is very different from white-on-black. We favor the more common + * case where the final output is dark-on-light. + */ + cairo_win32_surface_t *tmp_surface; + cairo_surface_t *mask_surface; + cairo_surface_pattern_t mask; + RECT r; + + tmp_surface = (cairo_win32_surface_t *)_cairo_win32_surface_create_dib (CAIRO_FORMAT_ARGB32, width, height); + if (!tmp_surface) + return CAIRO_STATUS_NO_MEMORY; + + r.left = 0; + r.top = 0; + r.right = width; + r.bottom = height; + FillRect (tmp_surface->dc, &r, GetStockObject (WHITE_BRUSH)); + + _draw_glyphs_on_surface (tmp_surface, font, RGB (0, 0, 0), + dest_x, dest_y, + glyphs, num_glyphs); + + if (font->quality == CLEARTYPE_QUALITY) { + /* For ClearType, we need a 4-channel mask. If we are compositing on + * a surface with alpha, we need to compute the alpha channel of + * the mask (we just copy the green channel). But for a destination + * surface without alpha the alpha channel of the mask is ignored + */ + + if (surface->format != CAIRO_FORMAT_RGB24) + _compute_argb32_mask_alpha (tmp_surface); + else + _invert_argb32_mask (tmp_surface); + + mask_surface = &tmp_surface->base; + + /* XXX: Hacky, should expose this in cairo_image_surface */ + pixman_image_set_component_alpha (((cairo_image_surface_t *)tmp_surface->image)->pixman_image, TRUE); + + } else { + mask_surface = _compute_a8_mask (tmp_surface); + cairo_surface_destroy (&tmp_surface->base); + if (!mask_surface) + return CAIRO_STATUS_NO_MEMORY; + } + + /* For operator == OVER, no-cleartype, a possible optimization here is to + * draw onto an intermediate ARGB32 surface and alpha-blend that with the + * destination + */ + _cairo_pattern_init_for_surface (&mask, mask_surface); + + status = _cairo_surface_composite (operator, pattern, + &mask.base, + &surface->base, + source_x, source_y, + 0, 0, + dest_x, dest_y, + width, height); + + _cairo_pattern_fini (&mask.base); + + cairo_surface_destroy (mask_surface); + + return status; + } +} + +static cairo_status_t +_cairo_win32_font_glyph_path (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path) +{ + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_font_create_glyph (cairo_image_glyph_cache_entry_t *val) +{ + return CAIRO_STATUS_NO_MEMORY; +} + +const cairo_font_backend_t cairo_win32_font_backend = { + _cairo_win32_font_create, + _cairo_win32_font_destroy_font, + _cairo_win32_font_destroy_unscaled_font, + _cairo_win32_font_font_extents, + _cairo_win32_font_text_to_glyphs, + _cairo_win32_font_glyph_extents, + _cairo_win32_font_glyph_bbox, + _cairo_win32_font_show_glyphs, + _cairo_win32_font_glyph_path, + _cairo_win32_font_get_glyph_cache_key, + _cairo_win32_font_create_glyph +}; + +/* implement the platform-specific interface */ + +/** + * cairo_win32_font_create_for_logfontw: + * @logfont: A #LOGFONTW structure specifying the font to use. + * The lfHeight, lfWidth, lfOrientation and lfEscapement + * fields of this structure are ignored; information from + * @scale will be used instead. + * @scale: The scale at which this font will be used. The + * scale is given by multiplying the font matrix (see + * cairo_transform_font()) by the current transformation matrix. + * The translation elements of the resulting matrix are ignored. + * + * Creates a new font for the Win32 font backend based on a + * #LOGFONT. This font can then be used with + * cairo_set_font(), cairo_font_glyph_extents(), or FreeType backend + * specific functions like cairo_win32_font_select_font(). + * + * Return value: a newly created #cairo_font_t. Free with + * cairo_font_destroy() when you are done using it. + **/ +cairo_font_t * +cairo_win32_font_create_for_logfontw (LOGFONTW *logfont, + cairo_matrix_t *scale) +{ + cairo_font_scale_t sc; + + cairo_matrix_get_affine (scale, + &sc.matrix[0][0], &sc.matrix[0][1], + &sc.matrix[1][0], &sc.matrix[1][1], + NULL, NULL); + + return _win32_font_create (logfont, &sc); +} + +/** + * cairo_win32_font_select_font: + * @font: A #cairo_font_t from the Win32 font backend. Such an + * object can be created with cairo_win32_font_create_for_logfontw(). + * @hdc: a device context + * + * Selects the font into the given device context and changes the + * map mode and world transformation of the device context to match + * that of the font. This function is intended for use when using + * layout APIs such as Uniscribe to do text layout with the + * Cairo font. After finishing using the device context, you must call + * cairo_win32_font_done_font() to release any resources allocated + * by this function. + * + * See cairo_win32_font_get_scale_factor() for converting logical + * coordinates from the device context to font space. + * + * Normally, calls to SaveDC() and RestoreDC() would be made around + * the use of this function to preserve the original graphics state. + * + * Return value: %CAIRO_STATUS_SUCCESS if the operation succeeded. + * otherwise an error such as %CAIRO_STATUS_NO_MEMORY and + * the device context is unchanged. + **/ +cairo_status_t +cairo_win32_font_select_font (cairo_font_t *font, + HDC hdc) +{ + cairo_status_t status; + HFONT hfont; + HFONT old_hfont = NULL; + int old_mode; + + hfont = _win32_font_get_scaled_font ((cairo_win32_font_t *)font); + if (!hfont) + return CAIRO_STATUS_NO_MEMORY; + + old_hfont = SelectObject (hdc, hfont); + if (!old_hfont) + return _cairo_win32_print_gdi_error ("cairo_win32_font_select_font"); + + old_mode = SetGraphicsMode (hdc, GM_ADVANCED); + if (!old_mode) { + status = _cairo_win32_print_gdi_error ("cairo_win32_font_select_font"); + SelectObject (hdc, old_hfont); + return status; + } + + status = _win32_font_set_world_transform ((cairo_win32_font_t *)font, hdc); + if (!CAIRO_OK (status)) { + SetGraphicsMode (hdc, old_mode); + SelectObject (hdc, old_hfont); + return status; + } + + SetMapMode (hdc, MM_TEXT); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_win32_font_done_font: + * @font: A #cairo_font_t from the Win32 font backend. + * + * Releases any resources allocated by cairo_win32_font_select_font() + **/ +void +cairo_win32_font_done_font (cairo_font_t *font) +{ +} + +/** + * cairo_win32_font_get_scale_factor: + * @font: a #cairo_font_t from the Win32 font backend + * + * Gets a scale factor between logical coordinates in the coordinate + * space used by cairo_win32_font_select_font() and font space coordinates. + * + * Return value: factor to multiply logical units by to get font space + * coordinates. + **/ +double +cairo_win32_font_get_scale_factor (cairo_font_t *font) +{ + return 1. / ((cairo_win32_font_t *)font)->logical_scale; +} diff --git a/src/cairo-win32-private.h b/src/cairo-win32-private.h new file mode 100644 index 000000000..71e677ad1 --- /dev/null +++ b/src/cairo-win32-private.h @@ -0,0 +1,87 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * 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 Red Hat, Inc. + * + * Contributor(s): + * Owen Taylor <otaylor@redhat.com> + */ + +#ifndef CAIRO_WIN32_PRIVATE_H +#define CAIRO_WIN32_PRIVATE_H + +/* We depend on various features introduced with Win2k and Win98, + * like AlphaBlend. If it turns out to be a problem, we could + * use GetProcAddress() to look them up. + */ +#define WINVER 0x0500 + +#include <cairo-win32.h> +#include <cairoint.h> + +typedef struct _cairo_win32_surface { + cairo_surface_t base; + + cairo_format_t format; + + HDC dc; + + /* We create off-screen surfaces as DIBs */ + HBITMAP bitmap; + + /* Used to save the initial 1x1 monochrome bitmap for the DC to + * select back into the DC before deleting the DC and our + * bitmap. For Windows XP, this doesn't seem to be necessary + * ... we can just delete the DC and that automatically unselects + * out bitmap. But it's standard practice so apparently is needed + * on some versions of Windows. + */ + HBITMAP saved_dc_bitmap; + + cairo_surface_t *image; + + cairo_rectangle_t clip_rect; + + int set_clip; + HRGN saved_clip; + +} cairo_win32_surface_t; + +cairo_status_t +_cairo_win32_print_gdi_error (const char *context); + +cairo_surface_t * +_cairo_win32_surface_create_dib (cairo_format_t format, + int width, + int height); + +cairo_bool_t +_cairo_surface_is_win32 (cairo_surface_t *surface); + +#endif /* CAIRO_WIN32_PRIVATE_H */ diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c new file mode 100644 index 000000000..dcfe6d044 --- /dev/null +++ b/src/cairo-win32-surface.c @@ -0,0 +1,931 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * + * 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 Red Hat, Inc. + * + * Contributor(s): + * Owen Taylor <otaylor@redhat.com> + */ + +#include <stdio.h> + +#include "cairo-win32-private.h" + +static const cairo_surface_backend_t cairo_win32_surface_backend; + +/** + * _cairo_win32_print_gdi_error: + * @context: context string to display along with the error + * + * Helper function to dump out a human readable form of the + * current error code. + * + * Return value: A Cairo status code for the error code + **/ +cairo_status_t +_cairo_win32_print_gdi_error (const char *context) +{ + void *lpMsgBuf; + DWORD last_error = GetLastError (); + + if (!FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + last_error, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL)) { + fprintf (stderr, "%s: Unknown GDI error", context); + } else { + fprintf (stderr, "%s: %s", context, (char *)lpMsgBuf); + + LocalFree (lpMsgBuf); + } + + /* We should switch off of last_status, but we'd either return + * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there + * is no CAIRO_STATUS_UNKNOWN_ERROR. + */ + + return CAIRO_STATUS_NO_MEMORY; +} + +void +cairo_set_target_win32 (cairo_t *cr, + HDC hdc) +{ + cairo_surface_t *surface; + + if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) + return; + + surface = cairo_win32_surface_create (hdc); + if (surface == NULL) { + cr->status = CAIRO_STATUS_NO_MEMORY; + return; + } + + cairo_set_target_surface (cr, surface); + + /* cairo_set_target_surface takes a reference, so we must destroy ours */ + cairo_surface_destroy (surface); +} + +static cairo_status_t +_create_dc_and_bitmap (cairo_win32_surface_t *surface, + HDC original_dc, + cairo_format_t format, + int width, + int height, + char **bits_out, + int *rowstride_out) +{ + cairo_status_t status; + + BITMAPINFO *bitmap_info = NULL; + struct { + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[2]; + } bmi_stack; + void *bits; + + int num_palette = 0; /* Quiet GCC */ + int i; + + surface->dc = NULL; + surface->bitmap = NULL; + + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + num_palette = 0; + break; + + case CAIRO_FORMAT_A8: + num_palette = 256; + break; + + case CAIRO_FORMAT_A1: + num_palette = 2; + break; + } + + if (num_palette > 2) { + bitmap_info = malloc (sizeof (BITMAPINFOHEADER) + num_palette * sizeof (RGBQUAD)); + if (!bitmap_info) + return CAIRO_STATUS_NO_MEMORY; + } else { + bitmap_info = (BITMAPINFO *)&bmi_stack; + } + + bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + bitmap_info->bmiHeader.biWidth = width; + bitmap_info->bmiHeader.biHeight = - height; /* top-down */ + bitmap_info->bmiHeader.biSizeImage = 0; + bitmap_info->bmiHeader.biXPelsPerMeter = 72. / 0.0254; /* unused here */ + bitmap_info->bmiHeader.biYPelsPerMeter = 72. / 0.0254; /* unused here */ + bitmap_info->bmiHeader.biPlanes = 1; + + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + bitmap_info->bmiHeader.biBitCount = 32; + bitmap_info->bmiHeader.biCompression = BI_RGB; + bitmap_info->bmiHeader.biClrUsed = 0; /* unused */ + bitmap_info->bmiHeader.biClrImportant = 0; + break; + + case CAIRO_FORMAT_A8: + bitmap_info->bmiHeader.biBitCount = 8; + bitmap_info->bmiHeader.biCompression = BI_RGB; + bitmap_info->bmiHeader.biClrUsed = 256; + bitmap_info->bmiHeader.biClrImportant = 0; + + for (i = 0; i < 256; i++) { + bitmap_info->bmiColors[i].rgbBlue = i; + bitmap_info->bmiColors[i].rgbGreen = i; + bitmap_info->bmiColors[i].rgbRed = i; + bitmap_info->bmiColors[i].rgbReserved = 0; + } + + break; + + case CAIRO_FORMAT_A1: + bitmap_info->bmiHeader.biBitCount = 1; + bitmap_info->bmiHeader.biCompression = BI_RGB; + bitmap_info->bmiHeader.biClrUsed = 2; + bitmap_info->bmiHeader.biClrImportant = 0; + + for (i = 0; i < 2; i++) { + bitmap_info->bmiColors[i].rgbBlue = i * 255; + bitmap_info->bmiColors[i].rgbGreen = i * 255; + bitmap_info->bmiColors[i].rgbRed = i * 255; + bitmap_info->bmiColors[i].rgbReserved = 0; + break; + } + } + + surface->dc = CreateCompatibleDC (original_dc); + if (!surface->dc) + goto FAIL; + + surface->bitmap = CreateDIBSection (surface->dc, + bitmap_info, + DIB_RGB_COLORS, + &bits, + NULL, 0); + if (!surface->bitmap) + goto FAIL; + + surface->saved_dc_bitmap = SelectObject (surface->dc, + surface->bitmap); + if (!surface->saved_dc_bitmap) + goto FAIL; + + if (bitmap_info && num_palette > 2) + free (bitmap_info); + + if (bits_out) + *bits_out = bits; + + if (rowstride_out) { + /* Windows bitmaps are padded to 16-bit (word) boundaries */ + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + *rowstride_out = 4 * width; + break; + + case CAIRO_FORMAT_A8: + *rowstride_out = (width + 1) & -2; + break; + + case CAIRO_FORMAT_A1: + *rowstride_out = ((width + 15) & -16) / 8; + break; + } + } + + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error ("_create_dc_and_bitmap"); + + if (bitmap_info && num_palette > 2) + free (bitmap_info); + + if (surface->saved_dc_bitmap) { + SelectObject (surface->dc, surface->saved_dc_bitmap); + surface->saved_dc_bitmap = NULL; + } + + if (surface->bitmap) { + DeleteObject (surface->bitmap); + surface->bitmap = NULL; + } + + if (surface->dc) { + DeleteDC (surface->dc); + surface->dc = NULL; + } + + return status; +} + +static cairo_surface_t * +_cairo_win32_surface_create_for_dc (HDC original_dc, + cairo_format_t format, + int drawable, + int width, + int height) +{ + cairo_win32_surface_t *surface; + char *bits; + int rowstride; + + surface = malloc (sizeof (cairo_win32_surface_t)); + if (!surface) + return NULL; + + if (_create_dc_and_bitmap (surface, original_dc, format, + width, height, + &bits, &rowstride) != CAIRO_STATUS_SUCCESS) + goto FAIL; + + surface->image = cairo_image_surface_create_for_data (bits, format, + width, height, rowstride); + if (!surface->image) + goto FAIL; + + surface->format = format; + + surface->clip_rect.x = 0; + surface->clip_rect.y = 0; + surface->clip_rect.width = width; + surface->clip_rect.height = height; + + surface->set_clip = 0; + surface->saved_clip = NULL; + + _cairo_surface_init (&surface->base, &cairo_win32_surface_backend); + + return (cairo_surface_t *)surface; + + FAIL: + if (surface->bitmap) { + SelectObject (surface->dc, surface->saved_dc_bitmap); + DeleteObject (surface->bitmap); + DeleteDC (surface->dc); + } + if (surface) + free (surface); + + return NULL; + +} + +static cairo_surface_t * +_cairo_win32_surface_create_similar (void *abstract_src, + cairo_format_t format, + int drawable, + int width, + int height) +{ + cairo_win32_surface_t *src = abstract_src; + + return _cairo_win32_surface_create_for_dc (src->dc, format, drawable, + width, height); +} + +/** + * _cairo_win32_surface_create_dib: + * @format: format of pixels in the surface to create + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates a device-independent-bitmap surface not associated with + * any particular existing surface or device context. The created + * bitmap will be unititialized. + * + * Return value: the newly created surface, or %NULL if it couldn't + * be created (probably because of lack of memory) + **/ +cairo_surface_t * +_cairo_win32_surface_create_dib (cairo_format_t format, + int width, + int height) +{ + return _cairo_win32_surface_create_for_dc (NULL, format, TRUE, + width, height); +} + +static void +_cairo_win32_surface_destroy (void *abstract_surface) +{ + cairo_win32_surface_t *surface = abstract_surface; + + if (surface->image) + cairo_surface_destroy (surface->image); + + if (surface->saved_clip) + DeleteObject (surface->saved_clip); + + /* If we created the Bitmap and DC, destroy them */ + if (surface->bitmap) { + SelectObject (surface->dc, surface->saved_dc_bitmap); + DeleteObject (surface->bitmap); + DeleteDC (surface->dc); + } + + free (surface); +} + +static double +_cairo_win32_surface_pixels_per_inch (void *abstract_surface) +{ + /* XXX: We should really get this value from somewhere */ + return 96.0; +} + +static cairo_status_t +_cairo_win32_surface_get_subimage (cairo_win32_surface_t *surface, + int x, + int y, + int width, + int height, + cairo_win32_surface_t **local_out) +{ + cairo_win32_surface_t *local; + cairo_status_t status; + + local = + (cairo_win32_surface_t *) _cairo_win32_surface_create_similar (surface, + surface->format, + 0, + width, height); + if (!local) + return CAIRO_STATUS_NO_MEMORY; + + if (!BitBlt (local->dc, + 0, 0, + width, height, + surface->dc, + x, y, + SRCCOPY)) + goto FAIL; + + *local_out = local; + + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_get_subimage"); + + if (local) + cairo_surface_destroy (&local->base); + + return status; +} + +static cairo_status_t +_cairo_win32_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_surface_t *local = NULL; + cairo_status_t status; + + if (surface->image) { + *image_out = (cairo_image_surface_t *)surface->image; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0, + surface->clip_rect.width, + surface->clip_rect.height, &local); + if (CAIRO_OK (status)) { + cairo_surface_set_filter (&local->base, surface->base.filter); + cairo_surface_set_matrix (&local->base, &surface->base.matrix); + cairo_surface_set_repeat (&local->base, surface->base.repeat); + + *image_out = (cairo_image_surface_t *)local->image; + *image_extra = local; + } + + return status; +} + +static void +_cairo_win32_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_win32_surface_t *local = image_extra; + + if (local) + cairo_surface_destroy ((cairo_surface_t *)local); +} + +static cairo_status_t +_cairo_win32_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect, + void **image_extra) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_surface_t *local = NULL; + cairo_status_t status; + RECT clip_box; + int x1, y1, x2, y2; + + if (surface->image) { + image_rect->x = 0; + image_rect->y = 0; + image_rect->width = surface->clip_rect.width; + image_rect->height = surface->clip_rect.height; + + *image_out = (cairo_image_surface_t *)surface->image; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; + } + + if (GetClipBox (surface->dc, &clip_box) == ERROR) + return _cairo_win32_print_gdi_error ("_cairo_win3_surface_acquire_dest_image"); + + x1 = clip_box.left; + x2 = clip_box.right; + y1 = clip_box.top; + y2 = clip_box.bottom; + + if (interest_rect->x > x1) + x1 = interest_rect->x; + if (interest_rect->y > y1) + y1 = interest_rect->y; + if (interest_rect->x + interest_rect->width < x2) + x2 = interest_rect->x + interest_rect->width; + if (interest_rect->y + interest_rect->height < y2) + y2 = interest_rect->y + interest_rect->height; + + if (x1 >= x2 || y1 >= y2) { + *image_out = NULL; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_win32_surface_get_subimage (abstract_surface, + x1, y1, x2 - x1, y2 - y1, + &local); + if (CAIRO_OK (status)) { + *image_out = (cairo_image_surface_t *)local->image; + *image_extra = local; + + image_rect->x = x1; + image_rect->y = y1; + image_rect->width = x2 - x1; + image_rect->height = y2 - y1; + } + + return status; +} + +static void +_cairo_win32_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_surface_t *local = image_extra; + + if (!local) + return; + + if (!BitBlt (surface->dc, + image_rect->x, image_rect->y, + image_rect->width, image_rect->height, + local->dc, + 0, 0, + SRCCOPY)) + _cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image"); + + cairo_surface_destroy ((cairo_surface_t *)local); +} + +static cairo_status_t +_cairo_win32_surface_clone_similar (void *surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_composite (cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) +{ + cairo_win32_surface_t *dst = abstract_dst; + cairo_win32_surface_t *src; + cairo_surface_pattern_t *src_surface_pattern; + int alpha; + int integer_transform; + int itx, ity; + + if (pattern->type != CAIRO_PATTERN_SURFACE || + pattern->extend != CAIRO_EXTEND_NONE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (mask_pattern) { + /* FIXME: When we fully support RENDER style 4-channel + * masks we need to check r/g/b != 1.0. + */ + if (mask_pattern->type != CAIRO_PATTERN_SOLID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + alpha = (int)(0xffff * pattern->alpha * mask_pattern->alpha) >> 8; + } else { + alpha = (int)(0xffff * pattern->alpha) >> 8; + } + + src_surface_pattern = (cairo_surface_pattern_t *)pattern; + src = (cairo_win32_surface_t *)src_surface_pattern->surface; + + if (src->base.backend != dst->base.backend) + return CAIRO_INT_STATUS_UNSUPPORTED; + + integer_transform = _cairo_matrix_is_integer_translation (&pattern->matrix, &itx, &ity); + if (!integer_transform) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (alpha == 255 && + src->format == dst->format && + (operator == CAIRO_OPERATOR_SRC || + (src->format == CAIRO_FORMAT_RGB24 && operator == CAIRO_OPERATOR_OVER))) { + + if (!BitBlt (dst->dc, + dst_x, dst_y, + width, height, + src->dc, + src_x + itx, src_y + ity, + SRCCOPY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite"); + + return CAIRO_STATUS_SUCCESS; + + } else if (integer_transform && + (src->format == CAIRO_FORMAT_RGB24 || src->format == CAIRO_FORMAT_ARGB32) && + dst->format == CAIRO_FORMAT_RGB24 && + !src->base.repeat && + operator == CAIRO_OPERATOR_OVER) { + + BLENDFUNCTION blend_function; + + blend_function.BlendOp = AC_SRC_OVER; + blend_function.BlendFlags = 0; + blend_function.SourceConstantAlpha = alpha; + blend_function.AlphaFormat = src->format == CAIRO_FORMAT_ARGB32 ? AC_SRC_ALPHA : 0; + + if (!AlphaBlend (dst->dc, + dst_x, dst_y, + width, height, + src->dc, + src_x + itx, src_y + ity, + width, height, + blend_function)) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite"); + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t operator, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_status_t status; + COLORREF new_color; + HBRUSH new_brush; + int i; + + /* If we have a local image, use the fallback code; it will be as fast + * as calling out to GDI. + */ + if (surface->image) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* We could support possibly support more operators for color->alpha = 0xffff. + * for CAIRO_OPERATOR_SRC, alpha doesn't matter since we know the destination + * image doesn't have alpha. (surface->pixman_image is non-NULL for all + * surfaces with alpha.) + */ + if (operator != CAIRO_OPERATOR_SRC) + return CAIRO_INT_STATUS_UNSUPPORTED; + + new_color = RGB (color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8); + + new_brush = CreateSolidBrush (new_color); + if (!new_brush) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); + + for (i = 0; i < num_rects; i++) { + RECT rect; + + rect.left = rects[i].x; + rect.top = rects[i].y; + rect.right = rects[i].x + rects[i].width; + rect.bottom = rects[i].y + rects[i].height; + + if (!FillRect (surface->dc, &rect, new_brush)) + goto FAIL; + } + + DeleteObject (new_brush); + + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); + + DeleteObject (new_brush); + + return status; +} + +static cairo_int_status_t +_cairo_win32_surface_composite_trapezoids (cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_dst, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps) + +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_copy_page (void *abstract_surface) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_show_page (void *abstract_surface) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_status_t status; + + /* If we are in-memory, then we set the clip on the image surface + * as well as on the underlying GDI surface. + */ + if (surface->image) + _cairo_surface_set_clip_region (surface->image, region); + + /* The semantics we want is that any clip set by Cairo combines + * is intersected with the clip on device context that the + * surface was created for. To implement this, we need to + * save the original clip when first setting a clip on surface. + */ + + if (region == NULL) { + /* Clear any clip set by Cairo, return to the original */ + + if (surface->set_clip) { + if (SelectClipRgn (surface->dc, surface->saved_clip) == ERROR) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region"); + + if (surface->saved_clip) { + DeleteObject (surface->saved_clip); + surface->saved_clip = NULL; + } + + surface->set_clip = 0; + } + + + return CAIRO_STATUS_SUCCESS; + + } else { + pixman_box16_t *boxes = pixman_region_rects (region); + int num_boxes = pixman_region_num_rects (region); + pixman_box16_t *extents = pixman_region_extents (region); + RGNDATA *data; + size_t data_size; + RECT *rects; + int i; + HRGN gdi_region; + + /* Create a GDI region for the cairo region */ + + data_size = sizeof (RGNDATAHEADER) + num_boxes * sizeof (RECT); + data = malloc (data_size); + if (!data) + return CAIRO_STATUS_NO_MEMORY; + rects = (RECT *)data->Buffer; + + data->rdh.dwSize = sizeof (RGNDATAHEADER); + data->rdh.iType = RDH_RECTANGLES; + data->rdh.nCount = num_boxes; + data->rdh.nRgnSize = num_boxes * sizeof (RECT); + data->rdh.rcBound.left = extents->x1; + data->rdh.rcBound.top = extents->y1; + data->rdh.rcBound.right = extents->x2; + data->rdh.rcBound.bottom = extents->y2; + + for (i = 0; i < num_boxes; i++) { + rects[i].left = boxes[i].x1; + rects[i].top = boxes[i].y1; + rects[i].right = boxes[i].x2; + rects[i].bottom = boxes[i].y2; + } + + gdi_region = ExtCreateRegion (NULL, data_size, data); + free (data); + + if (!gdi_region) + return CAIRO_STATUS_NO_MEMORY; + + if (surface->set_clip) { + /* Combine the new region with the original clip */ + + if (surface->saved_clip) { + if (CombineRgn (gdi_region, gdi_region, surface->saved_clip, RGN_AND) == ERROR) + goto FAIL; + } + + if (SelectClipRgn (surface->dc, gdi_region) == ERROR) + goto FAIL; + + } else { + /* Save the the current region */ + + surface->saved_clip = CreateRectRgn (0, 0, 0, 0); + if (!surface->saved_clip) { + goto FAIL; } + + /* This function has no error return! */ + if (GetClipRgn (surface->dc, surface->saved_clip) == 0) { /* No clip */ + DeleteObject (surface->saved_clip); + surface->saved_clip = NULL; + } + + if (ExtSelectClipRgn (surface->dc, gdi_region, RGN_AND) == ERROR) + goto FAIL; + + surface->set_clip = 1; + } + + DeleteObject (gdi_region); + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region"); + DeleteObject (gdi_region); + return status; + } +} + +static cairo_status_t +_cairo_win32_surface_show_glyphs (cairo_font_t *font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +cairo_surface_t * +cairo_win32_surface_create (HDC hdc) +{ + cairo_win32_surface_t *surface; + RECT rect; + + /* Try to figure out the drawing bounds for the Device context + */ + if (GetClipBox (hdc, &rect) == ERROR) { + _cairo_win32_print_gdi_error ("cairo_win32_surface_create"); + return NULL; + } + + surface = malloc (sizeof (cairo_win32_surface_t)); + if (!surface) + return NULL; + + surface->image = NULL; + surface->format = CAIRO_FORMAT_RGB24; + + surface->dc = hdc; + surface->bitmap = NULL; + + surface->clip_rect.x = rect.left; + surface->clip_rect.y = rect.top; + surface->clip_rect.width = rect.right - rect.left; + surface->clip_rect.height = rect.bottom - rect.top; + + surface->set_clip = 0; + surface->saved_clip = NULL; + + _cairo_surface_init (&surface->base, &cairo_win32_surface_backend); + + return (cairo_surface_t *)surface; +} + +/** + * _cairo_surface_is_win32: + * @surface: a #cairo_surface_t + * + * Checks if a surface is an #cairo_win32_surface_t + * + * Return value: True if the surface is an win32 surface + **/ +int +_cairo_surface_is_win32 (cairo_surface_t *surface) +{ + return surface->backend == &cairo_win32_surface_backend; +} + +static const cairo_surface_backend_t cairo_win32_surface_backend = { + _cairo_win32_surface_create_similar, + _cairo_win32_surface_destroy, + _cairo_win32_surface_pixels_per_inch, + _cairo_win32_surface_acquire_source_image, + _cairo_win32_surface_release_source_image, + _cairo_win32_surface_acquire_dest_image, + _cairo_win32_surface_release_dest_image, + _cairo_win32_surface_clone_similar, + _cairo_win32_surface_composite, + _cairo_win32_surface_fill_rectangles, + _cairo_win32_surface_composite_trapezoids, + _cairo_win32_surface_copy_page, + _cairo_win32_surface_show_page, + _cairo_win32_surface_set_clip_region, + _cairo_win32_surface_show_glyphs +}; diff --git a/src/cairo-win32.h b/src/cairo-win32.h new file mode 100644 index 000000000..fab497aa4 --- /dev/null +++ b/src/cairo-win32.h @@ -0,0 +1,71 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * 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 Red Hat, Inc. + * + * Contributor(s): + * Owen Taylor <otaylor@redhat.com> + */ + +#ifndef _CAIRO_WIN32_H_ + +#include <cairo.h> + +#ifdef CAIRO_HAS_WIN32_SURFACE + +#include <windows.h> + +CAIRO_BEGIN_DECLS + +void +cairo_set_target_win32 (cairo_t *cr, + HDC hdc); + +cairo_surface_t * +cairo_win32_surface_create (HDC hdc); + +cairo_font_t * +cairo_win32_font_create_for_logfontw (LOGFONTW *logfont, + cairo_matrix_t *scale); + +cairo_status_t +cairo_win32_font_select_font (cairo_font_t *font, + HDC hdc); + +void +cairo_win32_font_done_font (cairo_font_t *font); + +double +cairo_win32_font_get_scale_factor (cairo_font_t *font); + +#endif /* CAIRO_HAS_WIN32_SURFACE */ + +CAIRO_END_DECLS + +#endif /* _CAIRO_WIN32_H_ */ diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c index 758cf26de..0694b77a2 100644 --- a/src/cairo-xcb-surface.c +++ b/src/cairo-xcb-surface.c @@ -31,10 +31,11 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" +#include "cairo-xcb.h" cairo_surface_t * cairo_xcb_surface_create (XCBConnection *dpy, @@ -327,14 +328,17 @@ bytes_per_line(XCBConnection *c, int width, int bpp) return ((bpp * width + bitmap_pad - 1) & -bitmap_pad) >> 3; } -static cairo_image_surface_t * -_cairo_xcb_surface_get_image (void *abstract_surface) +static cairo_status_t +_get_image_surface (cairo_xcb_surface_t *surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect) { - cairo_xcb_surface_t *surface = abstract_surface; cairo_image_surface_t *image; XCBGetGeometryRep *geomrep; XCBGetImageRep *imagerep; int bpp; + int x1, y1, x2, y2; geomrep = XCBGetGeometryReply(surface->dpy, XCBGetGeometry(surface->dpy, surface->drawable), 0); if(!geomrep) @@ -344,11 +348,39 @@ _cairo_xcb_surface_get_image (void *abstract_surface) surface->height = geomrep->height; free(geomrep); + x1 = 0; + y1 = 0; + x2 = surface->width; + y2 = surface->height; + + if (interest_rect) { + if (interest_rect->x > x1) + x1 = interest_rect->x; + if (interest_rect->y > y1) + y1 = interest_rect->y; + if (interest_rect->x + interest_rect->width < x2) + x2 = interest_rect->x + interest_rect->width; + if (interest_rect->y + interest_rect->height < y2) + y2 = interest_rect->y + interest_rect->height; + + if (x1 >= x2 || y1 >= y2) { + *image_out = NULL; + return CAIRO_STATUS_SUCCESS; + } + } + + if (image_rect) { + image_rect->x = x1; + image_rect->y = y1; + image_rect->width = x2 - x1; + image_rect->height = y2 - y1; + } + imagerep = XCBGetImageReply(surface->dpy, XCBGetImage(surface->dpy, ZPixmap, surface->drawable, - 0, 0, - surface->width, surface->height, + x1, y1, + x2 - x1, y2 - y1, AllPlanes), 0); if(!imagerep) return 0; @@ -368,15 +400,15 @@ _cairo_xcb_surface_get_image (void *abstract_surface) image = _cairo_image_surface_create_with_masks (XCBGetImageData(imagerep), &masks, - surface->width, - surface->height, + x2 - x1, + y2 - y1, bytes_per_line(surface->dpy, surface->width, bpp)); } else { image = (cairo_image_surface_t *) cairo_image_surface_create_for_data (XCBGetImageData(imagerep), surface->format, - surface->width, - surface->height, + x2 - x1, + y2 - y1, bytes_per_line(surface->dpy, surface->width, bpp)); } @@ -388,7 +420,8 @@ _cairo_xcb_surface_get_image (void *abstract_surface) _cairo_image_surface_set_repeat (image, surface->base.repeat); _cairo_image_surface_set_matrix (image, &(surface->base.matrix)); - return image; + *image_out = image; + return CAIRO_STATUS_SUCCESS; } static void @@ -402,10 +435,11 @@ _cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface) } static cairo_status_t -_cairo_xcb_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +_draw_image_surface (cairo_xcb_surface_t *surface, + cairo_image_surface_t *image, + int dst_x, + int dst_y) { - cairo_xcb_surface_t *surface = abstract_surface; int bpp, data_len; _cairo_xcb_surface_ensure_gc (surface); @@ -414,7 +448,7 @@ _cairo_xcb_surface_set_image (void *abstract_surface, XCBPutImage(surface->dpy, ZPixmap, surface->drawable, surface->gc, image->width, image->height, - /* dst_x */ 0, /* dst_y */ 0, + dst_x, dst_y, /* left_pad */ 0, image->depth, data_len, image->data); @@ -422,9 +456,107 @@ _cairo_xcb_surface_set_image (void *abstract_surface, } static cairo_status_t -_cairo_xcb_surface_set_matrix (void *abstract_surface, cairo_matrix_t *matrix) +_cairo_xcb_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _get_image_surface (surface, NULL, &image, NULL); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_surface_set_filter (&image->base, surface->base.filter); + cairo_surface_set_matrix (&image->base, &surface->base.matrix); + cairo_surface_set_repeat (&image->base, surface->base.repeat); + + *image_out = image; + } + + return status; +} + +static void +_cairo_xcb_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_xcb_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect_out, + void **image_extra) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _get_image_surface (surface, interest_rect, &image, image_rect_out); + if (status == CAIRO_STATUS_SUCCESS) + *image_out = image; + + return status; +} + +static void +_cairo_xcb_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ + cairo_xcb_surface_t *surface = abstract_surface; + + /* ignore errors */ + _draw_image_surface (surface, image, image_rect->x, image_rect->y); + + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_xcb_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { cairo_xcb_surface_t *surface = abstract_surface; + cairo_xcb_surface_t *clone; + + if (src->backend == surface->base.backend ) { + cairo_xcb_surface_t *xcb_src = (cairo_xcb_surface_t *)src; + + if (xcb_src->dpy == surface->dpy) { + *clone_out = src; + cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } + } else if (_cairo_surface_is_image (src)) { + cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; + + clone = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_similar (surface, image_src->format, 0, + image_src->width, image_src->height); + if (clone == NULL) + return CAIRO_STATUS_NO_MEMORY; + + _draw_image_surface (clone, image_src, 0, 0); + + *clone_out = &clone->base; + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +_cairo_xcb_surface_set_matrix (cairo_xcb_surface_t *surface, + cairo_matrix_t *matrix) +{ XCBRenderTRANSFORM xtransform; if (!surface->picture.xid) @@ -442,27 +574,42 @@ _cairo_xcb_surface_set_matrix (void *abstract_surface, cairo_matrix_t *matrix) xtransform.matrix32 = 0; xtransform.matrix33 = _cairo_fixed_from_double (1); - if (CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) + if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) { - XCBRenderSetPictureTransform (surface->dpy, surface->picture, xtransform); - } else { - /* XXX: Need support here if using an old RENDER without support - for SetPictureTransform */ + static const XCBRenderTRANSFORM identity = { + 1 << 16, 0x00000, 0x00000, + 0x00000, 1 << 16, 0x00000, + 0x00000, 0x00000, 1 << 16 + }; + + if (memcmp (&xtransform, &identity, sizeof (XCBRenderTRANSFORM)) == 0) + return CAIRO_STATUS_SUCCESS; + + return CAIRO_INT_STATUS_UNSUPPORTED; } + + XCBRenderSetPictureTransform (surface->dpy, surface->picture, xtransform); return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_xcb_surface_set_filter (void *abstract_surface, cairo_filter_t filter) +_cairo_xcb_surface_set_filter (cairo_xcb_surface_t *surface, + cairo_filter_t filter) { - cairo_xcb_surface_t *surface = abstract_surface; char *render_filter; - if (!(surface->picture.xid - && CAIRO_SURFACE_RENDER_HAS_FILTERS(surface))) + if (!surface->picture.xid) return CAIRO_STATUS_SUCCESS; - + + if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) + { + if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) + return CAIRO_STATUS_SUCCESS; + + return CAIRO_INT_STATUS_UNSUPPORTED; + } + switch (filter) { case CAIRO_FILTER_FAST: render_filter = "fast"; @@ -491,10 +638,8 @@ _cairo_xcb_surface_set_filter (void *abstract_surface, cairo_filter_t filter) } static cairo_status_t -_cairo_xcb_surface_set_repeat (void *abstract_surface, int repeat) +_cairo_xcb_surface_set_repeat (cairo_xcb_surface_t *surface, int repeat) { - cairo_xcb_surface_t *surface = abstract_surface; - CARD32 mask = XCBRenderCPRepeat; CARD32 pa[] = { repeat }; @@ -506,33 +651,32 @@ _cairo_xcb_surface_set_repeat (void *abstract_surface, int repeat) return CAIRO_STATUS_SUCCESS; } -static cairo_xcb_surface_t * -_cairo_xcb_surface_clone_similar (cairo_surface_t *src, - cairo_xcb_surface_t *template, - cairo_format_t format, - int depth) +static cairo_int_status_t +_cairo_xcb_surface_set_attributes (cairo_xcb_surface_t *surface, + cairo_surface_attributes_t *attributes) { - cairo_xcb_surface_t *clone; - cairo_image_surface_t *src_image; - - src_image = _cairo_surface_get_image (src); - - clone = (cairo_xcb_surface_t *) - _cairo_xcb_surface_create_similar (template, format, 0, - src_image->width, - src_image->height); - if (clone == NULL) - return NULL; - - _cairo_xcb_surface_set_filter (clone, cairo_surface_get_filter(src)); - - _cairo_xcb_surface_set_image (clone, src_image); + cairo_int_status_t status; - _cairo_xcb_surface_set_matrix (clone, &(src_image->base.matrix)); + status = _cairo_xcb_surface_set_matrix (surface, &attributes->matrix); + if (status) + return status; + + switch (attributes->extend) { + case CAIRO_EXTEND_NONE: + _cairo_xcb_surface_set_repeat (surface, 0); + break; + case CAIRO_EXTEND_REPEAT: + _cairo_xcb_surface_set_repeat (surface, 1); + break; + case CAIRO_EXTEND_REFLECT: + return CAIRO_INT_STATUS_UNSUPPORTED; + } - cairo_surface_destroy (&src_image->base); + status = _cairo_xcb_surface_set_filter (surface, attributes->filter); + if (status) + return status; - return clone; + return CAIRO_STATUS_SUCCESS; } static int @@ -574,65 +718,80 @@ _render_operator (cairo_operator_t operator) static cairo_int_status_t _cairo_xcb_surface_composite (cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) { - cairo_xcb_surface_t *dst = abstract_dst; - cairo_xcb_surface_t *src = (cairo_xcb_surface_t *) generic_src; - cairo_xcb_surface_t *mask = (cairo_xcb_surface_t *) generic_mask; - cairo_xcb_surface_t *src_clone = NULL; - cairo_xcb_surface_t *mask_clone = NULL; - XCBRenderPICTURE maskpict = { 0 }; - + cairo_surface_attributes_t src_attr, mask_attr; + cairo_xcb_surface_t *dst = abstract_dst; + cairo_xcb_surface_t *src; + cairo_xcb_surface_t *mask; + cairo_int_status_t status; if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; - if (generic_src->backend != dst->base.backend || src->dpy != dst->dpy) { - src_clone = _cairo_xcb_surface_clone_similar (generic_src, dst, - CAIRO_FORMAT_ARGB32, 32); - if (!src_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; - src = src_clone; - } - if (generic_mask && (generic_mask->backend != dst->base.backend || mask->dpy != dst->dpy)) { - mask_clone = _cairo_xcb_surface_clone_similar (generic_mask, dst, - CAIRO_FORMAT_A8, 8); - if (!mask_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; - mask = mask_clone; + status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, + &dst->base, + src_x, src_y, + mask_x, mask_y, + width, height, + (cairo_surface_t **) &src, + (cairo_surface_t **) &mask, + &src_attr, &mask_attr); + if (status) + return status; + + status = _cairo_xcb_surface_set_attributes (src, &src_attr); + if (CAIRO_OK (status)) + { + if (mask) + { + status = _cairo_xcb_surface_set_attributes (mask, &mask_attr); + if (CAIRO_OK (status)) + XCBRenderComposite (dst->dpy, + _render_operator (operator), + src->picture, + mask->picture, + dst->picture, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + mask_x + mask_attr.x_offset, + mask_y + mask_attr.y_offset, + dst_x, dst_y, + width, height); + } + else + { + static XCBRenderPICTURE maskpict = { 0 }; + + XCBRenderComposite (dst->dpy, + _render_operator (operator), + src->picture, + maskpict, + dst->picture, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + } } - if(mask) - maskpict = mask->picture; - - XCBRenderComposite (dst->dpy, - _render_operator (operator), - src->picture, - maskpict, - dst->picture, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); - - /* XXX: This is messed up. If I can xcb_surface_create, then I - should be able to xcb_surface_destroy. */ - if (src_clone) - cairo_surface_destroy (&src_clone->base); - if (mask_clone) - cairo_surface_destroy (&mask_clone->base); + if (mask) + _cairo_pattern_release_surface (&dst->base, &mask->base, &mask_attr); + + _cairo_pattern_release_surface (&dst->base, &src->base, &src_attr); - return CAIRO_STATUS_SUCCESS; + return status; } static cairo_int_status_t @@ -664,42 +823,60 @@ _cairo_xcb_surface_fill_rectangles (void *abstract_surface, static cairo_int_status_t _cairo_xcb_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *generic_src, - void *abstract_dst, - int xSrc, - int ySrc, - cairo_trapezoid_t *traps, - int num_traps) + cairo_pattern_t *pattern, + void *abstract_dst, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps) { - cairo_xcb_surface_t *dst = abstract_dst; - cairo_xcb_surface_t *src = (cairo_xcb_surface_t *) generic_src; - cairo_xcb_surface_t *src_clone = NULL; + cairo_surface_attributes_t attributes; + cairo_xcb_surface_t *dst = abstract_dst; + cairo_xcb_surface_t *src; + cairo_int_status_t status; + int render_reference_x, render_reference_y; + int render_src_x, render_src_y; if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; - - if (generic_src->backend != dst->base.backend || src->dpy != dst->dpy) { - src_clone = _cairo_xcb_surface_clone_similar (generic_src, dst, - CAIRO_FORMAT_ARGB32, 32); - if (!src_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; - src = src_clone; + + status = _cairo_pattern_acquire_surface (pattern, &dst->base, + src_x, src_y, width, height, + (cairo_surface_t **) &src, + &attributes); + if (status) + return status; + + if (traps[0].left.p1.y < traps[0].left.p2.y) { + render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); + render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); + } else { + render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); + render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); } - /* XXX: The XCBRenderTRAP cast is evil and needs to go away somehow. */ - /* XXX: format_from_cairo is slow. should cache something. */ - XCBRenderTrapezoids (dst->dpy, - _render_operator (operator), - src->picture, dst->picture, - format_from_cairo (dst->dpy, CAIRO_FORMAT_A8), - xSrc, ySrc, num_traps, (XCBRenderTRAP *) traps); - - /* XXX: This is messed up. If I can xcb_surface_create, then I - should be able to xcb_surface_destroy. */ - if (src_clone) - cairo_surface_destroy (&src_clone->base); + render_src_x = src_x + render_reference_x - dst_x; + render_src_y = src_y + render_reference_y - dst_y; - return CAIRO_STATUS_SUCCESS; + /* XXX: The XTrapezoid cast is evil and needs to go away somehow. */ + /* XXX: format_from_cairo is slow. should cache something. */ + status = _cairo_xcb_surface_set_attributes (src, &attributes); + if (CAIRO_OK (status)) + XCBRenderTrapezoids (dst->dpy, + _render_operator (operator), + src->picture, dst->picture, + format_from_cairo (dst->dpy, CAIRO_FORMAT_A8), + render_src_x + attributes.x_offset, + render_src_y + attributes.y_offset, + num_traps, (XCBRenderTRAP *) traps); + + _cairo_pattern_release_surface (&dst->base, &src->base, &attributes); + + return status; } static cairo_int_status_t @@ -722,30 +899,21 @@ _cairo_xcb_surface_set_clip_region (void *abstract_surface, return CAIRO_INT_STATUS_UNSUPPORTED; } -static cairo_int_status_t -_cairo_xcb_surface_create_pattern (void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *extents) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - static const cairo_surface_backend_t cairo_xcb_surface_backend = { _cairo_xcb_surface_create_similar, _cairo_xcb_surface_destroy, _cairo_xcb_surface_pixels_per_inch, - _cairo_xcb_surface_get_image, - _cairo_xcb_surface_set_image, - _cairo_xcb_surface_set_matrix, - _cairo_xcb_surface_set_filter, - _cairo_xcb_surface_set_repeat, + _cairo_xcb_surface_acquire_source_image, + _cairo_xcb_surface_release_source_image, + _cairo_xcb_surface_acquire_dest_image, + _cairo_xcb_surface_release_dest_image, + _cairo_xcb_surface_clone_similar, _cairo_xcb_surface_composite, _cairo_xcb_surface_fill_rectangles, _cairo_xcb_surface_composite_trapezoids, _cairo_xcb_surface_copy_page, _cairo_xcb_surface_show_page, _cairo_xcb_surface_set_clip_region, - _cairo_xcb_surface_create_pattern, NULL /* show_glyphs */ }; diff --git a/src/cairo-xcb.h b/src/cairo-xcb.h index 27ebad523..a5c65f441 100644 --- a/src/cairo-xcb.h +++ b/src/cairo-xcb.h @@ -31,18 +31,21 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ -#include <cairo.h> - #ifndef CAIRO_XCB_H #define CAIRO_XCB_H + +#include <cairo.h> + #ifdef CAIRO_HAS_XCB_SURFACE #include <X11/XCB/xcb.h> #include <X11/XCB/render.h> +CAIRO_BEGIN_DECLS + void cairo_set_target_xcb (cairo_t *cr, XCBConnection *dpy, @@ -50,5 +53,7 @@ cairo_set_target_xcb (cairo_t *cr, XCBVISUALTYPE *visual, cairo_format_t format); +CAIRO_END_DECLS + #endif /* CAIRO_HAS_XCB_SURFACE */ #endif /* CAIRO_XCB_H */ diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index d9d74f583..3eaef57e5 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -31,12 +31,28 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-xlib.h" +/** + * cairo_set_target_drawable: + * @cr: a #cairo_t + * @dpy: an X display + * @drawable: a window or pixmap on the default screen of @dpy + * + * Directs output for a #cairo_t to an Xlib drawable. @drawable must + * be a Window or Pixmap on the default screen of @dpy using the + * default colormap and visual. Using this function is slow because + * the function must retrieve information about @drawable from the X + * server. + + * The combination of cairo_xlib_surface_create() and + * cairo_set_target_surface() is somewhat more flexible, although + * it still is slow. + **/ void cairo_set_target_drawable (cairo_t *cr, Display *dpy, @@ -87,6 +103,8 @@ typedef struct _cairo_xlib_surface { #define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) #define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) +#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) + #define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) #define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) @@ -141,18 +159,13 @@ _cairo_xlib_surface_create_similar (void *abstract_src, Pixmap pix; cairo_xlib_surface_t *surface; - /* XXX: There's a pretty lame heuristic here. This assumes that - * all non-Render X servers do not support depth-32 pixmaps, (and - * that they do support depths 1, 8, and 24). Obviously, it would - * be much better to check the depths that are actually - * supported. */ - if (!dpy - || (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src) - && format == CAIRO_FORMAT_ARGB32)) - { - return NULL; + /* As a good first approximation, if the display doesn't have COMPOSITE, + * we're better off using image surfaces for all temporary operations + */ + if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE(src)) { + return cairo_image_surface_create (format, width, height); } - + scr = DefaultScreen (dpy); pix = XCreatePixmap (dpy, DefaultRootWindow (dpy), @@ -196,15 +209,17 @@ _cairo_xlib_surface_pixels_per_inch (void *abstract_surface) return 96.0; } -static cairo_image_surface_t * -_cairo_xlib_surface_get_image (void *abstract_surface) +static cairo_status_t +_get_image_surface (cairo_xlib_surface_t *surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect) { - cairo_xlib_surface_t *surface = abstract_surface; cairo_image_surface_t *image; - XImage *ximage; Window root_ignore; int x_ignore, y_ignore, bwidth_ignore, depth_ignore; + int x1, y1, x2, y2; XGetGeometry (surface->dpy, surface->drawable, @@ -212,11 +227,39 @@ _cairo_xlib_surface_get_image (void *abstract_surface) &surface->width, &surface->height, &bwidth_ignore, &depth_ignore); + x1 = 0; + y1 = 0; + x2 = surface->width; + y2 = surface->height; + + if (interest_rect) { + if (interest_rect->x > x1) + x1 = interest_rect->x; + if (interest_rect->y > y1) + y1 = interest_rect->y; + if (interest_rect->x + interest_rect->width < x2) + x2 = interest_rect->x + interest_rect->width; + if (interest_rect->y + interest_rect->height < y2) + y2 = interest_rect->y + interest_rect->height; + + if (x1 >= x2 || y1 >= y2) { + *image_out = NULL; + return CAIRO_STATUS_SUCCESS; + } + } + + if (image_rect) { + image_rect->x = x1; + image_rect->y = y1; + image_rect->width = x2 - x1; + image_rect->height = y2 - y1; + } + /* XXX: This should try to use the XShm extension if availible */ ximage = XGetImage (surface->dpy, surface->drawable, - 0, 0, - surface->width, surface->height, + x1, y1, + x2 - x1, y2 - y1, AllPlanes, ZPixmap); if (surface->visual) { @@ -253,7 +296,8 @@ _cairo_xlib_surface_get_image (void *abstract_surface) _cairo_image_surface_set_repeat (image, surface->base.repeat); _cairo_image_surface_set_matrix (image, &(surface->base.matrix)); - return image; + *image_out = image; + return CAIRO_STATUS_SUCCESS; } static void @@ -266,10 +310,11 @@ _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface) } static cairo_status_t -_cairo_xlib_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +_draw_image_surface (cairo_xlib_surface_t *surface, + cairo_image_surface_t *image, + int dst_x, + int dst_y) { - cairo_xlib_surface_t *surface = abstract_surface; XImage *ximage; unsigned bitmap_pad; @@ -295,9 +340,8 @@ _cairo_xlib_surface_set_image (void *abstract_surface, _cairo_xlib_surface_ensure_gc (surface); XPutImage(surface->dpy, surface->drawable, surface->gc, - ximage, 0, 0, 0, 0, - surface->width, - surface->height); + ximage, 0, 0, dst_x, dst_y, + image->width, image->height); /* Foolish XDestroyImage thinks it can free my data, but I won't stand for it. */ @@ -305,17 +349,116 @@ _cairo_xlib_surface_set_image (void *abstract_surface, XDestroyImage (ximage); return CAIRO_STATUS_SUCCESS; + +} + +static cairo_status_t +_cairo_xlib_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _get_image_surface (surface, NULL, &image, NULL); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_surface_set_filter (&image->base, surface->base.filter); + cairo_surface_set_matrix (&image->base, &surface->base.matrix); + cairo_surface_set_repeat (&image->base, surface->base.repeat); + + *image_out = image; + } + + return status; +} + +static void +_cairo_xlib_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_xlib_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect_out, + void **image_extra) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _get_image_surface (surface, interest_rect, &image, image_rect_out); + if (status == CAIRO_STATUS_SUCCESS) + *image_out = image; + + return status; +} + +static void +_cairo_xlib_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ + cairo_xlib_surface_t *surface = abstract_surface; + + /* ignore errors */ + _draw_image_surface (surface, image, image_rect->x, image_rect->y); + + cairo_surface_destroy (&image->base); } static cairo_status_t -_cairo_xlib_surface_set_matrix (void *abstract_surface, cairo_matrix_t *matrix) +_cairo_xlib_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { cairo_xlib_surface_t *surface = abstract_surface; + cairo_xlib_surface_t *clone; + + if (src->backend == surface->base.backend ) { + cairo_xlib_surface_t *xlib_src = (cairo_xlib_surface_t *)src; + + if (xlib_src->dpy == surface->dpy) { + *clone_out = src; + cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } + } else if (_cairo_surface_is_image (src)) { + cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; + + clone = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_similar (surface, image_src->format, 0, + image_src->width, image_src->height); + if (clone == NULL) + return CAIRO_STATUS_NO_MEMORY; + + _draw_image_surface (clone, image_src, 0, 0); + + *clone_out = &clone->base; + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +_cairo_xlib_surface_set_matrix (cairo_xlib_surface_t *surface, + cairo_matrix_t *matrix) +{ XTransform xtransform; if (!surface->picture) return CAIRO_STATUS_SUCCESS; - + xtransform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); xtransform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); xtransform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); @@ -328,26 +471,41 @@ _cairo_xlib_surface_set_matrix (void *abstract_surface, cairo_matrix_t *matrix) xtransform.matrix[2][1] = 0; xtransform.matrix[2][2] = _cairo_fixed_from_double (1); - if (CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) + if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) { - XRenderSetPictureTransform (surface->dpy, surface->picture, &xtransform); - } else { - /* XXX: Need support here if using an old RENDER without support - for SetPictureTransform */ + static const XTransform identity = { { + { 1 << 16, 0x00000, 0x00000 }, + { 0x00000, 1 << 16, 0x00000 }, + { 0x00000, 0x00000, 1 << 16 }, + } }; + + if (memcmp (&xtransform, &identity, sizeof (XTransform)) == 0) + return CAIRO_STATUS_SUCCESS; + + return CAIRO_INT_STATUS_UNSUPPORTED; } + XRenderSetPictureTransform (surface->dpy, surface->picture, &xtransform); + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_xlib_surface_set_filter (void *abstract_surface, cairo_filter_t filter) +_cairo_xlib_surface_set_filter (cairo_xlib_surface_t *surface, + cairo_filter_t filter) { - cairo_xlib_surface_t *surface = abstract_surface; char *render_filter; - if (!(surface->picture - && CAIRO_SURFACE_RENDER_HAS_FILTERS(surface))) + if (!surface->picture) return CAIRO_STATUS_SUCCESS; + + if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) + { + if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) + return CAIRO_STATUS_SUCCESS; + + return CAIRO_INT_STATUS_UNSUPPORTED; + } switch (filter) { case CAIRO_FILTER_FAST: @@ -377,11 +535,10 @@ _cairo_xlib_surface_set_filter (void *abstract_surface, cairo_filter_t filter) } static cairo_status_t -_cairo_xlib_surface_set_repeat (void *abstract_surface, int repeat) +_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, int repeat) { - cairo_xlib_surface_t *surface = abstract_surface; - unsigned long mask; XRenderPictureAttributes pa; + unsigned long mask; if (!surface->picture) return CAIRO_STATUS_SUCCESS; @@ -394,33 +551,32 @@ _cairo_xlib_surface_set_repeat (void *abstract_surface, int repeat) return CAIRO_STATUS_SUCCESS; } -static cairo_xlib_surface_t * -_cairo_xlib_surface_clone_similar (cairo_surface_t *src, - cairo_xlib_surface_t *template, - cairo_format_t format, - int depth) +static cairo_int_status_t +_cairo_xlib_surface_set_attributes (cairo_xlib_surface_t *surface, + cairo_surface_attributes_t *attributes) { - cairo_xlib_surface_t *clone; - cairo_image_surface_t *src_image; + cairo_int_status_t status; - src_image = _cairo_surface_get_image (src); - - clone = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_similar (template, format, 0, - src_image->width, - src_image->height); - if (clone == NULL) - return NULL; - - _cairo_xlib_surface_set_filter (clone, cairo_surface_get_filter(src)); - - _cairo_xlib_surface_set_image (clone, src_image); - - _cairo_xlib_surface_set_matrix (clone, &(src_image->base.matrix)); + status = _cairo_xlib_surface_set_matrix (surface, &attributes->matrix); + if (status) + return status; + + switch (attributes->extend) { + case CAIRO_EXTEND_NONE: + _cairo_xlib_surface_set_repeat (surface, 0); + break; + case CAIRO_EXTEND_REPEAT: + _cairo_xlib_surface_set_repeat (surface, 1); + break; + case CAIRO_EXTEND_REFLECT: + return CAIRO_INT_STATUS_UNSUPPORTED; + } - cairo_surface_destroy (&src_image->base); + status = _cairo_xlib_surface_set_filter (surface, attributes->filter); + if (status) + return status; - return clone; + return CAIRO_STATUS_SUCCESS; } static int @@ -462,8 +618,8 @@ _render_operator (cairo_operator_t operator) static cairo_int_status_t _cairo_xlib_surface_composite (cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, @@ -474,73 +630,66 @@ _cairo_xlib_surface_composite (cairo_operator_t operator, unsigned int width, unsigned int height) { - cairo_xlib_surface_t *dst = abstract_dst; - cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) generic_src; - cairo_xlib_surface_t *mask = (cairo_xlib_surface_t *) generic_mask; - cairo_xlib_surface_t *src_clone = NULL; - cairo_xlib_surface_t *mask_clone = NULL; - XGCValues gc_values; - int src_x_off, src_y_off, dst_x_off, dst_y_off; + cairo_surface_attributes_t src_attr, mask_attr; + cairo_xlib_surface_t *dst = abstract_dst; + cairo_xlib_surface_t *src; + cairo_xlib_surface_t *mask; + cairo_int_status_t status; if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; - if (generic_src->backend != dst->base.backend || src->dpy != dst->dpy) { - src_clone = _cairo_xlib_surface_clone_similar (generic_src, dst, - CAIRO_FORMAT_ARGB32, 32); - if (!src_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; - src = src_clone; - } - if (generic_mask && (generic_mask->backend != dst->base.backend || mask->dpy != dst->dpy)) { - mask_clone = _cairo_xlib_surface_clone_similar (generic_mask, dst, - CAIRO_FORMAT_A8, 8); - if (!mask_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; - mask = mask_clone; - } - - if (operator == CAIRO_OPERATOR_SRC - && !mask - && _cairo_matrix_is_integer_translation(&(src->base.matrix), - &src_x_off, &src_y_off) - && _cairo_matrix_is_integer_translation(&(dst->base.matrix), - &dst_x_off, &dst_y_off)) { - /* Fast path for copying "raw" areas. */ - _cairo_xlib_surface_ensure_gc (dst); - XGetGCValues(dst->dpy, dst->gc, GCGraphicsExposures, &gc_values); - XSetGraphicsExposures(dst->dpy, dst->gc, False); - XCopyArea(dst->dpy, - src->drawable, - dst->drawable, - dst->gc, - src_x + src_x_off, - src_y + src_y_off, - width, height, - dst_x + dst_x_off, - dst_y + dst_y_off); - XSetGraphicsExposures(dst->dpy, dst->gc, gc_values.graphics_exposures); - - } else { - XRenderComposite (dst->dpy, - _render_operator (operator), - src->picture, - mask ? mask->picture : 0, - dst->picture, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); + status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, + &dst->base, + src_x, src_y, + mask_x, mask_y, + width, height, + (cairo_surface_t **) &src, + (cairo_surface_t **) &mask, + &src_attr, &mask_attr); + if (status) + return status; + + status = _cairo_xlib_surface_set_attributes (src, &src_attr); + if (CAIRO_OK (status)) + { + if (mask) + { + status = _cairo_xlib_surface_set_attributes (mask, &mask_attr); + if (CAIRO_OK (status)) + XRenderComposite (dst->dpy, + _render_operator (operator), + src->picture, + mask->picture, + dst->picture, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + mask_x + mask_attr.x_offset, + mask_y + mask_attr.y_offset, + dst_x, dst_y, + width, height); + } + else + { + XRenderComposite (dst->dpy, + _render_operator (operator), + src->picture, + 0, + dst->picture, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + } } - /* XXX: This is messed up. If I can xlib_surface_create, then I - should be able to xlib_surface_destroy. */ - if (src_clone) - cairo_surface_destroy (&src_clone->base); - if (mask_clone) - cairo_surface_destroy (&mask_clone->base); + if (mask) + _cairo_pattern_release_surface (&dst->base, &mask->base, &mask_attr); + + _cairo_pattern_release_surface (&dst->base, &src->base, &src_attr); - return CAIRO_STATUS_SUCCESS; + return status; } static cairo_int_status_t @@ -572,41 +721,59 @@ _cairo_xlib_surface_fill_rectangles (void *abstract_surface, static cairo_int_status_t _cairo_xlib_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *generic_src, + cairo_pattern_t *pattern, void *abstract_dst, - int xSrc, - int ySrc, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps) { - cairo_xlib_surface_t *dst = abstract_dst; - cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) generic_src; - cairo_xlib_surface_t *src_clone = NULL; + cairo_surface_attributes_t attributes; + cairo_xlib_surface_t *dst = abstract_dst; + cairo_xlib_surface_t *src; + cairo_int_status_t status; + int render_reference_x, render_reference_y; + int render_src_x, render_src_y; if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; - - if (generic_src->backend != dst->base.backend || src->dpy != dst->dpy) { - src_clone = _cairo_xlib_surface_clone_similar (generic_src, dst, - CAIRO_FORMAT_ARGB32, 32); - if (!src_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; - src = src_clone; + + status = _cairo_pattern_acquire_surface (pattern, &dst->base, + src_x, src_y, width, height, + (cairo_surface_t **) &src, + &attributes); + if (status) + return status; + + if (traps[0].left.p1.y < traps[0].left.p2.y) { + render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); + render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); + } else { + render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); + render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); } - /* XXX: The XTrapezoid cast is evil and needs to go away somehow. */ - XRenderCompositeTrapezoids (dst->dpy, - _render_operator (operator), - src->picture, dst->picture, - XRenderFindStandardFormat (dst->dpy, PictStandardA8), - xSrc, ySrc, (XTrapezoid *) traps, num_traps); + render_src_x = src_x + render_reference_x - dst_x; + render_src_y = src_y + render_reference_y - dst_y; - /* XXX: This is messed up. If I can xlib_surface_create, then I - should be able to xlib_surface_destroy. */ - if (src_clone) - cairo_surface_destroy (&src_clone->base); + /* XXX: The XTrapezoid cast is evil and needs to go away somehow. */ + status = _cairo_xlib_surface_set_attributes (src, &attributes); + if (CAIRO_OK (status)) + XRenderCompositeTrapezoids (dst->dpy, + _render_operator (operator), + src->picture, dst->picture, + XRenderFindStandardFormat (dst->dpy, PictStandardA8), + render_src_x + attributes.x_offset, + render_src_y + attributes.y_offset, + (XTrapezoid *) traps, num_traps); + + _cairo_pattern_release_surface (&dst->base, &src->base, &attributes); - return CAIRO_STATUS_SUCCESS; + return status; } static cairo_int_status_t @@ -685,22 +852,17 @@ _cairo_xlib_surface_set_clip_region (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_xlib_surface_create_pattern (void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *extents) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - static cairo_status_t -_cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, +_cairo_xlib_surface_show_glyphs (cairo_font_t *font, cairo_operator_t operator, - cairo_surface_t *source, + cairo_pattern_t *pattern, void *abstract_surface, int source_x, int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, const cairo_glyph_t *glyphs, int num_glyphs); @@ -708,18 +870,17 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = { _cairo_xlib_surface_create_similar, _cairo_xlib_surface_destroy, _cairo_xlib_surface_pixels_per_inch, - _cairo_xlib_surface_get_image, - _cairo_xlib_surface_set_image, - _cairo_xlib_surface_set_matrix, - _cairo_xlib_surface_set_filter, - _cairo_xlib_surface_set_repeat, + _cairo_xlib_surface_acquire_source_image, + _cairo_xlib_surface_release_source_image, + _cairo_xlib_surface_acquire_dest_image, + _cairo_xlib_surface_release_dest_image, + _cairo_xlib_surface_clone_similar, _cairo_xlib_surface_composite, _cairo_xlib_surface_fill_rectangles, _cairo_xlib_surface_composite_trapezoids, _cairo_xlib_surface_copy_page, _cairo_xlib_surface_show_page, _cairo_xlib_surface_set_clip_region, - _cairo_xlib_surface_create_pattern, _cairo_xlib_surface_show_glyphs }; @@ -827,6 +988,7 @@ typedef struct { cairo_glyph_cache_key_t key; Glyph glyph; XGlyphInfo info; + int refcount; } glyphset_cache_entry_t; static Glyph @@ -854,17 +1016,18 @@ _xlib_glyphset_cache_create_entry (void *cache, _cairo_lock_global_image_glyph_cache (); im_cache = _cairo_get_global_image_glyph_cache (); - if (g == NULL || v == NULL ||g == NULL || im_cache == NULL) { + if (g == NULL || v == NULL || im_cache == NULL) { _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_NO_MEMORY; } - status = _cairo_cache_lookup (im_cache, key, (void **) (&im)); + status = _cairo_cache_lookup (im_cache, key, (void **) (&im), NULL); if (status != CAIRO_STATUS_SUCCESS || im == NULL) { _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_NO_MEMORY; } + v->refcount = 1; v->key = *k; _cairo_unscaled_font_reference (v->key.unscaled); @@ -925,6 +1088,12 @@ _xlib_glyphset_cache_create_entry (void *cache, return CAIRO_STATUS_SUCCESS; } +static void +_glyphset_cache_entry_reference (glyphset_cache_entry_t *e) +{ + e->refcount++; +} + static void _xlib_glyphset_cache_destroy_cache (void *cache) { @@ -940,6 +1109,9 @@ _xlib_glyphset_cache_destroy_entry (void *cache, void *entry) g = (glyphset_cache_t *) cache; v = (glyphset_cache_entry_t *) entry; + if (--v->refcount > 0) + return; + _cairo_unscaled_font_destroy (v->key.unscaled); XRenderFreeGlyphs (g->display, g->glyphset, &(v->glyph), 1); free (v); @@ -1014,8 +1186,7 @@ _get_glyphset_cache (Display *d) #define N_STACK_BUF 1024 static cairo_status_t -_cairo_xlib_surface_show_glyphs32 (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, +_cairo_xlib_surface_show_glyphs32 (cairo_font_t *font, cairo_operator_t operator, glyphset_cache_t *g, cairo_glyph_cache_key_t *key, @@ -1092,8 +1263,7 @@ _cairo_xlib_surface_show_glyphs32 (cairo_unscaled_font_t *font, static cairo_status_t -_cairo_xlib_surface_show_glyphs16 (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, +_cairo_xlib_surface_show_glyphs16 (cairo_font_t *font, cairo_operator_t operator, glyphset_cache_t *g, cairo_glyph_cache_key_t *key, @@ -1169,10 +1339,9 @@ _cairo_xlib_surface_show_glyphs16 (cairo_unscaled_font_t *font, } static cairo_status_t -_cairo_xlib_surface_show_glyphs8 (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, +_cairo_xlib_surface_show_glyphs8 (cairo_font_t *font, cairo_operator_t operator, - glyphset_cache_t *g, + glyphset_cache_t *g, cairo_glyph_cache_key_t *key, cairo_xlib_surface_t *src, cairo_xlib_surface_t *self, @@ -1247,27 +1416,44 @@ _cairo_xlib_surface_show_glyphs8 (cairo_unscaled_font_t *font, static cairo_status_t -_cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, +_cairo_xlib_surface_show_glyphs (cairo_font_t *font, cairo_operator_t operator, - cairo_surface_t *source, + cairo_pattern_t *pattern, void *abstract_surface, int source_x, int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, const cairo_glyph_t *glyphs, int num_glyphs) { + cairo_surface_attributes_t attributes; + cairo_int_status_t status; unsigned int elt_size; cairo_xlib_surface_t *self = abstract_surface; - cairo_image_surface_t *tmp = NULL; - cairo_xlib_surface_t *src = NULL; + cairo_xlib_surface_t *src; glyphset_cache_t *g; - cairo_status_t status; cairo_glyph_cache_key_t key; glyphset_cache_entry_t **entries; glyphset_cache_entry_t *stack_entries [N_STACK_BUF]; int i; + if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (self)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_pattern_acquire_surface (pattern, &self->base, + source_x, source_y, width, height, + (cairo_surface_t **) &src, + &attributes); + if (status) + return status; + + status = _cairo_xlib_surface_set_attributes (src, &attributes); + if (status) + goto FAIL; + /* Acquire an entry array of suitable size. */ if (num_glyphs < N_STACK_BUF) { entries = stack_entries; @@ -1278,26 +1464,6 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, goto FAIL; } - /* prep the source surface. */ - if (source->backend == self->base.backend) { - src = (cairo_xlib_surface_t *) source; - - } else { - tmp = _cairo_surface_get_image (source); - if (tmp == NULL) - goto FREE_ENTRIES; - - src = (cairo_xlib_surface_t *) - _cairo_surface_create_similar_scratch (&self->base, self->format, 1, - tmp->width, tmp->height); - - if (src == NULL) - goto FREE_TMP; - - if (_cairo_surface_set_image (&(src->base), tmp) != CAIRO_STATUS_SUCCESS) - goto FREE_SRC; - } - _lock_xlib_glyphset_caches (); g = _get_glyphset_cache (self->dpy); if (g == NULL) @@ -1305,15 +1471,23 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, /* Work out the index size to use. */ elt_size = 8; - key.scale = *scale; - key.unscaled = font; + _cairo_font_get_glyph_cache_key (font, &key); for (i = 0; i < num_glyphs; ++i) { key.index = glyphs[i].index; - status = _cairo_cache_lookup (&g->base, &key, (void **) (&entries[i])); + status = _cairo_cache_lookup (&g->base, &key, (void **) (&entries[i]), NULL); if (status != CAIRO_STATUS_SUCCESS || entries[i] == NULL) goto UNLOCK; + /* Referencing the glyph entries we use prevents them from + * being freed if lookup of later entries causes them to + * be ejected from the cache. It would be more efficient + * (though more complex) to prevent them from being ejected + * from the cache at all, so they could get reused later + * in the same string. + */ + _glyphset_cache_entry_reference (entries[i]); + if (elt_size == 8 && entries[i]->glyph > 0xff) elt_size = 16; if (elt_size == 16 && entries[i]->glyph > 0xffff) { @@ -1325,43 +1499,32 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, /* Call the appropriate sub-function. */ if (elt_size == 8) - status = _cairo_xlib_surface_show_glyphs8 (font, scale, operator, g, &key, src, self, - source_x, source_y, + status = _cairo_xlib_surface_show_glyphs8 (font, operator, g, &key, src, self, + source_x + attributes.x_offset, + source_y + attributes.y_offset, glyphs, entries, num_glyphs); else if (elt_size == 16) - status = _cairo_xlib_surface_show_glyphs16 (font, scale, operator, g, &key, src, self, - source_x, source_y, + status = _cairo_xlib_surface_show_glyphs16 (font, operator, g, &key, src, self, + source_x + attributes.x_offset, + source_y + attributes.y_offset, glyphs, entries, num_glyphs); else - status = _cairo_xlib_surface_show_glyphs32 (font, scale, operator, g, &key, src, self, - source_x, source_y, + status = _cairo_xlib_surface_show_glyphs32 (font, operator, g, &key, src, self, + source_x + attributes.x_offset, + source_y + attributes.y_offset, glyphs, entries, num_glyphs); - _unlock_xlib_glyphset_caches (); - - if (tmp != NULL) { - cairo_surface_destroy (&(src->base)); - cairo_surface_destroy (&(tmp->base)); - } - - if (num_glyphs >= N_STACK_BUF) - free (entries); - - return status; + for (i = 0; i < num_glyphs; ++i) + _xlib_glyphset_cache_destroy_entry (g, entries[i]); UNLOCK: _unlock_xlib_glyphset_caches (); - FREE_SRC: - cairo_surface_destroy (&(src->base)); - - FREE_TMP: - cairo_surface_destroy (&(tmp->base)); - - FREE_ENTRIES: if (num_glyphs >= N_STACK_BUF) free (entries); FAIL: - return CAIRO_STATUS_NO_MEMORY; + _cairo_pattern_release_surface (&self->base, &src->base, &attributes); + + return status; } diff --git a/src/cairo-xlib.h b/src/cairo-xlib.h index 4f241b034..18db7b114 100644 --- a/src/cairo-xlib.h +++ b/src/cairo-xlib.h @@ -31,17 +31,20 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ -#include <cairo.h> - #ifndef CAIRO_XLIB_H #define CAIRO_XLIB_H + +#include <cairo.h> + #ifdef CAIRO_HAS_XLIB_SURFACE #include <X11/extensions/Xrender.h> +CAIRO_BEGIN_DECLS + /* XXX: This shold be renamed to cairo_set_target_xlib to match the * other backends */ void @@ -66,6 +69,8 @@ cairo_status_t cairo_xlib_surface_set_size (cairo_surface_t *surface, int width, int height); */ +CAIRO_END_DECLS + #endif /* CAIRO_HAS_XLIB_SURFACE */ #endif /* CAIRO_XLIB_H */ diff --git a/src/cairo.c b/src/cairo.c index 20d94938c..fd10b5cac 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ @@ -68,6 +68,18 @@ cairo_sane_state (cairo_t *cr) #endif +/** + * cairo_create: + * + * Creates a new #cairo_t with default values. The target + * surface must be set on the #cairo_t with cairo_set_target_surface(), + * or a backend-specific function like cairo_set_target_image() before + * drawing with the #cairo_t. + * + * Return value: a newly allocated #cairo_t with a reference + * count of 1. The initial reference count should be released + * with cairo_destroy() when you are done using the #cairo_t. + **/ cairo_t * cairo_create (void) { @@ -88,6 +100,14 @@ cairo_create (void) return cr; } +/** + * cairo_reference: + * @cr: a #cairo_t + * + * Increases the reference count on @cr by one. This prevents + * @cr from being destroyed until a matching call to cairo_destroy() + * is made. + **/ void cairo_reference (cairo_t *cr) { @@ -99,6 +119,14 @@ cairo_reference (cairo_t *cr) CAIRO_CHECK_SANITY (cr); } +/** + * cairo_destroy: + * @cr: a #cairo_t + * + * Decreases the reference count on @cr by one. If the result + * is zero, then @cr and all associated resources are freed. + * See cairo_destroy(). + **/ void cairo_destroy (cairo_t *cr) { @@ -117,6 +145,22 @@ cairo_destroy (cairo_t *cr) free (cr); } +/** + * cairo_save: + * @cr: a #cairo_t + * + * Makes a copy of the current state of @cr and saves it + * on an internal stack of saved states for @cr. When + * cairo_restore() is called, @cr will be restored to + * the saved state. Multiple calls to cairo_save() and + * cairo_restore() can be nested; each call to cairo_restore() + * restores the state from the matching paired cairo_save(). + * + * It isn't necessary to clear all saved states before + * a #cairo_t is freed. If the reference count of a #cairo_t + * drops to zero in response to a call to cairo_destroy(), + * any saved states will be freed along with the #cairo_t. + **/ void cairo_save (cairo_t *cr) { @@ -144,6 +188,14 @@ cairo_save (cairo_t *cr) } slim_hidden_def(cairo_save); +/** + * cairo_restore: + * @cr: a #cairo_t + * + * Restores @cr to the state saved by a preceding call to + * cairo_save() and removes that state from the stack of + * saved states. + **/ void cairo_restore (cairo_t *cr) { @@ -170,6 +222,20 @@ cairo_restore (cairo_t *cr) } slim_hidden_def(cairo_restore); +/** + * cairo_copy: + * @dest: a #cairo_t + * @src: another #cairo_t + * + * This function copies all current state information from src to + * dest. This includes the current point and path, the target surface, + * the transformation matrix, and so forth. + * + * The stack of states saved with cairo_save() is <emphasis>not</emphasis> + * not copied; nor are any saved states on @dest cleared. The + * operation only copies the current state of @src to the current + * state of @dest. + **/ void cairo_copy (cairo_t *dest, cairo_t *src) { @@ -216,6 +282,16 @@ cairo_pop_group (cairo_t *cr) } */ +/** + * cairo_set_target_surface: + * @cr: a #cairo_t + * @surface: a #cairo_surface_t + * + * Directs output for a #cairo_t to a given surface. The surface + * will be referenced by the #cairo_t, so you can immediately + * call cairo_surface_destroy() on it if you don't need to + * keep a reference to it around. + **/ void cairo_set_target_surface (cairo_t *cr, cairo_surface_t *surface) { @@ -228,6 +304,26 @@ cairo_set_target_surface (cairo_t *cr, cairo_surface_t *surface) } slim_hidden_def(cairo_set_target_surface); +/** + * cairo_set_target_image: + * @cr: a #cairo_t + * @data: a pointer to a buffer supplied by the application + * in which to write contents. + * @format: the format of pixels in the buffer + * @width: the width of the image to be stored in the buffer + * @height: the eight of the image to be stored in the buffer + * @stride: the number of bytes between the start of rows + * in the buffer. Having this be specified separate from @width + * allows for padding at the end of rows, or for writing + * to a subportion of a larger image. + * + * Directs output for a #cairo_t to an in-memory image. The output + * buffer must be kept around until the #cairo_t is destroyed or set + * to to have a different target. The initial contents of @buffer + * will be used as the inital image contents; you must explicitely + * clear the buffer, using, for example, cairo_rectangle() and + * cairo_fill() if you want it cleared. + **/ void cairo_set_target_image (cairo_t *cr, char *data, @@ -268,6 +364,18 @@ cairo_set_operator (cairo_t *cr, cairo_operator_t op) CAIRO_CHECK_SANITY (cr); } +/** + * cairo_set_rgb_color: + * @cr: a #cairo_t + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * + * Sets a constant color for filling and stroking. This replaces any + * pattern set with cairo_set_pattern(). The color components are + * floating point numbers in the range 0 to 1. If the values passed in + * are outside that range, they will be clamped. + **/ void cairo_set_rgb_color (cairo_t *cr, double red, double green, double blue) { @@ -301,6 +409,19 @@ cairo_current_pattern (cairo_t *cr) return _cairo_gstate_current_pattern (cr->gstate); } +/** + * cairo_set_tolerance: + * @cr: a #cairo_t + * @tolerance: the tolerance, in device units (typically pixels) + * + * Sets the tolerance used when converting paths into trapezoids. + * Curved segments of the path will be subdivided until the maximum + * deviation between the original path and the polygonal approximation + * is less than @tolerance. The default value is 0.1. A larger + * value will give better performance, a smaller value, better + * appearance. (Reducing the value from the default value of 0.1 + * is unlikely to improve appearance significantly.) + **/ void cairo_set_tolerance (cairo_t *cr, double tolerance) { @@ -314,6 +435,17 @@ cairo_set_tolerance (cairo_t *cr, double tolerance) CAIRO_CHECK_SANITY (cr); } +/** + * cairo_set_alpha: + * @cr: a #cairo_t + * @alpha: the alpha value. 0 is transparent, 1 fully opaque. + * if the value is outside the range 0 to 1, it will be + * clamped to that range. + * + * Sets an overall alpha value used for stroking and filling. This + * value is multiplied with any alpha value coming from a gradient or + * image pattern. + **/ void cairo_set_alpha (cairo_t *cr, double alpha) { @@ -569,6 +701,39 @@ cairo_curve_to (cairo_t *cr, CAIRO_CHECK_SANITY (cr); } +/** + * cairo_arc: + * @cr: a Cairo context + * @xc: X position of the center of the arc + * @yc: Y position of the center of the arc + * @radius: the radius of the arc + * @angle1: the start angle, in radians + * @angle2: the end angle, in radians + * + * Adds an arc from @angle1 to @angle2 to the current path. If there + * is a current point, that point is connected to the start of the arc + * by a straight line segment. Angles are measured in radians with an + * angle of 0 along the X axis and an angle of %M_PI radians (90 + * degrees) along the Y axis, so with the default transformation + * matrix, positive angles are clockwise. (To convert from degrees to + * radians, use <literal>degrees * (M_PI / 180.)</literal>.) This + * function gives the arc in the direction of increasing angle; see + * cairo_arc_negative() to get the arc in the direction of decreasing + * angle. + * + * A full arc is drawn as a circle. To make an oval arc, you can scale + * the current transformation matrix by different amounts in the X and + * Y directions. For example, to draw a full oval in the box given + * by @x, @y, @width, @height: + + * <informalexample><programlisting> + * cairo_save (cr); + * cairo_translate (x + width / 2., y + height / 2.); + * cairo_scale (1. / (height / 2.), 1. / (width / 2.)); + * cairo_arc (cr, 0., 0., 1., 0., 2 * M_PI); + * cairo_restore (cr); + * </programlisting></informalexample> + **/ void cairo_arc (cairo_t *cr, double xc, double yc, @@ -586,6 +751,20 @@ cairo_arc (cairo_t *cr, CAIRO_CHECK_SANITY (cr); } +/** + * cairo_arc_negative: + * @cr: a Cairo context + * @xc: X position of the center of the arc + * @yc: Y position of the center of the arc + * @radius: the radius of the arc + * @angle1: the start angle, in radians + * @angle2: the end angle, in radians + * + * Adds an arc from @angle1 to @angle2 to the current path. The + * function behaves identically to cairo_arc() except that instead of + * giving the arc in the direction of increasing angle, it gives + * the arc in the direction of decreasing angle. + **/ void cairo_arc_negative (cairo_t *cr, double xc, double yc, @@ -744,7 +923,7 @@ cairo_show_page (cairo_t *cr) CAIRO_CHECK_SANITY (cr); } -int +cairo_bool_t cairo_in_stroke (cairo_t *cr, double x, double y) { int inside; @@ -842,6 +1021,20 @@ cairo_select_font (cairo_t *cr, CAIRO_CHECK_SANITY (cr); } +/** + * cairo_current_font: + * @cr: a #cairo_t + * + * Gets the current font object for a #cairo_t. If there is no current + * font object, because the font parameters, transform, or target + * surface has been changed since a font was last used, a font object + * will be created and stored in in the #cairo_t. + * + * Return value: the current font object. Can return %NULL + * on out-of-memory or if the context is already in + * an error state. This object is owned by Cairo. To keep + * a reference to it, you must call cairo_font_reference(). + **/ cairo_font_t * cairo_current_font (cairo_t *cr) { @@ -869,6 +1062,22 @@ cairo_current_font_extents (cairo_t *cr, } +/** + * cairo_set_font: + * @cr: a #cairo_t + * @font: a #cairo_font_t, or %NULL to unset any previously set font. + * + * Replaces the current #cairo_font_t object in the #cairo_t with + * @font. The replaced font in the #cairo_t will be destroyed if there + * are no other references to it. Since a #cairo_font_t is specific to + * a particular output device and size, changing the transformation, + * font transformation, or target surfaces of a #cairo_t will clear + * any previously set font. Setting the font using cairo_set_font() is + * exclusive with the simple font selection API provided by + * cairo_select_font(). The size and transformation set by + * cairo_scale_font() and cairo_transform_font() are ignored unless + * they were taken into account when creating @font. + **/ void cairo_set_font (cairo_t *cr, cairo_font_t *font) { @@ -914,6 +1123,16 @@ cairo_text_extents (cairo_t *cr, if (cr->status) return; + if (utf8 == NULL) { + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; + return; + } + cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8, &glyphs, &nglyphs); CAIRO_CHECK_SANITY (cr); @@ -1205,6 +1424,8 @@ cairo_status_string (cairo_t *cr) return "no target surface has been set"; case CAIRO_STATUS_NULL_POINTER: return "NULL pointer"; + case CAIRO_STATUS_INVALID_STRING: + return "input string not valid UTF-8"; } return "<unknown error status>"; diff --git a/src/cairo.h b/src/cairo.h index b7bcc1dbb..03e063242 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -31,23 +31,83 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #ifndef CAIRO_H #define CAIRO_H +#ifdef __cplusplus +# define CAIRO_BEGIN_DECLS extern "C" { +# define CAIRO_END_DECLS } +#else +# define CAIRO_BEGIN_DECLS +# define CAIRO_END_DECLS +#endif + #include <cairo-features.h> #include <pixman.h> +CAIRO_BEGIN_DECLS + +/** + * cairo_bool_t: + * + * #cairo_bool_t is used for boolean values. Returns of type + * #cairo_bool_t will always be either 0 or 1, but testing against + * these values explicitely is not encouraged; just use the + * value as a boolean condition. + * + * <informalexample><programlisting> + * if (cairo_in_stroke (cr, x, y)) { + * /<!-- -->* do something *<!-- -->/ + * } + * </programlisting></informalexample> + */ +typedef int cairo_bool_t; + +/** + * cairo_t: + * + * A #cairo_t contains the current state of the rendering device, + * including coordinates of yet to be drawn shapes. + **/ typedef struct _cairo cairo_t; + +/** + * cairo_surface_t: + * + * A #cairo_surface_t represents an image, either as the destination + * of a drawing operation or as source when drawing onto another + * surface. There are different subtypes of cairo_surface_t for + * different drawing backends; for example, cairo_image_surface_create() + * creates a bitmap image in memory. + * + * Memory management of #cairo_surface_t is done with + * cairo_surface_reference() and cairo_surface_destroy(). + */ typedef struct _cairo_surface cairo_surface_t; + +/** + * cairo_matrix_t: + * + * A #cairo_matrix_t holds an affine transformation, such as a scale, + * rotation, or shear, or a combination of those. + **/ typedef struct _cairo_matrix cairo_matrix_t; typedef struct _cairo_pattern cairo_pattern_t; -#ifdef __cplusplus -extern "C" { -#endif +typedef enum cairo_status { + CAIRO_STATUS_SUCCESS = 0, + CAIRO_STATUS_NO_MEMORY, + CAIRO_STATUS_INVALID_RESTORE, + CAIRO_STATUS_INVALID_POP_GROUP, + CAIRO_STATUS_NO_CURRENT_POINT, + CAIRO_STATUS_INVALID_MATRIX, + CAIRO_STATUS_NO_TARGET_SURFACE, + CAIRO_STATUS_NULL_POINTER, + CAIRO_STATUS_INVALID_STRING +} cairo_status_t; /* Functions for manipulating state objects */ cairo_t * @@ -81,6 +141,28 @@ cairo_pop_group (cairo_t *cr); void cairo_set_target_surface (cairo_t *cr, cairo_surface_t *surface); +/** + * cairo_format_t + * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with + * alpha in the upper 8 bits, then red, then green, then blue. + * The 32-bit quanties are stored native-endian. Pre-multiplied + * alpha is used. (That is, 50% transparent red is 0x80800000, + * not 0x80ff0000.) + * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with + * the upper 8 bits unused. Red, Green, and Blue are stored + * in the remaining 24 bits in that order. + * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding + * an alpha value. + * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding + * an alpha value. Pixels are packed together into 32-bit + * quantities. The ordering of the bits matches the + * endianess of the platform. On a big-endian machine, the + * first pixel is in the uppermost bit, on a little-endian + * machine the first pixel is in the least-significant bit. + * + * #cairo_format_t is used to identify the memory format of + * image data. + */ typedef enum cairo_format { CAIRO_FORMAT_ARGB32, CAIRO_FORMAT_RGB24, @@ -88,6 +170,7 @@ typedef enum cairo_format { CAIRO_FORMAT_A1 } cairo_format_t; +/* XXX: Need to add cairo_set_target_image_data */ void cairo_set_target_image (cairo_t *cr, char *data, @@ -149,6 +232,27 @@ cairo_set_alpha (cairo_t *cr, double alpha); void cairo_set_tolerance (cairo_t *cr, double tolerance); +/** + * cairo_fill_rule_t + * @CAIRO_FILL_RULE_WINDING: If the path crosses the ray from + * left-to-right, counts +1. If the path crosses the ray + * from right to left, counts -1. (Left and right are determined + * from the perspective of looking along the ray from the starting + * point.) If the total count is non-zero, the point will be filled. + * @CAIRO_FILL_RULE_EVEN_ODD: Counts the total number of + * intersections, without regard to the orientation of the contour. If + * the total number of intersections is odd, the point will be + * filled. + * + * #cairo_fill_rule_t is used to select how paths are filled. For both + * fill rules, whether or not a point is included in the fill is + * determined by taking a ray from that point to infinity and looking + * at intersections with the path. The ray can be in any direction, + * as long as it doesn't pass through the end point of a segment + * or have a tricky intersection such as intersecting tangent to the path. + * (Note that filling is not actually implemented in this way. This + * is just a description of the rule that is applied.) + **/ typedef enum cairo_fill_rule { CAIRO_FILL_RULE_WINDING, CAIRO_FILL_RULE_EVEN_ODD @@ -160,6 +264,15 @@ cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule); void cairo_set_line_width (cairo_t *cr, double width); + +/** + * cairo_line_cap_t + * @CAIRO_LINE_CAP_BUTT: start(stop) the line exactly at the start(end) point + * @CAIRO_LINE_CAP_ROUND: use a round ending, the center of the circle is the end point + * @CAIRO_LINE_CAP_SQUARE: use squared ending, the center of the square is the end point + * + * enumeration for style of line-endings + **/ typedef enum cairo_line_cap { CAIRO_LINE_CAP_BUTT, CAIRO_LINE_CAP_ROUND, @@ -303,10 +416,10 @@ void cairo_show_page (cairo_t *cr); /* Insideness testing */ -int +cairo_bool_t cairo_in_stroke (cairo_t *cr, double x, double y); -int +cairo_bool_t cairo_in_fill (cairo_t *cr, double x, double y); /* Rectangular extents */ @@ -330,14 +443,75 @@ cairo_clip (cairo_t *cr); /* Font/Text functions */ +/** + * cairo_font_t: + * + * A #cairo_font_t is a font scaled to a particular size and device + * resolution. A font can be set on a #cairo_t by using + * cairo_set_font() assuming that the current transformation and + * target surface of the #cairo_t match that for which the + * #cairo_font_t was created. The effect of using a mismatched + * #cairo_font_t will be incorrect font metrics. + */ typedef struct _cairo_font cairo_font_t; +/** + * cairo_glyph_t: + * @index: glyph index in the font. The exact interpretation of the + * glyph index depends on the font technology being used. + * @x: the offset in the X direction between the origin used for + * drawing or measuring the string and the origin of this glyph. + * @y: the offset in the Y direction between the origin used for + * drawing or measuring the string and the origin of this glyph. + * + * The #cairo_glyph_t structure holds information about a single glyph + * when drawing or measuring text. A font is (in simple terms) a + * collection of shapes used to draw text. A glyph is one of these + * shapes. There can be multiple glyphs for a single character + * (alternates to be used in different contexts, for example), or a + * glyph can be a <firstterm>ligature</firstterm> of multiple + * characters. Cairo doesn't expose any way of converting input text + * into glyphs, so in order to use the Cairo interfaces that take + * arrays of glyphs, you must directly access the appropriate + * underlying font system. + * + * Note that the offsets given by @x and @y are not cumulative. When + * drawing or measuring text, each glyph is individually positioned + * with respect to the overall origin + **/ typedef struct { unsigned long index; double x; double y; } cairo_glyph_t; +/** + * cairo_text_extents_t: + * @x_bearing: the horizontal distance from the origin to the + * leftmost part of the glyphs as drawn. Positive if the + * glyphs lie entirely to the right of the origin. + * @y_bearing: the vertical distance from the origin to the + * topmost part of the glyphs as drawn. Positive only if the + * glyphs lie completely below the origin; will usually be + * negative. + * @width: width of the glyphs as drawn + * @height: height of the glyphs as drawn + * @x_advance:distance to advance in the X direction + * after drawing these glyphs + * @y_advance: distance to advance in the Y direction + * after drawing these glyphs. Will typically be zero except + * for vertical text layout as found in East-Asian languages. + * + * The #cairo_text_extents_t< structure stores the extents of a single + * glyph or a string of glyphs in user-space coordinates. Because text + * extents are in user-space coordinates, they don't scale along with + * the current transformation matrix. If you call + * <literal>cairo_scale(cr, 2.0, 2.0)</literal>, text will + * be drawn twice as big, but the reported text extents will not be + * doubled. They will change slightly due to hinting (so you can't + * assume that metrics are independent of the transformation matrix), + * but otherwise will remain unchanged. + */ typedef struct { double x_bearing; double y_bearing; @@ -422,13 +596,17 @@ cairo_font_reference (cairo_font_t *font); void cairo_font_destroy (cairo_font_t *font); -void -cairo_font_set_transform (cairo_font_t *font, - cairo_matrix_t *matrix); +cairo_status_t +cairo_font_extents (cairo_font_t *font, + cairo_matrix_t *font_matrix, + cairo_font_extents_t *extents); void -cairo_font_current_transform (cairo_font_t *font, - cairo_matrix_t *matrix); +cairo_font_glyph_extents (cairo_font_t *font, + cairo_matrix_t *font_matrix, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); /* Image functions */ @@ -521,17 +699,6 @@ cairo_current_path_flat (cairo_t *cr, /* Error status queries */ -typedef enum cairo_status { - CAIRO_STATUS_SUCCESS = 0, - CAIRO_STATUS_NO_MEMORY, - CAIRO_STATUS_INVALID_RESTORE, - CAIRO_STATUS_INVALID_POP_GROUP, - CAIRO_STATUS_NO_CURRENT_POINT, - CAIRO_STATUS_INVALID_MATRIX, - CAIRO_STATUS_NO_TARGET_SURFACE, - CAIRO_STATUS_NULL_POINTER -} cairo_status_t; - cairo_status_t cairo_status (cairo_t *cr); @@ -671,7 +838,7 @@ cairo_status_t cairo_matrix_set_identity (cairo_matrix_t *matrix); cairo_status_t -cairo_matrix_set_affine (cairo_matrix_t *cr, +cairo_matrix_set_affine (cairo_matrix_t *matrix, double a, double b, double c, double d, double tx, double ty); @@ -724,8 +891,6 @@ cairo_matrix_transform_point (cairo_matrix_t *matrix, double *x, double *y); #define cairo_get_status_string cairo_get_status_string_DEPRECATED_BY_cairo_status_string #endif -#ifdef __cplusplus -} -#endif +CAIRO_END_DECLS #endif /* CAIRO_H */ diff --git a/src/cairo_atsui_font.c b/src/cairo_atsui_font.c index 52cfc6bd8..cb4b1c5d7 100644 --- a/src/cairo_atsui_font.c +++ b/src/cairo_atsui_font.c @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2004 Calum Robinson + * Copyright © 2004 Calum Robinson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public diff --git a/src/cairo_cache.c b/src/cairo_cache.c index b097b609b..d1ad5a4e2 100644 --- a/src/cairo_cache.c +++ b/src/cairo_cache.c @@ -94,9 +94,9 @@ static const cairo_cache_arrangement_t cache_arrangements [] = { * a mostly-dead table. * * Generally you do not need to worry about freeing cache entries; the - * cache will expire entries randomly as it experiences memory pressure. - * There is currently no explicit entry-removing call, though one can be - * added easily. + * cache will expire entries randomly as it experiences memory pressure. + * If max_memory is set, entries are not expired, and must be explicitely + * removed. * * This table is open-addressed with double hashing. Each table size is a * prime chosen to be a little more than double the high water mark for a @@ -282,17 +282,51 @@ _load_factor (cairo_cache_t *cache) } #endif -static unsigned long -_random_live_entry (cairo_cache_t *cache) -{ - unsigned long idx; - assert(cache != NULL); - do { - idx = rand () % cache->arrangement->size; - } while (! LIVE_ENTRY_P(cache, idx)); - return idx; -} +/* Find a random in the cache matching the given predicate. We use the + * same algorithm as the probing algorithm to walk over the entries in + * the hash table in a pseudo-random order. Walking linearly would + * favor entries following gaps in the hash table. We could also + * call rand() repeatedly, which works well for almost-full tables, + * but degrades when the table is almost empty, or predicate + * returns false for most entries. + */ +static cairo_cache_entry_base_t ** +_random_entry (cairo_cache_t *cache, + int (*predicate)(void*)) +{ + cairo_cache_entry_base_t **probe; + unsigned long hash; + unsigned long table_size, i, idx, step; + + _cache_sane_state (cache); + + table_size = cache->arrangement->size; + hash = rand (); + idx = hash % table_size; + step = 0; + + for (i = 0; i < table_size; ++i) + { + assert(idx < table_size); + probe = cache->entries + idx; + if (LIVE_ENTRY_P(cache, idx) + && (!predicate || predicate (*probe))) + return probe; + + if (step == 0) { + step = hash % cache->arrangement->rehash; + if (step == 0) + step = 1; + } + + idx += step; + if (idx >= table_size) + idx -= table_size; + } + + return NULL; +} /* public API follows */ @@ -356,8 +390,9 @@ _cairo_cache_destroy (cairo_cache_t *cache) cairo_status_t _cairo_cache_lookup (cairo_cache_t *cache, - void *key, - void **entry_return) + void *key, + void **entry_return, + int *created_entry) { unsigned long idx; @@ -392,6 +427,8 @@ _cairo_cache_lookup (cairo_cache_t *cache, cache->hits++; #endif *entry_return = *slot; + if (created_entry) + *created_entry = 0; return status; } @@ -401,19 +438,18 @@ _cairo_cache_lookup (cairo_cache_t *cache, /* Build the new entry. */ status = cache->backend->create_entry (cache, key, - entry_return); + (void **)&new_entry); if (status != CAIRO_STATUS_SUCCESS) return status; - new_entry = (cairo_cache_entry_base_t *) (*entry_return); - /* Store the hash value in case the backend forgot. */ new_entry->hashcode = cache->backend->hash (cache, key); /* Make some entries die if we're under memory pressure. */ while (cache->live_entries > 0 && + cache->max_memory > 0 && ((cache->max_memory - cache->used_memory) < new_entry->memory)) { - idx = _random_live_entry (cache); + idx = _random_entry (cache, NULL) - cache->entries; assert (idx < cache->arrangement->size); _entry_destroy (cache, idx); } @@ -425,7 +461,6 @@ _cairo_cache_lookup (cairo_cache_t *cache, status = _resize_cache (cache, cache->live_entries + 1); if (status != CAIRO_STATUS_SUCCESS) { cache->backend->destroy_entry (cache, new_entry); - *entry_return = NULL; return status; } @@ -439,9 +474,38 @@ _cairo_cache_lookup (cairo_cache_t *cache, _cache_sane_state (cache); + *entry_return = new_entry; + if (created_entry) + *created_entry = 1; + return status; } +cairo_status_t +_cairo_cache_remove (cairo_cache_t *cache, + void *key) +{ + cairo_cache_entry_base_t **slot; + + _cache_sane_state (cache); + + /* See if we have an entry in the table already. */ + slot = _find_exact_live_entry_for (cache, key); + if (slot != NULL) + _entry_destroy (cache, slot - cache->entries); + + return CAIRO_STATUS_SUCCESS; +} + +void * +_cairo_cache_random_entry (cairo_cache_t *cache, + int (*predicate)(void*)) +{ + cairo_cache_entry_base_t **slot = _random_entry (cache, predicate); + + return slot ? *slot : NULL; +} + unsigned long _cairo_hash_string (const char *c) { diff --git a/src/cairo_color.c b/src/cairo_color.c index 899b1e3d5..f203d96cc 100644 --- a/src/cairo_color.c +++ b/src/cairo_color.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" @@ -67,7 +67,8 @@ _cairo_color_set_rgb (cairo_color_t *color, double red, double green, double blu } void -_cairo_color_get_rgb (cairo_color_t *color, double *red, double *green, double *blue) +_cairo_color_get_rgb (const cairo_color_t *color, + double *red, double *green, double *blue) { if (red) *red = color->red; diff --git a/src/cairo_fixed.c b/src/cairo_fixed.c index ee31718ef..a4faa1708 100644 --- a/src/cairo_fixed.c +++ b/src/cairo_fixed.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo_font.c b/src/cairo_font.c index f5fc0e981..529c1c7c3 100644 --- a/src/cairo_font.c +++ b/src/cairo_font.c @@ -1,6 +1,7 @@ /* cairo - a vector graphics library with display and print output * * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -31,293 +32,129 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> + * Graydon Hoare <graydon@redhat.com> + * Owen Taylor <otaylor@redhat.com> */ #include "cairoint.h" -/* First we implement a global font cache for named fonts. */ - -typedef struct { - cairo_cache_entry_base_t base; - const char *family; - cairo_font_slant_t slant; - cairo_font_weight_t weight; -} cairo_font_cache_key_t; - -typedef struct { - cairo_font_cache_key_t key; - cairo_unscaled_font_t *unscaled; -} cairo_font_cache_entry_t; - -static unsigned long -_font_cache_hash (void *cache, void *key) -{ - unsigned long hash; - cairo_font_cache_key_t *in; - in = (cairo_font_cache_key_t *) key; - - /* 1607 and 1451 are just a couple random primes. */ - hash = _cairo_hash_string (in->family); - hash += ((unsigned long) in->slant) * 1607; - hash += ((unsigned long) in->weight) * 1451; - return hash; -} - - -static int -_font_cache_keys_equal (void *cache, - void *k1, - void *k2) -{ - cairo_font_cache_key_t *a, *b; - a = (cairo_font_cache_key_t *) k1; - b = (cairo_font_cache_key_t *) k2; - - return (strcmp (a->family, b->family) == 0) - && (a->weight == b->weight) - && (a->slant == b->slant); -} - - -static cairo_status_t -_font_cache_create_entry (void *cache, - void *key, - void **return_value) -{ - const cairo_font_backend_t *backend = CAIRO_FONT_BACKEND_DEFAULT; - cairo_font_cache_key_t *k; - cairo_font_cache_entry_t *entry; - k = (cairo_font_cache_key_t *) key; - - /* XXX: The current freetype backend may return NULL, (for example - * if no fonts are installed), but I would like to guarantee that - * the toy API always returns at least *some* font, so I would - * like to build in some sort fo font here, (even a really lame, - * ugly one if necessary). */ - - entry = malloc (sizeof (cairo_font_cache_entry_t)); - if (entry == NULL) - goto FAIL; - - entry->key.slant = k->slant; - entry->key.weight = k->weight; - entry->key.family = strdup(k->family); - if (entry->key.family == NULL) - goto FREE_ENTRY; - - entry->unscaled = backend->create (k->family, k->slant, k->weight); - if (entry->unscaled == NULL) - goto FREE_FAMILY; - - /* Not sure how to measure backend font mem; use a simple count for now.*/ - entry->key.base.memory = 1; - *return_value = entry; - return CAIRO_STATUS_SUCCESS; - - FREE_FAMILY: - free ((void *) entry->key.family); - - FREE_ENTRY: - free (entry); - - FAIL: - return CAIRO_STATUS_NO_MEMORY; -} - -static void -_font_cache_destroy_entry (void *cache, - void *entry) -{ - cairo_font_cache_entry_t *e; - - e = (cairo_font_cache_entry_t *) entry; - _cairo_unscaled_font_destroy (e->unscaled); - free ((void *) e->key.family); - free (e); -} - -static void -_font_cache_destroy_cache (void *cache) -{ - free (cache); -} - -static const cairo_cache_backend_t cairo_font_cache_backend = { - _font_cache_hash, - _font_cache_keys_equal, - _font_cache_create_entry, - _font_cache_destroy_entry, - _font_cache_destroy_cache -}; - -static void -_lock_global_font_cache (void) -{ - /* FIXME: implement locking. */ -} - -static void -_unlock_global_font_cache (void) -{ - /* FIXME: implement locking. */ -} - -static cairo_cache_t * -_global_font_cache = NULL; - -static cairo_cache_t * -_get_global_font_cache (void) -{ - if (_global_font_cache == NULL) { - _global_font_cache = malloc (sizeof (cairo_cache_t)); - - if (_global_font_cache == NULL) - goto FAIL; - - if (_cairo_cache_init (_global_font_cache, - &cairo_font_cache_backend, - CAIRO_FONT_CACHE_NUM_FONTS_DEFAULT)) - goto FAIL; - } - - return _global_font_cache; - - FAIL: - if (_global_font_cache) - free (_global_font_cache); - _global_font_cache = NULL; - return NULL; -} - - /* Now the internal "unscaled + scale" font API */ -cairo_unscaled_font_t * -_cairo_unscaled_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) +cairo_private cairo_status_t +_cairo_font_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight, + cairo_font_scale_t *sc, + cairo_font_t **font) { - cairo_cache_t * cache; - cairo_font_cache_key_t key; - cairo_font_cache_entry_t *font; - cairo_status_t status; - - _lock_global_font_cache (); - cache = _get_global_font_cache (); - if (cache == NULL) { - _unlock_global_font_cache (); - return NULL; - } - - key.family = family; - key.slant = slant; - key.weight = weight; - - status = _cairo_cache_lookup (cache, &key, (void **) &font); - if (status) { - _unlock_global_font_cache (); - return NULL; - } + const cairo_font_backend_t *backend = CAIRO_FONT_BACKEND_DEFAULT; - _cairo_unscaled_font_reference (font->unscaled); - _unlock_global_font_cache (); - return font->unscaled; + return backend->create (family, slant, weight, sc, font); } void -_cairo_font_init (cairo_font_t *scaled, - cairo_font_scale_t *scale, - cairo_unscaled_font_t *unscaled) +_cairo_font_init (cairo_font_t *font, + cairo_font_scale_t *scale, + const cairo_font_backend_t *backend) { - scaled->scale = *scale; - scaled->unscaled = unscaled; - scaled->refcount = 1; + font->scale = *scale; + font->refcount = 1; + font->backend = backend; } -cairo_status_t -_cairo_unscaled_font_init (cairo_unscaled_font_t *font, - const cairo_font_backend_t *backend) +void +_cairo_unscaled_font_init (cairo_unscaled_font_t *font, + const cairo_font_backend_t *backend) { font->refcount = 1; font->backend = backend; - return CAIRO_STATUS_SUCCESS; } - cairo_status_t -_cairo_unscaled_font_text_to_glyphs (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs) +_cairo_font_text_to_glyphs (cairo_font_t *font, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) { - return font->backend->text_to_glyphs (font, scale, utf8, glyphs, num_glyphs); + return font->backend->text_to_glyphs (font, utf8, glyphs, num_glyphs); } cairo_status_t -_cairo_unscaled_font_glyph_extents (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) +_cairo_font_glyph_extents (cairo_font_t *font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) { - return font->backend->glyph_extents(font, scale, glyphs, num_glyphs, extents); + return font->backend->glyph_extents(font, glyphs, num_glyphs, extents); } cairo_status_t -_cairo_unscaled_font_glyph_bbox (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) +_cairo_font_glyph_bbox (cairo_font_t *font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) { - return font->backend->glyph_bbox (font, scale, glyphs, num_glyphs, bbox); + return font->backend->glyph_bbox (font, glyphs, num_glyphs, bbox); } cairo_status_t -_cairo_unscaled_font_show_glyphs (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_font_show_glyphs (cairo_font_t *font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs) { cairo_status_t status; if (surface->backend->show_glyphs != NULL) { - status = surface->backend->show_glyphs (font, scale, operator, source, - surface, source_x, source_y, + status = surface->backend->show_glyphs (font, operator, pattern, + surface, + source_x, source_y, + dest_x, dest_y, + width, height, glyphs, num_glyphs); if (status == CAIRO_STATUS_SUCCESS) return status; } /* Surface display routine either does not exist or failed. */ - return font->backend->show_glyphs (font, scale, operator, source, - surface, source_x, source_y, + return font->backend->show_glyphs (font, operator, pattern, + surface, + source_x, source_y, + dest_x, dest_y, + width, height, glyphs, num_glyphs); } cairo_status_t -_cairo_unscaled_font_glyph_path (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) +_cairo_font_glyph_path (cairo_font_t *font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path) +{ + return font->backend->glyph_path (font, glyphs, num_glyphs, path); +} + +void +_cairo_font_get_glyph_cache_key (cairo_font_t *font, + cairo_glyph_cache_key_t *key) { - return font->backend->glyph_path (font, scale, glyphs, num_glyphs, path); + font->backend->get_glyph_cache_key (font, key); } cairo_status_t -_cairo_unscaled_font_font_extents (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - cairo_font_extents_t *extents) +_cairo_font_font_extents (cairo_font_t *font, + cairo_font_extents_t *extents) { - return font->backend->font_extents(font, scale, extents); + return font->backend->font_extents (font, extents); } void @@ -332,8 +169,7 @@ _cairo_unscaled_font_destroy (cairo_unscaled_font_t *font) if (--(font->refcount) > 0) return; - if (font->backend) - font->backend->destroy (font); + font->backend->destroy_unscaled_font (font); } @@ -352,37 +188,154 @@ cairo_font_destroy (cairo_font_t *font) if (--(font->refcount) > 0) return; - if (font->unscaled) - _cairo_unscaled_font_destroy (font->unscaled); - - free (font); + font->backend->destroy_font (font); } -void -cairo_font_set_transform (cairo_font_t *font, - cairo_matrix_t *matrix) +/** + * cairo_font_extents: + * @font: a #cairo_font_t + * @font_matrix: the font transformation for which this font was + * created. (See cairo_transform_font()). This is needed + * properly convert the metrics from the font into user space. + * @extents: a #cairo_font_extents_t which to store the retrieved extents. + * + * Gets the metrics for a #cairo_font_t. + * + * Return value: %CAIRO_STATUS_SUCCESS on success. Otherwise, an + * error such as %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_status_t +cairo_font_extents (cairo_font_t *font, + cairo_matrix_t *font_matrix, + cairo_font_extents_t *extents) { - double dummy; - cairo_matrix_get_affine (matrix, - &font->scale.matrix[0][0], - &font->scale.matrix[0][1], - &font->scale.matrix[1][0], - &font->scale.matrix[1][1], - &dummy, &dummy); + cairo_int_status_t status; + double font_scale_x, font_scale_y; + + status = _cairo_font_font_extents (font, extents); + + if (!CAIRO_OK (status)) + return status; + + _cairo_matrix_compute_scale_factors (font_matrix, + &font_scale_x, &font_scale_y, + /* XXX */ 1); + + /* + * The font responded in unscaled units, scale by the font + * matrix scale factors to get to user space + */ + + extents->ascent *= font_scale_y; + extents->descent *= font_scale_y; + extents->height *= font_scale_y; + extents->max_x_advance *= font_scale_x; + extents->max_y_advance *= font_scale_y; + + return status; } +/** + * cairo_font_glyph_extents: + * @font: a #cairo_font_t + * @font_matrix: the font transformation for which this font was + * created. (See cairo_transform_font()). This is needed + * properly convert the metrics from the font into user space. + * @glyphs: an array of glyph IDs with X and Y offsets. + * @num_glyphs: the number of glyphs in the @glyphs array + * @extents: a #cairo_text_extents_t which to store the retrieved extents. + * + * cairo_font_glyph_extents() gets the overall metrics for a string of + * glyphs. The X and Y offsets in @glyphs are taken from an origin of 0,0. + **/ void -cairo_font_current_transform (cairo_font_t *font, - cairo_matrix_t *matrix) +cairo_font_glyph_extents (cairo_font_t *font, + cairo_matrix_t *font_matrix, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) { - cairo_matrix_set_affine (matrix, - font->scale.matrix[0][0], - font->scale.matrix[0][1], - font->scale.matrix[1][0], - font->scale.matrix[1][1], - 0, 0); -} + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_glyph_t origin_glyph; + cairo_text_extents_t origin_extents; + int i; + double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0; + double x_pos = 0.0, y_pos = 0.0; + int set = 0; + + if (!num_glyphs) + { + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; + + return; + } + for (i = 0; i < num_glyphs; i++) + { + double x, y; + double wm, hm; + + origin_glyph = glyphs[i]; + origin_glyph.x = 0.0; + origin_glyph.y = 0.0; + status = _cairo_font_glyph_extents (font, + &origin_glyph, 1, + &origin_extents); + + /* + * Transform font space metrics into user space metrics + * by running the corners through the font matrix and + * expanding the bounding box as necessary + */ + x = origin_extents.x_bearing; + y = origin_extents.y_bearing; + cairo_matrix_transform_point (font_matrix, + &x, &y); + + for (hm = 0.0; hm <= 1.0; hm += 1.0) + for (wm = 0.0; wm <= 1.0; wm += 1.0) + { + x = origin_extents.x_bearing + origin_extents.width * wm; + y = origin_extents.y_bearing + origin_extents.height * hm; + cairo_matrix_transform_point (font_matrix, + &x, &y); + x += glyphs[i].x; + y += glyphs[i].y; + if (!set) + { + min_x = max_x = x; + min_y = max_y = y; + set = 1; + } + else + { + if (x < min_x) min_x = x; + if (x > max_x) max_x = x; + if (y < min_y) min_y = y; + if (y > max_y) max_y = y; + } + } + + x = origin_extents.x_advance; + y = origin_extents.y_advance; + cairo_matrix_transform_point (font_matrix, + &x, &y); + x_pos = glyphs[i].x + x; + y_pos = glyphs[i].y + y; + } + + extents->x_bearing = min_x - glyphs[0].x; + extents->y_bearing = min_y - glyphs[0].y; + extents->width = max_x - min_x; + extents->height = max_y - min_y; + extents->x_advance = x_pos - glyphs[0].x; + extents->y_advance = y_pos - glyphs[0].y; +} /* Now we implement functions to access a default global image & metrics * cache. @@ -398,7 +351,8 @@ _cairo_glyph_cache_hash (void *cache, void *key) ^ ((unsigned long) in->scale.matrix[0][0]) ^ ((unsigned long) in->scale.matrix[0][1]) ^ ((unsigned long) in->scale.matrix[1][0]) - ^ ((unsigned long) in->scale.matrix[1][1]) + ^ ((unsigned long) in->scale.matrix[1][1]) + ^ (in->flags * 1451) /* 1451 is just an abitrary prime */ ^ in->index; } @@ -412,6 +366,7 @@ _cairo_glyph_cache_keys_equal (void *cache, b = (cairo_glyph_cache_key_t *) k2; return (a->index == b->index) && (a->unscaled == b->unscaled) + && (a->flags == b->flags) && (a->scale.matrix[0][0] == b->scale.matrix[0][0]) && (a->scale.matrix[0][1] == b->scale.matrix[0][1]) && (a->scale.matrix[1][0] == b->scale.matrix[1][0]) diff --git a/src/cairo_ft_font.c b/src/cairo_ft_font.c index b928b04fc..44e1b0e84 100644 --- a/src/cairo_ft_font.c +++ b/src/cairo_ft_font.c @@ -1,29 +1,40 @@ -/* - * Copyright © 2003 Red Hat Inc. +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * 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 * - * 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. + * 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/ * - * 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. + * 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. * - * Author: Graydon Hoare <graydon@redhat.com> + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Graydon Hoare <graydon@redhat.com> + * Owen Taylor <otaylor@redhat.com> */ -#include "cairoint.h" -#include "cairo-ft.h" +#include "cairo-ft-private.h" #include <fontconfig/fontconfig.h> #include <fontconfig/fcfreetype.h> @@ -38,19 +49,9 @@ #define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) #define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) -/* - * First we make a private, sharable implementation object which can be - * stored both in a private cache and in public font objects (including - * those connected to fonts we don't own) +/* This is the max number of FT_face objects we keep open at once */ - -typedef struct { - int refcount; - - FT_Face face; - int owns_face; - -} ft_font_val_t; +#define MAX_OPEN_FACES 10 /* * The simple 2x2 matrix is converted into separate scale and shape @@ -62,141 +63,126 @@ typedef struct { double shape[2][2]; } ft_font_transform_t; -static ft_font_val_t * -_create_from_face (FT_Face face, int owns_face) -{ - ft_font_val_t *tmp = malloc (sizeof(ft_font_val_t)); - if (tmp) { - tmp->refcount = 1; - tmp->face = face; - tmp->owns_face = owns_face; - FT_Set_Char_Size (face, - DOUBLE_TO_26_6 (1.0), - DOUBLE_TO_26_6 (1.0), - 0, 0); - } - return tmp; -} +/* + * We create an object that corresponds to a single font on the disk; + * (identified by a filename/id pair) these are shared between all + * fonts using that file. For cairo_ft_font_create_for_ft_face(), we + * just create a one-off version with a permanent face value. + */ -static void -_reference_font_val (ft_font_val_t *f) -{ - f->refcount++; -} +typedef struct { + cairo_unscaled_font_t base; -static void -_destroy_font_val (ft_font_val_t *f) -{ - if (--(f->refcount) > 0) - return; + int from_face; /* from cairo_ft_font_create_for_ft_face()? */ + FT_Face face; /* provided or cached face */ - if (f->owns_face) - FT_Done_Face (f->face); + /* only set if from_face is false */ + FT_Library library; + char *filename; + int id; - free (f); -} + /* We temporarily scale the unscaled font as neede */ + int have_scale; + cairo_font_scale_t current_scale; + double x_scale; /* Extracted X scale factor */ + double y_scale; /* Extracted Y scale factor */ + + int lock; /* count of how many times this font has been locked */ +} ft_unscaled_font_t; -static ft_font_val_t * -_create_from_library_and_pattern (FT_Library ft_library, FcPattern *pattern) -{ - ft_font_val_t *f = NULL; - char *filename = NULL; - int owns_face = 0; - FT_Face face = NULL; - FcPattern *resolved = NULL; - FcResult result = FcResultMatch; +const cairo_font_backend_t cairo_ft_font_backend; - if (pattern == NULL) - goto FAIL; +static ft_unscaled_font_t * +_ft_unscaled_font_create_from_face (FT_Face face) +{ + ft_unscaled_font_t *unscaled = malloc (sizeof(ft_unscaled_font_t)); + if (!unscaled) + return NULL; + + unscaled->from_face = 1; + unscaled->face = face; - FcConfigSubstitute (0, pattern, FcMatchPattern); - FcDefaultSubstitute (pattern); + unscaled->library = NULL; + unscaled->filename = NULL; + unscaled->id = 0; - resolved = FcFontMatch (0, pattern, &result); - if (!resolved) - goto FAIL; + unscaled->have_scale = 0; + unscaled->lock = 0; - if (result != FcResultMatch) - goto FREE_RESOLVED; + _cairo_unscaled_font_init ((cairo_unscaled_font_t *)unscaled, + &cairo_ft_font_backend); + return unscaled; +} + +static ft_unscaled_font_t * +_ft_unscaled_font_create_from_filename (FT_Library library, + const char *filename, + int id) +{ + ft_unscaled_font_t *unscaled; + char *new_filename; - /* If the pattern has an FT_Face object, use that. */ - if (FcPatternGetFTFace (resolved, FC_FT_FACE, 0, &face) != FcResultMatch - || face == NULL) - { - /* otherwise it had better have a filename */ - result = FcPatternGetString (resolved, FC_FILE, 0, (FcChar8 **)(&filename)); - - if (result == FcResultMatch) - if (FT_New_Face (ft_library, filename, 0, &face)) - goto FREE_RESOLVED; - - if (face == NULL) - goto FREE_RESOLVED; - - owns_face = 1; + new_filename = strdup (filename); + if (!new_filename) + return NULL; + + unscaled = malloc (sizeof (ft_unscaled_font_t)); + if (!unscaled) { + free (new_filename); + return NULL; } + + unscaled->from_face = 0; + unscaled->face = NULL; - f = _create_from_face (face, owns_face); - - FcPatternDestroy (resolved); - return f; + unscaled->library = library; + unscaled->filename = new_filename; + unscaled->id = id; - FREE_RESOLVED: - if (resolved) - FcPatternDestroy (resolved); - - FAIL: - return NULL; + unscaled->have_scale = 0; + unscaled->lock = 0; + + _cairo_unscaled_font_init ((cairo_unscaled_font_t *)unscaled, + &cairo_ft_font_backend); + return unscaled; } - -/* - * We then make the user-exposed structure out of one of these impls, such - * that it is reasonably cheap to copy and/or destroy. Unfortunately this - * duplicates a certain amount of the caching machinery in the font cache, - * but that's unavoidable as we also provide an FcPattern resolution API, - * which is not part of cairo's generic font finding system. - */ - -typedef struct { - cairo_unscaled_font_t base; - FcPattern *pattern; - ft_font_val_t *val; -} cairo_ft_font_t; - -/* - * We then make a key and entry type which are compatible with the generic - * cache system. This cache serves to share single ft_font_val_t instances - * between fonts (or between font lifecycles). +/* + * We keep a global cache from [file/id] => [ft_unscaled_font_t]. This + * hash isn't limited in size. However, we limit the number of + * FT_Face objects we keep around; when we've exceeeded that + * limit and need to create a new FT_Face, we dump the FT_Face from + * a random ft_unscaled_font_t. */ typedef struct { cairo_cache_entry_base_t base; - FcPattern *pattern; + char *filename; + int id; } cairo_ft_cache_key_t; typedef struct { cairo_ft_cache_key_t key; - ft_font_val_t *val; + ft_unscaled_font_t *unscaled; } cairo_ft_cache_entry_t; -/* - * Then we create a cache which maps FcPattern keys to the refcounted - * ft_font_val_t values. - */ - typedef struct { cairo_cache_t base; FT_Library lib; + int n_faces; /* Number of open FT_Face objects */ } ft_cache_t; - static unsigned long _ft_font_cache_hash (void *cache, void *key) { - cairo_ft_cache_key_t *in; - in = (cairo_ft_cache_key_t *) key; - return FcPatternHash (in->pattern); + cairo_ft_cache_key_t *in = (cairo_ft_cache_key_t *) key; + unsigned long hash; + + /* 1607 is just a random prime. */ + hash = _cairo_hash_string (in->filename); + hash += ((unsigned long) in->id) * 1607; + + return hash; } static int @@ -208,10 +194,10 @@ _ft_font_cache_keys_equal (void *cache, cairo_ft_cache_key_t *b; a = (cairo_ft_cache_key_t *) k1; b = (cairo_ft_cache_key_t *) k2; - - return FcPatternEqual (a->pattern, b->pattern); -} + return strcmp (a->filename, b->filename) == 0 && + a->id == b->id; +} static cairo_status_t _ft_font_cache_create_entry (void *cache, @@ -226,27 +212,33 @@ _ft_font_cache_create_entry (void *cache, if (entry == NULL) return CAIRO_STATUS_NO_MEMORY; - entry->key.pattern = FcPatternDuplicate (k->pattern); - if (!entry->key.pattern) { + entry->unscaled = _ft_unscaled_font_create_from_filename (ftcache->lib, + k->filename, + k->id); + if (!entry->unscaled) { free (entry); return CAIRO_STATUS_NO_MEMORY; } - - entry->val = _create_from_library_and_pattern (ftcache->lib, entry->key.pattern); - entry->key.base.memory = 1; - + + entry->key.base.memory = 0; + entry->key.filename = entry->unscaled->filename; + entry->key.id = entry->unscaled->id; + *return_entry = entry; return CAIRO_STATUS_SUCCESS; } +/* Entries are never spontaneously destroyed; but only when + * we remove them from the cache specifically. We free entry->unscaled + * in the code that removes the entry from the cache + */ static void _ft_font_cache_destroy_entry (void *cache, void *entry) { cairo_ft_cache_entry_t *e = (cairo_ft_cache_entry_t *) entry; - FcPatternDestroy (e->key.pattern); - _destroy_font_val (e->val); + free (e); } @@ -291,11 +283,12 @@ _get_global_ft_cache (void) if (_cairo_cache_init (&_global_ft_cache->base, &_ft_font_cache_backend, - CAIRO_FT_CACHE_NUM_FONTS_DEFAULT)) + 0)) /* No memory limit */ goto FAIL; if (FT_Init_FreeType (&_global_ft_cache->lib)) goto FAIL; + _global_ft_cache->n_faces = 0; } return &_global_ft_cache->base; @@ -306,30 +299,304 @@ _get_global_ft_cache (void) return NULL; } -/* implement the backend interface */ +/* Finds or creates a ft_unscaled_font for the filename/id from pattern. + * Returns a new reference to the unscaled font. + */ +static ft_unscaled_font_t * +_ft_unscaled_font_get_for_pattern (FcPattern *pattern) +{ + cairo_ft_cache_entry_t *entry; + cairo_ft_cache_key_t key; + cairo_cache_t *cache; + cairo_status_t status; + FcChar8 *filename; + int created_entry; + + if (FcPatternGetString (pattern, FC_FILE, 0, &filename) != FcResultMatch) + return NULL; + key.filename = (char *)filename; + + if (FcPatternGetInteger (pattern, FC_INDEX, 0, &key.id) != FcResultMatch) + return NULL; + + _lock_global_ft_cache (); + cache = _get_global_ft_cache (); + if (cache == NULL) { + _unlock_global_ft_cache (); + return NULL; + } + + status = _cairo_cache_lookup (cache, &key, (void **) &entry, &created_entry); + _unlock_global_ft_cache (); + if (status) + return NULL; + + if (!created_entry) + _cairo_unscaled_font_reference ((cairo_unscaled_font_t *)entry->unscaled); + + return entry->unscaled; +} + +static int +_has_unlocked_face (void *entry) +{ + cairo_ft_cache_entry_t *e = entry; -const cairo_font_backend_t cairo_ft_font_backend; + return (e->unscaled->lock == 0 && e->unscaled->face); +} + +/* Ensures that an unscaled font has a face object. If we exceed + * MAX_OPEN_FACES, try to close some. + */ +static FT_Face +_ft_unscaled_font_lock_face (ft_unscaled_font_t *unscaled) +{ + ft_cache_t *ftcache; + FT_Face face = NULL; + + if (unscaled->face) { + unscaled->lock++; + return unscaled->face; + } -static cairo_unscaled_font_t * -_cairo_ft_font_create (const char *family, + assert (!unscaled->from_face); + + _lock_global_ft_cache (); + ftcache = (ft_cache_t *) _get_global_ft_cache (); + assert (ftcache != NULL); + + while (ftcache->n_faces >= MAX_OPEN_FACES) { + cairo_ft_cache_entry_t *entry; + + entry = _cairo_cache_random_entry ((cairo_cache_t *)ftcache, _has_unlocked_face); + if (entry) { + FT_Done_Face (entry->unscaled->face); + entry->unscaled->face = NULL; + entry->unscaled->have_scale = 0; + ftcache->n_faces--; + } else { + break; + } + } + + if (FT_New_Face (ftcache->lib, + unscaled->filename, + unscaled->id, + &face) != FT_Err_Ok) + goto FAIL; + + unscaled->face = face; + unscaled->lock++; + ftcache->n_faces++; + + FAIL: + _unlock_global_ft_cache (); + return face; +} + +/* Unlock unscaled font locked with _ft_unscaled_font_lock_face + */ +static void +_ft_unscaled_font_unlock_face (ft_unscaled_font_t *unscaled) +{ + assert (unscaled->lock > 0); + + unscaled->lock--; +} + +static void +_compute_transform (ft_font_transform_t *sf, + cairo_font_scale_t *sc) +{ + cairo_matrix_t normalized; + double tx, ty; + + /* The font matrix has x and y "scale" components which we extract and + * use as character scale values. These influence the way freetype + * chooses hints, as well as selecting different bitmaps in + * hand-rendered fonts. We also copy the normalized matrix to + * freetype's transformation. + */ + + cairo_matrix_set_affine (&normalized, + sc->matrix[0][0], + sc->matrix[0][1], + sc->matrix[1][0], + sc->matrix[1][1], + 0, 0); + + _cairo_matrix_compute_scale_factors (&normalized, + &sf->x_scale, &sf->y_scale, + /* XXX */ 1); + cairo_matrix_scale (&normalized, 1.0 / sf->x_scale, 1.0 / sf->y_scale); + cairo_matrix_get_affine (&normalized, + &sf->shape[0][0], &sf->shape[0][1], + &sf->shape[1][0], &sf->shape[1][1], + &tx, &ty); +} + +/* Temporarily scales an unscaled font to the give scale. We catch + * scaling to the same size, since changing a FT_Face is expensive. + */ +static void +_ft_unscaled_font_set_scale (ft_unscaled_font_t *unscaled, + cairo_font_scale_t *scale) +{ + ft_font_transform_t sf; + FT_Matrix mat; + + assert (unscaled->face != NULL); + + if (unscaled->have_scale && + scale->matrix[0][0] == unscaled->current_scale.matrix[0][0] && + scale->matrix[0][1] == unscaled->current_scale.matrix[0][1] && + scale->matrix[1][0] == unscaled->current_scale.matrix[1][0] && + scale->matrix[1][1] == unscaled->current_scale.matrix[1][1]) + return; + + unscaled->have_scale = 1; + unscaled->current_scale = *scale; + + _compute_transform (&sf, scale); + + unscaled->x_scale = sf.x_scale; + unscaled->y_scale = sf.y_scale; + + mat.xx = DOUBLE_TO_16_16(sf.shape[0][0]); + mat.yx = - DOUBLE_TO_16_16(sf.shape[0][1]); + mat.xy = - DOUBLE_TO_16_16(sf.shape[1][0]); + mat.yy = DOUBLE_TO_16_16(sf.shape[1][1]); + + FT_Set_Transform(unscaled->face, &mat, NULL); + + FT_Set_Pixel_Sizes(unscaled->face, + (FT_UInt) sf.x_scale, + (FT_UInt) sf.y_scale); +} + +/* implement the font backend interface */ + +typedef struct { + cairo_font_t base; + FcPattern *pattern; + int load_flags; + ft_unscaled_font_t *unscaled; +} cairo_ft_font_t; + +/* for compatibility with older freetype versions */ +#ifndef FT_LOAD_TARGET_MONO +#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME +#endif + +/* The load flags passed to FT_Load_Glyph control aspects like hinting and + * antialiasing. Here we compute them from the fields of a FcPattern. + */ +static int +_get_load_flags (FcPattern *pattern) +{ + FcBool antialias, hinting, autohint; +#ifdef FC_HINT_STYLE + int hintstyle; +#endif + int load_flags = 0; + + /* disable antialiasing if requested */ + if (FcPatternGetBool (pattern, + FC_ANTIALIAS, 0, &antialias) != FcResultMatch) + antialias = FcTrue; + + if (antialias) + load_flags |= FT_LOAD_NO_BITMAP; + else + load_flags |= FT_LOAD_TARGET_MONO; + + /* disable hinting if requested */ + if (FcPatternGetBool (pattern, + FC_HINTING, 0, &hinting) != FcResultMatch) + hinting = FcTrue; + +#ifdef FC_HINT_STYLE + if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch) + hintstyle = FC_HINT_FULL; + + if (!hinting || hintstyle == FC_HINT_NONE) + load_flags |= FT_LOAD_NO_HINTING; + + switch (hintstyle) { + case FC_HINT_SLIGHT: + case FC_HINT_MEDIUM: + load_flags |= FT_LOAD_TARGET_LIGHT; + break; + default: + load_flags |= FT_LOAD_TARGET_NORMAL; + break; + } +#else /* !FC_HINT_STYLE */ + if (!hinting) + load_flags |= FT_LOAD_NO_HINTING; +#endif /* FC_FHINT_STYLE */ + + /* force autohinting if requested */ + if (FcPatternGetBool (pattern, + FC_AUTOHINT, 0, &autohint) != FcResultMatch) + autohint = FcFalse; + + if (autohint) + load_flags |= FT_LOAD_FORCE_AUTOHINT; + + return load_flags; +} + +/* Like the public cairo_ft_font_create, but takes a cairo_font_scale_t, + * rather than a cairo_font_t + */ +static cairo_font_t * +_ft_font_create (FcPattern *pattern, + cairo_font_scale_t *scale) +{ + cairo_ft_font_t *f = NULL; + ft_unscaled_font_t *unscaled = NULL; + + unscaled = _ft_unscaled_font_get_for_pattern (pattern); + if (unscaled == NULL) + return NULL; + + f = malloc (sizeof(cairo_ft_font_t)); + if (f == NULL) + goto FREE_UNSCALED; + + f->unscaled = unscaled; + f->pattern = pattern; + FcPatternReference (pattern); + f->load_flags = _get_load_flags (pattern); + + _cairo_font_init ((cairo_font_t *)f, scale, &cairo_ft_font_backend); + + return (cairo_font_t *)f; + + FREE_UNSCALED: + _cairo_unscaled_font_destroy ((cairo_unscaled_font_t *)unscaled); + + return NULL; +} + +static cairo_status_t +_cairo_ft_font_create (const char *family, cairo_font_slant_t slant, - cairo_font_weight_t weight) + cairo_font_weight_t weight, + cairo_font_scale_t *scale, + cairo_font_t **font) { - cairo_status_t status; - cairo_ft_font_t *font = NULL; + FcPattern *pattern, *resolved; + cairo_font_t *new_font; + FcResult result; int fcslant; int fcweight; - cairo_cache_t *cache; - cairo_ft_cache_entry_t *entry; - cairo_ft_cache_key_t key; - - key.pattern = FcPatternCreate (); - if (key.pattern == NULL) - goto FAIL; + ft_font_transform_t sf; - font = malloc (sizeof (cairo_ft_font_t)); - if (font == NULL) - goto FREE_PATTERN; + pattern = FcPatternCreate (); + if (!pattern) + return CAIRO_STATUS_NO_MEMORY; switch (weight) { @@ -356,46 +623,44 @@ _cairo_ft_font_create (const char *family, break; } - FcPatternAddString (key.pattern, FC_FAMILY, family); - FcPatternAddInteger (key.pattern, FC_SLANT, fcslant); - FcPatternAddInteger (key.pattern, FC_WEIGHT, fcweight); - - if (_cairo_unscaled_font_init (&font->base, &cairo_ft_font_backend)) + if (!FcPatternAddString (pattern, FC_FAMILY, family)) goto FREE_PATTERN; - - _lock_global_ft_cache (); - cache = _get_global_ft_cache (); - if (cache == NULL) { - _unlock_global_ft_cache (); + if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) + goto FREE_PATTERN; + if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) goto FREE_PATTERN; - } - status = _cairo_cache_lookup (cache, &key, (void **) &entry); - _unlock_global_ft_cache (); + _compute_transform (&sf, scale); - if (status) - goto FREE_PATTERN; + FcPatternAddInteger (pattern, FC_PIXEL_SIZE, sf.y_scale); - font->pattern = FcPatternDuplicate (entry->key.pattern); - if (font->pattern == NULL) + FcConfigSubstitute (NULL, pattern, FcMatchPattern); + FcDefaultSubstitute (pattern); + + resolved = FcFontMatch (NULL, pattern, &result); + if (!resolved) goto FREE_PATTERN; - font->val = entry->val; - _reference_font_val (font->val); - - return &font->base; + new_font = _ft_font_create (resolved, scale); - FREE_PATTERN: - FcPatternDestroy (key.pattern); + FcPatternDestroy (resolved); + FcPatternDestroy (pattern); - FAIL: - return NULL; + if (new_font) { + *font = new_font; + return CAIRO_STATUS_SUCCESS; + } else { + return CAIRO_STATUS_NO_MEMORY; /* A guess */ + } -} + FREE_PATTERN: + FcPatternDestroy (pattern); + return CAIRO_STATUS_NO_MEMORY; +} static void -_cairo_ft_font_destroy (void *abstract_font) +_cairo_ft_font_destroy_font (void *abstract_font) { cairo_ft_font_t * font = abstract_font; @@ -405,179 +670,94 @@ _cairo_ft_font_destroy (void *abstract_font) if (font->pattern != NULL) FcPatternDestroy (font->pattern); - _destroy_font_val (font->val); + _cairo_unscaled_font_destroy ((cairo_unscaled_font_t *)font->unscaled); free (font); } static void -_utf8_to_ucs4 (char const *utf8, - FT_ULong **ucs4, - int *nchars) +_cairo_ft_font_destroy_unscaled_font (void *abstract_font) { - int len = 0, step = 0; - int n = 0, alloc = 0; - FcChar32 u = 0; + ft_unscaled_font_t *unscaled = abstract_font; - if (utf8 == NULL || ucs4 == NULL || nchars == NULL) - return; + if (!unscaled->from_face) { + cairo_cache_t *cache; + cairo_ft_cache_key_t key; + + _lock_global_ft_cache (); + cache = _get_global_ft_cache (); + assert (cache); - len = strlen (utf8); - alloc = len; - *ucs4 = malloc (sizeof (FT_ULong) * alloc); - if (*ucs4 == NULL) - return; - - while (len && (step = FcUtf8ToUcs4(utf8, &u, len)) > 0) - { - if (n == alloc) - { - alloc *= 2; - *ucs4 = realloc (*ucs4, sizeof (FT_ULong) * alloc); - if (*ucs4 == NULL) - return; - } - (*ucs4)[n++] = u; - len -= step; - utf8 += step; + key.filename = unscaled->filename; + key.id = unscaled->id; + + _cairo_cache_remove (cache, &key); + + _unlock_global_ft_cache (); } - *nchars = n; -} - -/* - * Split a matrix into the component pieces of scale and shape - */ - -static void -_cairo_ft_font_compute_transform (ft_font_transform_t *sf, cairo_font_scale_t *sc) -{ - cairo_matrix_t normalized; - double tx, ty; - /* The font matrix has x and y "scale" components which we extract and - * use as character scale values. These influence the way freetype - * chooses hints, as well as selecting different bitmaps in - * hand-rendered fonts. We also copy the normalized matrix to - * freetype's transformation. - */ - - cairo_matrix_set_affine (&normalized, - sc->matrix[0][0], - sc->matrix[0][1], - sc->matrix[1][0], - sc->matrix[1][1], - 0, 0); - - _cairo_matrix_compute_scale_factors (&normalized, - &sf->x_scale, &sf->y_scale, - /* XXX */ 1); - cairo_matrix_scale (&normalized, 1.0 / sf->x_scale, 1.0 / sf->y_scale); - cairo_matrix_get_affine (&normalized, - &sf->shape[0][0], &sf->shape[0][1], - &sf->shape[1][0], &sf->shape[1][1], - &tx, &ty); -} - -/* - * Set the font transformation - */ - -static void -_cairo_ft_font_install_transform (ft_font_transform_t *sf, FT_Face face) -{ - FT_Matrix mat; - - mat.xx = DOUBLE_TO_16_16(sf->shape[0][0]); - mat.yx = -DOUBLE_TO_16_16(sf->shape[0][1]); - mat.xy = -DOUBLE_TO_16_16(sf->shape[1][0]); - mat.yy = DOUBLE_TO_16_16(sf->shape[1][1]); + if (unscaled == NULL) + return; - FT_Set_Transform(face, &mat, NULL); + if (!unscaled->from_face && unscaled->face) + FT_Done_Face (unscaled->face); - FT_Set_Char_Size(face, - (FT_F26Dot6) (sf->x_scale * 64.0), - (FT_F26Dot6) (sf->y_scale * 64.0), - 0, 0); + if (unscaled->filename) + free (unscaled->filename); + + free (unscaled); } static void -_install_font_scale (cairo_font_scale_t *sc, FT_Face face) +_cairo_ft_font_get_glyph_cache_key (void *abstract_font, + cairo_glyph_cache_key_t *key) { - cairo_matrix_t normalized; - double x_scale, y_scale; - double xx, xy, yx, yy, tx, ty; - FT_Matrix mat; - - /* The font matrix has x and y "scale" components which we extract and - * use as character scale values. These influence the way freetype - * chooses hints, as well as selecting different bitmaps in - * hand-rendered fonts. We also copy the normalized matrix to - * freetype's transformation. - */ - - cairo_matrix_set_affine (&normalized, - sc->matrix[0][0], - sc->matrix[0][1], - sc->matrix[1][0], - sc->matrix[1][1], - 0, 0); - - _cairo_matrix_compute_scale_factors (&normalized, &x_scale, &y_scale, - /* XXX */ 1); - cairo_matrix_scale (&normalized, 1.0 / x_scale, 1.0 / y_scale); - cairo_matrix_get_affine (&normalized, - &xx /* 00 */ , &yx /* 01 */, - &xy /* 10 */, &yy /* 11 */, - &tx, &ty); - - mat.xx = DOUBLE_TO_16_16(xx); - mat.xy = -DOUBLE_TO_16_16(xy); - mat.yx = -DOUBLE_TO_16_16(yx); - mat.yy = DOUBLE_TO_16_16(yy); - - FT_Set_Transform(face, &mat, NULL); + cairo_ft_font_t *font = abstract_font; - FT_Set_Pixel_Sizes(face, - (FT_UInt) x_scale, - (FT_UInt) y_scale); + key->unscaled = (cairo_unscaled_font_t *)font->unscaled; + key->scale = font->base.scale; + key->flags = font->load_flags; } static cairo_status_t _cairo_ft_font_text_to_glyphs (void *abstract_font, - cairo_font_scale_t *sc, const unsigned char *utf8, cairo_glyph_t **glyphs, int *nglyphs) { double x = 0., y = 0.; size_t i; - FT_ULong *ucs4 = NULL; + uint32_t *ucs4 = NULL; cairo_ft_font_t *font = abstract_font; - FT_Face face = font->val->face; + FT_Face face; cairo_glyph_cache_key_t key; cairo_image_glyph_cache_entry_t *val; - cairo_cache_t *cache; + cairo_cache_t *cache = NULL; + cairo_status_t status = CAIRO_STATUS_SUCCESS; - key.unscaled = &font->base; - key.scale = *sc; + _cairo_ft_font_get_glyph_cache_key (font, &key); - _utf8_to_ucs4 (utf8, &ucs4, nglyphs); - - if (ucs4 == NULL) - return CAIRO_STATUS_NO_MEMORY; + status = _cairo_utf8_to_ucs4 (utf8, -1, &ucs4, nglyphs); + if (!CAIRO_OK (status)) + return status; - *glyphs = (cairo_glyph_t *) malloc ((*nglyphs) * (sizeof (cairo_glyph_t))); - if (*glyphs == NULL) - { - free (ucs4); - return CAIRO_STATUS_NO_MEMORY; + face = cairo_ft_font_lock_face ((cairo_font_t *)font); + if (!face) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL1; } _cairo_lock_global_image_glyph_cache (); cache = _cairo_get_global_image_glyph_cache (); if (cache == NULL) { - _cairo_unlock_global_image_glyph_cache (); - return CAIRO_STATUS_NO_MEMORY; + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL2; + } + + *glyphs = (cairo_glyph_t *) malloc ((*nglyphs) * (sizeof (cairo_glyph_t))); + if (*glyphs == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL2; } for (i = 0; i < *nglyphs; i++) @@ -589,51 +769,62 @@ _cairo_ft_font_text_to_glyphs (void *abstract_font, val = NULL; key.index = (*glyphs)[i].index; - if (_cairo_cache_lookup (cache, &key, (void **) &val) + if (_cairo_cache_lookup (cache, &key, (void **) &val, NULL) != CAIRO_STATUS_SUCCESS || val == NULL) continue; x += val->extents.x_advance; y += val->extents.y_advance; } - _cairo_unlock_global_image_glyph_cache (); + FAIL2: + if (cache) + _cairo_unlock_global_image_glyph_cache (); + + cairo_ft_font_unlock_face ((cairo_font_t *)font); + + FAIL1: free (ucs4); - return CAIRO_STATUS_SUCCESS; + + return status; } static cairo_status_t _cairo_ft_font_font_extents (void *abstract_font, - cairo_font_scale_t *sc, cairo_font_extents_t *extents) { cairo_ft_font_t *font = abstract_font; - FT_Face face = font->val->face; - FT_Size_Metrics *metrics = &face->size->metrics; - ft_font_transform_t sf; + FT_Face face; + FT_Size_Metrics *metrics; + + face = _ft_unscaled_font_lock_face (font->unscaled); + if (!face) + return CAIRO_STATUS_NO_MEMORY; - _cairo_ft_font_compute_transform (&sf, sc); - _cairo_ft_font_install_transform (&sf, face); + metrics = &face->size->metrics; + _ft_unscaled_font_set_scale (font->unscaled, &font->base.scale); + /* * Get to unscaled metrics so that the upper level can get back to * user space */ - extents->ascent = DOUBLE_FROM_26_6(metrics->ascender) / sf.y_scale; - extents->descent = DOUBLE_FROM_26_6(metrics->descender) / sf.y_scale; - extents->height = DOUBLE_FROM_26_6(metrics->height) / sf.y_scale; - extents->max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) / sf.x_scale; + extents->ascent = DOUBLE_FROM_26_6(metrics->ascender) / font->unscaled->y_scale; + extents->descent = DOUBLE_FROM_26_6(metrics->descender) / font->unscaled->y_scale; + extents->height = DOUBLE_FROM_26_6(metrics->height) / font->unscaled->y_scale; + extents->max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) / font->unscaled->x_scale; /* FIXME: this doesn't do vertical layout atm. */ extents->max_y_advance = 0.0; + _ft_unscaled_font_unlock_face (font->unscaled); + return CAIRO_STATUS_SUCCESS; } static cairo_status_t _cairo_ft_font_glyph_extents (void *abstract_font, - cairo_font_scale_t *sc, cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents) @@ -670,14 +861,13 @@ _cairo_ft_font_glyph_extents (void *abstract_font, return CAIRO_STATUS_NO_MEMORY; } - key.unscaled = &font->base; - key.scale = *sc; + _cairo_ft_font_get_glyph_cache_key (font, &key); for (i = 0; i < num_glyphs; i++) { img = NULL; key.index = glyphs[i].index; - if (_cairo_cache_lookup (cache, &key, (void **) &img) + if (_cairo_cache_lookup (cache, &key, (void **) &img, NULL) != CAIRO_STATUS_SUCCESS || img == NULL) continue; @@ -721,7 +911,6 @@ _cairo_ft_font_glyph_extents (void *abstract_font, static cairo_status_t _cairo_ft_font_glyph_bbox (void *abstract_font, - cairo_font_scale_t *sc, const cairo_glyph_t *glyphs, int num_glyphs, cairo_box_t *bbox) @@ -747,16 +936,15 @@ _cairo_ft_font_glyph_bbox (void *abstract_font, return CAIRO_STATUS_NO_MEMORY; } - key.unscaled = &font->base; - key.scale = *sc; - + _cairo_ft_font_get_glyph_cache_key (font, &key); + for (i = 0; i < num_glyphs; i++) { img = NULL; key.index = glyphs[i].index; - if (_cairo_cache_lookup (cache, &key, (void **) &img) + if (_cairo_cache_lookup (cache, &key, (void **) &img, NULL) != CAIRO_STATUS_SUCCESS || img == NULL) continue; @@ -785,12 +973,15 @@ _cairo_ft_font_glyph_bbox (void *abstract_font, static cairo_status_t _cairo_ft_font_show_glyphs (void *abstract_font, - cairo_font_scale_t *sc, cairo_operator_t operator, - cairo_surface_t *source, + cairo_pattern_t *pattern, cairo_surface_t *surface, int source_x, int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, const cairo_glyph_t *glyphs, int num_glyphs) { @@ -798,9 +989,9 @@ _cairo_ft_font_show_glyphs (void *abstract_font, cairo_cache_t *cache; cairo_glyph_cache_key_t key; cairo_ft_font_t *font = abstract_font; + cairo_surface_pattern_t glyph_pattern; cairo_status_t status; - - double x, y; + int x, y; int i; _cairo_lock_global_image_glyph_cache (); @@ -808,47 +999,54 @@ _cairo_ft_font_show_glyphs (void *abstract_font, if (cache == NULL || font == NULL - || source == NULL + || pattern == NULL || surface == NULL || glyphs == NULL) { _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_NO_MEMORY; } - key.unscaled = &font->base; - key.scale = *sc; + key.unscaled = (cairo_unscaled_font_t *)font->unscaled; + key.scale = font->base.scale; + key.flags = font->load_flags; for (i = 0; i < num_glyphs; i++) { img = NULL; key.index = glyphs[i].index; - if (_cairo_cache_lookup (cache, &key, (void **) &img) + if (_cairo_cache_lookup (cache, &key, (void **) &img, NULL) != CAIRO_STATUS_SUCCESS || img == NULL || img->image == NULL) continue; - x = glyphs[i].x; - y = glyphs[i].y; + x = (int) floor (glyphs[i].x + 0.5); + y = (int) floor (glyphs[i].y + 0.5); + + _cairo_pattern_init_for_surface (&glyph_pattern, &(img->image->base)); - status = _cairo_surface_composite (operator, source, - &(img->image->base), + status = _cairo_surface_composite (operator, pattern, + &glyph_pattern.base, surface, - source_x + x + img->size.x, - source_y + y + img->size.y, + x + img->size.x, + y + img->size.y, 0, 0, x + img->size.x, y + img->size.y, (double) img->size.width, (double) img->size.height); + _cairo_pattern_fini (&glyph_pattern.base); + if (status) { - _cairo_unlock_global_image_glyph_cache (); + _cairo_unlock_global_image_glyph_cache (); return status; } } + _cairo_unlock_global_image_glyph_cache (); + return CAIRO_STATUS_SUCCESS; } @@ -932,7 +1130,6 @@ _cubic_to (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *closur static cairo_status_t _cairo_ft_font_glyph_path (void *abstract_font, - cairo_font_scale_t *sc, cairo_glyph_t *glyphs, int num_glyphs, cairo_path_t *path) @@ -940,6 +1137,7 @@ _cairo_ft_font_glyph_path (void *abstract_font, int i; cairo_ft_font_t *font = abstract_font; FT_GlyphSlot glyph; + FT_Face face; FT_Error error; FT_Outline_Funcs outline_funcs = { _move_to, @@ -949,10 +1147,12 @@ _cairo_ft_font_glyph_path (void *abstract_font, 0, /* shift */ 0, /* delta */ }; + + face = cairo_ft_font_lock_face (abstract_font); + if (!face) + return CAIRO_STATUS_NO_MEMORY; - glyph = font->val->face->glyph; - - _install_font_scale (sc, font->val->face); + glyph = face->glyph; for (i = 0; i < num_glyphs; i++) { @@ -961,7 +1161,7 @@ _cairo_ft_font_glyph_path (void *abstract_font, 0, DOUBLE_TO_16_16 (-1.0), }; - error = FT_Load_Glyph (font->val->face, glyphs[i].index, FT_LOAD_DEFAULT); + error = FT_Load_Glyph (font->unscaled->face, glyphs[i].index, font->load_flags | FT_LOAD_NO_BITMAP); /* XXX: What to do in this error case? */ if (error) continue; @@ -977,32 +1177,39 @@ _cairo_ft_font_glyph_path (void *abstract_font, FT_Outline_Decompose (&glyph->outline, &outline_funcs, path); } _cairo_path_close_path (path); + + cairo_ft_font_unlock_face (abstract_font); return CAIRO_STATUS_SUCCESS; } - static cairo_status_t -_cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) +_cairo_ft_font_create_glyph (cairo_image_glyph_cache_entry_t *val) { - cairo_ft_font_t *font = (cairo_ft_font_t *)val->key.unscaled; + ft_unscaled_font_t *unscaled = (ft_unscaled_font_t *)val->key.unscaled; FT_GlyphSlot glyphslot; unsigned int width, height, stride; + FT_Face face; FT_Outline *outline; FT_BBox cbox; FT_Bitmap bitmap; FT_Glyph_Metrics *metrics; - ft_font_transform_t sf; + cairo_status_t status = CAIRO_STATUS_SUCCESS; - glyphslot = font->val->face->glyph; + glyphslot = unscaled->face->glyph; metrics = &glyphslot->metrics; - _cairo_ft_font_compute_transform (&sf, &val->key.scale); - _cairo_ft_font_install_transform (&sf, font->val->face); - - if (FT_Load_Glyph (font->val->face, val->key.index, FT_LOAD_DEFAULT) != 0) + face = _ft_unscaled_font_lock_face (unscaled); + if (!face) return CAIRO_STATUS_NO_MEMORY; + _ft_unscaled_font_set_scale (unscaled, &val->key.scale); + + if (FT_Load_Glyph (face, val->key.index, val->key.flags) != 0) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + /* * Note: the font's coordinate system is upside down from ours, so the * Y coordinates of the bearing and advance need to be negated. @@ -1011,11 +1218,11 @@ _cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) * by FreeType */ - val->extents.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) / sf.x_scale; - val->extents.y_bearing = -DOUBLE_FROM_26_6 (metrics->horiBearingY) / sf.y_scale; + val->extents.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) / unscaled->x_scale; + val->extents.y_bearing = -DOUBLE_FROM_26_6 (metrics->horiBearingY) / unscaled->y_scale; - val->extents.width = DOUBLE_FROM_26_6 (metrics->width) / sf.x_scale; - val->extents.height = DOUBLE_FROM_26_6 (metrics->height) / sf.y_scale; + val->extents.width = DOUBLE_FROM_26_6 (metrics->width) / unscaled->x_scale; + val->extents.height = DOUBLE_FROM_26_6 (metrics->height) / unscaled->y_scale; /* * use untransformed advance values @@ -1023,8 +1230,8 @@ _cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) should provide FT_LOAD_VERTICAL_LAYOUT */ - val->extents.x_advance = DOUBLE_FROM_26_6 (font->val->face->glyph->metrics.horiAdvance) / sf.x_scale; - val->extents.y_advance = 0 / sf.y_scale; + val->extents.x_advance = DOUBLE_FROM_26_6 (face->glyph->metrics.horiAdvance) / unscaled->x_scale; + val->extents.y_advance = 0 / unscaled->y_scale; outline = &glyphslot->outline; @@ -1052,14 +1259,16 @@ _cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) bitmap.buffer = calloc (1, stride * height); if (bitmap.buffer == NULL) { - return CAIRO_STATUS_NO_MEMORY; - }; + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } FT_Outline_Translate (outline, -cbox.xMin, -cbox.yMin); if (FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap) != 0) { free (bitmap.buffer); - return CAIRO_STATUS_NO_MEMORY; + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; } val->image = (cairo_image_surface_t *) @@ -1068,7 +1277,8 @@ _cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) width, height, stride); if (val->image == NULL) { free (bitmap.buffer); - return CAIRO_STATUS_NO_MEMORY; + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; } _cairo_image_surface_assume_ownership_of_data (val->image); @@ -1084,138 +1294,245 @@ _cairo_ft_font_create_glyph(cairo_image_glyph_cache_entry_t *val) val->size.x = (short) (cbox.xMin >> 6); val->size.y = - (short) (cbox.yMax >> 6); - return CAIRO_STATUS_SUCCESS; + FAIL: + _ft_unscaled_font_unlock_face (unscaled); + + return status; } const cairo_font_backend_t cairo_ft_font_backend = { _cairo_ft_font_create, - _cairo_ft_font_destroy, + _cairo_ft_font_destroy_font, + _cairo_ft_font_destroy_unscaled_font, _cairo_ft_font_font_extents, _cairo_ft_font_text_to_glyphs, _cairo_ft_font_glyph_extents, _cairo_ft_font_glyph_bbox, _cairo_ft_font_show_glyphs, _cairo_ft_font_glyph_path, + _cairo_ft_font_get_glyph_cache_key, _cairo_ft_font_create_glyph }; - /* implement the platform-specific interface */ +/** + * cairo_ft_font_create: + * @pattern: A fully resolved fontconfig + * pattern. A pattern can be resolved, by, among other things, calling + * FcConfigSubstitute(), FcDefaultSubstitute(), then + * FcFontMatch(). Cairo will call FcPatternReference() on this + * pattern, so you should not further modify the pattern, but you can + * release your reference to the pattern with FcPatternDestroy() if + * you no longer need to access it. + * @scale: The scale at which this font will be used. The + * scale is given by multiplying the font matrix (see + * cairo_transform_font()) by the current transformation matrix. + * The translation elements of the resulting matrix are ignored. + * + * Creates a new font for the FreeType font backend based on a + * fontconfig pattern. This font can then be used with + * cairo_set_font(), cairo_font_glyph_extents(), or FreeType backend + * specific functions like cairo_ft_font_lock_face(). + * + * Return value: a newly created #cairo_font_t. Free with + * cairo_font_destroy() when you are done using it. + **/ cairo_font_t * -cairo_ft_font_create (FT_Library ft_library, FcPattern *pattern) +cairo_ft_font_create (FcPattern *pattern, + cairo_matrix_t *scale) { - cairo_font_scale_t scale; - cairo_font_t *scaled; - cairo_ft_font_t *f = NULL; - ft_font_val_t *v = NULL; - FcPattern *dup; - - scale.matrix[0][0] = 1.; - scale.matrix[0][1] = 0.; - scale.matrix[1][0] = 0.; - scale.matrix[1][1] = 1.; - - scaled = malloc (sizeof (cairo_font_t)); - if (scaled == NULL) - goto FAIL; - - dup = FcPatternDuplicate(pattern); - if (dup == NULL) - goto FREE_SCALED; - - v = _create_from_library_and_pattern (ft_library, pattern); - if (v == NULL) - goto FREE_PATTERN; - - f = malloc (sizeof(cairo_ft_font_t)); - if (f == NULL) - goto FREE_VAL; - - if (_cairo_unscaled_font_init (&f->base, &cairo_ft_font_backend)) - goto FREE_VAL; - - f->pattern = dup; - f->val = v; - - _cairo_font_init (scaled, &scale, &f->base); - - return scaled; - - FREE_VAL: - _destroy_font_val (v); - - FREE_PATTERN: - FcPatternDestroy (dup); + cairo_font_scale_t sc; + double tx, ty; - FREE_SCALED: - free (scaled); + cairo_matrix_get_affine (scale, + &sc.matrix[0][0], &sc.matrix[0][1], + &sc.matrix[1][0], &sc.matrix[1][1], + &tx, &ty); - FAIL: - return NULL; + return _ft_font_create (pattern, &sc); } +/** + * cairo_ft_font_create_for_ft_face: + * @face: A FreeType face object, already opened. This must + * be kept around until the font object's refcount drops to + * zero and it is freed. The font object can be kept alive by + * internal caching, so it's safest to keep the face object + * around forever. + * @load_flags: The flags to pass to FT_Load_Glyph when loading + * glyphs from the font. These flags control aspects of + * rendering such as hinting and antialiasing. See the FreeType + * docs for full information. + * @scale: The scale at which this font will be used. The + * scale is given by multiplying the font matrix (see + * cairo_transform_font()) by the current transformation matrix. + * The translation elements of the resulting matrix are ignored. + * + * Creates a new font forthe FreeType font backend from a pre-opened + * FreeType face. This font can then be used with cairo_set_font(), + * cairo_font_glyph_extents(), or FreeType backend specific + * functions like cairo_ft_font_lock_face() Cairo will determine the + * pixel size and transformation from the @scale parameter and call + * FT_Set_Transform() and FT_Set_Pixel_Sizes(). + * + * Return value: a newly created #cairo_font_t. Free with + * cairo_font_destroy() when you are done using it. + **/ cairo_font_t * -cairo_ft_font_create_for_ft_face (FT_Face face) +cairo_ft_font_create_for_ft_face (FT_Face face, + int load_flags, + cairo_matrix_t *scale) { - cairo_font_scale_t scale; - cairo_font_t *scaled; cairo_ft_font_t *f = NULL; - ft_font_val_t *v = NULL; - - scale.matrix[0][0] = 1.; - scale.matrix[0][1] = 0.; - scale.matrix[1][0] = 0.; - scale.matrix[1][1] = 1.; - - scaled = malloc (sizeof (cairo_font_t)); - if (scaled == NULL) - goto FAIL; + ft_unscaled_font_t *unscaled = NULL; + cairo_font_scale_t sc; + double tx, ty; - v = _create_from_face (face, 0); - if (v == NULL) - goto FREE_SCALED; + unscaled = _ft_unscaled_font_create_from_face (face); + if (unscaled == NULL) + return NULL; f = malloc (sizeof(cairo_ft_font_t)); if (f == NULL) - goto FREE_VAL; + goto FREE_UNSCALED; - _cairo_unscaled_font_init (&f->base, &cairo_ft_font_backend); + f->unscaled = unscaled; f->pattern = NULL; - f->val = v; + f->load_flags = load_flags; - _cairo_font_init (scaled, &scale, &f->base); + cairo_matrix_get_affine (scale, + &sc.matrix[0][0], &sc.matrix[0][1], + &sc.matrix[1][0], &sc.matrix[1][1], + &tx, &ty); - return scaled; + _cairo_font_init ((cairo_font_t *)f, &sc, &cairo_ft_font_backend); - FREE_VAL: - _destroy_font_val (v); + return (cairo_font_t *)f; - FREE_SCALED: - free (scaled); + FREE_UNSCALED: + _cairo_unscaled_font_destroy ((cairo_unscaled_font_t *)unscaled); - FAIL: return NULL; } + +/** + * cairo_ft_font_lock_face: + * @ft_font: A #cairo_font_t from the FreeType font backend. Such an + * object can be created with cairo_ft_font_create() or + * cairo_ft_font_create_for_ft_face(). On some platforms the font from + * cairo_current_font() will also be a FreeType font, but using this + * functionality with fonts you don't create yourself is not + * recommended. + * + * cairo_ft_font_lock_face() gets the #FT_Face object from a FreeType + * backend font and scales it appropriately for the font. You must + * release the face with cairo_ft_font_unlock_face() + * when you are done using it. Since the #FT_Face object can be + * shared between multiple #cairo_font_t objects, you must not + * lock any other font objects until you unlock this one. A count is + * kept of the number of times cairo_ft_font_lock_face() is + * called. cairo_ft_font_unlock_face() must be called the same number + * of times. + * + * You must be careful when using this function in a library or in a + * threaded application, because other threads may lock faces that + * share the same #FT_Face object. For this reason, you must call + * cairo_ft_lock() before locking any face objects, and + * cairo_ft_unlock() after you are done. (These functions are not yet + * implemented, so this function cannot be currently safely used in a + * threaded application.) + + * Return value: The #FT_Face object for @font, scaled appropriately. + **/ FT_Face -cairo_ft_font_face (cairo_font_t *abstract_font) +cairo_ft_font_lock_face (cairo_font_t *abstract_font) { - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font->unscaled; + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + FT_Face face; - if (font == NULL || font->val == NULL) - return NULL; + face = _ft_unscaled_font_lock_face (font->unscaled); + if (!face) + return NULL; + + _ft_unscaled_font_set_scale (font->unscaled, &font->base.scale); + + return face; +} - return font->val->face; +/** + * cairo_ft_font_unlock_face: + * @ft_font: A #cairo_font_t from the FreeType font backend. Such an + * object can be created with cairo_ft_font_create() or + * cairo_ft_font_create_for_ft_face(). On some platforms the font from + * cairo_current_font() will also be a FreeType font, but using this + * functionality with fonts you don't create yourself is not + * recommended. + * + * Releases a face obtained with cairo_ft_font_lock_face(). See the + * documentation for that function for full details. + **/ +void +cairo_ft_font_unlock_face (cairo_font_t *abstract_font) +{ + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + + _ft_unscaled_font_unlock_face (font->unscaled); } +/** + * cairo_ft_font_get_pattern: + * @ft_font: A #cairo_font_t from the FreeType font backend. Such an + * object can be created with cairo_ft_font_create() or + * cairo_ft_font_create_for_ft_face(). On some platforms the font from + * cairo_current_font() will also be a FreeType font, but using this + * functionality with fonts you don't create yourself is not + * recommended. + * + * cairo_ft_font_get_pattern() gets the #FcPattern for a FreeType + * backend font. + + * Return value: The #FcPattenr for @font. The return value is owned + * by the font, so you must not modify it, and must call + * FcPatternReference() to keep a persistant reference to the + * pattern. If the font was created with cairo_ft_font_create_for_ft_face() + * returns %NULL. + **/ FcPattern * -cairo_ft_font_pattern (cairo_font_t *abstract_font) +cairo_ft_font_get_pattern (cairo_font_t *abstract_font) { - cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font->unscaled; + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; if (font == NULL) return NULL; return font->pattern; } + +/* We expose our unscaled font implementation internally for the the + * PDF backend, which needs to keep track of the the different + * fonts-on-disk used by a document, so it can embed them. + */ +cairo_unscaled_font_t * +_cairo_ft_font_get_unscaled_font (cairo_font_t *abstract_font) +{ + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + + return (cairo_unscaled_font_t *)font->unscaled; +} + +/* This differs from _cairo_ft_scaled_font_lock_face in that it doesn't + * set the scale on the face, but just returns it at the last scale. + */ +FT_Face +_cairo_ft_unscaled_font_lock_face (cairo_unscaled_font_t *unscaled_font) +{ + return _ft_unscaled_font_lock_face ((ft_unscaled_font_t *)unscaled_font); +} + +void +_cairo_ft_unscaled_font_unlock_face (cairo_unscaled_font_t *unscaled_font) +{ + _ft_unscaled_font_unlock_face ((ft_unscaled_font_t *)unscaled_font); +} diff --git a/src/cairo_gdip_font.cpp b/src/cairo_gdip_font.cpp deleted file mode 100644 index e932e3bac..000000000 --- a/src/cairo_gdip_font.cpp +++ /dev/null @@ -1,665 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2004 Stuart Parmenter - * - * 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 Stuart Parmenter. - * - * Contributor(s): - * Stuart Parmenter <pavlov@pavlov.net> - */ - -extern "C" { -#include "cairoint.h" -} - -#include <windows.h> - -#include <gdiplus.h> -using namespace Gdiplus; - -#if 0 -#include <fontconfig/fontconfig.h> -#include <fontconfig/fcfreetype.h> - -#include <ft2build.h> -#include FT_FREETYPE_H -#include FT_OUTLINE_H -#include FT_IMAGE_H -#endif - -typedef struct { - cairo_font_t base; - HDC hdc; - HFONT hfont; -} cairo_win32_font_t; - - - -static int -_utf8_to_glyphs (cairo_win32_font_t *font, - const unsigned char *utf8, - double x0, - double y0, - cairo_glyph_t **glyphs, - size_t *nglyphs) -{ - /* XXX implement me */ - *glyphs = NULL; - *nglyphs = 0; - - return 0; -} - -/* implement the platform-specific interface */ - -cairo_font_t * -cairo_win32_font_create (HFONT hfont) -{ - cairo_win32_font_t *f = (cairo_win32_font_t*)malloc(sizeof(cairo_win32_font_t)); - if (f == NULL) - return NULL; - - f->hfont = hfont; - - _cairo_font_init (&f->base, &cairo_win32_font_backend); - - return (cairo_font_t *) f; -} - -#if 0 -FT_Face -cairo_win32_font_face (cairo_font_t *abstract_font) -{ - cairo_win32_font_t *font = (cairo_win32_font_t *) abstract_font; - - if (font == NULL) - return NULL; - - return font->face; -} - -FcPattern * -cairo_win32_font_pattern (cairo_font_t *abstract_font) -{ - cairo_win32_font_t *font = (cairo_win32_font_t *) abstract_font; - - if (font == NULL) - return NULL; - - return font->pattern; -} -#endif - -/* implement the backend interface */ - -static cairo_font_t * -_cairo_win32_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight) -{ - int fontHeight = 60; // in Pixels in this case - int fontWidth = 0; - int italic = 0; - int bold = FW_REGULAR; - - switch (slant) { - case CAIRO_FONT_SLANT_ITALIC: - italic = 1; - case CAIRO_FONT_SLANT_OBLIQUE: - case CAIRO_FONT_SLANT_NORMAL: - default: - break; - } - - if (weight == CAIRO_FONT_WEIGHT_BOLD) - bold = FW_BOLD; - - HFONT hfont = CreateFont(fontHeight, // height of font - fontWidth, // average character width - 0, // angle of escapement - 0, // base-line orientation angle - bold, // font weight - italic, // italic attribute option - FALSE, // underline attribute option - FALSE, // strikeout attribute option - ANSI_CHARSET, // character set identifier - OUT_DEFAULT_PRECIS, // output precision - CLIP_DEFAULT_PRECIS, // clipping precision - ANTIALIASED_QUALITY, // output quality - FF_DONTCARE, // pitch and family - family); // typeface name - - return cairo_win32_font_create(hfont); -} - -static cairo_font_t * -_cairo_win32_font_copy (void *abstract_font) -{ - cairo_win32_font_t *font_new = NULL; - cairo_win32_font_t *font = (cairo_win32_font_t*)abstract_font; - - if (font->base.backend != &cairo_win32_font_backend) - return NULL; - - font_new = (cairo_win32_font_t *) cairo_win32_font_create(font->hfont); - if (font_new == NULL) - return NULL; - - return (cairo_font_t *) font_new; -} - -static void -_cairo_win32_font_destroy (void *abstract_font) -{ - cairo_win32_font_t *font = (cairo_win32_font_t*)abstract_font; - - //delete font->font; - - free (font); -} - -static cairo_status_t -_cairo_win32_font_font_extents (void *abstract_font, - cairo_font_extents_t *extents) -{ - cairo_win32_font_t *font = (cairo_win32_font_t*)abstract_font; - - TEXTMETRIC metrics; - GetTextMetrics(font->hdc, &metrics); - - extents->ascent = metrics.tmAscent; - extents->descent = metrics.tmDescent; - extents->height = metrics.tmHeight; - extents->max_x_advance = 0; /* XXX */ - extents->max_y_advance = 0; /* XXX */ - - -#if 0 - FT_Face face = font->face; - double scale_x, scale_y; - - double upm = face->units_per_EM; - - _cairo_matrix_compute_scale_factors (&font->base.matrix, &scale_x, &scale_y); - - extents->ascent = face->ascender / upm * scale_y; - extents->descent = face->descender / upm * scale_y; - extents->height = face->height / upm * scale_y; - extents->max_x_advance = face->max_advance_width / upm * scale_x; - extents->max_y_advance = face->max_advance_height / upm * scale_y; -#endif - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_font_glyph_extents (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) -{ - cairo_win32_font_t *font = (cairo_win32_font_t*)abstract_font; - - int i; - for (i = 0; i < num_glyphs; ++i) { - GLYPHMETRICS metrics; - GetGlyphOutline(font->hdc, 'a', GGO_METRICS, &metrics, 0, NULL, NULL); - - extents->width += metrics.gmBlackBoxX; - extents->height += metrics.gmBlackBoxY; - /* metrics has: - UINT gmBlackBoxX; - UINT gmBlackBoxY; - POINT gmptGlyphOrigin; - short gmCellIncX; - short gmCellIncY; - - extents has: - double x_bearing; - double y_bearing; - double width; - double height; - double x_advance; - double y_advance; - */ - } - -#if 0 - int i; - cairo_win32_font_t *font = abstract_font; - cairo_point_double_t origin; - cairo_point_double_t glyph_min, glyph_max; - cairo_point_double_t total_min, total_max; - FT_Error error; - FT_Face face = font->face; - FT_GlyphSlot glyph = face->glyph; - FT_Glyph_Metrics *metrics = &glyph->metrics; - - if (num_glyphs == 0) - { - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; - - return CAIRO_STATUS_SUCCESS; - } - - origin.x = glyphs[0].x; - origin.y = glyphs[0].y; - - _install_font_matrix (&font->base.matrix, face); - - for (i = 0; i < num_glyphs; i++) - { - error = FT_Load_Glyph (face, glyphs[i].index, FT_LOAD_DEFAULT); - /* XXX: What to do in this error case? */ - if (error) - continue; - - /* XXX: Need to add code here to check the font's FcPattern - for FC_VERTICAL_LAYOUT and if set get vertBearingX/Y - instead. This will require that - cairo_win32_font_create_for_ft_face accept an - FcPattern. */ - glyph_min.x = glyphs[i].x + DOUBLE_FROM_26_6 (metrics->horiBearingX); - glyph_min.y = glyphs[i].y - DOUBLE_FROM_26_6 (metrics->horiBearingY); - glyph_max.x = glyph_min.x + DOUBLE_FROM_26_6 (metrics->width); - glyph_max.y = glyph_min.y + DOUBLE_FROM_26_6 (metrics->height); - - if (i==0) { - total_min = glyph_min; - total_max = glyph_max; - } else { - if (glyph_min.x < total_min.x) - total_min.x = glyph_min.x; - if (glyph_min.y < total_min.y) - total_min.y = glyph_min.y; - - if (glyph_max.x > total_max.x) - total_max.x = glyph_max.x; - if (glyph_max.y > total_max.y) - total_max.y = glyph_max.y; - } - } - - extents->x_bearing = total_min.x - origin.x; - extents->y_bearing = total_min.y - origin.y; - extents->width = total_max.x - total_min.x; - extents->height = total_max.y - total_min.y; - extents->x_advance = glyphs[i-1].x + DOUBLE_FROM_26_6 (metrics->horiAdvance) - origin.x; - extents->y_advance = glyphs[i-1].y + 0 - origin.y; -#endif - return CAIRO_STATUS_SUCCESS; -} - - -static cairo_status_t -_cairo_win32_font_text_extents (void *abstract_font, - const unsigned char *utf8, - cairo_text_extents_t *extents) -{ - cairo_win32_font_t *font = (cairo_win32_font_t*)abstract_font; - - cairo_glyph_t *glyphs; - size_t nglyphs; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (_utf8_to_glyphs (font, utf8, 0, 0, &glyphs, &nglyphs)) - { - status = _cairo_win32_font_glyph_extents (font, glyphs, nglyphs, - extents); - free (glyphs); - } - - return status; -} - -static cairo_status_t -_cairo_win32_font_glyph_bbox (void *abstract_font, - cairo_surface_t *surface, - const cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox) -{ - cairo_win32_font_t *font = (cairo_win32_font_t*)abstract_font; -#if 0 - cairo_surface_t *mask = NULL; - cairo_glyph_size_t size; - - cairo_fixed_t x1, y1, x2, y2; - int i; - - bbox->p1.x = bbox->p1.y = CAIRO_MAXSHORT << 16; - bbox->p2.x = bbox->p2.y = CAIRO_MINSHORT << 16; - - if (font == NULL - || surface == NULL - || glyphs == NULL) - return CAIRO_STATUS_NO_MEMORY; - - for (i = 0; i < num_glyphs; i++) - { - mask = _cairo_font_lookup_glyph (&font->base, surface, - &glyphs[i], &size); - if (mask == NULL) - continue; - - x1 = _cairo_fixed_from_double (glyphs[i].x + size.x); - y1 = _cairo_fixed_from_double (glyphs[i].y - size.y); - x2 = x1 + _cairo_fixed_from_double (size.width); - y2 = y1 + _cairo_fixed_from_double (size.height); - - if (x1 < bbox->p1.x) - bbox->p1.x = x1; - - if (y1 < bbox->p1.y) - bbox->p1.y = y1; - - if (x2 > bbox->p2.x) - bbox->p2.x = x2; - - if (y2 > bbox->p2.y) - bbox->p2.y = y2; - - if (mask) - cairo_surface_destroy (mask); - } -#endif - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_font_text_bbox (void *abstract_font, - cairo_surface_t *surface, - double x0, - double y0, - const unsigned char *utf8, - cairo_box_t *bbox) -{ - cairo_win32_font_t *font = (cairo_win32_font_t*)abstract_font; - - cairo_glyph_t *glyphs; - size_t num_glyphs; - - if (_utf8_to_glyphs (font, utf8, x0, y0, &glyphs, &num_glyphs)) - { - cairo_status_t res; - res = _cairo_win32_font_glyph_bbox (font, surface, - glyphs, num_glyphs, bbox); - free (glyphs); - return res; - } - else - return CAIRO_STATUS_NO_MEMORY; -} - -static cairo_status_t -_cairo_win32_font_show_glyphs (void *abstract_font, - cairo_operator_t op, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - const cairo_glyph_t *glyphs, - int num_glyphs) -{ - cairo_win32_font_t *font = (cairo_win32_font_t*)abstract_font; -#if 0 - cairo_status_t status; - cairo_surface_t *mask = NULL; - cairo_glyph_size_t size; - - double x, y; - int i; - - if (font == NULL - || source == NULL - || surface == NULL - || glyphs == NULL) - return CAIRO_STATUS_NO_MEMORY; - - for (i = 0; i < num_glyphs; i++) - { - mask = _cairo_font_lookup_glyph (&font->base, surface, - &glyphs[i], &size); - if (mask == NULL) - continue; - - x = glyphs[i].x; - y = glyphs[i].y; - - status = _cairo_surface_composite (operator, source, mask, surface, - source_x + x + size.x, - source_y + y - size.y, - 0, 0, - x + size.x, - y - size.y, - (double) size.width, - (double) size.height); - - cairo_surface_destroy (mask); - - if (status) - return status; - } -#endif - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_font_show_text (void *abstract_font, - cairo_operator_t op, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - double x0, - double y0, - const unsigned char *utf8) -{ - cairo_win32_font_t *font = (cairo_win32_font_t*)abstract_font; - - cairo_glyph_t *glyphs; - size_t num_glyphs; - - if (_utf8_to_glyphs (font, utf8, x0, y0, &glyphs, &num_glyphs)) - { - cairo_status_t res; - res = _cairo_win32_font_show_glyphs (font, op, - source, surface, - source_x, source_y, - glyphs, num_glyphs); - free (glyphs); - return res; - } - else - return CAIRO_STATUS_NO_MEMORY; -} - -static cairo_status_t -_cairo_win32_font_glyph_path (void *abstract_font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path) -{ -#if 0 - int i; - cairo_win32_font_t *font = abstract_font; - FT_GlyphSlot glyph; - FT_Error error; - FT_Outline_Funcs outline_funcs = { - _move_to, - _line_to, - _conic_to, - _cubic_to, - 0, /* shift */ - 0, /* delta */ - }; - - glyph = font->face->glyph; - _install_font_matrix (&font->base.matrix, font->face); - - for (i = 0; i < num_glyphs; i++) - { - FT_Matrix invert_y = { - DOUBLE_TO_16_16 (1.0), 0, - 0, DOUBLE_TO_16_16 (-1.0), - }; - - error = FT_Load_Glyph (font->face, glyphs[i].index, FT_LOAD_DEFAULT); - /* XXX: What to do in this error case? */ - if (error) - continue; - /* XXX: Do we want to support bitmap fonts here? */ - if (glyph->format == ft_glyph_format_bitmap) - continue; - - /* Font glyphs have an inverted Y axis compared to cairo. */ - FT_Outline_Transform (&glyph->outline, &invert_y); - FT_Outline_Translate (&glyph->outline, - DOUBLE_TO_26_6(glyphs[i].x), - DOUBLE_TO_26_6(glyphs[i].y)); - FT_Outline_Decompose (&glyph->outline, &outline_funcs, path); - } - _cairo_path_close_path (path); -#endif - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_font_text_path (void *abstract_font, - double x, - double y, - const unsigned char *utf8, - cairo_path_t *path) -{ -#if 0 - cairo_win32_font_t *font = abstract_font; - cairo_glyph_t *glyphs; - size_t nglyphs; - - if (_utf8_to_glyphs (font, utf8, x, y, &glyphs, &nglyphs)) - { - cairo_status_t res; - res = _cairo_win32_font_glyph_path (font, glyphs, nglyphs, path); - free (glyphs); - return res; - } - else -#endif - return CAIRO_STATUS_NO_MEMORY; -} - -static cairo_surface_t * -_cairo_win32_font_create_glyph (void *abstract_font, - const cairo_glyph_t *glyph, - cairo_glyph_size_t *return_size) -{ -#if 0 - cairo_win32_font_t *font = abstract_font; - cairo_image_surface_t *image; - FT_GlyphSlot glyphslot; - unsigned int width, height, stride; - FT_Outline *outline;s - FT_BBox cbox; - FT_Bitmap bitmap; - - glyphslot = font->face->glyph; - _install_font_matrix (&font->base.matrix, font->face); - - FT_Load_Glyph (font->face, glyph->index, FT_LOAD_DEFAULT); - - outline = &glyphslot->outline; - - FT_Outline_Get_CBox (outline, &cbox); - - cbox.xMin &= -64; - cbox.yMin &= -64; - cbox.xMax = (cbox.xMax + 63) & -64; - cbox.yMax = (cbox.yMax + 63) & -64; - - width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6); - height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6); - stride = (width + 3) & -4; - - bitmap.pixel_mode = ft_pixel_mode_grays; - bitmap.num_grays = 256; - bitmap.width = width; - bitmap.rows = height; - bitmap.pitch = stride; - - if (width * height == 0) - return NULL; - - bitmap.buffer = malloc (stride * height); - if (bitmap.buffer == NULL) - return NULL; - - memset (bitmap.buffer, 0x0, stride * height); - - FT_Outline_Translate (outline, -cbox.xMin, -cbox.yMin); - FT_Outline_Get_Bitmap (glyphslot->library, outline, &bitmap); - - image = (cairo_image_surface_t *) - cairo_image_surface_create_for_data ((char *) bitmap.buffer, - CAIRO_FORMAT_A8, - width, height, stride); - if (image == NULL) { - free (bitmap.buffer); - return NULL; - } - - _cairo_image_surface_assume_ownership_of_data (image); - - return_size->width = (unsigned short) width; - return_size->height = (unsigned short) height; - return_size->x = (short) (cbox.xMin >> 6); - return_size->y = (short) (cbox.yMax >> 6); - - return &image->base; -#endif - return NULL; -} - -const struct cairo_font_backend cairo_win32_font_backend = { - _cairo_win32_font_create, - _cairo_win32_font_copy, - _cairo_win32_font_destroy, - _cairo_win32_font_font_extents, - _cairo_win32_font_text_extents, - _cairo_win32_font_glyph_extents, - _cairo_win32_font_text_bbox, - _cairo_win32_font_glyph_bbox, - _cairo_win32_font_show_text, - _cairo_win32_font_show_glyphs, - _cairo_win32_font_text_path, - _cairo_win32_font_glyph_path, - _cairo_win32_font_create_glyph -}; diff --git a/src/cairo_gdip_surface.cpp b/src/cairo_gdip_surface.cpp deleted file mode 100644 index ec1982b55..000000000 --- a/src/cairo_gdip_surface.cpp +++ /dev/null @@ -1,727 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2004 Stuart Parmenter - * - * 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 Stuart Parmenter. - * - * Contributor(s): - * Stuart Parmenter <pavlov@pavlov.net> - */ - -extern "C" { -#include "cairoint.h" -} - -/* export symbols, for cairo.dll only */ - -#pragma comment(linker, "/EXPORT:_cairo_create") -#pragma comment(linker, "/EXPORT:_cairo_reference") -#pragma comment(linker, "/EXPORT:_cairo_destroy") -#pragma comment(linker, "/EXPORT:_cairo_save") -#pragma comment(linker, "/EXPORT:_cairo_restore") -#pragma comment(linker, "/EXPORT:_cairo_copy") -#pragma comment(linker, "/EXPORT:_cairo_set_target_surface") -#pragma comment(linker, "/EXPORT:_cairo_set_target_image") - -#ifdef CAIRO_HAS_PS_SURFACE -#pragma comment(linker, "/EXPORT:_cairo_set_target_ps") -#endif - -#ifdef CAIRO_HAS_PS_SURFACE -#pragma comment(linker, "/EXPORT:_cairo_set_target_pdf") -#endif - -#ifdef CAIRO_HAS_PNG_SURFACE -#pragma comment(linker, "/EXPORT:_cairo_set_target_png") -#endif - -#ifdef CAIRO_HAS_GL_SURFACE -#pragma comment(linker, "/EXPORT:_cairo_set_target_gl") -#endif - -#ifdef CAIRO_HAS_WIN32_SURFACE -#pragma comment(linker, "/EXPORT:_cairo_set_target_win32") -#endif - -#pragma comment(linker, "/EXPORT:_cairo_set_operator") -#pragma comment(linker, "/EXPORT:_cairo_set_rgb_color") -#pragma comment(linker, "/EXPORT:_cairo_set_pattern") -#pragma comment(linker, "/EXPORT:_cairo_set_alpha") -#pragma comment(linker, "/EXPORT:_cairo_set_tolerance") -#pragma comment(linker, "/EXPORT:_cairo_set_fill_rule") -#pragma comment(linker, "/EXPORT:_cairo_set_line_width") -#pragma comment(linker, "/EXPORT:_cairo_set_line_cap") -#pragma comment(linker, "/EXPORT:_cairo_set_line_join") -#pragma comment(linker, "/EXPORT:_cairo_set_dash") -#pragma comment(linker, "/EXPORT:_cairo_set_miter_limit") -#pragma comment(linker, "/EXPORT:_cairo_set_line_cap") - -#pragma comment(linker, "/EXPORT:_cairo_translate") -#pragma comment(linker, "/EXPORT:_cairo_scale") -#pragma comment(linker, "/EXPORT:_cairo_rotate") - -#pragma comment(linker, "/EXPORT:_cairo_concat_matrix") -#pragma comment(linker, "/EXPORT:_cairo_set_matrix") -#pragma comment(linker, "/EXPORT:_cairo_default_matrix") -#pragma comment(linker, "/EXPORT:_cairo_identity_matrix") -#pragma comment(linker, "/EXPORT:_cairo_transform_point") -#pragma comment(linker, "/EXPORT:_cairo_transform_distance") -#pragma comment(linker, "/EXPORT:_cairo_inverse_transform_point") -#pragma comment(linker, "/EXPORT:_cairo_inverse_transform_distance") - -#pragma comment(linker, "/EXPORT:_cairo_new_path") -#pragma comment(linker, "/EXPORT:_cairo_move_to") -#pragma comment(linker, "/EXPORT:_cairo_line_to") -#pragma comment(linker, "/EXPORT:_cairo_curve_to") -#pragma comment(linker, "/EXPORT:_cairo_arc") -#pragma comment(linker, "/EXPORT:_cairo_arc_negative") -#pragma comment(linker, "/EXPORT:_cairo_rel_move_to") -#pragma comment(linker, "/EXPORT:_cairo_rel_line_to") -#pragma comment(linker, "/EXPORT:_cairo_rel_curve_to") -#pragma comment(linker, "/EXPORT:_cairo_rectangle") -#pragma comment(linker, "/EXPORT:_cairo_close_path") - -#pragma comment(linker, "/EXPORT:_cairo_stroke") -#pragma comment(linker, "/EXPORT:_cairo_fill") -#pragma comment(linker, "/EXPORT:_cairo_copy_page") -#pragma comment(linker, "/EXPORT:_cairo_show_page") -#pragma comment(linker, "/EXPORT:_cairo_in_stroke") -#pragma comment(linker, "/EXPORT:_cairo_in_fill") -#pragma comment(linker, "/EXPORT:_cairo_stroke_extents") -#pragma comment(linker, "/EXPORT:_cairo_fill_extents") - -#pragma comment(linker, "/EXPORT:_cairo_init_clip") -#pragma comment(linker, "/EXPORT:_cairo_clip") -#pragma comment(linker, "/EXPORT:_cairo_select_font") -#pragma comment(linker, "/EXPORT:_cairo_scale_font") -#pragma comment(linker, "/EXPORT:_cairo_transform_font") -#pragma comment(linker, "/EXPORT:_cairo_show_text") -#pragma comment(linker, "/EXPORT:_cairo_show_glyphs") -#pragma comment(linker, "/EXPORT:_cairo_current_font") -#pragma comment(linker, "/EXPORT:_cairo_current_font_extents") -#pragma comment(linker, "/EXPORT:_cairo_set_font") -#pragma comment(linker, "/EXPORT:_cairo_text_extents") -#pragma comment(linker, "/EXPORT:_cairo_glyph_extents") -#pragma comment(linker, "/EXPORT:_cairo_text_path") -#pragma comment(linker, "/EXPORT:_cairo_glyph_path") -#pragma comment(linker, "/EXPORT:_cairo_font_reference") -#pragma comment(linker, "/EXPORT:_cairo_font_destroy") -#pragma comment(linker, "/EXPORT:_cairo_font_set_transform") -#pragma comment(linker, "/EXPORT:_cairo_font_current_transform") - -/*#pragma comment(linker, "/EXPORT:_cairo_ft_font_create") -#pragma comment(linker, "/EXPORT:_cairo_ft_font_create_for_ft_face") -*/ -#if 0 -/* hmm, this function doesn't exist, but __cairo_ft_font_destroy does */ -#pragma comment(linker, "/EXPORT:_cairo_ft_font_destroy") -#endif -/* -#pragma comment(linker, "/EXPORT:_cairo_ft_font_face") -#pragma comment(linker, "/EXPORT:_cairo_ft_font_pattern") -*/ -#pragma comment(linker, "/EXPORT:_cairo_show_surface") -#pragma comment(linker, "/EXPORT:_cairo_current_operator") -#pragma comment(linker, "/EXPORT:_cairo_current_rgb_color") -#pragma comment(linker, "/EXPORT:_cairo_current_pattern") -#pragma comment(linker, "/EXPORT:_cairo_current_alpha") -#pragma comment(linker, "/EXPORT:_cairo_current_tolerance") -#pragma comment(linker, "/EXPORT:_cairo_current_point") -#pragma comment(linker, "/EXPORT:_cairo_current_fill_rule") -#pragma comment(linker, "/EXPORT:_cairo_current_line_width") -#pragma comment(linker, "/EXPORT:_cairo_current_line_cap") -#pragma comment(linker, "/EXPORT:_cairo_current_line_join") -#pragma comment(linker, "/EXPORT:_cairo_current_rgb_color") -#pragma comment(linker, "/EXPORT:_cairo_current_miter_limit") -#pragma comment(linker, "/EXPORT:_cairo_current_matrix") -#pragma comment(linker, "/EXPORT:_cairo_current_target_surface") -#pragma comment(linker, "/EXPORT:_cairo_current_path") -#pragma comment(linker, "/EXPORT:_cairo_current_path_flat") - -#pragma comment(linker, "/EXPORT:_cairo_status") -#pragma comment(linker, "/EXPORT:_cairo_status_string") - -#pragma comment(linker, "/EXPORT:_cairo_surface_create_for_image") -#pragma comment(linker, "/EXPORT:_cairo_surface_create_similar") -#pragma comment(linker, "/EXPORT:_cairo_surface_reference") -#pragma comment(linker, "/EXPORT:_cairo_surface_destroy") -#pragma comment(linker, "/EXPORT:_cairo_surface_set_repeat") -#pragma comment(linker, "/EXPORT:_cairo_surface_set_matrix") -#pragma comment(linker, "/EXPORT:_cairo_surface_get_matrix") -#pragma comment(linker, "/EXPORT:_cairo_surface_set_filter") -#pragma comment(linker, "/EXPORT:_cairo_surface_get_filter") - -#pragma comment(linker, "/EXPORT:_cairo_image_surface_create") -#pragma comment(linker, "/EXPORT:_cairo_image_surface_create_for_data") -#pragma comment(linker, "/EXPORT:_cairo_pattern_create_for_surface") -#pragma comment(linker, "/EXPORT:_cairo_pattern_create_linear") -#pragma comment(linker, "/EXPORT:_cairo_pattern_create_radial") -#pragma comment(linker, "/EXPORT:_cairo_pattern_reference") -#pragma comment(linker, "/EXPORT:_cairo_pattern_destroy") -#pragma comment(linker, "/EXPORT:_cairo_pattern_add_color_stop") -#pragma comment(linker, "/EXPORT:_cairo_pattern_set_matrix") -#pragma comment(linker, "/EXPORT:_cairo_pattern_get_matrix") -#pragma comment(linker, "/EXPORT:_cairo_pattern_set_extend") -#pragma comment(linker, "/EXPORT:_cairo_pattern_get_extend") -#pragma comment(linker, "/EXPORT:_cairo_pattern_set_filter") -#pragma comment(linker, "/EXPORT:_cairo_pattern_get_filter") - -#ifdef CAIRO_HAS_PS_SURFACE -#pragma comment(linker, "/EXPORT:_cairo_ps_surface_create") -#endif - -#ifdef CAIRO_HAS_PNG_SURFACE -#pragma comment(linker, "/EXPORT:_cairo_png_surface_create") -#endif - -#ifdef CAIRO_HAS_GL_SURFACE -#pragma comment(linker, "/EXPORT:_cairo_gl_surface_create") -#endif - -#pragma comment(linker, "/EXPORT:_cairo_matrix_create") -#pragma comment(linker, "/EXPORT:_cairo_matrix_destroy") -#pragma comment(linker, "/EXPORT:_cairo_matrix_copy") -#pragma comment(linker, "/EXPORT:_cairo_matrix_set_identity") -#pragma comment(linker, "/EXPORT:_cairo_matrix_set_affine") -#pragma comment(linker, "/EXPORT:_cairo_matrix_get_affine") -#pragma comment(linker, "/EXPORT:_cairo_matrix_translate") -#pragma comment(linker, "/EXPORT:_cairo_matrix_scale") -#pragma comment(linker, "/EXPORT:_cairo_matrix_rotate") -#pragma comment(linker, "/EXPORT:_cairo_matrix_invert") -#pragma comment(linker, "/EXPORT:_cairo_matrix_multiply") -#pragma comment(linker, "/EXPORT:_cairo_matrix_transform_distance") -#pragma comment(linker, "/EXPORT:_cairo_matrix_transform_point") - -#include <windows.h> - -#include <gdiplus.h> -using namespace Gdiplus; - -extern const cairo_surface_backend_t cairo_win32_surface_backend; - -cairo_surface_t *_cairo_win32_surface_create (HDC dc); - -void -cairo_set_target_win32(cairo_t *cr, HDC dc) -{ - cairo_surface_t *surface; - - surface = _cairo_win32_surface_create(dc); - if (surface == NULL) { - cr->status = CAIRO_STATUS_NO_MEMORY; - return; - } - - cairo_set_target_surface (cr, surface); - - /* cairo_set_target_surface takes a reference, so we must destroy ours */ - cairo_surface_destroy (surface); -} - -typedef struct cairo_win32_surface { - cairo_surface_t base; - Graphics *gr; - - Brush *brush; -} cairo_win32_surface_t; - - -static void -_cairo_win32_surface_erase(cairo_win32_surface_t *surface); - -cairo_surface_t * -_cairo_win32_surface_create(HDC dc) -{ - cairo_win32_surface_t *surface; - - surface = (cairo_win32_surface_t*)malloc(sizeof(cairo_win32_surface_t)); - if (surface == NULL) - return NULL; - - surface->gr = new Graphics(dc); - surface->brush = NULL; -// surface->gr->TranslateTransform(-2000*2.5, -3000*2.2); -// surface->gr->ScaleTransform(20, 20); - - surface->gr->SetSmoothingMode(SmoothingModeAntiAlias); - - /* do pixmap creation, etc */ - - _cairo_surface_init(&surface->base, &cairo_win32_surface_backend); - - _cairo_win32_surface_erase(surface); - - return &surface->base; -} - -static cairo_surface_t * -_cairo_win32_surface_create_similar(void *abstract_src, - cairo_format_t format, - int drawable, - int width, - int height) -{ - return NULL; -} - -static void -_cairo_win32_surface_destroy (void *abstract_surface) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)abstract_surface; - - delete surface->gr; - delete surface->brush; - - free(surface); -} - -static void -_cairo_win32_surface_erase(cairo_win32_surface_t *surface) -{ - surface->gr->Clear(Color(255, 0, 0, 0)); -} - -/* XXX: We should re-work this interface to return both X/Y ppi values. */ -static double -_cairo_win32_surface_pixels_per_inch(void *abstract_surface) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)abstract_surface; - - return surface->gr->GetDpiY(); -} - -static Image * -make_image(cairo_image_surface_t *image) -{ - Rect r(0, 0, image->width, image->height); - Bitmap *b = new Bitmap(image->width, image->height, PixelFormat32bppPARGB); - BitmapData data; - - b->LockBits(&r, ImageLockModeWrite, PixelFormat32bppPARGB, &data); - - memcpy(data.Scan0, image->data, image->stride * image->height); - - b->UnlockBits(&data); - - return b; -} - -static cairo_image_surface_t * -_cairo_win32_surface_get_image(void *abstract_surface) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)abstract_surface; - - /* need to figure out how to get the data from a Graphics and turn it in to an Image */ - return NULL; -} - - -static cairo_status_t -_cairo_win32_surface_set_image(void *abstract_surface, - cairo_image_surface_t *image) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)abstract_surface; - - Image *img = make_image(image); - surface->gr->DrawImage(img, 0, 0); - delete img; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_surface_set_matrix(void *abstract_surface, - cairo_matrix_t *matrix) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)abstract_surface; - - Matrix m((float)matrix->m[0][0], (float)matrix->m[1][0], (float)matrix->m[2][0], - (float)matrix->m[0][1], (float)matrix->m[1][1], (float)matrix->m[2][1]); - - - surface->gr->SetTransform(&m); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_surface_set_filter (void *abstract_surface, - cairo_filter_t filter) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)abstract_surface; - SmoothingMode mode; - - switch (filter) { - case CAIRO_FILTER_FAST: - mode = SmoothingModeNone; - break; - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_NEAREST: - case CAIRO_FILTER_BILINEAR: - case CAIRO_FILTER_GAUSSIAN: - default: - mode = SmoothingModeAntiAlias; - break; - } - surface->gr->SetSmoothingMode(mode); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_win32_surface_set_repeat (void *abstract_surface, - int repeat) -{ - /* what is this function supposed to do? */ - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)abstract_surface; - - return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_int_status_t -_cairo_win32_surface_composite(cairo_operator_t op, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - - -static cairo_int_status_t -_cairo_win32_surface_fill_rectangles(void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_t *rects, - int num_rects) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)abstract_surface; - - SolidBrush brush(Color(color->alpha_short, color->red_short, color->green_short, color->blue_short)); - - RectF *r = new RectF[num_rects]; /* should really allocate a small number here on the stack and use those if possible */ - for (int i = 0; i < num_rects; ++i) { - r[i].X = rects[i].x; - r[i].Y = rects[i].y; - r[i].Width = rects[i].width; - r[i].Height = rects[i].height; - } - - surface->gr->FillRectangles(&brush, r, num_rects); - - delete[] r; - - return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; -} - - - -static int -_cairo_win32_extract_rectangle (cairo_trapezoid_t *trap, - RectF *rect) -{ - if (trap->left.p1.x == trap->left.p2.x && - trap->right.p1.x == trap->right.p2.x && - trap->left.p1.y == trap->right.p1.y && - trap->left.p2.y == trap->right.p2.y) { - - double x = _cairo_fixed_to_double (trap->left.p1.x); - double y = _cairo_fixed_to_double (trap->left.p1.y); - rect->X = (float)x; - rect->Y = (float)y; - rect->Width = (float)(_cairo_fixed_to_double(trap->right.p1.x) - x); - rect->Height = (float)(_cairo_fixed_to_double(trap->left.p2.y) - y); - - return 1; - } - - return 0; -} - -static cairo_int_status_t -_cairo_win32_surface_composite_trapezoids (cairo_operator_t op, - cairo_surface_t *generic_src, - void *abstract_dst, - int x_src, - int y_src, - cairo_trapezoid_t *traps, - int num_traps) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)abstract_dst; - - static int zzz = 0; - zzz += num_traps; - - Image *img = NULL; - Brush *brush = NULL; /* i'd really like to not allocate this on the heap.. */ - -#define FIXED_TO_FLOAT(x) (float)_cairo_fixed_to_double((x)) - - /* ugh.. figure out if we're a "native" pattern or an image_surface pattern.. */ - if (generic_src->backend == &cairo_win32_surface_backend) { - brush = ((cairo_win32_surface_t*)generic_src)->brush; - /* XXX move this outside so that TextureBrushes can take advantage of it */ - /* Check to see if we can represent these traps as a rectangle. */ - RectF rect; - if (num_traps == 1 && _cairo_win32_extract_rectangle(traps, &rect)) { - surface->gr->FillRectangle(brush, rect); - return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; - } - } else { - /* I would really love to move this code in to create_pattern if possible */ - img = make_image((cairo_image_surface_t*)generic_src); - brush = new TextureBrush(img); - - // XXX this will probably break. Don't know why we have +1 and +2 offsets.. - float xoff = (FIXED_TO_FLOAT(traps[0].left.p1.x) + 0.5f); - float yoff = (FIXED_TO_FLOAT(traps[0].left.p1.y) + 1.5f); - static_cast<TextureBrush*>(brush)->TranslateTransform((int)(-x_src + xoff), - (int)(-y_src + yoff)); - } - - CompositingMode mode; - switch (op) { - case CAIRO_OPERATOR_OVER: - mode = CompositingModeSourceOver; - break; - case CAIRO_OPERATOR_CLEAR: - mode = CompositingModeSourceCopy; - break; - case CAIRO_OPERATOR_SRC: - case CAIRO_OPERATOR_DST: - case CAIRO_OPERATOR_OVER_REVERSE: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_IN_REVERSE: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_OUT_REVERSE: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_ATOP_REVERSE: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: - case CAIRO_OPERATOR_SATURATE: - mode = CompositingModeSourceOver; - break; - } - surface->gr->SetCompositingMode(mode); - - PointF points[4]; - for (int i = 0; i < num_traps; ++i) { - float top = FIXED_TO_FLOAT(traps[i].top); - float bottom = FIXED_TO_FLOAT(traps[i].bottom); - - /* left line */ - points[0].X = FIXED_TO_FLOAT(traps[i].left.p1.x); - points[0].Y = FIXED_TO_FLOAT(traps[i].left.p1.y); - points[1].X = FIXED_TO_FLOAT(traps[i].left.p2.x); - points[1].Y = FIXED_TO_FLOAT(traps[i].left.p2.y); - - /* right line */ - points[2].X = FIXED_TO_FLOAT(traps[i].right.p2.x); - points[2].Y = FIXED_TO_FLOAT(traps[i].right.p2.y); - points[3].X = FIXED_TO_FLOAT(traps[i].right.p1.x); - points[3].Y = FIXED_TO_FLOAT(traps[i].right.p1.y); - - surface->gr->FillPolygon(brush, points, 4); - //Pen p(Color(255,0,0,0), 1.0f/10.0f); - //surface->gr->DrawPolygon(&p, points, 4); - } - -#undef FIXED_TO_FLOAT - - delete img; - return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_win32_surface_copy_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - - -static cairo_int_status_t -_cairo_win32_surface_show_page (void *abstract_surface) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - - -static cairo_int_status_t -_cairo_win32_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - cairo_win32_surface_t *surface = (cairo_win32_surface_t *)abstract_surface; - - pixman_box16_t *box; - int n = pixman_region_num_rects(region); - - if (n == 0) - return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; - - box = pixman_region_rects(region); - - surface->gr->ResetClip(); - for (int i = 0; i < n; ++i) { - Rect r(box[i].x1, box[i].y1, box[i].x2 - box[i].x1, box[i].y2 - box[i].y1); - surface->gr->SetClip(r, CombineModeUnion); /* do I need to do replace once and then union? */ - } - - return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_win32_surface_create_pattern (void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *box) -{ - /* SOLID is easy -> SolidBrush - LINEAR we can only do a gradient from 1 color to another -> LinearGradientBrush - We should do this if thats all we need, otherwise use software. - RADIAL we can't really do it. Let it fall back to software I guess. */ - - - /* in the case of PATTERN_SOLID, we really just want to create a brush and hand that back... */ - if (pattern->type == CAIRO_PATTERN_SOLID) { - - /* ugh, this surface creation code should _really_ live somewhere else */ - cairo_win32_surface_t *src = (cairo_win32_surface_t*)malloc(sizeof(cairo_win32_surface_t)); - if (src == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - _cairo_surface_init(&src->base, &cairo_win32_surface_backend); - pattern->source = &src->base; - - src->gr = NULL; - src->brush = new SolidBrush(Color(pattern->color.alpha_short, - pattern->color.red_short, - pattern->color.green_short, - pattern->color.blue_short)); - - - return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; - } - - else if (pattern->type == CAIRO_PATTERN_LINEAR && pattern->n_stops == 2) { - - /* ugh, this surface creation code should _really_ live somewhere else */ - cairo_win32_surface_t *src = (cairo_win32_surface_t*)malloc(sizeof(cairo_win32_surface_t)); - if (src == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - _cairo_surface_init(&src->base, &cairo_win32_surface_backend); - pattern->source = &src->base; - - src->gr = NULL; - - RectF r((float)pattern->u.linear.point0.x, - (float)pattern->u.linear.point0.y, - ((box->p2.x + 65535) >> 16) - (box->p1.x >> 16), - ((box->p2.y + 65535) >> 16) - (box->p1.y >> 16)); - - src->brush = new LinearGradientBrush(r, - Color(pattern->stops[0].color_char[3], - pattern->stops[0].color_char[0], - pattern->stops[0].color_char[1], - pattern->stops[0].color_char[2]), - Color(pattern->stops[1].color_char[3], - pattern->stops[1].color_char[0], - pattern->stops[1].color_char[1], - pattern->stops[1].color_char[2]), - 90.0, FALSE); - - static_cast<LinearGradientBrush*>(src->brush)->TranslateTransform((float)pattern->source_offset.x, - (float)pattern->source_offset.y); - return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; - } - - else if (pattern->type == CAIRO_PATTERN_RADIAL) { -#if 0 /* XXX not sure this will work.. do we have to draw with FillPath() in order for this brush to work properly? */ - /* use PathGradientBrush here */ - - /* ugh, this surface creation code should _really_ live somewhere else */ - cairo_win32_surface_t *src = (cairo_win32_surface_t*)malloc(sizeof(cairo_win32_surface_t)); - if (src == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - _cairo_surface_init(&src->base, &cairo_win32_surface_backend); - pattern->source = &src->base; - - src->gr = NULL; - - PointF center(pattern->u.radial.center1.x, pattern->u.radial.center1.y); - - GraphicsPath path; - path.AddEllipse(0, 0, 140, 70); - - // Use the path to construct a brush. - PathGradientBrush *brush = new PathGradientBrush(&path); - src->brush = brush; - - // Set the color at the center of the path to blue. - brush->SetCenterColor(Color(50, 100, 0, 255)); - - // Set the color along the entire boundary of the path to aqua. - Color colors[] = {Color(255, 0, 255, 255)}; - int count = 1; - brush->SetSurroundColors(colors, &count); - - brush->SetCenterPoint(center); - - return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; -#endif - - } - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static const cairo_surface_backend_t cairo_win32_surface_backend = { - _cairo_win32_surface_create_similar, - _cairo_win32_surface_destroy, - _cairo_win32_surface_pixels_per_inch, - _cairo_win32_surface_get_image, - _cairo_win32_surface_set_image, - _cairo_win32_surface_set_matrix, - _cairo_win32_surface_set_filter, - _cairo_win32_surface_set_repeat, - _cairo_win32_surface_composite, - _cairo_win32_surface_fill_rectangles, - _cairo_win32_surface_composite_trapezoids, - _cairo_win32_surface_copy_page, - _cairo_win32_surface_show_page, - _cairo_win32_surface_set_clip_region, - _cairo_win32_surface_create_pattern, -}; diff --git a/src/cairo_glitz_surface.c b/src/cairo_glitz_surface.c index 69fc82f2e..ee664e1cc 100644 --- a/src/cairo_glitz_surface.c +++ b/src/cairo_glitz_surface.c @@ -21,30 +21,12 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * Author: David Reveman <c99drn@cs.umu.se> + * Author: David Reveman <davidr@novell.com> */ #include "cairoint.h" #include "cairo-glitz.h" -#define GLITZ_FIXED_TO_FLOAT(f) \ - (((glitz_float_t) (f)) / 65536) - -#define GLITZ_FIXED_LINE_X_TO_FLOAT(line, v) \ - (((glitz_float_t) \ - ((line).p1.x + (cairo_fixed_16_16_t) \ - (((cairo_fixed_32_32_t) ((v) - (line).p1.y) * \ - ((line).p2.x - (line).p1.x)) / \ - ((line).p2.y - (line).p1.y)))) / 65536) - -#define GLITZ_FIXED_LINE_X_CEIL_TO_FLOAT(line, v) \ - (((glitz_float_t) \ - ((line).p1.x + (cairo_fixed_16_16_t) \ - (((((line).p2.y - (line).p1.y) - 1) + \ - ((cairo_fixed_32_32_t) ((v) - (line).p1.y) * \ - ((line).p2.x - (line).p1.x))) / \ - ((line).p2.y - (line).p1.y)))) / 65536) - void cairo_set_target_glitz (cairo_t *cr, glitz_surface_t *surface) { @@ -65,13 +47,11 @@ cairo_set_target_glitz (cairo_t *cr, glitz_surface_t *surface) } typedef struct _cairo_glitz_surface { - cairo_surface_t base; - - glitz_surface_t *surface; - glitz_format_t *format; + cairo_surface_t base; - cairo_pattern_t pattern; - cairo_box_t pattern_box; + glitz_surface_t *surface; + glitz_format_t *format; + pixman_region16_t *clip; } cairo_glitz_surface_t; static void @@ -79,11 +59,60 @@ _cairo_glitz_surface_destroy (void *abstract_surface) { cairo_glitz_surface_t *surface = abstract_surface; + if (surface->clip) + { + glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); + pixman_region_destroy (surface->clip); + } + glitz_surface_destroy (surface->surface); + free (surface); +} - _cairo_pattern_fini (&surface->pattern); +static glitz_format_name_t +_glitz_format (cairo_format_t format) +{ + switch (format) { + default: + case CAIRO_FORMAT_ARGB32: + return GLITZ_STANDARD_ARGB32; + case CAIRO_FORMAT_RGB24: + return GLITZ_STANDARD_RGB24; + case CAIRO_FORMAT_A8: + return GLITZ_STANDARD_A8; + case CAIRO_FORMAT_A1: + return GLITZ_STANDARD_A1; + } +} - free (surface); +static cairo_surface_t * +_cairo_glitz_surface_create_similar (void *abstract_src, + cairo_format_t format, + int draw, + int width, + int height) +{ + cairo_glitz_surface_t *src = abstract_src; + cairo_surface_t *crsurface; + glitz_drawable_t *drawable; + glitz_surface_t *surface; + glitz_format_t *gformat; + + drawable = glitz_surface_get_drawable (src->surface); + + gformat = glitz_find_standard_format (drawable, _glitz_format (format)); + if (!gformat) + return NULL; + + surface = glitz_surface_create (drawable, gformat, width, height, 0, NULL); + if (!surface) + return NULL; + + crsurface = cairo_glitz_surface_create (surface); + + glitz_surface_destroy (surface); + + return crsurface; } static double @@ -92,31 +121,54 @@ _cairo_glitz_surface_pixels_per_inch (void *abstract_surface) return 96.0; } -static cairo_image_surface_t * -_cairo_glitz_surface_get_image (void *abstract_surface) +static cairo_status_t +_cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface, + cairo_rectangle_t *interest, + cairo_image_surface_t **image_out, + cairo_rectangle_t *rect_out) { - cairo_glitz_surface_t *surface = abstract_surface; cairo_image_surface_t *image; - char *pixels; - int width, height; - cairo_format_masks_t format; - glitz_buffer_t *buffer; - glitz_pixel_format_t pf; - - if (surface->pattern.type != CAIRO_PATTERN_SURFACE) { - cairo_box_t box; - - box.p1.x = box.p1.y = 0; - box.p2.x = surface->pattern_box.p2.x; - box.p2.y = surface->pattern_box.p2.y; - - return _cairo_pattern_get_image (&surface->pattern, &box); + int x1, y1, x2, y2; + int width, height; + char *pixels; + cairo_format_masks_t format; + glitz_buffer_t *buffer; + glitz_pixel_format_t pf; + + x1 = 0; + y1 = 0; + x2 = glitz_surface_get_width (surface->surface); + y2 = glitz_surface_get_height (surface->surface); + + if (interest) + { + if (interest->x > x1) + x1 = interest->x; + if (interest->y > y1) + y1 = interest->y; + if (interest->x + interest->width < x2) + x2 = interest->x + interest->width; + if (interest->y + interest->height < y2) + y2 = interest->y + interest->height; + + if (x1 >= x2 || y1 >= y2) + { + *image_out = NULL; + return CAIRO_STATUS_SUCCESS; + } } + width = x2 - x1; + height = y2 - y1; - width = glitz_surface_get_width (surface->surface); - height = glitz_surface_get_height (surface->surface); - + if (rect_out) + { + rect_out->x = x1; + rect_out->y = y1; + rect_out->width = width; + rect_out->height = height; + } + if (surface->format->type == GLITZ_FORMAT_TYPE_COLOR) { if (surface->format->color.red_size > 0) { format.bpp = 32; @@ -149,21 +201,24 @@ _cairo_glitz_surface_get_image (void *abstract_surface) pf.masks.blue_mask = format.blue_mask; pf.xoffset = 0; pf.skip_lines = 0; + + /* XXX: we should eventually return images with negative stride, + need to verify that libpixman have no problem with this first. */ pf.bytes_per_line = (((width * format.bpp) / 8) + 3) & -4; pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; pixels = malloc (height * pf.bytes_per_line); if (!pixels) - return NULL; + return CAIRO_STATUS_NO_MEMORY; buffer = glitz_buffer_create_for_data (pixels); if (!buffer) { free (pixels); - return NULL; + return CAIRO_STATUS_NO_MEMORY; } glitz_get_pixels (surface->surface, - 0, 0, + x1, y1, width, height, &pf, buffer); @@ -175,27 +230,38 @@ _cairo_glitz_surface_get_image (void *abstract_surface) &format, width, height, pf.bytes_per_line); - + + if (!image) + { + free (pixels); + return CAIRO_STATUS_NO_MEMORY; + } + _cairo_image_surface_assume_ownership_of_data (image); _cairo_image_surface_set_repeat (image, surface->base.repeat); _cairo_image_surface_set_matrix (image, &(surface->base.matrix)); - return image; + *image_out = image; + + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_glitz_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +_cairo_glitz_surface_set_image (void *abstract_surface, + cairo_image_surface_t *image, + int x_dst, + int y_dst) { cairo_glitz_surface_t *surface = abstract_surface; - glitz_buffer_t *buffer; - glitz_pixel_format_t pf; - pixman_format_t *format; - int am, rm, gm, bm; + glitz_buffer_t *buffer; + glitz_pixel_format_t pf; + pixman_format_t *format; + int am, rm, gm, bm; + char *data; format = pixman_image_get_format (image->pixman_image); - if (format == NULL) + if (!format) return CAIRO_STATUS_NO_MEMORY; pixman_format_get_masks (format, &pf.masks.bpp, &am, &rm, &gm, &bm); @@ -206,15 +272,27 @@ _cairo_glitz_surface_set_image (void *abstract_surface, pf.masks.blue_mask = bm; pf.xoffset = 0; pf.skip_lines = 0; - pf.bytes_per_line = image->stride; - pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; - buffer = glitz_buffer_create_for_data (image->data); + /* check for negative stride */ + if (image->stride < 0) + { + pf.bytes_per_line = -image->stride; + pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP; + data = (char *) image->data + image->stride * (image->height - 1); + } + else + { + pf.bytes_per_line = image->stride; + pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN; + data = (char *) image->data; + } + + buffer = glitz_buffer_create_for_data (data); if (!buffer) return CAIRO_STATUS_NO_MEMORY; glitz_set_pixels (surface->surface, - 0, 0, + x_dst, y_dst, image->width, image->height, &pf, buffer); @@ -225,63 +303,118 @@ _cairo_glitz_surface_set_image (void *abstract_surface, } static cairo_status_t -_cairo_glitz_surface_set_matrix (void *abstract_surface, - cairo_matrix_t *matrix) +_cairo_glitz_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) { cairo_glitz_surface_t *surface = abstract_surface; - glitz_transform_t transform; - transform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); - transform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); - transform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); + *image_extra = NULL; + + return _cairo_glitz_surface_get_image (surface, NULL, image_out, NULL); +} - transform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); - transform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); - transform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); +static void +_cairo_glitz_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} - transform.matrix[2][0] = 0; - transform.matrix[2][1] = 0; - transform.matrix[2][2] = 1 << 16; +static cairo_status_t +_cairo_glitz_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect_out, + void **image_extra) +{ + cairo_glitz_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; - glitz_surface_set_transform (surface->surface, &transform); + status = _cairo_glitz_surface_get_image (surface, interest_rect, &image, + image_rect_out); + if (status) + return status; - return CAIRO_STATUS_SUCCESS; + *image_out = image; + *image_extra = NULL; + + return status; } +static void +_cairo_glitz_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ + cairo_glitz_surface_t *surface = abstract_surface; + + _cairo_glitz_surface_set_image (surface, image, + image_rect->x, image_rect->y); + + cairo_surface_destroy (&image->base); +} + + static cairo_status_t -_cairo_glitz_surface_set_filter (void *abstract_surface, cairo_filter_t filter) +_cairo_glitz_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { cairo_glitz_surface_t *surface = abstract_surface; - glitz_filter_t glitz_filter; + cairo_glitz_surface_t *clone; - switch (filter) { - case CAIRO_FILTER_FAST: - case CAIRO_FILTER_NEAREST: - glitz_filter = GLITZ_FILTER_NEAREST; - break; - case CAIRO_FILTER_GOOD: - case CAIRO_FILTER_BEST: - case CAIRO_FILTER_BILINEAR: - default: - glitz_filter = GLITZ_FILTER_BILINEAR; - break; + if (src->backend == surface->base.backend) + { + *clone_out = src; + cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; } + else if (_cairo_surface_is_image (src)) + { + cairo_image_surface_t *image_src = (cairo_image_surface_t *) src; + + clone = (cairo_glitz_surface_t *) + _cairo_glitz_surface_create_similar (surface, image_src->format, 0, + image_src->width, + image_src->height); + if (!clone) + return CAIRO_STATUS_NO_MEMORY; - glitz_surface_set_filter (surface->surface, glitz_filter, NULL, 0); + _cairo_glitz_surface_set_image (clone, image_src, 0, 0); + + *clone_out = &clone->base; - return CAIRO_STATUS_SUCCESS; + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; } -static cairo_status_t -_cairo_glitz_surface_set_repeat (void *abstract_surface, int repeat) +static void +_cairo_glitz_surface_set_matrix (cairo_glitz_surface_t *surface, + cairo_matrix_t *matrix) { - cairo_glitz_surface_t *surface = abstract_surface; + glitz_transform_t transform; + + transform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); + transform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); + transform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); - glitz_surface_set_fill (surface->surface, - (repeat)? GLITZ_FILL_REPEAT: - GLITZ_FILL_TRANSPARENT); + transform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); + transform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); + transform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); - return CAIRO_STATUS_SUCCESS; + transform.matrix[2][0] = 0; + transform.matrix[2][1] = 0; + transform.matrix[2][2] = 1 << 16; + + glitz_surface_set_transform (surface->surface, &transform); } static glitz_operator_t @@ -318,32 +451,6 @@ _glitz_operator (cairo_operator_t op) } } -static glitz_surface_t * -_glitz_surface_create_solid (glitz_surface_t *other, - glitz_format_name_t format_name, - glitz_color_t *color) -{ - glitz_drawable_t *drawable; - glitz_format_t *format; - glitz_surface_t *surface; - - drawable = glitz_surface_get_drawable (other); - - format = glitz_find_standard_format (drawable, format_name); - if (format == NULL) - return NULL; - - surface = glitz_surface_create (drawable, format, 1, 1); - if (surface == NULL) - return NULL; - - glitz_set_rectangle (surface, color, 0, 0, 1, 1); - - glitz_surface_set_fill (surface, GLITZ_FILL_REPEAT); - - return surface; -} - static glitz_status_t _glitz_ensure_target (glitz_surface_t *surface) { @@ -355,7 +462,6 @@ _glitz_ensure_target (glitz_surface_t *surface) glitz_drawable_format_t templ; glitz_format_t *format; glitz_drawable_t *pbuffer; - glitz_pbuffer_attributes_t attributes; unsigned long mask; int i; @@ -397,21 +503,13 @@ _glitz_ensure_target (glitz_surface_t *surface) if (!dformat) return CAIRO_INT_STATUS_UNSUPPORTED; - attributes.width = glitz_surface_get_width (surface); - attributes.height = glitz_surface_get_height (surface); - mask = GLITZ_PBUFFER_WIDTH_MASK | GLITZ_PBUFFER_HEIGHT_MASK; - - pbuffer = glitz_create_pbuffer_drawable (drawable, dformat, - &attributes, mask); + pbuffer = + glitz_create_pbuffer_drawable (drawable, dformat, + glitz_surface_get_width (surface), + glitz_surface_get_height (surface)); if (!pbuffer) return CAIRO_INT_STATUS_UNSUPPORTED; - if (glitz_drawable_get_width (pbuffer) < attributes.width || - glitz_drawable_get_height (pbuffer) < attributes.height) { - glitz_drawable_destroy (pbuffer); - return CAIRO_INT_STATUS_UNSUPPORTED; - } - glitz_surface_attach (surface, pbuffer, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR, 0, 0); @@ -422,388 +520,711 @@ _glitz_ensure_target (glitz_surface_t *surface) return CAIRO_STATUS_SUCCESS; } -static glitz_format_name_t -_glitz_format (cairo_format_t format) +typedef struct _cairo_glitz_surface_attributes { + cairo_surface_attributes_t base; + + glitz_fill_t fill; + glitz_filter_t filter; + glitz_fixed16_16_t *params; + int n_params; + cairo_bool_t acquired; +} cairo_glitz_surface_attributes_t; + +static cairo_int_status_t +_cairo_glitz_pattern_acquire_surface (cairo_pattern_t *pattern, + cairo_glitz_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_glitz_surface_t **surface_out, + cairo_glitz_surface_attributes_t *attr) { - switch (format) { + cairo_glitz_surface_t *src = NULL; + + attr->acquired = FALSE; + + switch (pattern->type) { + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: { + cairo_gradient_pattern_t *gradient = + (cairo_gradient_pattern_t *) pattern; + glitz_drawable_t *drawable; + glitz_fixed16_16_t *params; + int n_params; + int i; + unsigned short alpha; + + /* XXX: the current color gradient acceleration provided by glitz is + * experimental, it's been proven inappropriate in a number of ways, + * most importantly, it's currently implemented as filters and + * gradients are not filters. eventually, it will be replaced with + * something more appropriate. + */ + + if (gradient->n_stops < 2) + break; + + /* glitz doesn't support inner and outer circle with different + center points. */ + if (pattern->type == CAIRO_PATTERN_RADIAL) + { + cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern; + + if (grad->center0.x != grad->center1.x || + grad->center0.y != grad->center1.y) + break; + } + + drawable = glitz_surface_get_drawable (dst->surface); + if (!(glitz_drawable_get_features (drawable) & + GLITZ_FEATURE_FRAGMENT_PROGRAM_MASK)) + break; + + if (pattern->filter != CAIRO_FILTER_BILINEAR && + pattern->filter != CAIRO_FILTER_GOOD && + pattern->filter != CAIRO_FILTER_BEST) + break; + + alpha = (gradient->stops[0].color.alpha * pattern->alpha) * 0xffff; + for (i = 1; i < gradient->n_stops; i++) + { + unsigned short a; + + a = (gradient->stops[i].color.alpha * pattern->alpha) * 0xffff; + if (a != alpha) + break; + } + + /* we can't have color stops with different alpha as gradient color + interpolation should be done to unpremultiplied colors. */ + if (i < gradient->n_stops) + break; + + n_params = gradient->n_stops * 3 + 4; + + params = malloc (sizeof (glitz_fixed16_16_t) * n_params); + if (!params) + return CAIRO_STATUS_NO_MEMORY; + + src = (cairo_glitz_surface_t *) + _cairo_surface_create_similar_scratch (&dst->base, + CAIRO_FORMAT_ARGB32, 0, + gradient->n_stops, 1); + if (!src) + { + free (params); + return CAIRO_STATUS_NO_MEMORY; + } + + for (i = 0; i < gradient->n_stops; i++) { + glitz_color_t color; + + color.red = gradient->stops[i].color.red * alpha; + color.green = gradient->stops[i].color.green * alpha; + color.blue = gradient->stops[i].color.blue * alpha; + color.alpha = alpha; + + glitz_set_rectangle (src->surface, &color, i, 0, 1, 1); + + params[4 + 3 * i] = gradient->stops[i].offset; + params[5 + 3 * i] = i << 16; + params[6 + 3 * i] = 0; + } + + if (pattern->type == CAIRO_PATTERN_LINEAR) + { + cairo_linear_pattern_t *grad = (cairo_linear_pattern_t *) pattern; + + params[0] = _cairo_fixed_from_double (grad->point0.x); + params[1] = _cairo_fixed_from_double (grad->point0.y); + params[2] = _cairo_fixed_from_double (grad->point1.x); + params[3] = _cairo_fixed_from_double (grad->point1.y); + attr->filter = GLITZ_FILTER_LINEAR_GRADIENT; + } + else + { + cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern; + + params[0] = _cairo_fixed_from_double (grad->center0.x); + params[1] = _cairo_fixed_from_double (grad->center0.y); + params[2] = _cairo_fixed_from_double (grad->radius0); + params[3] = _cairo_fixed_from_double (grad->radius1); + attr->filter = GLITZ_FILTER_RADIAL_GRADIENT; + } + + switch (pattern->extend) { + case CAIRO_EXTEND_NONE: + attr->fill = GLITZ_FILL_NEAREST; + break; + case CAIRO_EXTEND_REPEAT: + attr->fill = GLITZ_FILL_REPEAT; + break; + case CAIRO_EXTEND_REFLECT: + attr->fill = GLITZ_FILL_REFLECT; + break; + } + + attr->params = params; + attr->n_params = n_params; + attr->base.matrix = pattern->matrix; + attr->base.x_offset = 0; + attr->base.y_offset = 0; + } break; default: - case CAIRO_FORMAT_ARGB32: - return GLITZ_STANDARD_ARGB32; - case CAIRO_FORMAT_RGB24: - return GLITZ_STANDARD_RGB24; - case CAIRO_FORMAT_A8: - return GLITZ_STANDARD_A8; - case CAIRO_FORMAT_A1: - return GLITZ_STANDARD_A1; + break; } -} -static cairo_surface_t * -_cairo_glitz_surface_create_similar (void *abstract_src, - cairo_format_t format, - int draw, - int width, - int height) -{ - cairo_glitz_surface_t *src = abstract_src; - cairo_surface_t *crsurface; - glitz_drawable_t *drawable; - glitz_surface_t *surface; - glitz_format_t *gformat; + if (!src) + { + cairo_int_status_t status; - drawable = glitz_surface_get_drawable (src->surface); - - gformat = glitz_find_standard_format (drawable, _glitz_format (format)); - if (gformat == NULL) - return NULL; - - surface = glitz_surface_create (drawable, gformat, width, height); - if (surface == NULL) - return NULL; + status = _cairo_pattern_acquire_surface (pattern, &dst->base, + x, y, width, height, + (cairo_surface_t **) &src, + &attr->base); + if (status) + return status; + + if (src) + { + switch (attr->base.extend) { + case CAIRO_EXTEND_NONE: + attr->fill = GLITZ_FILL_TRANSPARENT; + break; + case CAIRO_EXTEND_REPEAT: + attr->fill = GLITZ_FILL_REPEAT; + break; + case CAIRO_EXTEND_REFLECT: + attr->fill = GLITZ_FILL_REFLECT; + break; + } - crsurface = cairo_glitz_surface_create (surface); - - glitz_surface_destroy (surface); + switch (attr->base.filter) { + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + attr->filter = GLITZ_FILTER_NEAREST; + break; + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + default: + attr->filter = GLITZ_FILTER_BILINEAR; + break; + } + + attr->params = NULL; + attr->n_params = 0; + attr->acquired = TRUE; + } + } - return crsurface; + *surface_out = src; + + return CAIRO_STATUS_SUCCESS; } -static cairo_glitz_surface_t * -_cairo_glitz_surface_clone_similar (cairo_glitz_surface_t *templ, - cairo_surface_t *src, - cairo_format_t format) +static void +_cairo_glitz_pattern_release_surface (cairo_glitz_surface_t *dst, + cairo_glitz_surface_t *surface, + cairo_glitz_surface_attributes_t *attr) { - cairo_glitz_surface_t *clone; - cairo_image_surface_t *src_image; + if (attr->acquired) + _cairo_pattern_release_surface (&dst->base, &surface->base, + &attr->base); + else + _cairo_glitz_surface_destroy (surface); +} - src_image = _cairo_surface_get_image (src); +static cairo_int_status_t +_cairo_glitz_pattern_acquire_surfaces (cairo_pattern_t *src, + cairo_pattern_t *mask, + cairo_glitz_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + unsigned int width, + unsigned int height, + cairo_glitz_surface_t **src_out, + cairo_glitz_surface_t **mask_out, + cairo_glitz_surface_attributes_t *sattr, + cairo_glitz_surface_attributes_t *mattr) +{ + cairo_int_status_t status; + cairo_pattern_union_t tmp; + cairo_bool_t src_opaque, mask_opaque; + double src_alpha, mask_alpha; - clone = (cairo_glitz_surface_t *) - _cairo_glitz_surface_create_similar (templ, format, 0, - src_image->width, - src_image->height); - if (clone == NULL) - return NULL; - - _cairo_glitz_surface_set_filter (clone, cairo_surface_get_filter (src)); + src_opaque = _cairo_pattern_is_opaque (src); + mask_opaque = !mask || _cairo_pattern_is_opaque (mask); - _cairo_glitz_surface_set_image (clone, src_image); - - _cairo_glitz_surface_set_matrix (clone, &(src_image->base.matrix)); + /* For surface patterns, we move any translucency from src->alpha + * to mask->alpha so we can use the source unchanged. Otherwise we + * move the translucency from mask->alpha to src->alpha so that + * we can drop the mask if possible. + */ + if (src->type == CAIRO_PATTERN_SURFACE) + { + if (mask) { + mask_opaque = mask_opaque && src_opaque; + mask_alpha = mask->alpha * src->alpha; + } else { + mask_opaque = src_opaque; + mask_alpha = src->alpha; + } + + src_alpha = 1.0; + src_opaque = TRUE; + } + else + { + if (mask) + { + src_opaque = mask_opaque && src_opaque; + src_alpha = mask->alpha * src->alpha; + /* FIXME: This needs changing when we support RENDER + * style 4-channel masks. + */ + if (mask->type == CAIRO_PATTERN_SOLID) + mask = NULL; + } else + src_alpha = src->alpha; + + mask_alpha = 1.0; + mask_opaque = TRUE; + } + + _cairo_pattern_init_copy (&tmp.base, src); + _cairo_pattern_set_alpha (&tmp.base, src_alpha); + + status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, + src_x, src_y, + width, height, + src_out, sattr); - cairo_surface_destroy (&src_image->base); + _cairo_pattern_fini (&tmp.base); - return clone; -} + if (status) + return status; -static cairo_int_status_t -_glitz_composite (glitz_operator_t op, - glitz_surface_t *src, - glitz_surface_t *mask, - glitz_surface_t *dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - int width, - int height, - glitz_buffer_t *geometry, - glitz_geometry_format_t *format) -{ - if (_glitz_ensure_target (dst)) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (mask || !mask_opaque) + { + if (mask) + _cairo_pattern_init_copy (&tmp.base, mask); + else + _cairo_pattern_init_solid (&tmp.solid, 0.0, 0.0, 0.0); - if (glitz_surface_get_status (dst)) - return CAIRO_STATUS_NO_TARGET_SURFACE; - - glitz_set_geometry (dst, - 0, 0, - format, geometry); - - glitz_composite (op, - src, - mask, - dst, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); - - glitz_set_geometry (dst, 0, 0, NULL, NULL); + _cairo_pattern_set_alpha (&tmp.base, mask_alpha); + + status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, + mask_x, mask_y, + width, height, + mask_out, mattr); + + _cairo_pattern_fini (&tmp.base); - if (glitz_surface_get_status (dst) == GLITZ_STATUS_NOT_SUPPORTED) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (status) + { + _cairo_glitz_pattern_release_surface (dst, *src_out, sattr); + return status; + } + } + else + { + *mask_out = NULL; + } return CAIRO_STATUS_SUCCESS; } +static void +_cairo_glitz_surface_set_attributes (cairo_glitz_surface_t *surface, + cairo_glitz_surface_attributes_t *a) +{ + _cairo_glitz_surface_set_matrix (surface, &a->base.matrix); + glitz_surface_set_fill (surface->surface, a->fill); + glitz_surface_set_filter (surface->surface, a->filter, + a->params, a->n_params); +} + static cairo_int_status_t _cairo_glitz_surface_composite (cairo_operator_t op, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) { - cairo_glitz_surface_t *dst = abstract_dst; - cairo_glitz_surface_t *src = (cairo_glitz_surface_t *) generic_src; - cairo_glitz_surface_t *mask = (cairo_glitz_surface_t *) generic_mask; - cairo_glitz_surface_t *src_clone = NULL; - cairo_glitz_surface_t *mask_clone = NULL; - cairo_int_status_t status; + cairo_glitz_surface_attributes_t src_attr, mask_attr; + cairo_glitz_surface_t *dst = abstract_dst; + cairo_glitz_surface_t *src; + cairo_glitz_surface_t *mask; + cairo_int_status_t status; if (op == CAIRO_OPERATOR_SATURATE) return CAIRO_INT_STATUS_UNSUPPORTED; - if (generic_src->backend != dst->base.backend) { - src_clone = _cairo_glitz_surface_clone_similar (dst, generic_src, - CAIRO_FORMAT_ARGB32); - if (!src_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (_glitz_ensure_target (dst->surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_glitz_pattern_acquire_surfaces (src_pattern, mask_pattern, + dst, + src_x, src_y, + mask_x, mask_y, + width, height, + &src, &mask, + &src_attr, &mask_attr); + if (status) + return status; + + _cairo_glitz_surface_set_attributes (src, &src_attr); + if (mask) + { + _cairo_glitz_surface_set_attributes (mask, &mask_attr); + glitz_composite (_glitz_operator (op), + src->surface, + mask->surface, + dst->surface, + src_x + src_attr.base.x_offset, + src_y + src_attr.base.y_offset, + mask_x + mask_attr.base.x_offset, + mask_y + mask_attr.base.y_offset, + dst_x, dst_y, + width, height); - src = src_clone; - } - - if (generic_mask && (generic_mask->backend != dst->base.backend)) { - mask_clone = _cairo_glitz_surface_clone_similar (dst, generic_mask, - CAIRO_FORMAT_A8); - if (!mask_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; + if (mask_attr.n_params) + free (mask_attr.params); - mask = mask_clone; + _cairo_glitz_pattern_release_surface (dst, mask, &mask_attr); + } + else + { + glitz_composite (_glitz_operator (op), + src->surface, + NULL, + dst->surface, + src_x + src_attr.base.x_offset, + src_y + src_attr.base.y_offset, + 0, 0, + dst_x, dst_y, + width, height); } - status = _glitz_composite (_glitz_operator (op), - src->surface, - (mask)? mask->surface: NULL, - dst->surface, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height, - NULL, NULL); - - if (src_clone) - cairo_surface_destroy (&src_clone->base); - - if (mask_clone) - cairo_surface_destroy (&mask_clone->base); + if (src_attr.n_params) + free (src_attr.params); - return status; + _cairo_glitz_pattern_release_surface (dst, src, &src_attr); + + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t -_cairo_glitz_surface_fill_rectangles (void *abstract_dst, - cairo_operator_t op, +_cairo_glitz_surface_fill_rectangles (void *abstract_dst, + cairo_operator_t op, const cairo_color_t *color, - cairo_rectangle_t *rects, - int n_rects) + cairo_rectangle_t *rects, + int n_rects) { cairo_glitz_surface_t *dst = abstract_dst; - glitz_color_t glitz_color; - if (op == CAIRO_OPERATOR_SATURATE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - glitz_color.red = color->red_short; - glitz_color.green = color->green_short; - glitz_color.blue = color->blue_short; - glitz_color.alpha = color->alpha_short; + if (op == CAIRO_OPERATOR_SRC) + { + glitz_color_t glitz_color; + + glitz_color.red = color->red_short; + glitz_color.green = color->green_short; + glitz_color.blue = color->blue_short; + glitz_color.alpha = color->alpha_short; + + if (glitz_surface_get_width (dst->surface) != 1 || + glitz_surface_get_height (dst->surface) != 1) + _glitz_ensure_target (dst->surface); - if (op != CAIRO_OPERATOR_SRC) { - glitz_surface_t *solid; - glitz_float_t *vertices; - glitz_buffer_t *buffer; - glitz_geometry_format_t gf; - cairo_int_status_t status; - int width, height; - void *data; + glitz_set_rectangles (dst->surface, &glitz_color, + (glitz_rectangle_t *) rects, n_rects); + } + else + { + cairo_glitz_surface_t *src; - gf.mode = GLITZ_GEOMETRY_MODE_DIRECT; - gf.edge_hint = GLITZ_GEOMETRY_EDGE_HINT_SHARP; - gf.primitive = GLITZ_GEOMETRY_PRIMITIVE_QUADS; - gf.type = GLITZ_DATA_TYPE_FLOAT; - gf.first = 0; - gf.count = n_rects * 4; - - data = malloc (n_rects * 8 * sizeof (glitz_float_t)); - if (!data) - return CAIRO_STATUS_NO_MEMORY; - - buffer = glitz_buffer_create_for_data (data); - if (buffer == NULL) { - free (data); - return CAIRO_STATUS_NO_MEMORY; - } + if (op == CAIRO_OPERATOR_SATURATE) + return CAIRO_INT_STATUS_UNSUPPORTED; - width = height = 0; - vertices = glitz_buffer_map (buffer, GLITZ_BUFFER_ACCESS_WRITE_ONLY); - for (; n_rects; rects++, n_rects--) { - *vertices++ = (glitz_float_t) rects->x; - *vertices++ = (glitz_float_t) rects->y; - *vertices++ = (glitz_float_t) (rects->x + rects->width); - *vertices++ = (glitz_float_t) rects->y; - *vertices++ = (glitz_float_t) (rects->x + rects->width); - *vertices++ = (glitz_float_t) (rects->y + rects->height); - *vertices++ = (glitz_float_t) rects->x; - *vertices++ = (glitz_float_t) (rects->y + rects->height); - - if ((rects->x + rects->width) > width) - width = rects->x + rects->width; - - if ((rects->y + rects->height) > height) - height = rects->y + rects->height; - } - glitz_buffer_unmap (buffer); + if (_glitz_ensure_target (dst->surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; - solid = _glitz_surface_create_solid (dst->surface, - GLITZ_STANDARD_ARGB32, - &glitz_color); - if (solid == NULL) + src = (cairo_glitz_surface_t *) + _cairo_surface_create_similar_solid (&dst->base, + CAIRO_FORMAT_ARGB32, 1, 1, + (cairo_color_t *) color); + if (!src) return CAIRO_STATUS_NO_MEMORY; - - status = _glitz_composite (_glitz_operator (op), - solid, - NULL, - dst->surface, - 0, 0, - 0, 0, - 0, 0, - width, height, - buffer, &gf); - - glitz_surface_destroy (solid); - glitz_buffer_destroy (buffer); - free (data); - - return status; - } else { - if (glitz_surface_get_width (dst->surface) != 1 || - glitz_surface_get_height (dst->surface) != 1) { - if (_glitz_ensure_target (dst->surface)) - return CAIRO_INT_STATUS_UNSUPPORTED; + + while (n_rects--) + { + glitz_composite (_glitz_operator (op), + src->surface, + NULL, + dst->surface, + 0, 0, + 0, 0, + rects->x, rects->y, + rects->width, rects->height); + rects++; } - glitz_set_rectangles (dst->surface, &glitz_color, - (glitz_rectangle_t *) rects, n_rects); + cairo_surface_destroy (&src->base); } + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) + return CAIRO_INT_STATUS_UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t -_cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, - cairo_surface_t *generic_src, - void *abstract_dst, - int x_src, - int y_src, +_cairo_glitz_surface_composite_trapezoids (cairo_operator_t op, + cairo_pattern_t *pattern, + void *abstract_dst, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, - int n_traps) + int n_traps) { - cairo_glitz_surface_t *dst = abstract_dst; - cairo_glitz_surface_t *src = (cairo_glitz_surface_t *) generic_src; - glitz_surface_t *mask = NULL; - glitz_float_t *vertices; - glitz_buffer_t *buffer; - glitz_geometry_format_t gf; - cairo_int_status_t status; - int x_dst, y_dst, x_rel, y_rel, width, height; - void *data; + cairo_glitz_surface_attributes_t attributes; + cairo_glitz_surface_t *dst = abstract_dst; + cairo_glitz_surface_t *src; + cairo_glitz_surface_t *mask = NULL; + glitz_buffer_t *buffer = NULL; + void *data = NULL; + cairo_int_status_t status; + unsigned short alpha; if (op == CAIRO_OPERATOR_SATURATE) return CAIRO_INT_STATUS_UNSUPPORTED; - if (generic_src->backend != dst->base.backend) + if (_glitz_ensure_target (dst->surface)) return CAIRO_INT_STATUS_UNSUPPORTED; - gf.mode = GLITZ_GEOMETRY_MODE_DIRECT; - gf.edge_hint = GLITZ_GEOMETRY_EDGE_HINT_GOOD_SMOOTH; - gf.primitive = GLITZ_GEOMETRY_PRIMITIVE_QUADS; - gf.type = GLITZ_DATA_TYPE_FLOAT; - gf.first = 0; - gf.count = n_traps * 4; + if (pattern->type == CAIRO_PATTERN_SURFACE) + { + cairo_pattern_union_t tmp; - data = malloc (n_traps * 8 * sizeof (glitz_float_t)); - if (!data) - return CAIRO_STATUS_NO_MEMORY; - - buffer = glitz_buffer_create_for_data (data); - if (buffer == NULL) { - free (data); - return CAIRO_STATUS_NO_MEMORY; - } - - x_dst = traps[0].left.p1.x >> 16; - y_dst = traps[0].left.p1.y >> 16; + _cairo_pattern_init_copy (&tmp.base, pattern); + _cairo_pattern_set_alpha (&tmp.base, 1.0); - vertices = glitz_buffer_map (buffer, GLITZ_BUFFER_ACCESS_WRITE_ONLY); - for (; n_traps; traps++, n_traps--) { - glitz_float_t top, bottom; + status = _cairo_glitz_pattern_acquire_surface (&tmp.base, dst, + src_x, src_y, + width, height, + &src, &attributes); - top = GLITZ_FIXED_TO_FLOAT (traps->top); - bottom = GLITZ_FIXED_TO_FLOAT (traps->bottom); + _cairo_pattern_fini (&tmp.base); - *vertices++ = GLITZ_FIXED_LINE_X_TO_FLOAT (traps->left, traps->top); - *vertices++ = top; - *vertices++ = - GLITZ_FIXED_LINE_X_CEIL_TO_FLOAT (traps->right, traps->top); - *vertices++ = top; - *vertices++ = - GLITZ_FIXED_LINE_X_CEIL_TO_FLOAT (traps->right, traps->bottom); - *vertices++ = bottom; - *vertices++ = GLITZ_FIXED_LINE_X_TO_FLOAT (traps->left, traps->bottom); - *vertices++ = bottom; + alpha = pattern->alpha * 0xffff; } - glitz_buffer_unmap (buffer); + else + { + status = _cairo_glitz_pattern_acquire_surface (pattern, dst, + src_x, src_y, + width, height, + &src, &attributes); + alpha = 0xffff; + } + + if (status) + return status; + + if (op == CAIRO_OPERATOR_ADD || n_traps <= 1) + { + static glitz_color_t clear_black = { 0, 0, 0, 0 }; + glitz_color_t color; + glitz_geometry_format_t format; + int n_trap_added; + int offset = 0; + int data_size = 0; + int size = 30 * n_traps; /* just a guess */ + + format.vertex.primitive = GLITZ_PRIMITIVE_QUADS; + format.vertex.type = GLITZ_DATA_TYPE_FLOAT; + format.vertex.bytes_per_vertex = 3 * sizeof (glitz_float_t); + format.vertex.attributes = GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK; + format.vertex.mask.type = GLITZ_DATA_TYPE_FLOAT; + format.vertex.mask.size = GLITZ_COORDINATE_SIZE_X; + format.vertex.mask.offset = 2 * sizeof (glitz_float_t); + + mask = (cairo_glitz_surface_t *) + _cairo_glitz_surface_create_similar (&dst->base, + CAIRO_FORMAT_A8, 0, + 2, 1); + if (!mask) + { + _cairo_glitz_pattern_release_surface (dst, src, &attributes); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + color.red = color.green = color.blue = color.alpha = alpha; - if ((src->pattern.type == CAIRO_PATTERN_SURFACE) && - (src->pattern.color.alpha != 1.0)) { - glitz_color_t color; + glitz_set_rectangle (mask->surface, &clear_black, 0, 0, 1, 1); + glitz_set_rectangle (mask->surface, &color, 1, 0, 1, 1); + + glitz_surface_set_fill (mask->surface, GLITZ_FILL_NEAREST); + glitz_surface_set_filter (mask->surface, + GLITZ_FILTER_BILINEAR, + NULL, 0); + + size *= format.vertex.bytes_per_vertex; - color.red = color.green = color.blue = 0; - color.alpha = src->pattern.color.alpha_short; + while (n_traps) + { + if (data_size < size) + { + data_size = size; + data = realloc (data, data_size); + if (!data) + { + _cairo_glitz_pattern_release_surface (dst, src, + &attributes); + return CAIRO_STATUS_NO_MEMORY; + } + + if (buffer) + glitz_buffer_destroy (buffer); + + buffer = glitz_buffer_create_for_data (data); + if (!buffer) { + free (data); + _cairo_glitz_pattern_release_surface (dst, src, + &attributes); + return CAIRO_STATUS_NO_MEMORY; + } + } - mask = _glitz_surface_create_solid (dst->surface, - GLITZ_STANDARD_A8, - &color); + offset += + glitz_add_trapezoids (buffer, + offset, size - offset, + format.vertex.type, mask->surface, + (glitz_trapezoid_t *) traps, n_traps, + &n_trap_added); + + n_traps -= n_trap_added; + traps += n_trap_added; + size *= 2; + } + + glitz_set_geometry (dst->surface, + GLITZ_GEOMETRY_TYPE_VERTEX, + &format, buffer); + glitz_set_array (dst->surface, 0, 3, + offset / format.vertex.bytes_per_vertex, + 0, 0); } + else + { + cairo_image_surface_t *image; + char *ptr; + int stride; + + stride = (width + 3) & -4; + data = malloc (stride * height); + if (!data) + { + _cairo_glitz_pattern_release_surface (dst, src, &attributes); + return CAIRO_STATUS_NO_MEMORY; + } + + memset (data, 0, stride * height); - x_rel = (src->pattern_box.p1.x >> 16) + x_src - x_dst; - y_rel = (src->pattern_box.p1.y >> 16) + y_src - y_dst; + /* using negative stride */ + ptr = (char *) data + stride * (height - 1); + + image = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (ptr, + CAIRO_FORMAT_A8, + width, height, + -stride); + if (!image) + { + cairo_surface_destroy (&src->base); + free (data); + return CAIRO_STATUS_NO_MEMORY; + } - x_dst = src->pattern_box.p1.x >> 16; - y_dst = src->pattern_box.p1.y >> 16; + pixman_add_trapezoids (image->pixman_image, -dst_x, -dst_y, + (pixman_trapezoid_t *) traps, n_traps); + + if (alpha != 0xffff) + { + pixman_color_t color; + + color.red = color.green = color.blue = color.alpha = alpha; + + pixman_fill_rectangle (PIXMAN_OPERATOR_IN, + image->pixman_image, + &color, + 0, 0, width, height); + } + + mask = (cairo_glitz_surface_t *) + _cairo_surface_create_similar_scratch (&dst->base, + CAIRO_FORMAT_A8, 0, + width, height); + if (!mask) + { + _cairo_glitz_pattern_release_surface (dst, src, &attributes); + free (data); + cairo_surface_destroy (&image->base); + return CAIRO_STATUS_NO_MEMORY; + } + + _cairo_glitz_surface_set_image (mask, image, 0, 0); + } + + _cairo_glitz_surface_set_attributes (src, &attributes); - width = ((src->pattern_box.p2.x + 65535) >> 16) - - (src->pattern_box.p1.x >> 16); - height = ((src->pattern_box.p2.y + 65535) >> 16) - - (src->pattern_box.p1.y >> 16); + glitz_composite (_glitz_operator (op), + src->surface, + mask->surface, + dst->surface, + src_x + attributes.base.x_offset, + src_y + attributes.base.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + + if (attributes.n_params) + free (attributes.params); + + glitz_set_geometry (dst->surface, + GLITZ_GEOMETRY_TYPE_NONE, + NULL, NULL); + + if (buffer) + glitz_buffer_destroy (buffer); - status = _glitz_composite (_glitz_operator (op), - src->surface, - mask, - dst->surface, - x_rel, y_rel, - 0, 0, - x_dst, y_dst, - width, height, - buffer, &gf); + free (data); + _cairo_glitz_pattern_release_surface (dst, src, &attributes); if (mask) - glitz_surface_destroy (mask); + cairo_surface_destroy (&mask->base); - glitz_buffer_destroy (buffer); - free (data); + if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) + return CAIRO_INT_STATUS_UNSUPPORTED; - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t @@ -819,173 +1240,56 @@ _cairo_glitz_surface_show_page (void *abstract_surface) } static cairo_int_status_t -_cairo_glitz_surface_create_pattern (void *abstract_dst, - cairo_pattern_t *pattern, - cairo_box_t *box) +_cairo_glitz_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) { - cairo_glitz_surface_t *dst = abstract_dst; - cairo_surface_t *generic_src = NULL; - cairo_image_surface_t *image = NULL; - cairo_glitz_surface_t *src; - - switch (pattern->type) { - case CAIRO_PATTERN_SOLID: - generic_src = - _cairo_surface_create_similar_solid (abstract_dst, - CAIRO_FORMAT_ARGB32, - 1, 1, - &pattern->color); - if (generic_src) - cairo_surface_set_repeat (generic_src, 1); - break; - case CAIRO_PATTERN_RADIAL: - /* glitz doesn't support inner and outer circle with different - center points. */ - if (pattern->u.radial.center0.x != pattern->u.radial.center1.x || - pattern->u.radial.center0.y != pattern->u.radial.center1.y) - break; - /* fall-through */ - case CAIRO_PATTERN_LINEAR: { - glitz_drawable_t *drawable; - glitz_fixed16_16_t *params; - int i, n_params; - - drawable = glitz_surface_get_drawable (dst->surface); - if (!(glitz_drawable_get_features (drawable) & - GLITZ_FEATURE_FRAGMENT_PROGRAM_MASK)) - break; - - if (pattern->filter != CAIRO_FILTER_BILINEAR) - break; - - n_params = pattern->n_stops * 3 + 4; - - params = malloc (sizeof (glitz_fixed16_16_t) * n_params); - if (params == NULL) - return CAIRO_STATUS_NO_MEMORY; - - generic_src = - _cairo_glitz_surface_create_similar (abstract_dst, - CAIRO_FORMAT_ARGB32, 0, - pattern->n_stops, 1); - if (generic_src == NULL) { - free (params); - return CAIRO_STATUS_NO_MEMORY; - } - - src = (cairo_glitz_surface_t *) generic_src; - - for (i = 0; i < pattern->n_stops; i++) { - glitz_color_t color; + cairo_glitz_surface_t *surface = abstract_surface; - color.alpha = pattern->stops[i].color_char[3]; - color.red = pattern->stops[i].color_char[0] * color.alpha; - color.green = pattern->stops[i].color_char[1] * color.alpha; - color.blue = pattern->stops[i].color_char[2] * color.alpha; - color.alpha *= 256; + if (region) + { + glitz_box_t *box; + int n; - glitz_set_rectangle (src->surface, &color, i, 0, 1, 1); - - params[4 + 3 * i] = pattern->stops[i].offset; - params[5 + 3 * i] = i << 16; - params[6 + 3 * i] = 0; - } - - if (pattern->type == CAIRO_PATTERN_LINEAR) { - params[0] = _cairo_fixed_from_double (pattern->u.linear.point0.x); - params[1] = _cairo_fixed_from_double (pattern->u.linear.point0.y); - params[2] = _cairo_fixed_from_double (pattern->u.linear.point1.x); - params[3] = _cairo_fixed_from_double (pattern->u.linear.point1.y); - - glitz_surface_set_filter (src->surface, - GLITZ_FILTER_LINEAR_GRADIENT, - params, n_params); - } else { - params[0] = _cairo_fixed_from_double (pattern->u.radial.center0.x); - params[1] = _cairo_fixed_from_double (pattern->u.radial.center0.y); - params[2] = _cairo_fixed_from_double (pattern->u.radial.radius0); - params[3] = _cairo_fixed_from_double (pattern->u.radial.radius1); - - glitz_surface_set_filter (src->surface, - GLITZ_FILTER_RADIAL_GRADIENT, - params, n_params); - } - - switch (pattern->extend) { - case CAIRO_EXTEND_REPEAT: - glitz_surface_set_fill (src->surface, GLITZ_FILL_REPEAT); - break; - case CAIRO_EXTEND_REFLECT: - glitz_surface_set_fill (src->surface, GLITZ_FILL_REFLECT); - break; - case CAIRO_EXTEND_NONE: - default: - glitz_surface_set_fill (src->surface, GLITZ_FILL_NEAREST); - break; + if (!surface->clip) + { + surface->clip = pixman_region_create (); + if (!surface->clip) + return CAIRO_STATUS_NO_MEMORY; } + pixman_region_copy (surface->clip, region); - cairo_surface_set_matrix (&src->base, &pattern->matrix); - - free (params); - } break; - case CAIRO_PATTERN_SURFACE: - generic_src = pattern->u.surface.surface; - cairo_surface_reference (generic_src); - break; - } - - if (generic_src == NULL) { - image = _cairo_pattern_get_image (pattern, box); - if (image == NULL) - return CAIRO_STATUS_NO_MEMORY; - - generic_src = &image->base; + box = (glitz_box_t *) pixman_region_rects (surface->clip); + n = pixman_region_num_rects (surface->clip); + glitz_surface_set_clip_region (surface->surface, 0, 0, box, n); } + else + { + glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0); - if (generic_src->backend != dst->base.backend) { - src = _cairo_glitz_surface_clone_similar (dst, generic_src, - CAIRO_FORMAT_ARGB32); - if (src == NULL) - return CAIRO_STATUS_NO_MEMORY; - - cairo_surface_set_repeat (&src->base, generic_src->repeat); - } else - src = (cairo_glitz_surface_t *) generic_src; + if (surface->clip) + pixman_region_destroy (surface->clip); - if (image) - cairo_surface_destroy (&image->base); - - _cairo_pattern_init_copy (&src->pattern, pattern); - src->pattern_box = *box; - - pattern->source = &src->base; + surface->clip = NULL; + } return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_glitz_surface_set_clip_region (void *abstract_surface, - pixman_region16_t *region) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - static const cairo_surface_backend_t cairo_glitz_surface_backend = { _cairo_glitz_surface_create_similar, _cairo_glitz_surface_destroy, _cairo_glitz_surface_pixels_per_inch, - _cairo_glitz_surface_get_image, - _cairo_glitz_surface_set_image, - _cairo_glitz_surface_set_matrix, - _cairo_glitz_surface_set_filter, - _cairo_glitz_surface_set_repeat, + _cairo_glitz_surface_acquire_source_image, + _cairo_glitz_surface_release_source_image, + _cairo_glitz_surface_acquire_dest_image, + _cairo_glitz_surface_release_dest_image, + _cairo_glitz_surface_clone_similar, _cairo_glitz_surface_composite, _cairo_glitz_surface_fill_rectangles, _cairo_glitz_surface_composite_trapezoids, _cairo_glitz_surface_copy_page, _cairo_glitz_surface_show_page, _cairo_glitz_surface_set_clip_region, - _cairo_glitz_surface_create_pattern, NULL /* show_glyphs */ }; @@ -1004,12 +1308,10 @@ cairo_glitz_surface_create (glitz_surface_t *surface) _cairo_surface_init (&crsurface->base, &cairo_glitz_surface_backend); glitz_surface_reference (surface); - crsurface->surface = surface; - crsurface->format = glitz_surface_get_format (surface); - _cairo_pattern_init (&crsurface->pattern); - crsurface->pattern.type = CAIRO_PATTERN_SURFACE; - crsurface->pattern.u.surface.surface = NULL; + crsurface->surface = surface; + crsurface->format = glitz_surface_get_format (surface); + crsurface->clip = NULL; return (cairo_surface_t *) crsurface; } diff --git a/src/cairo_gstate.c b/src/cairo_gstate.c index e855a7a66..d6db560a3 100644 --- a/src/cairo_gstate.c +++ b/src/cairo_gstate.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include <stdlib.h> @@ -46,20 +46,33 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, cairo_surface_t *dst, cairo_traps_t *traps); +static cairo_status_t +_cairo_gstate_ensure_font (cairo_gstate_t *gstate); + +static void +_cairo_gstate_unset_font (cairo_gstate_t *gstate); + cairo_gstate_t * _cairo_gstate_create () { + cairo_status_t status; cairo_gstate_t *gstate; gstate = malloc (sizeof (cairo_gstate_t)); if (gstate) - _cairo_gstate_init (gstate); + { + status = _cairo_gstate_init (gstate); + if (status) { + free (gstate); + return NULL; + } + } return gstate; } -void +cairo_status_t _cairo_gstate_init (cairo_gstate_t *gstate) { gstate->operator = CAIRO_GSTATE_OPERATOR_DEFAULT; @@ -77,9 +90,11 @@ _cairo_gstate_init (cairo_gstate_t *gstate) gstate->num_dashes = 0; gstate->dash_offset = 0.0; - gstate->font = _cairo_unscaled_font_create (CAIRO_FONT_FAMILY_DEFAULT, - CAIRO_FONT_SLANT_DEFAULT, - CAIRO_FONT_WEIGHT_DEFAULT); + gstate->font_family = NULL; + gstate->font_slant = CAIRO_FONT_SLANT_DEFAULT; + gstate->font_weight = CAIRO_FONT_WEIGHT_DEFAULT; + + gstate->font = NULL; gstate->surface = NULL; @@ -87,6 +102,9 @@ _cairo_gstate_init (cairo_gstate_t *gstate) gstate->clip.surface = NULL; gstate->pattern = _cairo_pattern_create_solid (0.0, 0.0, 0.0); + if (!gstate->pattern) + return CAIRO_STATUS_NO_MEMORY; + gstate->alpha = 1.0; gstate->pixels_per_inch = CAIRO_GSTATE_PIXELS_PER_INCH_DEFAULT; @@ -97,6 +115,8 @@ _cairo_gstate_init (cairo_gstate_t *gstate) _cairo_pen_init_empty (&gstate->pen_regular); gstate->next = NULL; + + return CAIRO_STATUS_SUCCESS; } cairo_status_t @@ -118,9 +138,15 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) memcpy (gstate->dash, other->dash, other->num_dashes * sizeof (double)); } + if (other->font_family) { + gstate->font_family = strdup (other->font_family); + if (!gstate->font_family) + goto CLEANUP_DASH; + } + if (other->font) { gstate->font = other->font; - _cairo_unscaled_font_reference (gstate->font); + cairo_font_reference (gstate->font); } if (other->clip.region) @@ -148,18 +174,29 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) _cairo_path_fini (&gstate->path); CLEANUP_FONT: - _cairo_unscaled_font_destroy (gstate->font); + cairo_font_destroy (gstate->font); + gstate->font = NULL; + + if (gstate->font_family) { + free (gstate->font_family); + gstate->font_family = NULL; + } + CLEANUP_DASH: free (gstate->dash); gstate->dash = NULL; - return status; + return CAIRO_STATUS_NO_MEMORY; } void _cairo_gstate_fini (cairo_gstate_t *gstate) { - _cairo_unscaled_font_destroy (gstate->font); + if (gstate->font_family) + free (gstate->font_family); + + if (gstate->font) + cairo_font_destroy (gstate->font); if (gstate->surface) cairo_surface_destroy (gstate->surface); @@ -323,6 +360,8 @@ _cairo_gstate_set_target_surface (cairo_gstate_t *gstate, cairo_surface_t *surfa { double scale; + _cairo_gstate_unset_font (gstate); + if (gstate->surface) cairo_surface_destroy (gstate->surface); @@ -365,11 +404,9 @@ _cairo_gstate_set_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern) if (pattern == NULL) return CAIRO_STATUS_NULL_POINTER; - if (gstate->pattern) - cairo_pattern_destroy (gstate->pattern); - - gstate->pattern = pattern; cairo_pattern_reference (pattern); + cairo_pattern_destroy (gstate->pattern); + gstate->pattern = pattern; return CAIRO_STATUS_SUCCESS; } @@ -407,6 +444,8 @@ _cairo_gstate_set_rgb_color (cairo_gstate_t *gstate, double red, double green, d cairo_pattern_destroy (gstate->pattern); gstate->pattern = _cairo_pattern_create_solid (red, green, blue); + if (!gstate->pattern) + return CAIRO_STATUS_NO_MEMORY; return CAIRO_STATUS_SUCCESS; } @@ -549,6 +588,8 @@ _cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty) { cairo_matrix_t tmp; + _cairo_gstate_unset_font (gstate); + _cairo_matrix_set_translate (&tmp, tx, ty); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); @@ -566,6 +607,8 @@ _cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy) if (sx == 0 || sy == 0) return CAIRO_STATUS_INVALID_MATRIX; + _cairo_gstate_unset_font (gstate); + _cairo_matrix_set_scale (&tmp, sx, sy); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); @@ -580,6 +623,8 @@ _cairo_gstate_rotate (cairo_gstate_t *gstate, double angle) { cairo_matrix_t tmp; + _cairo_gstate_unset_font (gstate); + _cairo_matrix_set_rotate (&tmp, angle); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); @@ -595,6 +640,8 @@ _cairo_gstate_concat_matrix (cairo_gstate_t *gstate, { cairo_matrix_t tmp; + _cairo_gstate_unset_font (gstate); + cairo_matrix_copy (&tmp, matrix); cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); @@ -610,6 +657,8 @@ _cairo_gstate_set_matrix (cairo_gstate_t *gstate, { cairo_status_t status; + _cairo_gstate_unset_font (gstate); + cairo_matrix_copy (&gstate->ctm, matrix); cairo_matrix_copy (&gstate->ctm_inverse, matrix); @@ -627,6 +676,8 @@ _cairo_gstate_default_matrix (cairo_gstate_t *gstate) if (scale == 0) scale = 1; + _cairo_gstate_unset_font (gstate); + cairo_matrix_set_identity (&gstate->font_matrix); cairo_matrix_set_identity (&gstate->ctm); @@ -640,6 +691,8 @@ _cairo_gstate_default_matrix (cairo_gstate_t *gstate) cairo_status_t _cairo_gstate_identity_matrix (cairo_gstate_t *gstate) { + _cairo_gstate_unset_font (gstate); + cairo_matrix_set_identity (&gstate->ctm); cairo_matrix_set_identity (&gstate->ctm_inverse); @@ -1256,54 +1309,17 @@ _cairo_gstate_interpret_path (cairo_gstate_t *gstate, &gpi); } -/* This function modifies the pattern and the state of the pattern surface it - may contain. The pattern surface will be restored to its orignal state - when the pattern is destroyed. The appropriate way is to pass a copy of - the original pattern to this function just before the pattern should be - used and destroy the copy when done. */ -static cairo_status_t -_cairo_gstate_create_pattern (cairo_gstate_t *gstate, - cairo_pattern_t *pattern, - cairo_box_t *extents) +/* XXX: gstate->alpha will be going away before too long, and when it + * does, it may make sense for this function to just disappear. + */ +static void +_cairo_gstate_pattern_init_copy (cairo_gstate_t *gstate, + cairo_pattern_union_t *pattern, + cairo_pattern_t *src) { - cairo_int_status_t status; - - if (gstate->surface == NULL) { - _cairo_pattern_fini (pattern); - return CAIRO_STATUS_NO_TARGET_SURFACE; - } - - if (pattern->type == CAIRO_PATTERN_LINEAR || - pattern->type == CAIRO_PATTERN_RADIAL) { - if (pattern->n_stops < 2) { - pattern->type = CAIRO_PATTERN_SOLID; - - if (pattern->n_stops) { - cairo_color_stop_t *stop = pattern->stops; - - _cairo_color_set_rgb (&pattern->color, - (double) stop->color_char[0] / 0xff, - (double) stop->color_char[1] / 0xff, - (double) stop->color_char[2] / 0xff); - _cairo_color_set_alpha (&pattern->color, - (double) stop->color_char[3] / 0xff); - } - } - } - - _cairo_pattern_set_alpha (pattern, gstate->alpha); - _cairo_pattern_transform (pattern, &gstate->ctm_inverse); - - status = _cairo_surface_create_pattern (gstate->surface, pattern, extents); - if (status) { - _cairo_pattern_fini (pattern); - return status; - } - - if (pattern->type == CAIRO_PATTERN_SURFACE) - _cairo_pattern_prepare_surface (pattern); - - return CAIRO_STATUS_SUCCESS; + _cairo_pattern_init_copy (&pattern->base, src); + _cairo_pattern_transform (&pattern->base, &gstate->ctm_inverse); + _cairo_pattern_set_alpha (&pattern->base, gstate->alpha); } cairo_status_t @@ -1342,7 +1358,7 @@ cairo_status_t _cairo_gstate_in_stroke (cairo_gstate_t *gstate, double x, double y, - int *inside_ret) + cairo_bool_t *inside_ret) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_traps_t traps; @@ -1365,51 +1381,85 @@ BAIL: return status; } -static cairo_status_t -_calculate_region_for_intermediate_clip_surface (pixman_region16_t *out, - cairo_box_t *extents, - cairo_clip_rec_t *clip_rect) +/* XXX We currently have a confusing mix of boxes and rectangles as + * exemplified by this function. A cairo_box_t is a rectangular area + * represented by the coordinates of the upper left and lower right + * corners, expressed in fixed point numbers. A cairo_rectangle_t is + * also a rectangular area, but represented by the upper left corner + * and the width and the height, as integer numbers. + * + * This function converts a cairo_box_t to a cairo_rectangle_t by + * increasing the area to the nearest integer coordinates. We should + * standardize on cairo_rectangle_t and cairo_rectangle_fixed_t, and + * this function could be renamed to the more reasonable + * _cairo_rectangle_fixed_round. + */ + +static void +_cairo_box_round_to_rectangle (cairo_box_t *box, cairo_rectangle_t *rectangle) { - cairo_status_t status; - pixman_region16_t *extents_region, *clip_region; - pixman_box16_t clip_box, pixman_extents; - - pixman_extents.x1 = _cairo_fixed_integer_floor (extents->p1.x); - pixman_extents.y1 = _cairo_fixed_integer_floor (extents->p1.y); - pixman_extents.x2 = _cairo_fixed_integer_ceil (extents->p2.x); - pixman_extents.y2 = _cairo_fixed_integer_ceil (extents->p2.y); - extents_region = pixman_region_create_simple (&pixman_extents); - if (extents_region == NULL) - { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL0; - } + rectangle->x = _cairo_fixed_integer_floor (box->p1.x); + rectangle->y = _cairo_fixed_integer_floor (box->p1.y); + rectangle->width = _cairo_fixed_integer_ceil (box->p2.x) - rectangle->x; + rectangle->height = _cairo_fixed_integer_ceil (box->p2.y) - rectangle->y; +} - clip_box.x1 = clip_rect->x; - clip_box.y1 = clip_rect->y; - clip_box.x2 = clip_rect->x + clip_rect->width; - clip_box.y2 = clip_rect->y + clip_rect->height; - clip_region = pixman_region_create_simple (&clip_box); - if (clip_region == NULL) - { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL1; - } +static void +_cairo_rectangle_intersect (cairo_rectangle_t *dest, cairo_rectangle_t *src) +{ + int x1, y1, x2, y2; - if (pixman_region_intersect (out, - extents_region, - clip_region) - == PIXMAN_REGION_STATUS_FAILURE) - status = CAIRO_STATUS_NO_MEMORY; - else - status = CAIRO_STATUS_SUCCESS; - - pixman_region_destroy (extents_region); - BAIL1: - pixman_region_destroy (clip_region); - - BAIL0: - return status; + x1 = MAX (dest->x, src->x); + y1 = MAX (dest->y, src->y); + x2 = MIN (dest->x + dest->width, src->x + src->width); + y2 = MIN (dest->y + dest->height, src->y + src->height); + + if (x1 >= x2 || y1 >= y2) { + dest->x = 0; + dest->y = 0; + dest->width = 0; + dest->height = 0; + } else { + dest->x = x1; + dest->y = y1; + dest->width = x2 - x1; + dest->height = y2 - y1; + } +} + +static int +_cairo_rectangle_empty (cairo_rectangle_t *rect) +{ + return rect->width == 0 || rect->height == 0; +} + +static void +translate_traps (cairo_traps_t *traps, int x, int y) +{ + cairo_fixed_t xoff, yoff; + cairo_trapezoid_t *t; + int i; + + /* Ugh. The cairo_composite/(Render) interface doesn't allow + an offset for the trapezoids. Need to manually shift all + the coordinates to align with the offset origin of the + intermediate surface. */ + + xoff = _cairo_fixed_from_int (x); + yoff = _cairo_fixed_from_int (y); + + for (i = 0, t = traps->traps; i < traps->num_traps; i++, t++) { + t->top += yoff; + t->bottom += yoff; + t->left.p1.x += xoff; + t->left.p1.y += yoff; + t->left.p2.x += xoff; + t->left.p2.y += yoff; + t->right.p1.x += xoff; + t->right.p1.y += yoff; + t->right.p2.x += xoff; + t->right.p2.y += yoff; + } } @@ -1422,173 +1472,148 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, cairo_traps_t *traps) { cairo_status_t status; - cairo_pattern_t pattern; - cairo_box_t extents; - int x_src, y_src; + cairo_pattern_union_t pattern; + cairo_rectangle_t extents; + cairo_box_t trap_extents; if (traps->num_traps == 0) return CAIRO_STATUS_SUCCESS; + if (gstate->surface == NULL) + return CAIRO_STATUS_NO_TARGET_SURFACE; + + _cairo_traps_extents (traps, &trap_extents); + _cairo_box_round_to_rectangle (&trap_extents, &extents); + if (gstate->clip.surface) { - cairo_fixed_t xoff, yoff; - cairo_trapezoid_t *t; - int i; cairo_surface_t *intermediate; + cairo_surface_pattern_t intermediate_pattern; cairo_color_t empty_color; - pixman_box16_t *draw_extents; - pixman_region16_t *draw_region; - draw_region = pixman_region_create (); - if (draw_region == NULL) - { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL0; - } - - _cairo_traps_extents (traps, &extents); - - status = _calculate_region_for_intermediate_clip_surface (draw_region, - &extents, - &gstate->clip); - if (status) - goto BAIL1; + _cairo_rectangle_intersect (&extents, &gstate->clip.rect); - /* Shortcut if empty */ - if (!pixman_region_not_empty (draw_region)) { + if (_cairo_rectangle_empty (&extents)) { status = CAIRO_STATUS_SUCCESS; goto BAIL1; } - draw_extents = pixman_region_extents (draw_region); - - /* Ugh. The cairo_composite/(Render) interface doesn't allow - an offset for the trapezoids. Need to manually shift all - the coordinates to align with the offset origin of the - intermediate surface. */ - xoff = _cairo_fixed_from_int (draw_extents->x1); - yoff = _cairo_fixed_from_int (draw_extents->y1); - for (i=0, t=traps->traps; i < traps->num_traps; i++, t++) { - t->top -= yoff; - t->bottom -= yoff; - t->left.p1.x -= xoff; - t->left.p1.y -= yoff; - t->left.p2.x -= xoff; - t->left.p2.y -= yoff; - t->right.p1.x -= xoff; - t->right.p1.y -= yoff; - t->right.p2.x -= xoff; - t->right.p2.y -= yoff; - } - - if (traps->traps[0].left.p1.y < traps->traps[0].left.p2.y) { - x_src = _cairo_fixed_to_double (traps->traps[0].left.p1.x); - y_src = _cairo_fixed_to_double (traps->traps[0].left.p1.y); - } else { - x_src = _cairo_fixed_to_double (traps->traps[0].left.p2.x); - y_src = _cairo_fixed_to_double (traps->traps[0].left.p2.y); - } - - _cairo_pattern_init_solid (&pattern, 1.0, 1.0, 1.0); - _cairo_pattern_set_alpha (&pattern, 1.0); - - status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); - if (status) - goto BAIL1; + translate_traps (traps, -extents.x, -extents.y); _cairo_color_init (&empty_color); _cairo_color_set_alpha (&empty_color, 0.); intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, CAIRO_FORMAT_A8, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1, + extents.width, + extents.height, &empty_color); if (intermediate == NULL) { status = CAIRO_STATUS_NO_MEMORY; - goto BAIL2; + goto BAIL1; } + _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0); + status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD, - pattern.source, intermediate, - x_src, - y_src, + &pattern.base, + intermediate, + extents.x, extents.y, + 0, 0, + extents.width, + extents.height, traps->traps, traps->num_traps); + _cairo_pattern_fini (&pattern.base); + if (status) - goto BAIL3; + goto BAIL2; + + + _cairo_pattern_init_for_surface (&pattern.surface, + gstate->clip.surface); status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - gstate->clip.surface, + &pattern.base, NULL, intermediate, - draw_extents->x1 - gstate->clip.x, - draw_extents->y1 - gstate->clip.y, + extents.x - gstate->clip.rect.x, + extents.y - gstate->clip.rect.y, 0, 0, 0, 0, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1); - if (status) - goto BAIL3; - - _cairo_pattern_fini (&pattern); + extents.width, extents.height); + _cairo_pattern_fini (&pattern.base); - _cairo_pattern_init_copy (&pattern, src); - - extents.p1.x = _cairo_fixed_from_int (draw_extents->x1); - extents.p1.y = _cairo_fixed_from_int (draw_extents->y1); - extents.p2.x = _cairo_fixed_from_int (draw_extents->x2); - extents.p2.y = _cairo_fixed_from_int (draw_extents->y2); - status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); if (status) - goto BAIL3; + goto BAIL2; - if (dst == gstate->clip.surface) - xoff = yoff = 0; + _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); + _cairo_gstate_pattern_init_copy (gstate, &pattern, src); status = _cairo_surface_composite (operator, - pattern.source, intermediate, dst, - 0, 0, + &pattern.base, + &intermediate_pattern.base, + dst, + extents.x, extents.y, 0, 0, - xoff >> 16, - yoff >> 16, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1); + extents.x, extents.y, + extents.width, extents.height); + _cairo_pattern_fini (&pattern.base); + _cairo_pattern_fini (&intermediate_pattern.base); - BAIL3: - cairo_surface_destroy (intermediate); BAIL2: - _cairo_pattern_fini (&pattern); + cairo_surface_destroy (intermediate); BAIL1: - pixman_region_destroy (draw_region); - BAIL0: if (status) return status; } else { - if (traps->traps[0].left.p1.y < traps->traps[0].left.p2.y) { - x_src = _cairo_fixed_to_double (traps->traps[0].left.p1.x); - y_src = _cairo_fixed_to_double (traps->traps[0].left.p1.y); - } else { - x_src = _cairo_fixed_to_double (traps->traps[0].left.p2.x); - y_src = _cairo_fixed_to_double (traps->traps[0].left.p2.y); + if (gstate->clip.region) { + pixman_box16_t box; + pixman_box16_t *intersection_extents; + pixman_region16_t *rect, *intersection; + + box.x1 = _cairo_fixed_integer_floor (trap_extents.p1.x); + box.y1 = _cairo_fixed_integer_floor (trap_extents.p1.y); + box.x2 = _cairo_fixed_integer_ceil (trap_extents.p2.x); + box.y2 = _cairo_fixed_integer_ceil (trap_extents.p2.y); + + rect = pixman_region_create_simple (&box); + if (rect == NULL) + goto bail1; + intersection = pixman_region_create(); + if (intersection == NULL) + goto bail2; + + if (pixman_region_intersect (intersection, gstate->clip.region, + rect) != PIXMAN_REGION_STATUS_SUCCESS) + goto bail3; + intersection_extents = pixman_region_extents (intersection); + + extents.x = intersection_extents->x1; + extents.y = intersection_extents->y1; + extents.width = intersection_extents->x2 - intersection_extents->x1; + extents.height = intersection_extents->y2 - intersection_extents->y1; + bail3: + pixman_region_destroy (intersection); + bail2: + pixman_region_destroy (rect); + bail1: + ; } - _cairo_pattern_init_copy (&pattern, src); + _cairo_gstate_pattern_init_copy (gstate, &pattern, src); - _cairo_traps_extents (traps, &extents); - status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); - if (status) - return status; - status = _cairo_surface_composite_trapezoids (gstate->operator, - pattern.source, dst, - x_src - pattern.source_offset.x, - y_src - pattern.source_offset.y, + &pattern.base, dst, + extents.x, extents.y, + extents.x, extents.y, + extents.width, + extents.height, traps->traps, traps->num_traps); - _cairo_pattern_fini (&pattern); + _cairo_pattern_fini (&pattern.base); if (status) return status; @@ -1628,7 +1653,7 @@ cairo_status_t _cairo_gstate_in_fill (cairo_gstate_t *gstate, double x, double y, - int *inside_ret) + cairo_bool_t *inside_ret) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_traps_t traps; @@ -1807,9 +1832,10 @@ cairo_status_t _cairo_gstate_clip (cairo_gstate_t *gstate) { cairo_status_t status; - cairo_pattern_t pattern; + cairo_pattern_union_t pattern; cairo_traps_t traps; cairo_color_t white_color; + cairo_box_t extents; pixman_box16_t box; /* Fill the clip region as traps. */ @@ -1871,33 +1897,32 @@ _cairo_gstate_clip (cairo_gstate_t *gstate) _cairo_color_init (&white_color); if (gstate->clip.surface == NULL) { - cairo_box_t extents; - _cairo_traps_extents (&traps, &extents); - gstate->clip.x = extents.p1.x >> 16; - gstate->clip.y = extents.p1.y >> 16; - gstate->clip.width = ((extents.p2.x + 65535) >> 16) - gstate->clip.x; - gstate->clip.height = ((extents.p2.y + 65535) >> 16) - gstate->clip.y; + _cairo_box_round_to_rectangle (&extents, &gstate->clip.rect); gstate->clip.surface = _cairo_surface_create_similar_solid (gstate->surface, CAIRO_FORMAT_A8, - gstate->clip.width, - gstate->clip.height, + gstate->clip.rect.width, + gstate->clip.rect.height, &white_color); if (gstate->clip.surface == NULL) return CAIRO_STATUS_NO_MEMORY; } - _cairo_pattern_init_solid (&pattern, 1.0, 1.0, 1.0); - _cairo_pattern_set_alpha (&pattern, 1.0); - - _cairo_gstate_clip_and_composite_trapezoids (gstate, - &pattern, - CAIRO_OPERATOR_IN, - gstate->clip.surface, - &traps); + translate_traps (&traps, -gstate->clip.rect.x, -gstate->clip.rect.y); + _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0); - _cairo_pattern_fini (&pattern); + status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_IN, + &pattern.base, + gstate->clip.surface, + 0, 0, + 0, 0, + gstate->clip.rect.width, + gstate->clip.rect.height, + traps.traps, + traps.num_traps); + + _cairo_pattern_fini (&pattern.base); _cairo_traps_fini (&traps); @@ -1978,19 +2003,15 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, * */ - cairo_status_t status; - cairo_matrix_t user_to_image, image_to_user; - cairo_matrix_t image_to_device, device_to_image; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_matrix_t image_to_user, image_to_device; double device_x, device_y; double device_width, device_height; - cairo_pattern_t pattern; + cairo_surface_pattern_t pattern; cairo_box_t pattern_extents; + cairo_rectangle_t extents; - cairo_surface_get_matrix (surface, &user_to_image); - cairo_matrix_multiply (&device_to_image, &gstate->ctm_inverse, &user_to_image); - cairo_surface_set_matrix (surface, &device_to_image); - - image_to_user = user_to_image; + cairo_surface_get_matrix (surface, &image_to_user); cairo_matrix_invert (&image_to_user); cairo_matrix_multiply (&image_to_device, &image_to_user, &gstate->ctm); @@ -2001,126 +2022,82 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, &device_x, &device_y, &device_width, &device_height); - _cairo_pattern_init (&pattern); + _cairo_pattern_init_for_surface (&pattern, surface); + + /* inherit surface attributes while surface attribute functions still + exist */ + pattern.base.matrix = surface->matrix; + pattern.base.filter = surface->filter; + if (surface->repeat) + pattern.base.extend = CAIRO_EXTEND_REPEAT; + else + pattern.base.extend = CAIRO_EXTEND_NONE; + + _cairo_pattern_transform (&pattern.base, &gstate->ctm_inverse); + _cairo_pattern_set_alpha (&pattern.base, gstate->alpha); pattern_extents.p1.x = _cairo_fixed_from_double (device_x); pattern_extents.p1.y = _cairo_fixed_from_double (device_y); pattern_extents.p2.x = _cairo_fixed_from_double (device_x + device_width); pattern_extents.p2.y = _cairo_fixed_from_double (device_y + device_height); - - if ((gstate->pattern->type != CAIRO_PATTERN_SOLID) || - (gstate->alpha != 1.0)) { - /* I'm allowing any type of pattern for the mask right now. - Maybe this is bad. Will allow for some cool effects though. */ - _cairo_pattern_init_copy (&pattern, gstate->pattern); - status = _cairo_gstate_create_pattern (gstate, &pattern, &pattern_extents); - if (status) - return status; - } - + _cairo_box_round_to_rectangle (&pattern_extents, &extents); + if (gstate->clip.surface) { - cairo_surface_t *intermediate; - cairo_color_t empty_color; - pixman_box16_t *draw_extents; - pixman_region16_t *draw_region; - - draw_region = pixman_region_create (); - if (draw_region == NULL) - { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL0; + _cairo_rectangle_intersect (&extents, &gstate->clip.rect); + + /* We only need to composite if the rectangle is not empty. */ + if (!_cairo_rectangle_empty (&extents)) { + cairo_surface_pattern_t clip_pattern; + + _cairo_pattern_init_for_surface (&clip_pattern, + gstate->clip.surface); + + status = _cairo_surface_composite (gstate->operator, + &pattern.base, + &clip_pattern.base, + gstate->surface, + extents.x, extents.y, + 0, 0, + extents.x, extents.y, + extents.width, extents.height); + + _cairo_pattern_fini (&clip_pattern.base); } - - status = _calculate_region_for_intermediate_clip_surface (draw_region, - &pattern_extents, - &gstate->clip); - if (status) - goto BAIL1; - - /* Shortcut if empty */ - if (!pixman_region_not_empty (draw_region)) { - status = CAIRO_STATUS_SUCCESS; - goto BAIL1; - } - - draw_extents = pixman_region_extents (draw_region); - - _cairo_color_init (&empty_color); - _cairo_color_set_alpha (&empty_color, .0); - intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, - CAIRO_FORMAT_A8, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1, - &empty_color); - - /* it is not completely clear what the "right" way to combine the - pattern and mask surface is. I will use the the clip as a source - and the pattern as a mask in building up my temporary, because - this is not *totally* bogus and accomodates the case where - pattern's source image is NULL reasonably well. feel free to - correct this if you see a reason. */ - - status = _cairo_surface_composite (CAIRO_OPERATOR_SRC, - gstate->clip.surface, - pattern.source, - intermediate, - draw_extents->x1 - gstate->clip.x, - draw_extents->y1 - gstate->clip.y, - 0, 0, - 0, 0, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1); - - - if (status) - goto BAIL2; - - status = _cairo_surface_composite (gstate->operator, - surface, - intermediate, - gstate->surface, - draw_extents->x1, draw_extents->y1, - 0, 0, - draw_extents->x1, draw_extents->y1, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1); - - BAIL2: - cairo_surface_destroy (intermediate); - BAIL1: - pixman_region_destroy (draw_region); - BAIL0: - ; } else { - - /* XXX: The rendered size is sometimes 1 or 2 pixels short from - what I expect. Need to fix this. */ + /* XXX: The rendered size is sometimes 1 or 2 pixels short + * from what I expect. Need to fix this. + * KRH: I'm guessing this was due to rounding error when + * passing double coordinates for integer arguments. Using + * the extents rectangle should fix this, since it's properly + * rounded. Is this still the case? + */ status = _cairo_surface_composite (gstate->operator, - surface, - pattern.source, + &pattern.base, + NULL, gstate->surface, - device_x, device_y, + extents.x, extents.y, 0, 0, - device_x, device_y, - device_width, - device_height); + extents.x, extents.y, + extents.width, extents.height); } - _cairo_pattern_fini (&pattern); + _cairo_pattern_fini (&pattern.base); - /* restore the matrix originally in the surface */ - cairo_surface_set_matrix (surface, &user_to_image); - - if (status) - return status; - - return CAIRO_STATUS_SUCCESS; + return status; } +static void +_cairo_gstate_unset_font (cairo_gstate_t *gstate) +{ + if (gstate->font) { + cairo_font_destroy (gstate->font); + gstate->font = NULL; + } +} cairo_status_t _cairo_gstate_select_font (cairo_gstate_t *gstate, @@ -2128,12 +2105,17 @@ _cairo_gstate_select_font (cairo_gstate_t *gstate, cairo_font_slant_t slant, cairo_font_weight_t weight) { - if (gstate->font) - _cairo_unscaled_font_destroy (gstate->font); + char *new_family; - gstate->font = _cairo_unscaled_font_create (family, slant, weight); - if (gstate->font == NULL) + new_family = strdup (family); + if (!new_family) return CAIRO_STATUS_NO_MEMORY; + + _cairo_gstate_unset_font (gstate); + + gstate->font_family = new_family; + gstate->font_slant = slant; + gstate->font_weight = weight; cairo_matrix_set_identity (&gstate->font_matrix); @@ -2144,6 +2126,8 @@ cairo_status_t _cairo_gstate_scale_font (cairo_gstate_t *gstate, double scale) { + _cairo_gstate_unset_font (gstate); + return cairo_matrix_scale (&gstate->font_matrix, scale, scale); } @@ -2153,6 +2137,9 @@ _cairo_gstate_transform_font (cairo_gstate_t *gstate, { cairo_matrix_t tmp; double a, b, c, d, tx, ty; + + _cairo_gstate_unset_font (gstate); + cairo_matrix_get_affine (matrix, &a, &b, &c, &d, &tx, &ty); cairo_matrix_set_affine (&tmp, a, b, c, d, 0, 0); return cairo_matrix_multiply (&gstate->font_matrix, &gstate->font_matrix, &tmp); @@ -2160,28 +2147,16 @@ _cairo_gstate_transform_font (cairo_gstate_t *gstate, cairo_status_t -_cairo_gstate_current_font (cairo_gstate_t *gstate, - cairo_font_t **font) +_cairo_gstate_current_font (cairo_gstate_t *gstate, + cairo_font_t **font) { - cairo_font_scale_t scale; - cairo_font_t *scaled; - double dummy; - - scaled = malloc (sizeof (cairo_font_t)); - if (scaled == NULL) - return CAIRO_STATUS_NO_MEMORY; - - cairo_matrix_get_affine (&gstate->font_matrix, - &scale.matrix[0][0], - &scale.matrix[0][1], - &scale.matrix[1][0], - &scale.matrix[1][1], - &dummy, &dummy); - - _cairo_font_init (scaled, &scale, gstate->font); - _cairo_unscaled_font_reference (gstate->font); + cairo_status_t status; - *font = scaled; + status = _cairo_gstate_ensure_font (gstate); + if (status) + return status; + + *font = gstate->font; return CAIRO_STATUS_SUCCESS; } @@ -2190,6 +2165,8 @@ void _cairo_gstate_set_font_transform (cairo_gstate_t *gstate, cairo_matrix_t *matrix) { + _cairo_gstate_unset_font (gstate); + cairo_matrix_copy (&gstate->font_matrix, matrix); } @@ -2214,12 +2191,10 @@ _cairo_gstate_current_font_transform (cairo_gstate_t *gstate, * independently scale the user coordinate system *or* the font matrix, in * order to adjust the rendered size of the font. * - * If the user asks for a permanent reference to "a font", they are given a - * handle to a structure holding a scale matrix and an unscaled font. This - * effectively decouples the font from further changes to user space. Even - * if the user then "sets" the current cairo_t font to the handle they were - * passed, further changes to the cairo_t CTM will not affect externally - * held references to the font. + * The only font type exposed to the user is cairo_font_t which is a + * a font specialized to a particular scale matrix, CTM, and target + * surface. The user is responsible for not using a cairo_font_t + * after changing the parameters; doing so will produce garbled metrics. * * * The font's view @@ -2279,9 +2254,9 @@ _cairo_gstate_current_font_transform (cairo_gstate_t *gstate, * */ -static void -_build_font_scale (cairo_gstate_t *gstate, - cairo_font_scale_t *sc) +void +_cairo_gstate_current_font_scale (cairo_gstate_t *gstate, + cairo_font_scale_t *sc) { cairo_matrix_t tmp; double dummy; @@ -2294,34 +2269,46 @@ _build_font_scale (cairo_gstate_t *gstate, &dummy, &dummy); } -cairo_status_t -_cairo_gstate_current_font_extents (cairo_gstate_t *gstate, - cairo_font_extents_t *extents) +static cairo_status_t +_cairo_gstate_ensure_font (cairo_gstate_t *gstate) { - cairo_int_status_t status; cairo_font_scale_t sc; - double font_scale_x, font_scale_y; + cairo_status_t status; + const char *family; + + if (gstate->font) + return CAIRO_STATUS_SUCCESS; + + _cairo_gstate_current_font_scale (gstate, &sc); - _build_font_scale (gstate, &sc); + if (gstate->font_family) + family = gstate->font_family; + else + family = CAIRO_FONT_FAMILY_DEFAULT; + + status = _cairo_font_create (family, + gstate->font_slant, + gstate->font_weight, + &sc, + &gstate->font); - status = _cairo_unscaled_font_font_extents (gstate->font, &sc, extents); + if (status) + return status; - _cairo_matrix_compute_scale_factors (&gstate->font_matrix, - &font_scale_x, &font_scale_y, - /* XXX */ 1); - - /* - * The font responded in unscaled units, scale by the font - * matrix scale factors to get to user space - */ - - extents->ascent *= font_scale_y; - extents->descent *= font_scale_y; - extents->height *= font_scale_y; - extents->max_x_advance *= font_scale_x; - extents->max_y_advance *= font_scale_y; - - return status; + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_current_font_extents (cairo_gstate_t *gstate, + cairo_font_extents_t *extents) +{ + cairo_status_t status = _cairo_gstate_ensure_font (gstate); + if (status) + return status; + + return cairo_font_extents (gstate->font, + &gstate->font_matrix, + extents); } cairo_status_t @@ -2331,14 +2318,15 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, int *nglyphs) { cairo_status_t status; - cairo_font_scale_t sc; cairo_point_t point; double origin_x, origin_y; int i; - _build_font_scale (gstate, &sc); - + status = _cairo_gstate_ensure_font (gstate); + if (status) + return status; + status = _cairo_path_current_point (&gstate->path, &point); if (status == CAIRO_STATUS_NO_CURRENT_POINT) { origin_x = 0.0; @@ -2350,8 +2338,8 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, &origin_x, &origin_y); } - status = _cairo_unscaled_font_text_to_glyphs (gstate->font, - &sc, utf8, glyphs, nglyphs); + status = _cairo_font_text_to_glyphs (gstate->font, + utf8, glyphs, nglyphs); if (status || !glyphs || !nglyphs || !(*glyphs) || !(nglyphs)) return status; @@ -2373,18 +2361,16 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate, cairo_status_t _cairo_gstate_set_font (cairo_gstate_t *gstate, - cairo_font_t *font) -{ - if (gstate->font != NULL) - _cairo_unscaled_font_destroy (gstate->font); - gstate->font = font->unscaled; - _cairo_unscaled_font_reference (gstate->font); - cairo_matrix_set_affine (&gstate->font_matrix, - font->scale.matrix[0][0], - font->scale.matrix[0][1], - font->scale.matrix[1][0], - font->scale.matrix[1][1], - 0, 0); + cairo_font_t *font) +{ + if (font != gstate->font) { + if (gstate->font) + cairo_font_destroy (gstate->font); + gstate->font = font; + if (gstate->font) + cairo_font_reference (gstate->font); + } + return CAIRO_STATUS_SUCCESS; } @@ -2394,90 +2380,18 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, int num_glyphs, cairo_text_extents_t *extents) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_glyph_t origin_glyph; - cairo_text_extents_t origin_extents; - cairo_font_scale_t sc; - int i; - double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0; - double x_pos = 0.0, y_pos = 0.0; - int set = 0; - - if (!num_glyphs) - { - extents->x_bearing = 0.0; - extents->y_bearing = 0.0; - extents->width = 0.0; - extents->height = 0.0; - extents->x_advance = 0.0; - extents->y_advance = 0.0; - return CAIRO_STATUS_SUCCESS; - } - - _build_font_scale (gstate, &sc); - - for (i = 0; i < num_glyphs; i++) - { - double x, y; - double wm, hm; - - origin_glyph = glyphs[i]; - origin_glyph.x = 0.0; - origin_glyph.y = 0.0; - status = _cairo_unscaled_font_glyph_extents (gstate->font, &sc, - &origin_glyph, 1, - &origin_extents); - - /* - * Transform font space metrics into user space metrics - * by running the corners through the font matrix and - * expanding the bounding box as necessary - */ - x = origin_extents.x_bearing; - y = origin_extents.y_bearing; - cairo_matrix_transform_point (&gstate->font_matrix, - &x, &y); - - for (hm = 0.0; hm <= 1.0; hm += 1.0) - for (wm = 0.0; wm <= 1.0; wm += 1.0) - { - x = origin_extents.x_bearing + origin_extents.width * wm; - y = origin_extents.y_bearing + origin_extents.height * hm; - cairo_matrix_transform_point (&gstate->font_matrix, - &x, &y); - x += glyphs[i].x; - y += glyphs[i].y; - if (!set) - { - min_x = max_x = x; - min_y = max_y = y; - set = 1; - } - else - { - if (x < min_x) min_x = x; - if (x > max_x) max_x = x; - if (y < min_y) min_y = y; - if (y > max_y) max_y = y; - } - } + cairo_status_t status; - x = origin_extents.x_advance; - y = origin_extents.y_advance; - cairo_matrix_transform_point (&gstate->font_matrix, - &x, &y); - x_pos = glyphs[i].x + x; - y_pos = glyphs[i].y + y; - } + status = _cairo_gstate_ensure_font (gstate); + if (status) + return status; - extents->x_bearing = min_x - glyphs[0].x; - extents->y_bearing = min_y - glyphs[0].y; - extents->width = max_x - min_x; - extents->height = max_y - min_y; - extents->x_advance = x_pos - glyphs[0].x; - extents->y_advance = y_pos - glyphs[0].y; + cairo_font_glyph_extents (gstate->font, + &gstate->font_matrix, + glyphs, num_glyphs, + extents); - return status; + return CAIRO_STATUS_SUCCESS; } cairo_status_t @@ -2488,12 +2402,14 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, cairo_status_t status; int i; cairo_glyph_t *transformed_glyphs = NULL; - cairo_font_scale_t sc; - cairo_pattern_t pattern; + cairo_pattern_union_t pattern; cairo_box_t bbox; + cairo_rectangle_t extents; - _build_font_scale (gstate, &sc); - + status = _cairo_gstate_ensure_font (gstate); + if (status) + return status; + transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); if (transformed_glyphs == NULL) return CAIRO_STATUS_NO_MEMORY; @@ -2506,52 +2422,34 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, &transformed_glyphs[i].y); } - _cairo_pattern_init_copy (&pattern, gstate->pattern); - status = _cairo_unscaled_font_glyph_bbox (gstate->font, &sc, - transformed_glyphs, num_glyphs, - &bbox); - if (status) - goto CLEANUP_GLYPHS; + status = _cairo_font_glyph_bbox (gstate->font, + transformed_glyphs, num_glyphs, + &bbox); + _cairo_box_round_to_rectangle (&bbox, &extents); - status = _cairo_gstate_create_pattern (gstate, &pattern, &bbox); if (status) goto CLEANUP_GLYPHS; - + if (gstate->clip.surface) { cairo_surface_t *intermediate; + cairo_surface_pattern_t intermediate_pattern; cairo_color_t empty_color; - pixman_box16_t *draw_extents; - pixman_region16_t *draw_region; - - draw_region = pixman_region_create (); - if (draw_region == NULL) - { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL0; - } - status = _calculate_region_for_intermediate_clip_surface (draw_region, - &bbox, - &gstate->clip); - if (status) { - goto BAIL1; - } + _cairo_rectangle_intersect (&extents, &gstate->clip.rect); /* Shortcut if empty */ - if (!pixman_region_not_empty (draw_region)) { + if (_cairo_rectangle_empty (&extents)) { status = CAIRO_STATUS_SUCCESS; goto BAIL1; } - draw_extents = pixman_region_extents (draw_region); - _cairo_color_init (&empty_color); _cairo_color_set_alpha (&empty_color, .0); intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, CAIRO_FORMAT_A8, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1, + extents.width, + extents.height, &empty_color); if (intermediate == NULL) { status = CAIRO_STATUS_NO_MEMORY; @@ -2561,66 +2459,77 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, /* move the glyphs again, from dev space to intermediate space */ for (i = 0; i < num_glyphs; ++i) { - transformed_glyphs[i].x -= draw_extents->x1; - transformed_glyphs[i].y -= draw_extents->y1; + transformed_glyphs[i].x -= extents.x; + transformed_glyphs[i].y -= extents.y; } - status = _cairo_unscaled_font_show_glyphs (gstate->font, - &sc, - CAIRO_OPERATOR_ADD, - pattern.source, intermediate, - draw_extents->x1 - pattern.source_offset.x, - draw_extents->y1 - pattern.source_offset.y, - transformed_glyphs, num_glyphs); + _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0); + + status = _cairo_font_show_glyphs (gstate->font, + CAIRO_OPERATOR_ADD, + &pattern.base, intermediate, + extents.x, extents.y, + 0, 0, + extents.width, extents.height, + transformed_glyphs, num_glyphs); + + _cairo_pattern_fini (&pattern.base); if (status) goto BAIL2; + _cairo_pattern_init_for_surface (&pattern.surface, + gstate->clip.surface); + status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - gstate->clip.surface, + &pattern.base, NULL, intermediate, - draw_extents->x1 - gstate->clip.x, - draw_extents->y1 - gstate->clip.y, + extents.x - gstate->clip.rect.x, + extents.y - gstate->clip.rect.y, 0, 0, 0, 0, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1); + extents.width, extents.height); + + _cairo_pattern_fini (&pattern.base); if (status) goto BAIL2; + _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); + _cairo_gstate_pattern_init_copy (gstate, &pattern, gstate->pattern); + status = _cairo_surface_composite (gstate->operator, - pattern.source, - intermediate, + &pattern.base, + &intermediate_pattern.base, gstate->surface, - 0, 0, + extents.x, extents.y, 0, 0, - draw_extents->x1, - draw_extents->y1, - draw_extents->x2 - draw_extents->x1, - draw_extents->y2 - draw_extents->y1); + extents.x, extents.y, + extents.width, extents.height); + _cairo_pattern_fini (&pattern.base); + _cairo_pattern_fini (&intermediate_pattern.base); BAIL2: cairo_surface_destroy (intermediate); BAIL1: - pixman_region_destroy (draw_region); - BAIL0: ; } else { - status = _cairo_unscaled_font_show_glyphs (gstate->font, - &sc, - gstate->operator, pattern.source, - gstate->surface, - -pattern.source_offset.x, - -pattern.source_offset.y, - transformed_glyphs, num_glyphs); + _cairo_gstate_pattern_init_copy (gstate, &pattern, gstate->pattern); + + status = _cairo_font_show_glyphs (gstate->font, + gstate->operator, &pattern.base, + gstate->surface, + extents.x, extents.y, + extents.x, extents.y, + extents.width, extents.height, + transformed_glyphs, num_glyphs); + + _cairo_pattern_fini (&pattern.base); } - _cairo_pattern_fini (&pattern); - CLEANUP_GLYPHS: free (transformed_glyphs); @@ -2635,9 +2544,6 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, cairo_status_t status; int i; cairo_glyph_t *transformed_glyphs = NULL; - cairo_font_scale_t sc; - - _build_font_scale (gstate, &sc); transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); if (transformed_glyphs == NULL) @@ -2651,9 +2557,9 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, &(transformed_glyphs[i].y)); } - status = _cairo_unscaled_font_glyph_path (gstate->font, &sc, - transformed_glyphs, num_glyphs, - &gstate->path); + status = _cairo_font_glyph_path (gstate->font, + transformed_glyphs, num_glyphs, + &gstate->path); free (transformed_glyphs); return status; diff --git a/src/cairo_hull.c b/src/cairo_hull.c index 99b16d1ae..c93d70625 100644 --- a/src/cairo_hull.c +++ b/src/cairo_hull.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo_image_surface.c b/src/cairo_image_surface.c index 14e30f695..9745b3150 100644 --- a/src/cairo_image_surface.c +++ b/src/cairo_image_surface.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" @@ -54,7 +54,8 @@ _cairo_format_bpp (cairo_format_t format) } static cairo_image_surface_t * -_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image) +_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, + cairo_format_t format) { cairo_image_surface_t *surface; @@ -66,6 +67,7 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image) surface->pixman_image = pixman_image; + surface->format = format; surface->data = (char *) pixman_image_get_data (pixman_image); surface->owns_data = 0; @@ -105,7 +107,8 @@ _cairo_image_surface_create_with_masks (char *data, if (pixman_image == NULL) return NULL; - surface = _cairo_image_surface_create_for_pixman_image (pixman_image); + surface = _cairo_image_surface_create_for_pixman_image (pixman_image, + (cairo_format_t)-1); return surface; } @@ -130,6 +133,20 @@ _create_pixman_format (cairo_format_t format) } } +/** + * cairo_image_surface_create: + * @format: format of pixels in the surface to create + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates an image surface of the specified format and + * dimensions. The initial contents of the surface is undefined; you + * must explicitely clear the buffer, using, for example, + * cairo_rectangle() and cairo_fill() if you want it cleared. + * + * Return value: the newly created surface, or %NULL if it couldn't + * be created because of lack of memory + **/ cairo_surface_t * cairo_image_surface_create (cairo_format_t format, int width, @@ -150,11 +167,33 @@ cairo_image_surface_create (cairo_format_t format, if (pixman_image == NULL) return NULL; - surface = _cairo_image_surface_create_for_pixman_image (pixman_image); + surface = _cairo_image_surface_create_for_pixman_image (pixman_image, format); return &surface->base; } +/** + * cairo_image_surface_create_for_data: + * @data: a pointer to a buffer supplied by the application + * in which to write contents. + * @format: the format of pixels in the buffer + * @width: the width of the image to be stored in the buffer + * @height: the height of the image to be stored in the buffer + * @stride: the number of bytes between the start of rows + * in the buffer. Having this be specified separate from @width + * allows for padding at the end of rows, or for writing + * to a subportion of a larger image. + * + * Creates an image surface for the provided pixel data. The output + * buffer must be kept around until the #cairo_surface_t is destroyed + * or cairo_surface_finish() is called on the surface. The initial + * contents of @buffer will be used as the inital image contents; you + * must explicitely clear the buffer, using, for example, + * cairo_rectangle() and cairo_fill() if you want it cleared. + * + * Return value: the newly created surface, or %NULL if it couldn't + * be created because of lack of memory + **/ cairo_surface_t * cairo_image_surface_create_for_data (char *data, cairo_format_t format, @@ -180,7 +219,7 @@ cairo_image_surface_create_for_data (char *data, if (pixman_image == NULL) return NULL; - surface = _cairo_image_surface_create_for_pixman_image (pixman_image); + surface = _cairo_image_surface_create_for_pixman_image (pixman_image, format); return &surface->base; } @@ -224,33 +263,66 @@ _cairo_image_surface_pixels_per_inch (void *abstract_surface) return 96.0; } -static cairo_image_surface_t * -_cairo_image_surface_get_image (void *abstract_surface) +static cairo_status_t +_cairo_image_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) { - cairo_image_surface_t *surface = abstract_surface; - - cairo_surface_reference (&surface->base); + *image_out = abstract_surface; + + return CAIRO_STATUS_SUCCESS; +} - return surface; +static void +_cairo_image_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ } static cairo_status_t -_cairo_image_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +_cairo_image_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect_out, + void **image_extra) { - if (image == abstract_surface) - return CAIRO_STATUS_SUCCESS; + cairo_image_surface_t *surface = abstract_surface; + + image_rect_out->x = 0; + image_rect_out->y = 0; + image_rect_out->width = surface->width; + image_rect_out->height = surface->height; + + *image_out = surface; - /* XXX: This case has not yet been implemented. We'll lie for now. */ return CAIRO_STATUS_SUCCESS; } +static void +_cairo_image_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ +} + static cairo_status_t -_cairo_image_abstract_surface_set_matrix (void *abstract_surface, - cairo_matrix_t *matrix) +_cairo_image_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { cairo_image_surface_t *surface = abstract_surface; - return _cairo_image_surface_set_matrix (surface, matrix); + + if (src->backend == surface->base.backend) { + *clone_out = src; + cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; } cairo_status_t @@ -276,14 +348,6 @@ _cairo_image_surface_set_matrix (cairo_image_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_image_abstract_surface_set_filter (void *abstract_surface, cairo_filter_t filter) -{ - cairo_image_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_filter (surface, filter); -} - cairo_status_t _cairo_image_surface_set_filter (cairo_image_surface_t *surface, cairo_filter_t filter) { @@ -314,13 +378,6 @@ _cairo_image_surface_set_filter (cairo_image_surface_t *surface, cairo_filter_t return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_image_abstract_surface_set_repeat (void *abstract_surface, int repeat) -{ - cairo_image_surface_t *surface = abstract_surface; - return _cairo_image_surface_set_repeat (surface, repeat); -} - cairo_status_t _cairo_image_surface_set_repeat (cairo_image_surface_t *surface, int repeat) { @@ -329,6 +386,34 @@ _cairo_image_surface_set_repeat (cairo_image_surface_t *surface, int repeat) return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_image_surface_set_attributes (cairo_image_surface_t *surface, + cairo_surface_attributes_t *attributes) +{ + cairo_int_status_t status; + + status = _cairo_image_surface_set_matrix (surface, &attributes->matrix); + if (status) + return status; + + switch (attributes->extend) { + case CAIRO_EXTEND_NONE: + _cairo_image_surface_set_repeat (surface, 0); + break; + case CAIRO_EXTEND_REPEAT: + _cairo_image_surface_set_repeat (surface, 1); + break; + case CAIRO_EXTEND_REFLECT: + /* XXX: Obviously wrong. */ + _cairo_image_surface_set_repeat (surface, 1); + break; + } + + status = _cairo_image_surface_set_filter (surface, attributes->filter); + + return status; +} + static pixman_operator_t _pixman_operator (cairo_operator_t operator) { @@ -368,8 +453,8 @@ _pixman_operator (cairo_operator_t operator) static cairo_int_status_t _cairo_image_surface_composite (cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, @@ -380,26 +465,61 @@ _cairo_image_surface_composite (cairo_operator_t operator, unsigned int width, unsigned int height) { - cairo_image_surface_t *dst = abstract_dst; - cairo_image_surface_t *src = (cairo_image_surface_t *) generic_src; - cairo_image_surface_t *mask = (cairo_image_surface_t *) generic_mask; - - if (generic_src->backend != dst->base.backend || - (generic_mask && (generic_mask->backend != dst->base.backend))) + cairo_surface_attributes_t src_attr, mask_attr; + cairo_image_surface_t *dst = abstract_dst; + cairo_image_surface_t *src; + cairo_image_surface_t *mask; + cairo_int_status_t status; + + status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, + &dst->base, + src_x, src_y, + mask_x, mask_y, + width, height, + (cairo_surface_t **) &src, + (cairo_surface_t **) &mask, + &src_attr, &mask_attr); + if (status) + return status; + + status = _cairo_image_surface_set_attributes (src, &src_attr); + if (CAIRO_OK (status)) { - return CAIRO_INT_STATUS_UNSUPPORTED; + if (mask) + { + status = _cairo_image_surface_set_attributes (mask, &mask_attr); + if (CAIRO_OK (status)) + pixman_composite (_pixman_operator (operator), + src->pixman_image, + mask->pixman_image, + dst->pixman_image, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + mask_x + mask_attr.x_offset, + mask_y + mask_attr.y_offset, + dst_x, dst_y, + width, height); + } + else + { + pixman_composite (_pixman_operator (operator), + src->pixman_image, + NULL, + dst->pixman_image, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + } } - pixman_composite (_pixman_operator (operator), - src->pixman_image, - mask ? mask->pixman_image : NULL, - dst->pixman_image, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); - - return CAIRO_STATUS_SUCCESS; + if (mask) + _cairo_pattern_release_surface (&dst->base, &mask->base, &mask_attr); + + _cairo_pattern_release_surface (&dst->base, &src->base, &src_attr); + + return status; } static cairo_int_status_t @@ -427,24 +547,56 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, static cairo_int_status_t _cairo_image_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *generic_src, + cairo_pattern_t *pattern, void *abstract_dst, - int x_src, - int y_src, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps) { - cairo_image_surface_t *dst = abstract_dst; - cairo_image_surface_t *src = (cairo_image_surface_t *) generic_src; + cairo_surface_attributes_t attributes; + cairo_image_surface_t *dst = abstract_dst; + cairo_image_surface_t *src; + cairo_int_status_t status; + int render_reference_x, render_reference_y; + int render_src_x, render_src_y; + + status = _cairo_pattern_acquire_surface (pattern, &dst->base, + src_x, src_y, width, height, + (cairo_surface_t **) &src, + &attributes); + if (status) + return status; + + if (traps[0].left.p1.y < traps[0].left.p2.y) { + render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); + render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); + } else { + render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); + render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); + } - if (generic_src->backend != dst->base.backend) - return CAIRO_INT_STATUS_UNSUPPORTED; + render_src_x = src_x + render_reference_x - dst_x; + render_src_y = src_y + render_reference_y - dst_y; - /* XXX: The pixman_trapezoid_t cast is evil and needs to go away somehow. */ - pixman_composite_trapezoids (operator, src->pixman_image, dst->pixman_image, - x_src, y_src, (pixman_trapezoid_t *) traps, num_traps); + /* XXX: The pixman_trapezoid_t cast is evil and needs to go away + * somehow. */ + status = _cairo_image_surface_set_attributes (src, &attributes); + if (CAIRO_OK (status)) + pixman_composite_trapezoids (operator, + src->pixman_image, + dst->pixman_image, + render_src_x + attributes.x_offset, + render_src_y + attributes.y_offset, + (pixman_trapezoid_t *) traps, num_traps); - return CAIRO_STATUS_SUCCESS; + _cairo_pattern_release_surface (&dst->base, &src->base, &attributes); + + return status; } static cairo_int_status_t @@ -490,41 +642,34 @@ _cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_image_abstract_surface_create_pattern (void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *box) +/** + * _cairo_surface_is_image: + * @surface: a #cairo_surface_t + * + * Checks if a surface is an #cairo_image_surface_t + * + * Return value: True if the surface is an image surface + **/ +int +_cairo_surface_is_image (cairo_surface_t *surface) { - cairo_image_surface_t *image; - - /* Fall back to general pattern creation for surface patterns. */ - if (pattern->type == CAIRO_PATTERN_SURFACE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - image = _cairo_pattern_get_image (pattern, box); - if (image) { - pattern->source = &image->base; - - return CAIRO_STATUS_SUCCESS; - } else - return CAIRO_STATUS_NO_MEMORY; + return surface->backend == &cairo_image_surface_backend; } - + static const cairo_surface_backend_t cairo_image_surface_backend = { _cairo_image_surface_create_similar, _cairo_image_abstract_surface_destroy, _cairo_image_surface_pixels_per_inch, - _cairo_image_surface_get_image, - _cairo_image_surface_set_image, - _cairo_image_abstract_surface_set_matrix, - _cairo_image_abstract_surface_set_filter, - _cairo_image_abstract_surface_set_repeat, + _cairo_image_surface_acquire_source_image, + _cairo_image_surface_release_source_image, + _cairo_image_surface_acquire_dest_image, + _cairo_image_surface_release_dest_image, + _cairo_image_surface_clone_similar, _cairo_image_surface_composite, _cairo_image_surface_fill_rectangles, _cairo_image_surface_composite_trapezoids, _cairo_image_surface_copy_page, _cairo_image_surface_show_page, _cairo_image_abstract_surface_set_clip_region, - _cairo_image_abstract_surface_create_pattern, NULL /* show_glyphs */ }; diff --git a/src/cairo_matrix.c b/src/cairo_matrix.c index b964b688c..88e536e8a 100644 --- a/src/cairo_matrix.c +++ b/src/cairo_matrix.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #define _GNU_SOURCE @@ -54,6 +54,14 @@ _cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar); static void _cairo_matrix_compute_adjoint (cairo_matrix_t *matrix); +/** + * cairo_matrix_create: + * + * Creates a new identity matrix. + * + * Return value: a newly created matrix; free with cairo_matrix_destroy(), + * or %NULL if memory couldn't be allocated. + **/ cairo_matrix_t * cairo_matrix_create (void) { @@ -80,6 +88,12 @@ _cairo_matrix_fini (cairo_matrix_t *matrix) /* nothing to do here */ } +/** + * cairo_matrix_destroy: + * @matrix: a #cairo_matrix_t + * + * Frees a matrix created with cairo_matrix_create. + **/ void cairo_matrix_destroy (cairo_matrix_t *matrix) { @@ -87,6 +101,15 @@ cairo_matrix_destroy (cairo_matrix_t *matrix) free (matrix); } +/** + * cairo_matrix_copy: + * @matrix: a #cairo_matrix_t + * @other: another #cairo_ + * + * Modifies @matrix to be identical to @other. + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_copy (cairo_matrix_t *matrix, const cairo_matrix_t *other) { @@ -96,6 +119,14 @@ cairo_matrix_copy (cairo_matrix_t *matrix, const cairo_matrix_t *other) } slim_hidden_def(cairo_matrix_copy); +/** + * cairo_matrix_set_identity: + * @matrix: a #cairo_matrix_t + * + * Modifies @matrix to be an identity transformation. + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_set_identity (cairo_matrix_t *matrix) { @@ -105,6 +136,26 @@ cairo_matrix_set_identity (cairo_matrix_t *matrix) } slim_hidden_def(cairo_matrix_set_identity); +/** + * cairo_matrix_set_affine: + * @matrix: a cairo_matrix_t + * @a: a component of the affine transformation + * @b: b component of the affine transformation + * @c: c component of the affine transformation + * @d: d component of the affine transformation + * @tx: X translation component of the affine transformation + * @ty: Y translation component of the affine transformation + * + * Sets @matrix to be the affine transformation given by + * @a, b, @c, @d, @tx, @ty. The transformation is given + * by: + * <programlisting> + * x_new = x * a + y * c + tx; + * y_new = x * b + y * d + ty; + * </programlisting> + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_set_affine (cairo_matrix_t *matrix, double a, double b, @@ -119,6 +170,21 @@ cairo_matrix_set_affine (cairo_matrix_t *matrix, } slim_hidden_def(cairo_matrix_set_affine); +/** + * cairo_matrix_get_affine: + * @matrix: a @cairo_matrix_t + * @a: location to store a component of affine transformation, or %NULL + * @b: location to store b component of affine transformation, or %NULL + * @c: location to store c component of affine transformation, or %NULL + * @d: location to store d component of affine transformation, or %NULL + * @tx: location to store X-translation component of affine transformation, or %NULL + * @ty: location to store Y-translation component of affine transformation, or %NULL + * + * Gets the matrix values for the affine tranformation that @matrix represents. + * See cairo_matrix_set_affine(). + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_get_affine (cairo_matrix_t *matrix, double *a, double *b, @@ -153,6 +219,18 @@ _cairo_matrix_set_translate (cairo_matrix_t *matrix, tx, ty); } +/** + * cairo_matrix_translate: + * @matrix: a cairo_matrix_t + * @tx: amount to rotate in the X direction + * @ty: amount to rotate in the Y direction + * + * Applies a translation by @tx, @ty to the transformation in + * @matrix. The new transformation is given by first translating by + * @tx, @ty then applying the original transformation + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty) { @@ -173,6 +251,18 @@ _cairo_matrix_set_scale (cairo_matrix_t *matrix, 0, 0); } +/** + * cairo_matrix_scale: + * @matrix: a #cairo_matrix_t + * @sx: Scale factor in the X direction + * @sy: Scale factor in the Y direction + * + * Applies scaling by @tx, @ty to the transformation in + * @matrix. The new transformation is given by first scaling by @sx + * and @sy then applying the original transformation + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy) { @@ -202,6 +292,21 @@ _cairo_matrix_set_rotate (cairo_matrix_t *matrix, 0, 0); } +/** + * cairo_matrix_rotate: + * @matrix: a @cairo_matrix_t + * @radians: angle of rotation, in radians. Angles are defined + * so that an angle of 90 degrees (%M_PI radians) rotates the + * positive X axis into the positive Y axis. With the default + * Cairo choice of axis orientation, positive rotations are + * clockwise. + * + * Applies rotation by @radians to the transformation in + * @matrix. The new transformation is given by first rotating by + * @radians then applying the original transformation + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) { @@ -212,6 +317,19 @@ cairo_matrix_rotate (cairo_matrix_t *matrix, double radians) return cairo_matrix_multiply (matrix, &tmp, matrix); } +/** + * cairo_matrix_multiply: + * @result: a @cairo_matrix_t in which to store the result + * @a: a @cairo_matrix_t + * @b: a @cairo_matrix_t + * + * Multiplies the affine transformations in @a and @b together + * and stores the result in @result. The resulting transformation + * is given by first applying the transformation in @b then + * applying the transformation in @a. + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b) { @@ -238,6 +356,27 @@ cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const ca } slim_hidden_def(cairo_matrix_multiply); +/** + * cairo_matrix_transform_distance: + * @matrix: a @cairo_matrix_t + * @dx: a distance in the X direction. An in/out parameter + * @dy: a distance in the Y direction. An in/out parameter + * + * Transforms the vector (@dx,@dy) by @matrix. Translation is + * ignored. In terms of the components of the affine transformation: + * + * <programlisting> + * dx2 = dx1 * a + dy1 * c; + * dy2 = dx1 * b + dy1 * d; + * </programlisting> + * + * Affine transformations are position invariant, so the same vector + * always transforms to the same vector. If (@x1,@y1) transforms + * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to + * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2. + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_transform_distance (cairo_matrix_t *matrix, double *dx, double *dy) { @@ -255,6 +394,16 @@ cairo_matrix_transform_distance (cairo_matrix_t *matrix, double *dx, double *dy) } slim_hidden_def(cairo_matrix_transform_distance); +/** + * cairo_matrix_transform_point: + * @matrix: a @cairo_matrix_t + * @x: X position. An in/out parameter + * @y: Y position. An in/out parameter + * + * Transforms the point (@x, @y) by @matrix. + * + * Return value: %CAIRO_STATUS_SUCCESS, always. + **/ cairo_status_t cairo_matrix_transform_point (cairo_matrix_t *matrix, double *x, double *y) { @@ -351,6 +500,19 @@ _cairo_matrix_compute_adjoint (cairo_matrix_t *matrix) c*ty - d*tx, b*tx - a*ty); } +/** + * cairo_matrix_invert: + * @matrix: a @cairo_matrix_t + * + * Changes @matrix to be the inverse of it's original value. Not + * all transformation matrices have inverses; if the matrix + * collapses points together (it is <firstterm>degenerate</firstterm>), + * then it has no inverse and this function will fail. + * + * Returns: If @matrix has an inverse, modifies @matrix to + * be the inverse matrix and returns %CAIRO_STATUS_SUCCESS. Otherwise, + * returns %CAIRO_STATUS_INVALID_MATRIX. + **/ cairo_status_t cairo_matrix_invert (cairo_matrix_t *matrix) { @@ -458,7 +620,7 @@ _cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double return CAIRO_STATUS_SUCCESS; } -int +cairo_bool_t _cairo_matrix_is_integer_translation(cairo_matrix_t *mat, int *itx, int *ity) { @@ -477,7 +639,7 @@ _cairo_matrix_is_integer_translation(cairo_matrix_t *mat, if (ok) { *itx = _cairo_fixed_integer_part(ttx); *ity = _cairo_fixed_integer_part(tty); - return 1; + return TRUE; } - return 0; + return FALSE; } diff --git a/src/cairo_path.c b/src/cairo_path.c index 36c25d637..8314f601c 100644 --- a/src/cairo_path.c +++ b/src/cairo_path.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include <stdlib.h> @@ -100,6 +100,7 @@ _cairo_path_init_copy (cairo_path_t *path, cairo_path_t *other) for (other_op = other->op_head; other_op; other_op = other_op->next) { op = _cairo_path_op_buf_create (); if (op == NULL) { + _cairo_path_fini(path); return CAIRO_STATUS_NO_MEMORY; } *op = *other_op; @@ -109,6 +110,7 @@ _cairo_path_init_copy (cairo_path_t *path, cairo_path_t *other) for (other_arg = other->arg_head; other_arg; other_arg = other_arg->next) { arg = _cairo_path_arg_buf_create (); if (arg == NULL) { + _cairo_path_fini(path); return CAIRO_STATUS_NO_MEMORY; } *arg = *other_arg; diff --git a/src/cairo_path_bounds.c b/src/cairo_path_bounds.c index cfcdd97ee..7c5772a82 100644 --- a/src/cairo_path_bounds.c +++ b/src/cairo_path_bounds.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo_path_fill.c b/src/cairo_path_fill.c index 6c6ebd976..dc79b6b96 100644 --- a/src/cairo_path_fill.c +++ b/src/cairo_path_fill.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo_path_stroke.c b/src/cairo_path_stroke.c index ad0220370..08b380902 100644 --- a/src/cairo_path_stroke.c +++ b/src/cairo_path_stroke.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo_pattern.c b/src/cairo_pattern.c index 6cb981458..283c36dbd 100644 --- a/src/cairo_pattern.c +++ b/src/cairo_pattern.c @@ -21,58 +21,108 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - * Author: David Reveman <c99drn@cs.umu.se> + * Author: David Reveman <davidr@novell.com> */ #include "cairoint.h" +typedef void (*cairo_shader_function_t) (unsigned char *color0, + unsigned char *color1, + cairo_fixed_t factor, + uint32_t *pixel); + +typedef struct _cairo_shader_color_stop { + cairo_fixed_t offset; + cairo_fixed_48_16_t scale; + int id; + unsigned char color_char[4]; +} cairo_shader_color_stop_t; + +typedef struct _cairo_shader_op { + cairo_shader_color_stop_t *stops; + int n_stops; + cairo_extend_t extend; + cairo_shader_function_t shader_function; +} cairo_shader_op_t; + #define MULTIPLY_COLORCOMP(c1, c2) \ ((unsigned char) \ ((((unsigned char) (c1)) * (int) ((unsigned char) (c2))) / 0xff)) -void -_cairo_pattern_init (cairo_pattern_t *pattern) +static void +_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) { + pattern->type = type; pattern->ref_count = 1; - - pattern->extend = CAIRO_EXTEND_DEFAULT; - pattern->filter = CAIRO_FILTER_DEFAULT; - - _cairo_color_init (&pattern->color); + pattern->extend = CAIRO_EXTEND_DEFAULT; + pattern->filter = CAIRO_FILTER_DEFAULT; + pattern->alpha = 1.0; _cairo_matrix_init (&pattern->matrix); +} - pattern->stops = NULL; - pattern->n_stops = 0; +static cairo_status_t +_cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, + cairo_gradient_pattern_t *other) +{ + if (other->base.type == CAIRO_PATTERN_LINEAR) + { + cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern; + cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other; + + *dst = *src; + } + else + { + cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern; + cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other; + + *dst = *src; + } - pattern->type = CAIRO_PATTERN_SOLID; + if (other->n_stops) + { + pattern->stops = malloc (other->n_stops * sizeof (cairo_color_stop_t)); + if (!pattern->stops) + return CAIRO_STATUS_NO_MEMORY; + + memcpy (pattern->stops, other->stops, + other->n_stops * sizeof (cairo_color_stop_t)); + } - pattern->source = NULL; - pattern->source_offset.x = 0.0; - pattern->source_offset.y = 0.0; + return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other) { - *pattern = *other; - - pattern->ref_count = 1; + switch (other->type) { + case CAIRO_PATTERN_SOLID: { + cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern; + cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other; - if (pattern->n_stops) { - pattern->stops = - malloc (sizeof (cairo_color_stop_t) * pattern->n_stops); - if (pattern->stops == NULL) - return CAIRO_STATUS_NO_MEMORY; - memcpy (pattern->stops, other->stops, - sizeof (cairo_color_stop_t) * other->n_stops); + *dst = *src; + } break; + case CAIRO_PATTERN_SURFACE: { + cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern; + cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other; + + *dst = *src; + cairo_surface_reference (dst->surface); + } break; + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: { + cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern; + cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other; + cairo_status_t status; + + status = _cairo_gradient_pattern_init_copy (dst, src); + if (status) + return status; + } break; } - - if (pattern->source) - cairo_surface_reference (other->source); - - if (pattern->type == CAIRO_PATTERN_SURFACE) - cairo_surface_reference (other->u.surface.surface); + + pattern->ref_count = 1; return CAIRO_STATUS_SUCCESS; } @@ -80,110 +130,145 @@ _cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other) void _cairo_pattern_fini (cairo_pattern_t *pattern) { - if (pattern->n_stops) - free (pattern->stops); - - if (pattern->type == CAIRO_PATTERN_SURFACE) { - /* show_surface require us to restore surface matrix, repeat - attribute, filter type */ - if (pattern->source) { - cairo_surface_set_matrix (pattern->source, - &pattern->u.surface.save_matrix); - cairo_surface_set_repeat (pattern->source, - pattern->u.surface.save_repeat); - cairo_surface_set_filter (pattern->source, - pattern->u.surface.save_filter); - } - cairo_surface_destroy (pattern->u.surface.surface); + switch (pattern->type) { + case CAIRO_PATTERN_SOLID: + break; + case CAIRO_PATTERN_SURFACE: { + cairo_surface_pattern_t *fini = (cairo_surface_pattern_t *) pattern; + + cairo_surface_destroy (fini->surface); + } break; + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: { + cairo_gradient_pattern_t *fini = (cairo_gradient_pattern_t *) pattern; + + if (fini->n_stops) + free (fini->stops); + } break; } +} + +void +_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, + double red, + double green, + double blue) +{ + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SOLID); - if (pattern->source) - cairo_surface_destroy (pattern->source); + pattern->red = red; + pattern->green = green; + pattern->blue = blue; } void -_cairo_pattern_init_solid (cairo_pattern_t *pattern, - double red, double green, double blue) +_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, + cairo_surface_t *surface) { - _cairo_pattern_init (pattern); + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SURFACE); + + pattern->surface = surface; + cairo_surface_reference (surface); +} - pattern->type = CAIRO_PATTERN_SOLID; - _cairo_color_set_rgb (&pattern->color, red, green, blue); +static void +_cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern, + cairo_pattern_type_t type) +{ + _cairo_pattern_init (&pattern->base, type); + + pattern->stops = 0; + pattern->n_stops = 0; +} + +void +_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern, + double x0, double y0, double x1, double y1) +{ + _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_LINEAR); + + pattern->point0.x = x0; + pattern->point0.y = y0; + pattern->point1.x = x1; + pattern->point1.y = y1; +} + +void +_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, + double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1) +{ + _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_RADIAL); + + pattern->center0.x = cx0; + pattern->center0.y = cy0; + pattern->radius0 = fabs (radius0); + pattern->center1.x = cx1; + pattern->center1.y = cy1; + pattern->radius1 = fabs (radius1); } cairo_pattern_t * _cairo_pattern_create_solid (double red, double green, double blue) { - cairo_pattern_t *pattern; + cairo_solid_pattern_t *pattern; - pattern = malloc (sizeof (cairo_pattern_t)); + pattern = malloc (sizeof (cairo_solid_pattern_t)); if (pattern == NULL) return NULL; _cairo_pattern_init_solid (pattern, red, green, blue); - return pattern; + return &pattern->base; } cairo_pattern_t * cairo_pattern_create_for_surface (cairo_surface_t *surface) { - cairo_pattern_t *pattern; + cairo_surface_pattern_t *pattern; - pattern = malloc (sizeof (cairo_pattern_t)); + pattern = malloc (sizeof (cairo_surface_pattern_t)); if (pattern == NULL) return NULL; - _cairo_pattern_init (pattern); - - pattern->type = CAIRO_PATTERN_SURFACE; - pattern->u.surface.surface = surface; - cairo_surface_reference (surface); + _cairo_pattern_init_for_surface (pattern, surface); + + /* this will go away when we completely remove the surface attributes */ + if (surface->repeat) + pattern->base.extend = CAIRO_EXTEND_REPEAT; + else + pattern->base.extend = CAIRO_EXTEND_DEFAULT; - return pattern; + return &pattern->base; } cairo_pattern_t * cairo_pattern_create_linear (double x0, double y0, double x1, double y1) { - cairo_pattern_t *pattern; + cairo_linear_pattern_t *pattern; - pattern = malloc (sizeof (cairo_pattern_t)); + pattern = malloc (sizeof (cairo_linear_pattern_t)); if (pattern == NULL) return NULL; - _cairo_pattern_init (pattern); - - pattern->type = CAIRO_PATTERN_LINEAR; - pattern->u.linear.point0.x = x0; - pattern->u.linear.point0.y = y0; - pattern->u.linear.point1.x = x1; - pattern->u.linear.point1.y = y1; + _cairo_pattern_init_linear (pattern, x0, y0, x1, y1); - return pattern; + return &pattern->base.base; } cairo_pattern_t * cairo_pattern_create_radial (double cx0, double cy0, double radius0, double cx1, double cy1, double radius1) { - cairo_pattern_t *pattern; + cairo_radial_pattern_t *pattern; - pattern = malloc (sizeof (cairo_pattern_t)); + pattern = malloc (sizeof (cairo_radial_pattern_t)); if (pattern == NULL) return NULL; - _cairo_pattern_init (pattern); - - pattern->type = CAIRO_PATTERN_RADIAL; - pattern->u.radial.center0.x = cx0; - pattern->u.radial.center0.y = cy0; - pattern->u.radial.radius0 = fabs (radius0); - pattern->u.radial.center1.x = cx1; - pattern->u.radial.center1.y = cy1; - pattern->u.radial.radius1 = fabs (radius1); + _cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1); - return pattern; + return &pattern->base.base; } void @@ -209,37 +294,19 @@ cairo_pattern_destroy (cairo_pattern_t *pattern) free (pattern); } -static int -_cairo_pattern_stop_compare (const void *elem1, const void *elem2) -{ - return - (((cairo_color_stop_t *) elem1)->offset == - ((cairo_color_stop_t *) elem2)->offset) ? - /* equal offsets, sort on id */ - ((((cairo_color_stop_t *) elem1)->id < - ((cairo_color_stop_t *) elem2)->id) ? -1 : 1) : - /* sort on offset */ - ((((cairo_color_stop_t *) elem1)->offset < - ((cairo_color_stop_t *) elem2)->offset) ? -1 : 1); -} - -cairo_status_t -cairo_pattern_add_color_stop (cairo_pattern_t *pattern, - double offset, - double red, double green, double blue, - double alpha) +static cairo_status_t +_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, + double offset, + double red, + double green, + double blue, + double alpha) { cairo_color_stop_t *stop; - int i; - - _cairo_restrict_value (&offset, 0.0, 1.0); - _cairo_restrict_value (&red, 0.0, 1.0); - _cairo_restrict_value (&green, 0.0, 1.0); - _cairo_restrict_value (&blue, 0.0, 1.0); pattern->n_stops++; pattern->stops = realloc (pattern->stops, - sizeof (cairo_color_stop_t) * pattern->n_stops); + pattern->n_stops * sizeof (cairo_color_stop_t)); if (pattern->stops == NULL) { pattern->n_stops = 0; @@ -249,41 +316,51 @@ cairo_pattern_add_color_stop (cairo_pattern_t *pattern, stop = &pattern->stops[pattern->n_stops - 1]; stop->offset = _cairo_fixed_from_double (offset); - stop->id = pattern->n_stops; - stop->color_char[0] = red * 0xff; - stop->color_char[1] = green * 0xff; - stop->color_char[2] = blue * 0xff; - stop->color_char[3] = alpha * 0xff; + _cairo_color_init (&stop->color); + _cairo_color_set_rgb (&stop->color, red, green, blue); + _cairo_color_set_alpha (&stop->color, alpha); - /* sort stops in ascending order */ - qsort (pattern->stops, pattern->n_stops, sizeof (cairo_color_stop_t), - _cairo_pattern_stop_compare); - - for (i = 0; i < pattern->n_stops - 1; i++) { - pattern->stops[i + 1].scale = - pattern->stops[i + 1].offset - pattern->stops[i].offset; - if (pattern->stops[i + 1].scale == 65536) - pattern->stops[i + 1].scale = 0; + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +cairo_pattern_add_color_stop (cairo_pattern_t *pattern, + double offset, + double red, + double green, + double blue, + double alpha) +{ + if (pattern->type != CAIRO_PATTERN_LINEAR && + pattern->type != CAIRO_PATTERN_RADIAL) + { + /* XXX: CAIRO_STATUS_INVALID_PATTERN? */ + return CAIRO_STATUS_SUCCESS; } - return CAIRO_STATUS_SUCCESS; + _cairo_restrict_value (&offset, 0.0, 1.0); + _cairo_restrict_value (&red, 0.0, 1.0); + _cairo_restrict_value (&green, 0.0, 1.0); + _cairo_restrict_value (&blue, 0.0, 1.0); + _cairo_restrict_value (&alpha, 0.0, 1.0); + + return _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, + offset, + red, green, blue, + alpha); } cairo_status_t cairo_pattern_set_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) { - cairo_matrix_copy (&pattern->matrix, matrix); - - return CAIRO_STATUS_SUCCESS; + return cairo_matrix_copy (&pattern->matrix, matrix); } cairo_status_t cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) { - cairo_matrix_copy (matrix, &pattern->matrix); - - return CAIRO_STATUS_SUCCESS; + return cairo_matrix_copy (matrix, &pattern->matrix); } cairo_status_t @@ -316,9 +393,20 @@ cairo_pattern_get_extend (cairo_pattern_t *pattern) cairo_status_t _cairo_pattern_get_rgb (cairo_pattern_t *pattern, - double *red, double *green, double *blue) + double *red, + double *green, + double *blue) { - _cairo_color_get_rgb (&pattern->color, red, green, blue); + + if (pattern->type == CAIRO_PATTERN_SOLID) + { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + + *red = solid->red; + *green = solid->green; + *blue = solid->blue; + } else + *red = *green = *blue = 1.0; return CAIRO_STATUS_SUCCESS; } @@ -326,63 +414,16 @@ _cairo_pattern_get_rgb (cairo_pattern_t *pattern, void _cairo_pattern_set_alpha (cairo_pattern_t *pattern, double alpha) { - int i; - - _cairo_color_set_alpha (&pattern->color, alpha); - - for (i = 0; i < pattern->n_stops; i++) - pattern->stops[i].color_char[3] = - MULTIPLY_COLORCOMP (pattern->stops[i].color_char[3], alpha * 0xff); -} - -void -_cairo_pattern_set_source_offset (cairo_pattern_t *pattern, - double x, double y) -{ - pattern->source_offset.x = x; - pattern->source_offset.y = y; + pattern->alpha = alpha; } void _cairo_pattern_transform (cairo_pattern_t *pattern, - cairo_matrix_t *ctm_inverse) + cairo_matrix_t *ctm_inverse) { cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix); } -void -_cairo_pattern_prepare_surface (cairo_pattern_t *pattern) -{ - cairo_matrix_t device_to_source; - cairo_matrix_t user_to_source; - - /* should the surface matrix interface be remove from the API? - for now we multiple the surface matrix with the pattern matrix */ - cairo_surface_get_matrix (pattern->u.surface.surface, &user_to_source); - cairo_matrix_multiply (&device_to_source, &pattern->matrix, - &user_to_source); - cairo_surface_set_matrix (pattern->source, &device_to_source); - - /* storing original surface matrix in pattern */ - pattern->u.surface.save_matrix = user_to_source; - - /* storing original surface repeat mode in pattern */ - pattern->u.surface.save_repeat = pattern->source->repeat; - - /* what do we do with extend types pad and reflect? */ - if (pattern->extend == CAIRO_EXTEND_REPEAT - || pattern->source->repeat == 1) - cairo_surface_set_repeat (pattern->source, 1); - else - cairo_surface_set_repeat (pattern->source, 0); - - /* storing original surface filter in pattern */ - pattern->u.surface.save_filter = - cairo_surface_get_filter (pattern->source); - - cairo_surface_set_filter (pattern->source, pattern->filter); -} - #define INTERPOLATE_COLOR_NEAREST(c1, c2, factor) \ ((factor < 32768)? c1: c2) @@ -390,7 +431,7 @@ static void _cairo_pattern_shader_nearest (unsigned char *color0, unsigned char *color1, cairo_fixed_t factor, - int *pixel) + uint32_t *pixel) { *pixel = ((INTERPOLATE_COLOR_NEAREST (color0[3], color1[3], factor) << 24) | @@ -408,7 +449,7 @@ static void _cairo_pattern_shader_linear (unsigned char *color0, unsigned char *color1, cairo_fixed_t factor, - int *pixel) + uint32_t *pixel) { *pixel = ((INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor) << 24) | (INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor) << 16) | @@ -422,7 +463,7 @@ static void _cairo_pattern_shader_gaussian (unsigned char *color0, unsigned char *color1, cairo_fixed_t factor, - int *pixel) + uint32_t *pixel) { double f = ((double) factor) / 65536.0; @@ -436,17 +477,59 @@ _cairo_pattern_shader_gaussian (unsigned char *color0, #undef INTERPOLATE_COLOR_LINEAR -void -_cairo_pattern_shader_init (cairo_pattern_t *pattern, - cairo_shader_op_t *op) -{ - op->stops = pattern->stops; - op->n_stops = pattern->n_stops - 1; - op->min_offset = pattern->stops[0].offset; - op->max_offset = pattern->stops[op->n_stops].offset; - op->extend = pattern->extend; - - switch (pattern->filter) { +static int +_cairo_shader_color_stop_compare (const void *elem1, const void *elem2) +{ + cairo_shader_color_stop_t *s1 = (cairo_shader_color_stop_t *) elem1; + cairo_shader_color_stop_t *s2 = (cairo_shader_color_stop_t *) elem2; + + return + (s1->offset == s2->offset) ? + /* equal offsets, sort on id */ + ((s1->id < s2->id) ? -1 : 1) : + /* sort on offset */ + ((s1->offset < s2->offset) ? -1 : 1); +} + +static cairo_status_t +_cairo_pattern_shader_init (cairo_gradient_pattern_t *pattern, + cairo_shader_op_t *op) +{ + int i; + + op->stops = malloc (pattern->n_stops * sizeof (cairo_shader_color_stop_t)); + if (!op->stops) + return CAIRO_STATUS_NO_MEMORY; + + for (i = 0; i < pattern->n_stops; i++) + { + op->stops[i].color_char[0] = pattern->stops[i].color.red * 0xff; + op->stops[i].color_char[1] = pattern->stops[i].color.green * 0xff; + op->stops[i].color_char[2] = pattern->stops[i].color.blue * 0xff; + op->stops[i].color_char[3] = pattern->stops[i].color.alpha * + pattern->base.alpha * 0xff; + op->stops[i].offset = pattern->stops[i].offset; + op->stops[i].id = i; + } + + /* sort stops in ascending order */ + qsort (op->stops, pattern->n_stops, sizeof (cairo_shader_color_stop_t), + _cairo_shader_color_stop_compare); + + for (i = 0; i < pattern->n_stops - 1; i++) + { + op->stops[i + 1].scale = op->stops[i + 1].offset - op->stops[i].offset; + if (op->stops[i + 1].scale == 65536) + op->stops[i + 1].scale = 0; + } + + op->n_stops = pattern->n_stops; + op->extend = pattern->base.extend; + + /* XXX: this is wrong, the filter should not be used for selecting + color stop interpolation function. function should always be 'linear' + and filter should be used for computing pixels. */ + switch (pattern->base.filter) { case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: op->shader_function = _cairo_pattern_shader_nearest; @@ -460,15 +543,56 @@ _cairo_pattern_shader_init (cairo_pattern_t *pattern, op->shader_function = _cairo_pattern_shader_linear; break; } + + return CAIRO_STATUS_SUCCESS; } -void -_cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op, - cairo_fixed_t factor, - int *pixel) +static void +_cairo_pattern_shader_fini (cairo_shader_op_t *op) +{ + if (op->stops) + free (op->stops); +} + +/* Find two color stops bounding the given offset. If the given offset + * is before the first or after the last stop offset, the nearest + * offset is returned twice. + */ +static void +_cairo_shader_op_find_color_stops (cairo_shader_op_t *op, + cairo_fixed_t offset, + cairo_shader_color_stop_t *stops[2]) { int i; - + + /* Before first stop. */ + if (offset <= op->stops[0].offset) { + stops[0] = &op->stops[0]; + stops[1] = &op->stops[0]; + return; + } + + /* Between two stops. */ + for (i = 0; i < op->n_stops - 1; i++) { + if (offset <= op->stops[i + 1].offset) { + stops[0] = &op->stops[i]; + stops[1] = &op->stops[i + 1]; + return; + } + } + + /* After last stop. */ + stops[0] = &op->stops[op->n_stops - 1]; + stops[1] = &op->stops[op->n_stops - 1]; +} + +static void +_cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op, + cairo_fixed_t factor, + uint32_t *pixel) +{ + cairo_shader_color_stop_t *stops[2]; + switch (op->extend) { case CAIRO_EXTEND_REPEAT: factor -= factor & 0xffff0000; @@ -485,96 +609,158 @@ _cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op, break; } - if (factor < op->min_offset) - factor = op->min_offset; - else if (factor > op->max_offset) - factor = op->max_offset; - - for (i = 0; i < op->n_stops; i++) { - if (factor <= op->stops[i + 1].offset) { - - /* take offset as new 0 of coordinate system */ - factor -= op->stops[i].offset; - - /* difference between two offsets == 0, abrubt change */ - if (op->stops[i + 1].scale) - factor = ((cairo_fixed_48_16_t) factor << 16) / - op->stops[i + 1].scale; + _cairo_shader_op_find_color_stops (op, factor, stops); + + /* take offset as new 0 of coordinate system */ + factor -= stops[0]->offset; - op->shader_function (op->stops[i].color_char, - op->stops[i + 1].color_char, - factor, pixel); + /* difference between two offsets == 0, abrubt change */ + if (stops[1]->scale) + factor = ((cairo_fixed_48_16_t) factor << 16) / + stops[1]->scale; + + op->shader_function (stops[0]->color_char, + stops[1]->color_char, + factor, pixel); - /* multiply alpha */ - if (((unsigned char) (*pixel >> 24)) != 0xff) { - *pixel = (*pixel & 0xff000000) | - (MULTIPLY_COLORCOMP (*pixel >> 16, *pixel >> 24) << 16) | - (MULTIPLY_COLORCOMP (*pixel >> 8, *pixel >> 24) << 8) | - (MULTIPLY_COLORCOMP (*pixel >> 0, *pixel >> 24) << 0); - } - break; - } + /* multiply alpha */ + if (((unsigned char) (*pixel >> 24)) != 0xff) { + *pixel = (*pixel & 0xff000000) | + (MULTIPLY_COLORCOMP (*pixel >> 16, *pixel >> 24) << 16) | + (MULTIPLY_COLORCOMP (*pixel >> 8, *pixel >> 24) << 8) | + (MULTIPLY_COLORCOMP (*pixel >> 0, *pixel >> 24) << 0); } } -static void -_cairo_image_data_set_linear (cairo_pattern_t *pattern, - double offset_x, - double offset_y, - int *pixels, - int width, - int height) +static cairo_status_t +_cairo_image_data_set_linear (cairo_linear_pattern_t *pattern, + double offset_x, + double offset_y, + uint32_t *pixels, + int width, + int height) { int x, y; cairo_point_double_t point0, point1; - double px, py, ex, ey; double a, b, c, d, tx, ty; - double length, start, angle, fx, fy, factor; + double scale, start, dx, dy, factor; cairo_shader_op_t op; - - _cairo_pattern_shader_init (pattern, &op); - - point0.x = pattern->u.linear.point0.x; - point0.y = pattern->u.linear.point0.y; - point1.x = pattern->u.linear.point1.x; - point1.y = pattern->u.linear.point1.y; - - cairo_matrix_get_affine (&pattern->matrix, &a, &b, &c, &d, &tx, &ty); - - length = sqrt ((point1.x - point0.x) * (point1.x - point0.x) + - (point1.y - point0.y) * (point1.y - point0.y)); - length = (length) ? 1.0 / length : CAIRO_MAXSHORT; - - angle = -atan2 (point1.y - point0.y, point1.x - point0.x); - fx = cos (angle); - fy = -sin (angle); - - start = fx * point0.x; - start += fy * point0.y; + cairo_status_t status; + + status = _cairo_pattern_shader_init (&pattern->base, &op); + if (status) + return status; + + /* We compute the position in the linear gradient for + * a point q as: + * + * [q . (p1 - p0) - p0 . (p1 - p0)] / (p1 - p0) ^ 2 + * + * The computation is done in pattern space. The + * calculation could be heavily optimized by using the + * fact that 'factor' increases linearly in both + * directions. + */ + point0.x = pattern->point0.x; + point0.y = pattern->point0.y; + point1.x = pattern->point1.x; + point1.y = pattern->point1.y; + + cairo_matrix_get_affine (&pattern->base.base.matrix, + &a, &b, &c, &d, &tx, &ty); + + dx = point1.x - point0.x; + dy = point1.y - point0.y; + scale = dx * dx + dy * dy; + scale = (scale) ? 1.0 / scale : 1.0; + + start = dx * point0.x + dy * point0.y; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { - px = x + offset_x; - py = y + offset_y; + double qx_device = x + offset_x; + double qy_device = y + offset_y; - /* transform fragment */ - ex = a * px + c * py + tx; - ey = b * px + d * py + ty; + /* transform fragment into pattern space */ + double qx = a * qx_device + c * qy_device + tx; + double qy = b * qx_device + d * qy_device + ty; - factor = ((fx * ex + fy * ey) - start) * length; + factor = ((dx * qx + dy * qy) - start) * scale; _cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++); } } + + _cairo_pattern_shader_fini (&op); + + return CAIRO_STATUS_SUCCESS; } static void -_cairo_image_data_set_radial (cairo_pattern_t *pattern, - double offset_x, - double offset_y, - int *pixels, - int width, - int height) +_cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern, + double offset_x, + double offset_y, + int width, + int height, + cairo_bool_t *is_horizontal, + cairo_bool_t *is_vertical) +{ + cairo_point_double_t point0, point1; + double a, b, c, d, tx, ty; + double scale, start, dx, dy; + cairo_fixed_t factors[3]; + int i; + + /* To classidy a pattern as horizontal or vertical, we first + * compute the (fixed point) factors at the corners of the + * pattern. We actually only need 3/4 corners, so we skip the + * fourth. + */ + point0.x = pattern->point0.x; + point0.y = pattern->point0.y; + point1.x = pattern->point1.x; + point1.y = pattern->point1.y; + + cairo_matrix_get_affine (&pattern->base.base.matrix, + &a, &b, &c, &d, &tx, &ty); + + dx = point1.x - point0.x; + dy = point1.y - point0.y; + scale = dx * dx + dy * dy; + scale = (scale) ? 1.0 / scale : 1.0; + + start = dx * point0.x + dy * point0.y; + + for (i = 0; i < 3; i++) { + double qx_device = (i % 2) * (width - 1) + offset_x; + double qy_device = (i / 2) * (height - 1) + offset_y; + + /* transform fragment into pattern space */ + double qx = a * qx_device + c * qy_device + tx; + double qy = b * qx_device + d * qy_device + ty; + + factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale); + } + + /* We consider a pattern to be vertical if the fixed point factor + * at the two upper corners is the same. We could accept a small + * change, but determining what change is acceptable would require + * sorting the stops in the pattern and looking at the differences. + * + * Horizontal works the same way with the two left corners. + */ + + *is_vertical = factors[1] == factors[0]; + *is_horizontal = factors[2] == factors[0]; +} + +static cairo_status_t +_cairo_image_data_set_radial (cairo_radial_pattern_t *pattern, + double offset_x, + double offset_y, + uint32_t *pixels, + int width, + int height) { int x, y, aligned_circles; cairo_point_double_t c0, c1; @@ -584,15 +770,18 @@ _cairo_image_data_set_radial (cairo_pattern_t *pattern, c0_c1_x, c0_c1_y, c0_c1, angle_c0, c1_y, y_x, c0_y, c0_x, r1_2, denumerator, fraction, factor; cairo_shader_op_t op; + cairo_status_t status; - _cairo_pattern_shader_init (pattern, &op); + status = _cairo_pattern_shader_init (&pattern->base, &op); + if (status) + return status; - c0.x = pattern->u.radial.center0.x; - c0.y = pattern->u.radial.center0.y; - r0 = pattern->u.radial.radius0; - c1.x = pattern->u.radial.center1.x; - c1.y = pattern->u.radial.center1.y; - r1 = pattern->u.radial.radius1; + c0.x = pattern->center0.x; + c0.y = pattern->center0.y; + r0 = pattern->radius0; + c1.x = pattern->center1.x; + c1.y = pattern->center1.y; + r1 = pattern->radius1; if (c0.x != c1.x || c0.y != c1.y) { aligned_circles = 0; @@ -606,7 +795,8 @@ _cairo_image_data_set_radial (cairo_pattern_t *pattern, r1_2 = c0_c1 = 0.0; /* shut up compiler */ } - cairo_matrix_get_affine (&pattern->matrix, &a, &b, &c, &d, &tx, &ty); + cairo_matrix_get_affine (&pattern->base.base.matrix, + &a, &b, &c, &d, &tx, &ty); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { @@ -682,70 +872,454 @@ _cairo_image_data_set_radial (cairo_pattern_t *pattern, _cairo_pattern_calc_color_at_pixel (&op, factor * 65536, pixels++); } } + + _cairo_pattern_shader_fini (&op); + + return CAIRO_STATUS_SUCCESS; } -cairo_image_surface_t * -_cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box) +static cairo_int_status_t +_cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_surface_t **out, + cairo_surface_attributes_t *attr) { - cairo_surface_t *surface; + cairo_image_surface_t *image; + cairo_status_t status; + uint32_t *data; + cairo_bool_t repeat = FALSE; + + if (pattern->base.type == CAIRO_PATTERN_LINEAR) { + cairo_bool_t is_horizontal; + cairo_bool_t is_vertical; + + _cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern, + x, y, width, height, + &is_horizontal, &is_vertical); + if (is_horizontal) { + height = 1; + repeat = TRUE; + } + if (is_vertical) { + width = 1; + repeat = TRUE; + } + } + + data = malloc (width * height * 4); + if (!data) + return CAIRO_STATUS_NO_MEMORY; + + if (pattern->base.type == CAIRO_PATTERN_LINEAR) + { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern; + + status = _cairo_image_data_set_linear (linear, x, y, data, + width, height); + } + else + { + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern; + + status = _cairo_image_data_set_radial (radial, x, y, data, + width, height); + } - switch (pattern->type) { - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: { - char *data; - double x = box->p1.x >> 16; - double y = box->p1.y >> 16; - int width = ((box->p2.x + 65535) >> 16) - (box->p1.x >> 16); - int height = ((box->p2.y + 65535) >> 16) - (box->p1.y >> 16); + if (status) { + free (data); + return status; + } + + image = (cairo_image_surface_t *) + cairo_image_surface_create_for_data ((char *) data, + CAIRO_FORMAT_ARGB32, + width, height, + width * 4); + + if (image == NULL) { + free (data); + return CAIRO_STATUS_NO_MEMORY; + } + + _cairo_image_surface_assume_ownership_of_data (image); + + status = _cairo_surface_clone_similar (dst, &image->base, out); + + cairo_surface_destroy (&image->base); + + attr->x_offset = -x; + attr->y_offset = -y; + cairo_matrix_set_identity (&attr->matrix); + attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE; + attr->filter = CAIRO_FILTER_NEAREST; + attr->acquired = FALSE; + + return status; +} + +static cairo_int_status_t +_cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_surface_t **out, + cairo_surface_attributes_t *attribs) +{ + cairo_color_t color; + + _cairo_color_init (&color); + _cairo_color_set_rgb (&color, pattern->red, pattern->green, pattern->blue); + _cairo_color_set_alpha (&color, pattern->base.alpha); + + *out = _cairo_surface_create_similar_solid (dst, + CAIRO_FORMAT_ARGB32, + 1, 1, + &color); + + if (*out == NULL) + return CAIRO_STATUS_NO_MEMORY; + + attribs->x_offset = attribs->y_offset = 0; + cairo_matrix_set_identity (&attribs->matrix); + attribs->extend = CAIRO_EXTEND_REPEAT; + attribs->filter = CAIRO_FILTER_NEAREST; + attribs->acquired = FALSE; + + return CAIRO_STATUS_SUCCESS; +} + + +/** + * _cairo_pattern_is_opaque + * + * Convenience function to determine whether a pattern has an opaque + * alpha value. This is done by testing whether the pattern's alpha + * value when converted to a byte is 255, so if a backend actually + * supported deep alpha channels this function might not do the right + * thing. + * + * Note that for a gradient or surface pattern, the overall resulting + * alpha for the pattern can be non-opaque even this function returns + * %TRUE, since the resulting alpha is the multiplication of the + * alpha of the gradient or surface with the pattern's alpha. In + * the future, alpha will be moved from the base pattern to the + * solid pattern subtype, at which point this function should + * probably be renamed to _cairo_pattern_is_opaque_solid() + * + * Return value: %TRUE if the pattern is opaque + **/ +cairo_bool_t +_cairo_pattern_is_opaque (cairo_pattern_t *pattern) +{ + return (pattern->alpha >= ((double)0xff00 / (double)0xffff)); +} + +static cairo_int_status_t +_cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_surface_t **out, + cairo_surface_attributes_t *attr) +{ + cairo_int_status_t status; + + attr->acquired = FALSE; + + /* handle pattern opacity */ + if (!_cairo_pattern_is_opaque (&pattern->base)) + { + cairo_surface_pattern_t tmp; + cairo_color_t color; + + _cairo_color_init (&color); + _cairo_color_set_alpha (&color, pattern->base.alpha); + + *out = _cairo_surface_create_similar_solid (dst, + CAIRO_FORMAT_ARGB32, + width, height, + &color); + if (*out == NULL) + return CAIRO_STATUS_NO_MEMORY; + + status = _cairo_pattern_init_copy (&tmp.base, &pattern->base); + if (CAIRO_OK (status)) + { + tmp.base.alpha = 1.0; + status = _cairo_surface_composite (CAIRO_OPERATOR_IN, + &tmp.base, + NULL, + *out, + x, y, 0, 0, 0, 0, + width, height); + + _cairo_pattern_fini (&tmp.base); + } + + if (status) { + cairo_surface_destroy (*out); + return status; + } - data = malloc (width * height * 4); - if (!data) - return NULL; + attr->x_offset = -x; + attr->y_offset = -y; + attr->extend = CAIRO_EXTEND_NONE; + attr->filter = CAIRO_FILTER_NEAREST; + + cairo_matrix_set_identity (&attr->matrix); + } + else + { + int tx, ty; + + if (_cairo_surface_is_image (dst)) + { + cairo_image_surface_t *image; + + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, + &attr->extra); + if (CAIRO_OK (status)) + *out = &image->base; + + attr->acquired = TRUE; + } + else + status = _cairo_surface_clone_similar (dst, pattern->surface, out); - if (pattern->type == CAIRO_PATTERN_RADIAL) - _cairo_image_data_set_radial (pattern, x, y, (int *) data, - width, height); + attr->extend = pattern->base.extend; + attr->filter = pattern->base.filter; + if (_cairo_matrix_is_integer_translation (&pattern->base.matrix, + &tx, &ty)) + { + cairo_matrix_set_identity (&attr->matrix); + attr->x_offset = tx; + attr->y_offset = ty; + } else - _cairo_image_data_set_linear (pattern, x, y, (int *) data, - width, height); + { + attr->matrix = pattern->base.matrix; + attr->x_offset = attr->y_offset = 0; + } + } + + return status; +} - _cairo_pattern_set_source_offset (pattern, x, y); +/** + * _cairo_pattern_acquire_surface: + * @pattern: a #cairo_pattern_t + * @dst: destination surface + * @x: X coordinate in source corresponding to left side of destination area + * @y: Y coordinate in source corresponding to top side of destination area + * @width: width of destination area + * @height: height of destination area + * @surface_out: location to store a pointer to a surface + * @attributes: surface attributes that destination backend should apply to + * the returned surface + * + * A convenience function to obtain a surface to use as the source for + * drawing on @dst. + * + * Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out. + **/ +cairo_int_status_t +_cairo_pattern_acquire_surface (cairo_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_surface_t **surface_out, + cairo_surface_attributes_t *attributes) +{ + switch (pattern->type) { + case CAIRO_PATTERN_SOLID: { + cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) pattern; + + return _cairo_pattern_acquire_surface_for_solid (src, dst, + x, y, width, height, + surface_out, + attributes); + } break; + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: { + cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) pattern; + + /* fast path for gradients with less than 2 color stops */ + if (src->n_stops < 2) + { + cairo_solid_pattern_t solid; + + if (src->n_stops) + { + _cairo_pattern_init_solid (&solid, + src->stops->color.red, + src->stops->color.green, + src->stops->color.blue); + _cairo_pattern_set_alpha (&solid.base, + src->stops->color.alpha); + } + else + { + _cairo_pattern_init_solid (&solid, 0.0, 0.0, 0.0); + _cairo_pattern_set_alpha (&solid.base, 0.0); + } - surface = cairo_image_surface_create_for_data (data, - CAIRO_FORMAT_ARGB32, - width, height, - width * 4); + return _cairo_pattern_acquire_surface_for_solid (&solid, dst, + x, y, + width, height, + surface_out, + attributes); + } + else + return _cairo_pattern_acquire_surface_for_gradient (src, dst, + x, y, + width, height, + surface_out, + attributes); + } break; + case CAIRO_PATTERN_SURFACE: { + cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) pattern; - if (surface) - _cairo_image_surface_assume_ownership_of_data ( - (cairo_image_surface_t *) surface); + return _cairo_pattern_acquire_surface_for_surface (src, dst, + x, y, width, height, + surface_out, + attributes); + } break; } - break; - case CAIRO_PATTERN_SOLID: - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); - if (surface) { - _cairo_surface_fill_rectangle (surface, - CAIRO_OPERATOR_SRC, - &pattern->color, 0, 0, 1, 1); - cairo_surface_set_repeat (surface, 1); + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +/** + * _cairo_pattern_release_surface: + * @pattern: a #cairo_pattern_t + * @info: pointer to #cairo_surface_attributes_t filled in by + * _cairo_pattern_acquire_surface + * + * Releases resources obtained by _cairo_pattern_acquire_surface. + **/ +void +_cairo_pattern_release_surface (cairo_surface_t *dst, + cairo_surface_t *surface, + cairo_surface_attributes_t *attributes) +{ + if (attributes->acquired) + _cairo_surface_release_source_image (dst, + (cairo_image_surface_t *) surface, + attributes->extra); + else + cairo_surface_destroy (surface); +} + +cairo_int_status_t +_cairo_pattern_acquire_surfaces (cairo_pattern_t *src, + cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + unsigned int width, + unsigned int height, + cairo_surface_t **src_out, + cairo_surface_t **mask_out, + cairo_surface_attributes_t *src_attributes, + cairo_surface_attributes_t *mask_attributes) +{ + cairo_int_status_t status; + + cairo_pattern_union_t tmp; + cairo_bool_t src_opaque, mask_opaque; + double src_alpha, mask_alpha; + + src_opaque = _cairo_pattern_is_opaque (src); + mask_opaque = !mask || _cairo_pattern_is_opaque (mask); + + /* For surface patterns, we move any translucency from src->alpha + * to mask->alpha so we can use the source unchanged. Otherwise we + * move the translucency from mask->alpha to src->alpha so that + * we can drop the mask if possible. + */ + if (src->type == CAIRO_PATTERN_SURFACE) + { + if (mask) { + mask_opaque = mask_opaque && src_opaque; + mask_alpha = mask->alpha * src->alpha; + } else { + mask_opaque = src_opaque; + mask_alpha = src->alpha; } - break; - case CAIRO_PATTERN_SURFACE: { - cairo_image_surface_t *image; + + src_alpha = 1.0; + src_opaque = TRUE; + } + else + { + if (mask) + { + src_opaque = mask_opaque && src_opaque; + src_alpha = mask->alpha * src->alpha; + /* FIXME: This needs changing when we support RENDER + * style 4-channel masks. + */ + if (mask->type == CAIRO_PATTERN_SOLID) + mask = NULL; + } else + src_alpha = src->alpha; + + mask_alpha = 1.0; + mask_opaque = TRUE; + } - image = _cairo_surface_get_image (pattern->u.surface.surface); - if (image) - surface = &image->base; + _cairo_pattern_init_copy (&tmp.base, src); + _cairo_pattern_set_alpha (&tmp.base, src_alpha); + + status = _cairo_pattern_acquire_surface (&tmp.base, dst, + src_x, src_y, + width, height, + src_out, src_attributes); + + _cairo_pattern_fini (&tmp.base); + + if (status) + return status; + + if (mask || !mask_opaque) + { + if (mask) + _cairo_pattern_init_copy (&tmp.base, mask); else - surface = NULL; + _cairo_pattern_init_solid (&tmp.solid, 0.0, 0.0, 0.0); + + _cairo_pattern_set_alpha (&tmp.base, mask_alpha); + + status = _cairo_pattern_acquire_surface (&tmp.base, dst, + mask_x, mask_y, + width, height, + mask_out, mask_attributes); + _cairo_pattern_fini (&tmp.base); + + if (status) + { + _cairo_pattern_release_surface (dst, *src_out, src_attributes); + return status; + } } - break; - default: - surface = NULL; - break; + else + { + *mask_out = NULL; } - - return (cairo_image_surface_t *) surface; + + return CAIRO_STATUS_SUCCESS; } - diff --git a/src/cairo_pdf_surface.c b/src/cairo_pdf_surface.c index 23230aa74..fee918355 100644 --- a/src/cairo_pdf_surface.c +++ b/src/cairo_pdf_surface.c @@ -36,9 +36,8 @@ #include "cairoint.h" #include "cairo-pdf.h" -/* XXX: This seems broken to me. What about users without freetype - * that want to use a cairo PDF surface? */ -#include "cairo-ft.h" +/* XXX: Eventually, we need to handle other font backends */ +#include "cairo-ft-private.h" #include <ft2build.h> #include FT_FREETYPE_H @@ -54,10 +53,6 @@ * - Why doesn't pages inherit /alpha%d GS dictionaries from the Pages * object? * - * - Why isn't the pattern passed to composite traps instead of - * pattern->source? If composite traps needs an image or a surface it - * can call create_pattern(). - * * - We embed an image in the stream each time it's composited. We * could add generation counters to surfaces and remember the stream * ID for a particular generation for a particular surface. @@ -183,9 +178,6 @@ struct cairo_pdf_surface { double width_inches; double height_inches; - /* HACK: Non-null if this surface was created for a pattern. */ - cairo_pattern_t *pattern; - cairo_pdf_document_t *document; cairo_pdf_stream_t *current_stream; @@ -240,8 +232,6 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend; #define ARRAY_LENGTH(a) ( (sizeof (a)) / (sizeof ((a)[0])) ) #define SFNT_VERSION 0x00010000 -#define OFFSET_TABLE_SIZE 12 -#define TABLE_DIRECTORY_ENTRY_SIZE 16 #ifdef WORDS_BIGENDIAN @@ -300,19 +290,15 @@ cairo_pdf_font_destroy (cairo_pdf_font_t *font) } static cairo_pdf_font_t * -cairo_pdf_ft_font_create (cairo_pdf_document_t *document, - cairo_unscaled_font_t *unscaled_font, - cairo_font_scale_t *scale) +cairo_pdf_ft_font_create (cairo_pdf_document_t *document, + cairo_unscaled_font_t *unscaled_font) { - cairo_font_t scaled_font; FT_Face face; cairo_pdf_ft_font_t *font; unsigned long size; int i, j; - /* FIXME: Why do I have to pass a scaled font to get the FT_Face? */ - _cairo_font_init (&scaled_font, scale, unscaled_font); - face = cairo_ft_font_face (&scaled_font); + face = _cairo_ft_unscaled_font_lock_face (unscaled_font); /* We currently only support freetype truetype fonts. */ size = 0; @@ -333,7 +319,8 @@ cairo_pdf_ft_font_create (cairo_pdf_document_t *document, if (_cairo_array_grow_by (&font->output, 4096) != CAIRO_STATUS_SUCCESS) goto fail1; - font->face = face; + font->base.unscaled_font = unscaled_font; + _cairo_unscaled_font_reference (unscaled_font); font->glyphs = calloc (face->num_glyphs + 1, sizeof (ft_subset_glyph_t)); if (font->glyphs == NULL) goto fail2; @@ -364,6 +351,8 @@ cairo_pdf_ft_font_create (cairo_pdf_document_t *document, if (font->base.widths == NULL) goto fail5; + _cairo_ft_unscaled_font_unlock_face (unscaled_font); + font->status = CAIRO_STATUS_SUCCESS; return &font->base; @@ -447,7 +436,7 @@ cairo_pdf_ft_font_write_cmap_table (cairo_pdf_ft_font_t *font, unsigned long tag cairo_pdf_ft_font_write_be16 (font, 0); cairo_pdf_ft_font_write_be16 (font, 1); - cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 1); cairo_pdf_ft_font_write_be16 (font, 0); cairo_pdf_ft_font_write_be32 (font, 12); @@ -764,12 +753,15 @@ cairo_pdf_ft_font_generate (void *abstract_font, unsigned long start, end, next, checksum; int i; + font->face = _cairo_ft_unscaled_font_lock_face (font->base.unscaled_font); + if (cairo_pdf_ft_font_write_offset_table (font)) - return font->status; + goto fail; start = cairo_pdf_ft_font_align_output (font); end = start; + end = 0; for (i = 0; i < ARRAY_LENGTH (truetype_tables); i++) { if (truetype_tables[i].write (font, truetype_tables[i].tag)) goto fail; @@ -789,6 +781,9 @@ cairo_pdf_ft_font_generate (void *abstract_font, *length = _cairo_array_num_elements (&font->output); fail: + _cairo_ft_unscaled_font_unlock_face (font->base.unscaled_font); + font->face = NULL; + return font->status; } @@ -947,14 +942,13 @@ _cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document, surface->width_inches = width_inches; surface->height_inches = height_inches; - surface->pattern = NULL; _cairo_pdf_document_reference (document); surface->document = document; _cairo_array_init (&surface->streams, sizeof (cairo_pdf_stream_t *)); _cairo_array_init (&surface->patterns, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&surface->xobjects, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&surface->alphas, sizeof (double)); - _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t)); + _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_resource_t)); return &surface->base; } @@ -1102,38 +1096,46 @@ _cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface) } } -static cairo_image_surface_t * -_cairo_pdf_surface_get_image (void *abstract_surface) +static cairo_status_t +_cairo_pdf_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) { - return NULL; + return CAIRO_INT_STATUS_UNSUPPORTED; } -static cairo_status_t -_cairo_pdf_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +static void +_cairo_pdf_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) { - return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t -_cairo_pdf_surface_set_matrix (void *abstract_surface, - cairo_matrix_t *matrix) +_cairo_pdf_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect, + void **image_extra) { - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_UNSUPPORTED; } -static cairo_status_t -_cairo_pdf_surface_set_filter (void *abstract_surface, - cairo_filter_t filter) +static void +_cairo_pdf_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) { - return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_pdf_surface_set_repeat (void *abstract_surface, - int repeat) +_cairo_pdf_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_UNSUPPORTED; } static void * @@ -1210,23 +1212,34 @@ emit_image_data (cairo_pdf_document_t *document, } static cairo_int_status_t -_cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst, - cairo_image_surface_t *image) +_cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst, + cairo_surface_pattern_t *pattern) { cairo_pdf_document_t *document = dst->document; FILE *file = document->file; unsigned id; cairo_matrix_t i2u; + cairo_status_t status; + cairo_image_surface_t *image; + cairo_surface_t *src; + void *image_extra; + + src = pattern->surface; + status = _cairo_surface_acquire_source_image (src, &image, &image_extra); + if (!CAIRO_OK (status)) + return status; id = emit_image_data (dst->document, image); - if (id == 0) - return CAIRO_STATUS_NO_MEMORY; + if (id == 0) { + status = CAIRO_STATUS_NO_MEMORY; + goto bail; + } _cairo_pdf_surface_add_xobject (dst, id); _cairo_pdf_surface_ensure_stream (dst); - cairo_matrix_copy (&i2u, &image->base.matrix); + cairo_matrix_copy (&i2u, &pattern->base.matrix); cairo_matrix_invert (&i2u); cairo_matrix_translate (&i2u, 0, image->height); cairo_matrix_scale (&i2u, image->width, -image->height); @@ -1238,7 +1251,10 @@ _cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst, i2u.m[2][0], i2u.m[2][1], id); - return CAIRO_STATUS_SUCCESS; + bail: + _cairo_surface_release_source_image (src, image, image_extra); + + return status; } /* The contents of the surface is already transformed into PDF units, @@ -1253,20 +1269,19 @@ _cairo_pdf_surface_composite_image (cairo_pdf_surface_t *dst, static cairo_int_status_t _cairo_pdf_surface_composite_pdf (cairo_pdf_surface_t *dst, - cairo_pdf_surface_t *src, - int width, int height) + cairo_surface_pattern_t *pattern) { cairo_pdf_document_t *document = dst->document; FILE *file = document->file; cairo_matrix_t i2u; cairo_pdf_stream_t *stream; int num_streams, i; - - if (src->pattern != NULL) - return CAIRO_STATUS_SUCCESS; + cairo_pdf_surface_t *src; _cairo_pdf_surface_ensure_stream (dst); + src = (cairo_pdf_surface_t *) pattern->surface; + cairo_matrix_copy (&i2u, &src->base.matrix); cairo_matrix_invert (&i2u); cairo_matrix_scale (&i2u, @@ -1297,8 +1312,8 @@ _cairo_pdf_surface_composite_pdf (cairo_pdf_surface_t *dst, static cairo_int_status_t _cairo_pdf_surface_composite (cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, @@ -1310,17 +1325,18 @@ _cairo_pdf_surface_composite (cairo_operator_t operator, unsigned int height) { cairo_pdf_surface_t *dst = abstract_dst; - cairo_pdf_surface_t *src; - cairo_image_surface_t *image; + cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) src_pattern; - if (generic_src->backend == &cairo_pdf_surface_backend) { - src = (cairo_pdf_surface_t *) generic_src; - return _cairo_pdf_surface_composite_pdf (dst, src, width, height); - } - else { - image = _cairo_surface_get_image (generic_src); - return _cairo_pdf_surface_composite_image (dst, image); - } + if (mask_pattern) + return CAIRO_STATUS_SUCCESS; + + if (src_pattern->type != CAIRO_PATTERN_SURFACE) + return CAIRO_STATUS_SUCCESS; + + if (src->surface->backend == &cairo_pdf_surface_backend) + return _cairo_pdf_surface_composite_pdf (dst, src); + else + return _cairo_pdf_surface_composite_image (dst, src); } static cairo_int_status_t @@ -1335,9 +1351,6 @@ _cairo_pdf_surface_fill_rectangles (void *abstract_surface, FILE *file = document->file; int i; - if (surface->pattern != NULL) - return CAIRO_STATUS_SUCCESS; - _cairo_pdf_surface_ensure_stream (surface); fprintf (file, @@ -1355,23 +1368,44 @@ _cairo_pdf_surface_fill_rectangles (void *abstract_surface, } static void -emit_tiling_pattern (cairo_operator_t operator, - cairo_pdf_surface_t *dst, - cairo_pattern_t *pattern) +emit_solid_pattern (cairo_pdf_surface_t *surface, + cairo_solid_pattern_t *pattern) +{ + cairo_pdf_document_t *document = surface->document; + FILE *file = document->file; + unsigned int alpha; + + alpha = _cairo_pdf_surface_add_alpha (surface, pattern->base.alpha); + _cairo_pdf_surface_ensure_stream (surface); + fprintf (file, + "%f %f %f rg /a%d gs\r\n", + pattern->red, + pattern->green, + pattern->blue, + alpha); +} + +static void +emit_surface_pattern (cairo_pdf_surface_t *dst, + cairo_surface_pattern_t *pattern) { cairo_pdf_document_t *document = dst->document; FILE *file = document->file; cairo_pdf_stream_t *stream; cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; char entries[250]; unsigned int id, alpha; cairo_matrix_t pm; - if (pattern->u.surface.surface->backend == &cairo_pdf_surface_backend) { + if (pattern->surface->backend == &cairo_pdf_surface_backend) { return; } - - image = _cairo_surface_get_image (pattern->u.surface.surface); + + status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); + if (!CAIRO_OK (status)) + return; _cairo_pdf_document_close_stream (document); @@ -1382,7 +1416,7 @@ emit_tiling_pattern (cairo_operator_t operator, cairo_matrix_set_identity (&pm); cairo_matrix_scale (&pm, image->width, image->height); - cairo_matrix_copy (&pm, &pattern->matrix); + cairo_matrix_copy (&pm, &pattern->base.matrix); cairo_matrix_invert (&pm); snprintf (entries, sizeof entries, @@ -1401,6 +1435,8 @@ emit_tiling_pattern (cairo_operator_t operator, stream = _cairo_pdf_document_open_stream (document, entries); + /* FIXME: emit code to show surface here. */ + _cairo_pdf_surface_add_pattern (dst, stream->id); _cairo_pdf_surface_ensure_stream (dst); @@ -1408,10 +1444,12 @@ emit_tiling_pattern (cairo_operator_t operator, fprintf (file, "/Pattern cs /res%d scn /a%d gs\r\n", stream->id, alpha); + + _cairo_surface_release_source_image (pattern->surface, image, image_extra); } static unsigned int -emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) +emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_gradient_pattern_t *pattern) { cairo_pdf_document_t *document = surface->document; FILE *file = document->file; @@ -1430,12 +1468,12 @@ emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) "stream\r\n", function_id); - fputc (pattern->stops[0].color_char[0], file); - fputc (pattern->stops[0].color_char[1], file); - fputc (pattern->stops[0].color_char[2], file); - fputc (pattern->stops[1].color_char[0], file); - fputc (pattern->stops[1].color_char[1], file); - fputc (pattern->stops[1].color_char[2], file); + fputc (pattern->stops[0].color.red * 0xff, file); + fputc (pattern->stops[0].color.green * 0xff, file); + fputc (pattern->stops[0].color.blue * 0xff, file); + fputc (pattern->stops[1].color.red * 0xff, file); + fputc (pattern->stops[1].color.green * 0xff, file); + fputc (pattern->stops[1].color.blue * 0xff, file); fprintf (file, "\r\n" @@ -1446,7 +1484,7 @@ emit_pattern_stops (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) } static void -emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) +emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_linear_pattern_t *pattern) { cairo_pdf_document_t *document = surface->document; FILE *file = document->file; @@ -1456,16 +1494,16 @@ emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) _cairo_pdf_document_close_stream (document); - function_id = emit_pattern_stops (surface, pattern); + function_id = emit_pattern_stops (surface, &pattern->base); - cairo_matrix_copy (&p2u, &pattern->matrix); + cairo_matrix_copy (&p2u, &pattern->base.base.matrix); cairo_matrix_invert (&p2u); - x0 = pattern->u.linear.point0.x; - y0 = pattern->u.linear.point0.y; + x0 = pattern->point0.x; + y0 = pattern->point0.y; cairo_matrix_transform_point (&p2u, &x0, &y0); - x1 = pattern->u.linear.point1.x; - y1 = pattern->u.linear.point1.y; + x1 = pattern->point1.x; + y1 = pattern->point1.y; cairo_matrix_transform_point (&p2u, &x1, &y1); pattern_id = _cairo_pdf_document_new_object (document); @@ -1479,16 +1517,14 @@ emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) " /ColorSpace /DeviceRGB\r\n" " /Coords [ %f %f %f %f ]\r\n" " /Function %d 0 R\r\n" - " /Extend [ %s %s ]\r\n" + " /Extend [ true true ]\r\n" " >>\r\n" ">>\r\n" "endobj\r\n", pattern_id, document->height_inches * document->y_ppi, x0, y0, x1, y1, - function_id, - (1 || pattern->extend) ? "true" : "false", - (1 || pattern->extend) ? "true" : "false"); + function_id); _cairo_pdf_surface_add_pattern (surface, pattern_id); @@ -1502,7 +1538,7 @@ emit_linear_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) } static void -emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) +emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_radial_pattern_t *pattern) { cairo_pdf_document_t *document = surface->document; FILE *file = document->file; @@ -1512,24 +1548,31 @@ emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) _cairo_pdf_document_close_stream (document); - function_id = emit_pattern_stops (surface, pattern); + function_id = emit_pattern_stops (surface, &pattern->base); - cairo_matrix_copy (&p2u, &pattern->matrix); + cairo_matrix_copy (&p2u, &pattern->base.base.matrix); cairo_matrix_invert (&p2u); - x0 = pattern->u.radial.center0.x; - y0 = pattern->u.radial.center0.y; - r0 = pattern->u.radial.radius0; + x0 = pattern->center0.x; + y0 = pattern->center0.y; + r0 = pattern->radius0; cairo_matrix_transform_point (&p2u, &x0, &y0); - x1 = pattern->u.radial.center1.x; - y1 = pattern->u.radial.center1.y; - r1 = pattern->u.radial.radius1; + x1 = pattern->center1.x; + y1 = pattern->center1.y; + r1 = pattern->radius1; cairo_matrix_transform_point (&p2u, &x1, &y1); /* FIXME: This is surely crack, but how should you scale a radius * in a non-orthogonal coordinate system? */ cairo_matrix_transform_distance (&p2u, &r0, &r1); + /* FIXME: There is a difference between the cairo gradient extend + * semantics and PDF extend semantics. PDFs extend=false means + * that nothing is painted outside the gradient boundaries, + * whereas cairo takes this to mean that the end color is padded + * to infinity. Setting extend=true in PDF gives the cairo default + * behavoir, not yet sure how to implement the cairo mirror and + * repeat behaviour. */ pattern_id = _cairo_pdf_document_new_object (document); fprintf (file, "%d 0 obj\r\n" @@ -1541,16 +1584,14 @@ emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) " /ColorSpace /DeviceRGB\r\n" " /Coords [ %f %f %f %f %f %f ]\r\n" " /Function %d 0 R\r\n" - " /Extend [ %s %s ]\r\n" + " /Extend [ true true ]\r\n" " >>\r\n" ">>\r\n" "endobj\r\n", pattern_id, document->height_inches * document->y_ppi, x0, y0, r0, x1, y1, r1, - function_id, - (1 || pattern->extend) ? "true" : "false", - (1 || pattern->extend) ? "true" : "false"); + function_id); _cairo_pdf_surface_add_pattern (surface, pattern_id); @@ -1563,6 +1604,28 @@ emit_radial_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) pattern_id, alpha); } +static void +emit_pattern (cairo_pdf_surface_t *surface, cairo_pattern_t *pattern) +{ + switch (pattern->type) { + case CAIRO_PATTERN_SOLID: + emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_SURFACE: + emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_LINEAR: + emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_RADIAL: + emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern); + break; + } +} + static double intersect (cairo_line_t *line, cairo_fixed_t y) { @@ -1574,60 +1637,23 @@ intersect (cairo_line_t *line, cairo_fixed_t y) static cairo_int_status_t _cairo_pdf_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *generic_src, + cairo_pattern_t *pattern, void *abstract_dst, int x_src, int y_src, + int x_dst, + int y_dst, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps) { cairo_pdf_surface_t *surface = abstract_dst; - cairo_pdf_surface_t *source = (cairo_pdf_surface_t *) generic_src; cairo_pdf_document_t *document = surface->document; - cairo_pattern_t *pattern; FILE *file = document->file; int i; - unsigned int alpha; - /* FIXME: we really just want the original pattern here, not a - * source surface. */ - pattern = source->pattern; - - if (source->base.backend != &cairo_pdf_surface_backend) { - printf ("_cairo_pdf_surface_composite_trapezoids: not a pdf source\r"); - return CAIRO_STATUS_SUCCESS; - } - - if (pattern == NULL) { - printf ("_cairo_pdf_surface_composite_trapezoids: " - "non-pattern pdf source\r"); - return CAIRO_STATUS_SUCCESS; - } - - switch (pattern->type) { - case CAIRO_PATTERN_SOLID: - alpha = _cairo_pdf_surface_add_alpha (surface, pattern->color.alpha); - _cairo_pdf_surface_ensure_stream (surface); - fprintf (file, - "%f %f %f rg /a%d gs\r\n", - pattern->color.red, - pattern->color.green, - pattern->color.blue, - alpha); - break; - - case CAIRO_PATTERN_SURFACE: - emit_tiling_pattern (operator, surface, pattern); - break; - - case CAIRO_PATTERN_LINEAR: - emit_linear_pattern (surface, pattern); - break; - - case CAIRO_PATTERN_RADIAL: - emit_radial_pattern (surface, pattern ); - break; - } + emit_pattern (surface, pattern); /* After the above switch the current stream should belong to this * surface, so no need to _cairo_pdf_surface_ensure_stream() */ @@ -1686,59 +1712,48 @@ _cairo_pdf_surface_set_clip_region (void *abstract_surface, return CAIRO_INT_STATUS_UNSUPPORTED; } -static cairo_int_status_t -_cairo_pdf_surface_create_pattern (void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *extents) -{ - cairo_pdf_surface_t *surface = abstract_surface; - cairo_pdf_surface_t *source; - - source = (cairo_pdf_surface_t *) - _cairo_pdf_surface_create_for_document (surface->document, 0, 0); - source->pattern = pattern; - pattern->source = &source->base; - - return CAIRO_STATUS_SUCCESS; -} - static cairo_pdf_font_t * _cairo_pdf_document_get_font (cairo_pdf_document_t *document, - cairo_unscaled_font_t *unscaled_font, - cairo_font_scale_t *scale) + cairo_font_t *font) { - cairo_pdf_font_t *font; + cairo_unscaled_font_t *unscaled_font; + cairo_pdf_font_t *pdf_font; unsigned int num_fonts, i; + unscaled_font = _cairo_ft_font_get_unscaled_font (font); + num_fonts = _cairo_array_num_elements (&document->fonts); for (i = 0; i < num_fonts; i++) { - _cairo_array_copy_element (&document->fonts, i, &font); - if (font->unscaled_font == unscaled_font) - return font; + _cairo_array_copy_element (&document->fonts, i, &pdf_font); + if (pdf_font->unscaled_font == unscaled_font) + return pdf_font; } /* FIXME: Figure out here which font backend is in use and call * the appropriate constructor. */ - font = cairo_pdf_ft_font_create (document, unscaled_font, scale); - if (font == NULL) + pdf_font = cairo_pdf_ft_font_create (document, unscaled_font); + if (pdf_font == NULL) return NULL; - if (_cairo_array_append (&document->fonts, &font, 1) == NULL) { - cairo_pdf_font_destroy (font); + if (_cairo_array_append (&document->fonts, &pdf_font, 1) == NULL) { + cairo_pdf_font_destroy (pdf_font); return NULL; } - return font; + return pdf_font; } static cairo_status_t -_cairo_pdf_surface_show_glyphs (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, +_cairo_pdf_surface_show_glyphs (cairo_font_t *font, cairo_operator_t operator, - cairo_surface_t *source, + cairo_pattern_t *pattern, void *abstract_surface, int source_x, int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, const cairo_glyph_t *glyphs, int num_glyphs) { @@ -1748,23 +1763,23 @@ _cairo_pdf_surface_show_glyphs (cairo_unscaled_font_t *font, cairo_pdf_font_t *pdf_font; int i, index; - pdf_font = _cairo_pdf_document_get_font (document, font, scale); + pdf_font = _cairo_pdf_document_get_font (document, font); if (pdf_font == NULL) return CAIRO_STATUS_NO_MEMORY; - _cairo_pdf_surface_ensure_stream (surface); + emit_pattern (surface, pattern); - fprintf (file, "0 0 0 rg BT /res%u 1 Tf", pdf_font->font_id); + fprintf (file, "BT /res%u 1 Tf", pdf_font->font_id); for (i = 0; i < num_glyphs; i++) { index = cairo_pdf_font_use_glyph (pdf_font, glyphs[i].index); fprintf (file, - " %f %f %f %f %f %f Tm (%c) Tj", - scale->matrix[0][0], - scale->matrix[0][1], - scale->matrix[1][0], - -scale->matrix[1][1], + " %f %f %f %f %f %f Tm (\\%o) Tj", + font->scale.matrix[0][0], + font->scale.matrix[0][1], + font->scale.matrix[1][0], + -font->scale.matrix[1][1], glyphs[i].x, glyphs[i].y, index); @@ -1780,18 +1795,17 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { _cairo_pdf_surface_create_similar, _cairo_pdf_surface_destroy, _cairo_pdf_surface_pixels_per_inch, - _cairo_pdf_surface_get_image, - _cairo_pdf_surface_set_image, - _cairo_pdf_surface_set_matrix, - _cairo_pdf_surface_set_filter, - _cairo_pdf_surface_set_repeat, + _cairo_pdf_surface_acquire_source_image, + _cairo_pdf_surface_release_source_image, + _cairo_pdf_surface_acquire_dest_image, + _cairo_pdf_surface_release_dest_image, + _cairo_pdf_surface_clone_similar, _cairo_pdf_surface_composite, _cairo_pdf_surface_fill_rectangles, _cairo_pdf_surface_composite_trapezoids, _cairo_pdf_surface_copy_page, _cairo_pdf_surface_show_page, _cairo_pdf_surface_set_clip_region, - _cairo_pdf_surface_create_pattern, _cairo_pdf_surface_show_glyphs }; @@ -1930,8 +1944,8 @@ _cairo_pdf_document_write_fonts (cairo_pdf_document_t *document) fprintf (file, "%d 0 obj\r\n" "<< /Type /FontDescriptor\r\n" - " /FontName /%s\r\n" - " /Flags 32\r\n" + " /FontName /7%s\r\n" + " /Flags 4\r\n" " /FontBBox [ %ld %ld %ld %ld ]\r\n" " /ItalicAngle 0\r\n" " /Ascent %ld\r\n" diff --git a/src/cairo_pen.c b/src/cairo_pen.c index f365091dc..6ecaa00b3 100644 --- a/src/cairo_pen.c +++ b/src/cairo_pen.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo_png_surface.c b/src/cairo_png_surface.c index 2279b07a9..1ae745cb5 100644 --- a/src/cairo_png_surface.c +++ b/src/cairo_png_surface.c @@ -32,7 +32,7 @@ * * Contributor(s): * Olivier Andrieu <oliv__a@users.sourceforge.net> - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include <png.h> @@ -185,62 +185,65 @@ _cairo_png_surface_pixels_per_inch (void *abstract_surface) return 96.0; } -static cairo_image_surface_t * -_cairo_png_surface_get_image (void *abstract_surface) +static cairo_status_t +_cairo_png_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) { cairo_png_surface_t *surface = abstract_surface; + + *image_out = surface->image; - cairo_surface_reference (&surface->image->base); - - return surface->image; + return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_png_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +static void +_cairo_png_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) { - cairo_png_surface_t *surface = abstract_surface; - - if (image == surface->image) - return CAIRO_STATUS_SUCCESS; - - /* XXX: Need to call _cairo_image_surface_set_image here, but it's - not implemented yet. */ - - return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t -_cairo_png_surface_set_matrix (void *abstract_surface, - cairo_matrix_t *matrix) +_cairo_png_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect, + void **image_extra) { cairo_png_surface_t *surface = abstract_surface; + + image_rect->x = 0; + image_rect->y = 0; + image_rect->width = surface->image->width; + image_rect->height = surface->image->height; + + *image_out = surface->image; - return _cairo_image_surface_set_matrix (surface->image, matrix); + return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_png_surface_set_filter (void *abstract_surface, - cairo_filter_t filter) +static void +_cairo_png_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) { - cairo_png_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_filter (surface->image, filter); } static cairo_status_t -_cairo_png_surface_set_repeat (void *abstract_surface, - int repeat) +_cairo_png_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { - cairo_png_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_repeat (surface->image, repeat); + return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_int_status_t _cairo_png_surface_composite (cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, + cairo_pattern_t *src, + cairo_pattern_t *mask, void *abstract_dst, int src_x, int src_y, @@ -266,10 +269,14 @@ _cairo_png_surface_fill_rectangles (void *abstract_surface, static cairo_int_status_t _cairo_png_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *generic_src, + cairo_pattern_t *pattern, void *abstract_dst, - int x_src, - int y_src, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps) { @@ -297,18 +304,20 @@ _cairo_png_surface_copy_page (void *abstract_surface) rows[i] = surface->image->data + i * surface->image->stride; png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (png == NULL) - return CAIRO_STATUS_NO_MEMORY; + if (png == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto BAIL1; + } info = png_create_info_struct (png); if (info == NULL) { - png_destroy_write_struct (&png, NULL); - return CAIRO_STATUS_NO_MEMORY; + status = CAIRO_STATUS_NO_MEMORY; + goto BAIL2; } if (setjmp (png_jmpbuf (png))) { status = CAIRO_STATUS_NO_MEMORY; - goto BAIL; + goto BAIL2; } png_init_io (png, surface->file); @@ -332,7 +341,7 @@ _cairo_png_surface_copy_page (void *abstract_surface) break; default: status = CAIRO_STATUS_NULL_POINTER; - goto BAIL; + goto BAIL2; } png_set_IHDR (png, info, @@ -365,9 +374,9 @@ _cairo_png_surface_copy_page (void *abstract_surface) surface->copied = 1; -BAIL: +BAIL2: png_destroy_write_struct (&png, &info); - +BAIL1: free (rows); return status; @@ -397,29 +406,20 @@ _cairo_png_surface_set_clip_region (void *abstract_surface, return _cairo_image_surface_set_clip_region (surface->image, region); } -static cairo_int_status_t -_cairo_png_surface_create_pattern (void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *extents) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - static const cairo_surface_backend_t cairo_png_surface_backend = { _cairo_png_surface_create_similar, _cairo_png_surface_destroy, _cairo_png_surface_pixels_per_inch, - _cairo_png_surface_get_image, - _cairo_png_surface_set_image, - _cairo_png_surface_set_matrix, - _cairo_png_surface_set_filter, - _cairo_png_surface_set_repeat, + _cairo_png_surface_acquire_source_image, + _cairo_png_surface_release_source_image, + _cairo_png_surface_acquire_dest_image, + _cairo_png_surface_release_dest_image, + _cairo_png_surface_clone_similar, _cairo_png_surface_composite, _cairo_png_surface_fill_rectangles, _cairo_png_surface_composite_trapezoids, _cairo_png_surface_copy_page, _cairo_png_surface_show_page, _cairo_png_surface_set_clip_region, - _cairo_png_surface_create_pattern, NULL /* show_glyphs */ }; diff --git a/src/cairo_polygon.c b/src/cairo_polygon.c index e85858033..59c615da2 100644 --- a/src/cairo_polygon.c +++ b/src/cairo_polygon.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include <stdlib.h> diff --git a/src/cairo_ps_surface.c b/src/cairo_ps_surface.c index 4da8162c7..4a45fc679 100644 --- a/src/cairo_ps_surface.c +++ b/src/cairo_ps_surface.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" @@ -42,6 +42,22 @@ static const cairo_surface_backend_t cairo_ps_surface_backend; +/** + * cairo_set_target_ps: + * @cr: a #cairo_t + * @file: an open, writeable file + * @width_inches: width of the output page, in inches + * @height_inches: height of the output page, in inches + * @x_pixels_per_inch: X resolution to use for image fallbacks; + * not all Cairo drawing can be represented in a postscript + * file, so Cairo will write out images for some portions + * of the output. + * @y_pixels_per_inch: Y resolution to use for image fallbacks. + * + * Directs output for a #cairo_t to a postscript file. The file must + * be kept open until the #cairo_t is destroyed or set to have a + * different target, and then must be closed by the application. + **/ void cairo_set_target_ps (cairo_t *cr, FILE *file, @@ -192,62 +208,65 @@ _cairo_ps_surface_pixels_per_inch (void *abstract_surface) return surface->y_ppi; } -static cairo_image_surface_t * -_cairo_ps_surface_get_image (void *abstract_surface) +static cairo_status_t +_cairo_ps_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) { cairo_ps_surface_t *surface = abstract_surface; + + *image_out = surface->image; - cairo_surface_reference (&surface->image->base); - - return surface->image; + return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_ps_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +static void +_cairo_ps_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) { - cairo_ps_surface_t *surface = abstract_surface; - - if (image == surface->image) - return CAIRO_STATUS_SUCCESS; - - /* XXX: Need to call _cairo_image_surface_set_image here, but it's - not implemented yet. */ - - return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t -_cairo_ps_surface_set_matrix (void *abstract_surface, - cairo_matrix_t *matrix) +_cairo_ps_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect, + void **image_extra) { cairo_ps_surface_t *surface = abstract_surface; + + image_rect->x = 0; + image_rect->y = 0; + image_rect->width = surface->image->width; + image_rect->height = surface->image->height; + + *image_out = surface->image; - return _cairo_image_surface_set_matrix (surface->image, matrix); + return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_ps_surface_set_filter (void *abstract_surface, - cairo_filter_t filter) +static void +_cairo_ps_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) { - cairo_ps_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_filter (surface->image, filter); } static cairo_status_t -_cairo_ps_surface_set_repeat (void *abstract_surface, - int repeat) +_cairo_ps_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { - cairo_ps_surface_t *surface = abstract_surface; - - return _cairo_image_surface_set_repeat (surface->image, repeat); + return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_int_status_t _cairo_ps_surface_composite (cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, + cairo_pattern_t *src, + cairo_pattern_t *mask, void *abstract_dst, int src_x, int src_y, @@ -273,10 +292,14 @@ _cairo_ps_surface_fill_rectangles (void *abstract_surface, static cairo_int_status_t _cairo_ps_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *generic_src, + cairo_pattern_t *generic_src, void *abstract_dst, int x_src, int y_src, + int x_dst, + int y_dst, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps) { @@ -294,12 +317,10 @@ _cairo_ps_surface_copy_page (void *abstract_surface) int i, x, y; - cairo_surface_t *white_surface; + cairo_solid_pattern_t white_pattern; char *rgb, *compressed; long rgb_size, compressed_size; - cairo_color_t white; - rgb_size = 3 * width * height; rgb = malloc (rgb_size); if (rgb == NULL) { @@ -316,26 +337,19 @@ _cairo_ps_surface_copy_page (void *abstract_surface) /* PostScript can not represent the alpha channel, so we blend the current image over a white RGB surface to eliminate it. */ - white_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 1, 1); - if (white_surface == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL2; - } - _cairo_color_init (&white); - _cairo_surface_fill_rectangle (white_surface, - CAIRO_OPERATOR_SRC, - &white, - 0, 0, 1, 1); - cairo_surface_set_repeat (white_surface, 1); + _cairo_pattern_init_solid (&white_pattern, 1.0, 1.0, 1.0); + _cairo_surface_composite (CAIRO_OPERATOR_OVER_REVERSE, - white_surface, + &white_pattern.base, NULL, &surface->image->base, 0, 0, 0, 0, 0, 0, width, height); + + _cairo_pattern_fini (&white_pattern.base); i = 0; for (y = 0; y < height; y++) { @@ -379,8 +393,6 @@ _cairo_ps_surface_copy_page (void *abstract_surface) /* Page footer */ fprintf (file, "%%%%EndPage\n"); - cairo_surface_destroy (white_surface); - BAIL2: free (compressed); BAIL1: free (rgb); @@ -412,29 +424,20 @@ _cairo_ps_surface_set_clip_region (void *abstract_surface, return _cairo_image_surface_set_clip_region (surface->image, region); } -static cairo_int_status_t -_cairo_ps_surface_create_pattern (void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *extents) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - static const cairo_surface_backend_t cairo_ps_surface_backend = { _cairo_ps_surface_create_similar, _cairo_ps_surface_destroy, _cairo_ps_surface_pixels_per_inch, - _cairo_ps_surface_get_image, - _cairo_ps_surface_set_image, - _cairo_ps_surface_set_matrix, - _cairo_ps_surface_set_filter, - _cairo_ps_surface_set_repeat, + _cairo_ps_surface_acquire_source_image, + _cairo_ps_surface_release_source_image, + _cairo_ps_surface_acquire_dest_image, + _cairo_ps_surface_release_dest_image, + _cairo_ps_surface_clone_similar, _cairo_ps_surface_composite, _cairo_ps_surface_fill_rectangles, _cairo_ps_surface_composite_trapezoids, _cairo_ps_surface_copy_page, _cairo_ps_surface_show_page, _cairo_ps_surface_set_clip_region, - _cairo_ps_surface_create_pattern, NULL /* show_glyphs */ }; diff --git a/src/cairo_quartz_surface.c b/src/cairo_quartz_surface.c index b7103b051..01b345cdc 100644 --- a/src/cairo_quartz_surface.c +++ b/src/cairo_quartz_surface.c @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2004 Calum Robinson + * Copyright © 2004 Calum Robinson * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public diff --git a/src/cairo_slope.c b/src/cairo_slope.c index 1a1497988..a2edec038 100644 --- a/src/cairo_slope.c +++ b/src/cairo_slope.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo_spline.c b/src/cairo_spline.c index ff290d9dd..5119a8e2b 100644 --- a/src/cairo_spline.c +++ b/src/cairo_spline.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" diff --git a/src/cairo_surface.c b/src/cairo_surface.c index a457d2062..330d58b1e 100644 --- a/src/cairo_surface.c +++ b/src/cairo_surface.c @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include <stdlib.h> @@ -151,16 +151,151 @@ _cairo_surface_pixels_per_inch (cairo_surface_t *surface) return surface->backend->pixels_per_inch (surface); } -cairo_image_surface_t * -_cairo_surface_get_image (cairo_surface_t *surface) +/** + * _cairo_surface_acquire_source_image: + * @surface: a #cairo_surface_t + * @image_out: location to store a pointer to an image surface that includes at least + * the intersection of @interest_rect with the visible area of @surface. + * This surface could be @surface itself, a surface held internal to @surface, + * or it could be a new surface with a copy of the relevant portion of @surface. + * @image_extra: location to store image specific backend data + * + * Gets an image surface to use when drawing as a fallback when drawing with + * @surface as a source. _cairo_surface_release_source_image() must be called + * when finished. + * + * Return value: %CAIRO_STATUS_SUCCESS if a an image was stored in @image_out. + * %CAIRO_INT_STATUS_UNSUPPORTED if an image cannot be retrieved for the specified + * surface. Or %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_surface_acquire_source_image (cairo_surface_t *surface, + cairo_image_surface_t **image_out, + void **image_extra) { - return surface->backend->get_image (surface); + return surface->backend->acquire_source_image (surface, image_out, image_extra); } +/** + * _cairo_surface_release_source_image: + * @surface: a #cairo_surface_t + * @image_extra: same as return from the matching _cairo_surface_acquire_dest_image() + * + * Releases any resources obtained with _cairo_surface_acquire_source_image() + **/ +cairo_private void +_cairo_surface_release_source_image (cairo_surface_t *surface, + cairo_image_surface_t *image, + void *image_extra) +{ + surface->backend->release_source_image (surface, image, image_extra); +} + +/** + * _cairo_surface_acquire_dest_image: + * @surface: a #cairo_surface_t + * @interest_rect: area of @surface for which fallback drawing is being done. + * A value of %NULL indicates that the entire surface is desired. + * @image_out: location to store a pointer to an image surface that includes at least + * the intersection of @interest_rect with the visible area of @surface. + * This surface could be @surface itself, a surface held internal to @surface, + * or it could be a new surface with a copy of the relevant portion of @surface. + * @image_rect: location to store area of the original surface occupied + * by the surface stored in @image. + * @image_extra: location to store image specific backend data + * + * Retrieves a local image for a surface for implementing a fallback drawing + * operation. After calling this function, the implementation of the fallback + * drawing operation draws the primitive to the surface stored in @image_out + * then calls _cairo_surface_release_dest_fallback(), + * which, if a temporary surface was created, copies the bits back to the + * main surface and frees the temporary surface. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY. + * %CAIRO_INT_STATUS_UNSUPPORTED can be returned but this will mean that + * the backend can't draw with fallbacks. It's possible for the routine + * to store NULL in @local_out and return %CAIRO_STATUS_SUCCESS; + * that indicates that no part of @interest_rect is visible, so no drawing + * is necessary. _cairo_surface_release_dest_fallback() should not be called in that + * case. + **/ +cairo_status_t +_cairo_surface_acquire_dest_image (cairo_surface_t *surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect, + void **image_extra) +{ + return surface->backend->acquire_dest_image (surface, interest_rect, + image_out, image_rect, image_extra); +} + +/** + * _cairo_surface_end_fallback: + * @surface: a #cairo_surface_t + * @interest_rect: same as passed to the matching _cairo_surface_acquire_dest_image() + * @image: same as returned from the matching _cairo_surface_acquire_dest_image() + * @image_rect: same as returned from the matching _cairo_surface_acquire_dest_image() + * @image_extra: same as return from the matching _cairo_surface_acquire_dest_image() + * + * Finishes the operation started with _cairo_surface_acquire_dest_image(), by, if + * necessary, copying the image from @image back to @surface and freeing any + * resources that were allocated. + **/ +void +_cairo_surface_release_dest_image (cairo_surface_t *surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ + surface->backend->release_dest_image (surface, interest_rect, + image, image_rect, image_extra); +} + +/** + * _cairo_surface_clone_similar: + * @surface: a #cairo_surface_t + * @src: the source image + * @clone_out: location to store a surface compatible with @surface + * and with contents identical to @src. The caller must call + * cairo_surface_destroy() on the result. + * + * Creates a surface with contents identical to @src but that + * can be used efficiently with @surface. If @surface and @src are + * already compatible then it may return a new reference to @src. + * + * Return value: %CAIRO_STATUS_SUCCESS if a surface was created and stored + * in @clone_out. Otherwise %CAIRO_INT_STATUS_UNSUPPORTED or another + * error like %CAIRO_STATUS_NO_MEMORY. + **/ cairo_status_t -_cairo_surface_set_image (cairo_surface_t *surface, cairo_image_surface_t *image) +_cairo_surface_clone_similar (cairo_surface_t *surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { - return surface->backend->set_image (surface, image); + cairo_status_t status; + cairo_image_surface_t *image; + void *image_extra; + + status = surface->backend->clone_similar (surface, src, clone_out); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_surface_acquire_source_image (src, &image, &image_extra); + if (status != CAIRO_STATUS_SUCCESS) + return status; + + status = surface->backend->clone_similar (surface, &image->base, clone_out); + + /* If the above failed point, we could implement a full fallback + * using acquire_dest_image, but that's going to be very + * inefficient compared to a backend-specific implementation of + * clone_similar() with an image source. So we don't bother + */ + + _cairo_surface_release_source_image (src, image, image_extra); + return status; } cairo_status_t @@ -169,9 +304,7 @@ cairo_surface_set_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix) if (surface == NULL) return CAIRO_STATUS_NULL_POINTER; - cairo_matrix_copy (&surface->matrix, matrix); - - return surface->backend->set_matrix (surface, matrix); + return cairo_matrix_copy (&surface->matrix, matrix); } slim_hidden_def(cairo_surface_set_matrix); @@ -192,7 +325,7 @@ cairo_surface_set_filter (cairo_surface_t *surface, cairo_filter_t filter) return CAIRO_STATUS_NULL_POINTER; surface->filter = filter; - return surface->backend->set_filter (surface, filter); + return CAIRO_STATUS_SUCCESS; } cairo_filter_t @@ -224,14 +357,81 @@ cairo_surface_set_repeat (cairo_surface_t *surface, int repeat) surface->repeat = repeat; - return surface->backend->set_repeat (surface, repeat); + return CAIRO_STATUS_SUCCESS; } slim_hidden_def(cairo_surface_set_repeat); +typedef struct { + cairo_surface_t *dst; + cairo_rectangle_t extents; + cairo_image_surface_t *image; + cairo_rectangle_t image_rect; + void *image_extra; +} fallback_state_t; + +static cairo_status_t +_fallback_init (fallback_state_t *state, + cairo_surface_t *dst, + int x, + int y, + int width, + int height) +{ + state->extents.x = x; + state->extents.y = y; + state->extents.width = width; + state->extents.height = height; + + state->dst = dst; + + return _cairo_surface_acquire_dest_image (dst, &state->extents, + &state->image, &state->image_rect, &state->image_extra); +} + +static void +_fallback_cleanup (fallback_state_t *state) +{ + _cairo_surface_release_dest_image (state->dst, &state->extents, + state->image, &state->image_rect, state->image_extra); +} + +static cairo_status_t +_fallback_composite (cairo_operator_t operator, + cairo_pattern_t *src, + cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) +{ + fallback_state_t state; + cairo_status_t status; + + status = _fallback_init (&state, dst, dst_x, dst_y, width, height); + if (!CAIRO_OK (status) || !state.image) + return status; + + state.image->base.backend->composite (operator, src, mask, + &state.image->base, + src_x, src_y, mask_x, mask_y, + dst_x - state.image_rect.x, + dst_y - state.image_rect.y, + width, height); + + _fallback_cleanup (&state); + + return status; +} + cairo_status_t _cairo_surface_composite (cairo_operator_t operator, - cairo_surface_t *src, - cairo_surface_t *mask, + cairo_pattern_t *src, + cairo_pattern_t *mask, cairo_surface_t *dst, int src_x, int src_y, @@ -243,7 +443,6 @@ _cairo_surface_composite (cairo_operator_t operator, unsigned int height) { cairo_int_status_t status; - cairo_image_surface_t *src_image, *mask_image = 0, *dst_image; status = dst->backend->composite (operator, src, mask, dst, @@ -254,28 +453,12 @@ _cairo_surface_composite (cairo_operator_t operator, if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - src_image = _cairo_surface_get_image (src); - if (mask) - mask_image = _cairo_surface_get_image (mask); - dst_image = _cairo_surface_get_image (dst); - - dst_image->base.backend->composite (operator, - &src_image->base, - mask ? &mask_image->base : NULL, - dst_image, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); - - status = _cairo_surface_set_image (dst, dst_image); - - cairo_surface_destroy (&src_image->base); - if (mask) - cairo_surface_destroy (&mask_image->base); - cairo_surface_destroy (&dst_image->base); - - return status; + return _fallback_composite (operator, + src, mask, dst, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height); } cairo_status_t @@ -297,6 +480,77 @@ _cairo_surface_fill_rectangle (cairo_surface_t *surface, return _cairo_surface_fill_rectangles (surface, operator, color, &rect, 1); } +static cairo_status_t +_fallback_fill_rectangles (cairo_surface_t *surface, + cairo_operator_t operator, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) +{ + fallback_state_t state; + cairo_rectangle_t *offset_rects = NULL; + cairo_status_t status; + int x1, y1, x2, y2; + int i; + + if (num_rects <= 0) + return CAIRO_STATUS_SUCCESS; + + /* Compute the bounds of the rectangles, so that we know what area of the + * destination surface to fetch + */ + x1 = rects[0].x; + y1 = rects[0].y; + x2 = rects[0].x + rects[0].width; + y2 = rects[0].y + rects[0].height; + + for (i = 1; i < num_rects; i++) { + if (rects[0].x < x1) + x1 = rects[0].x; + if (rects[0].y < y1) + y1 = rects[0].y; + + if (rects[0].x + rects[0].width > x2) + x2 = rects[0].x + rects[0].width; + if (rects[0].y + rects[0].height > y2) + y2 = rects[0].y + rects[0].height; + } + + status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1); + if (!CAIRO_OK (status) || !state.image) + return status; + + /* If the fetched image isn't at 0,0, we need to offset the rectangles */ + + if (state.image_rect.x != 0 || state.image_rect.y != 0) { + offset_rects = malloc (sizeof (cairo_rectangle_t) * num_rects); + if (!offset_rects) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } + + for (i = 0; i < num_rects; i++) { + offset_rects[i].x = rects[i].x - state.image_rect.x; + offset_rects[i].y = rects[i].y - state.image_rect.y; + offset_rects[i].width = rects[i].width; + offset_rects[i].height = rects[i].height; + } + + rects = offset_rects; + } + + state.image->base.backend->fill_rectangles (&state.image->base, operator, color, + rects, num_rects); + + if (offset_rects) + free (offset_rects); + + FAIL: + _fallback_cleanup (&state); + + return status; +} + cairo_status_t _cairo_surface_fill_rectangles (cairo_surface_t *surface, cairo_operator_t operator, @@ -305,7 +559,6 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, int num_rects) { cairo_int_status_t status; - cairo_image_surface_t *surface_image; if (num_rects == 0) return CAIRO_STATUS_SUCCESS; @@ -317,54 +570,105 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - surface_image = _cairo_surface_get_image (surface); + return _fallback_fill_rectangles (surface, operator, color, rects, num_rects); +} + +static cairo_status_t +_fallback_composite_trapezoids (cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *dst, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps) +{ + fallback_state_t state; + cairo_trapezoid_t *offset_traps = NULL; + cairo_status_t status; + int i; + + status = _fallback_init (&state, dst, dst_x, dst_y, width, height); + if (!CAIRO_OK (status) || !state.image) + return status; - surface_image->base.backend->fill_rectangles (surface_image, - operator, - color, - rects, num_rects); + /* If the destination image isn't at 0,0, we need to offset the trapezoids */ + + if (state.image_rect.x != 0 || state.image_rect.y != 0) { + + cairo_fixed_t xoff = _cairo_fixed_from_int (state.image_rect.x); + cairo_fixed_t yoff = _cairo_fixed_from_int (state.image_rect.y); + + offset_traps = malloc (sizeof (cairo_trapezoid_t) * num_traps); + if (!offset_traps) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL; + } - status = _cairo_surface_set_image (surface, surface_image); + for (i = 0; i < num_traps; i++) { + offset_traps[i].top = traps[i].top - yoff; + offset_traps[i].bottom = traps[i].bottom - yoff; + offset_traps[i].left.p1.x = traps[i].left.p1.x - xoff; + offset_traps[i].left.p1.y = traps[i].left.p1.y - yoff; + offset_traps[i].left.p2.x = traps[i].left.p2.x - xoff; + offset_traps[i].left.p2.y = traps[i].left.p2.y - yoff; + offset_traps[i].right.p1.x = traps[i].right.p1.x - xoff; + offset_traps[i].right.p1.y = traps[i].right.p1.y - yoff; + offset_traps[i].right.p2.x = traps[i].right.p2.x - xoff; + offset_traps[i].right.p2.y = traps[i].right.p2.y - yoff; + } - cairo_surface_destroy (&surface_image->base); + traps = offset_traps; + } + state.image->base.backend->composite_trapezoids (operator, pattern, + &state.image->base, + src_x, src_y, + dst_x - state.image_rect.x, + dst_y - state.image_rect.y, + width, height, traps, num_traps); + if (offset_traps) + free (offset_traps); + + FAIL: + _fallback_cleanup (&state); + return status; } + cairo_status_t _cairo_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *src, + cairo_pattern_t *pattern, cairo_surface_t *dst, - int x_src, - int y_src, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps) { cairo_int_status_t status; - cairo_image_surface_t *src_image, *dst_image; status = dst->backend->composite_trapezoids (operator, - src, dst, - x_src, y_src, + pattern, dst, + src_x, src_y, + dst_x, dst_y, + width, height, traps, num_traps); if (status != CAIRO_INT_STATUS_UNSUPPORTED) return status; - src_image = _cairo_surface_get_image (src); - dst_image = _cairo_surface_get_image (dst); - - dst_image->base.backend->composite_trapezoids (operator, - &src_image->base, - dst_image, - x_src, y_src, - traps, num_traps); - - status = _cairo_surface_set_image (dst, dst_image); - - cairo_surface_destroy (&src_image->base); - cairo_surface_destroy (&dst_image->base); - - return status; + return _fallback_composite_trapezoids (operator, pattern, dst, + src_x, src_y, + dst_x, dst_y, + width, height, + traps, num_traps); } cairo_status_t @@ -402,109 +706,3 @@ _cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *reg { return surface->backend->set_clip_region (surface, region); } - -cairo_status_t -_cairo_surface_create_pattern (cairo_surface_t *surface, - cairo_pattern_t *pattern, - cairo_box_t *box) -{ - cairo_int_status_t status; - - status = surface->backend->create_pattern (surface, pattern, box); - - /* The backend cannot accelerate this pattern, lets create an - unaccelerated source instead. */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - - status = CAIRO_STATUS_SUCCESS; - switch (pattern->type) { - case CAIRO_PATTERN_LINEAR: - case CAIRO_PATTERN_RADIAL: { - cairo_image_surface_t *image; - - image = _cairo_pattern_get_image (pattern, box); - if (image) { - pattern->source = &image->base; - - return CAIRO_STATUS_SUCCESS; - } else - return CAIRO_STATUS_NO_MEMORY; - - } break; - case CAIRO_PATTERN_SOLID: - pattern->source = - _cairo_surface_create_similar_solid (surface, - CAIRO_FORMAT_ARGB32, - 1, 1, - &pattern->color); - if (pattern->source) { - cairo_surface_set_repeat (pattern->source, 1); - - return CAIRO_STATUS_SUCCESS; - } else - return CAIRO_STATUS_NO_MEMORY; - break; - case CAIRO_PATTERN_SURFACE: - status = CAIRO_INT_STATUS_UNSUPPORTED; - - /* handle pattern opacity */ - if (pattern->color.alpha != 1.0) { - double x = box->p1.x >> 16; - double y = box->p1.y >> 16; - int width = ((box->p2.x + 65535) >> 16) - (box->p1.x >> 16); - int height = ((box->p2.y + 65535) >> 16) - (box->p1.y >> 16); - cairo_pattern_t alpha; - - pattern->source = - cairo_surface_create_similar (surface, - CAIRO_FORMAT_ARGB32, - width, height); - if (pattern->source) { - _cairo_pattern_init_solid (&alpha, 1.0, 1.0, 1.0); - _cairo_pattern_set_alpha (&alpha, pattern->color.alpha); - - status = _cairo_surface_create_pattern (pattern->source, - &alpha, box); - - if (status == CAIRO_STATUS_SUCCESS) { - int save_repeat = pattern->u.surface.surface->repeat; - - if (pattern->extend == CAIRO_EXTEND_REPEAT || - pattern->u.surface.surface->repeat == 1) - cairo_surface_set_repeat (pattern->u.surface.surface, 1); - else - cairo_surface_set_repeat (pattern->u.surface.surface, 0); - - status = - _cairo_surface_composite (CAIRO_OPERATOR_OVER, - pattern->u.surface.surface, - alpha.source, - pattern->source, - 0, 0, 0, 0, 0, 0, - width, height); - - cairo_surface_set_repeat (pattern->u.surface.surface, - save_repeat); - - if (status == CAIRO_STATUS_SUCCESS) - _cairo_pattern_set_source_offset (pattern, x, y); - else - cairo_surface_destroy (pattern->source); - } - - _cairo_pattern_fini (&alpha); - } - } - - if (status != CAIRO_STATUS_SUCCESS) { - pattern->source = pattern->u.surface.surface; - cairo_surface_reference (pattern->u.surface.surface); - - return CAIRO_STATUS_SUCCESS; - } - break; - } - } - - return status; -} diff --git a/src/cairo_traps.c b/src/cairo_traps.c index d17a27281..79c7e16b6 100644 --- a/src/cairo_traps.c +++ b/src/cairo_traps.c @@ -1,31 +1,42 @@ /* - * Copyright © 2002 Keith Packard + * Copyright © 2002 Keith Packard * - * 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 Keith Packard not be used in - * advertising or publicity pertaining to distribution of the software without - * specific, written prior permission. Keith Packard makes no - * representations about the suitability of this software for any purpose. It - * is provided "as is" without express or implied warranty. + * 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. * - * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL KEITH PACKARD 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. + * 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 Keith Packard + * + * Contributor(s): + * Keith R. Packard <keithp@keithp.com> + * Carl D. Worth <cworth@cworth.org> * * 2002-07-15: Converted from XRenderCompositeDoublePoly to cairo_trap. Carl D. Worth */ #include "cairoint.h" -#define CAIRO_TRAPS_GROWTH_INC 10 - /* private functions */ static cairo_status_t @@ -52,12 +63,6 @@ _compare_cairo_edge_by_slope (const void *av, const void *bv); static cairo_fixed_16_16_t _compute_x (cairo_line_t *line, cairo_fixed_t y); -static double -_compute_inverse_slope (cairo_line_t *l); - -static double -_compute_x_intercept (cairo_line_t *l, double inverse_slope); - static int _line_segs_intersect_ceil (cairo_line_t *left, cairo_line_t *right, cairo_fixed_t *y_ret); @@ -68,6 +73,8 @@ _cairo_traps_init (cairo_traps_t *traps) traps->traps_size = 0; traps->traps = NULL; + traps->extents.p1.x = traps->extents.p1.y = CAIRO_MAXSHORT << 16; + traps->extents.p2.x = traps->extents.p2.y = CAIRO_MINSHORT << 16; } void @@ -93,7 +100,8 @@ _cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bo } if (traps->num_traps >= traps->traps_size) { - status = _cairo_traps_grow_by (traps, CAIRO_TRAPS_GROWTH_INC); + int inc = traps->traps_size ? traps->traps_size : 32; + status = _cairo_traps_grow_by (traps, inc); if (status) return status; } @@ -104,6 +112,28 @@ _cairo_traps_add_trap (cairo_traps_t *traps, cairo_fixed_t top, cairo_fixed_t bo trap->left = *left; trap->right = *right; + if (top < traps->extents.p1.y) + traps->extents.p1.y = top; + if (bottom > traps->extents.p2.y) + traps->extents.p2.y = bottom; + /* + * This isn't generally accurate, but it is close enough for + * this purpose. Assuming that the left and right segments always + * contain the trapezoid vertical extents, these compares will + * yield a containing box. Assuming that the points all come from + * the same figure which will eventually be completely drawn, then + * the compares will yield the correct overall extents + */ + if (left->p1.x < traps->extents.p1.x) + traps->extents.p1.x = left->p1.x; + if (left->p2.x < traps->extents.p1.x) + traps->extents.p1.x = left->p2.x; + + if (right->p1.x > traps->extents.p2.x) + traps->extents.p2.x = right->p1.x; + if (right->p2.x > traps->extents.p2.x) + traps->extents.p2.x = right->p2.x; + traps->num_traps++; return CAIRO_STATUS_SUCCESS; @@ -327,40 +357,132 @@ _compare_cairo_edge_by_current_x_slope (const void *av, const void *bv) sub-computations -- just a bunch of determinants. I haven't looked at complexity, (both are probably similar and it probably doesn't matter much anyway). + */ -static double -_det (double a, double b, double c, double d) +/* XXX: Keith's new intersection code is much cleaner, and uses + * sufficient precision for correctly sorting intersections according + * to the analysis in Hobby's paper. + * + * But, when we enable this code, some things are failing, (eg. the + * stars in test/fill_rule get filled wrong). This could indicate a + * bug in one of tree places: + * + * 1) The new intersection code in this file + * + * 2) cairo_wideint.c (which is only exercised here) + * + * 3) In the current tessellator, (where the old intersection + * code, with its mystic increments could be masking the bug). + * + * It will likely be easier to revisit this when the new tessellation + * code is in place. So, for now, we'll simply disable the new + * intersection code. + */ + +#define CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE 0 + +#if CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE +static const cairo_fixed_32_32_t +_det16_32 (cairo_fixed_16_16_t a, + cairo_fixed_16_16_t b, + cairo_fixed_16_16_t c, + cairo_fixed_16_16_t d) { - return a * d - b * c; + return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d), + _cairo_int32x32_64_mul (b, c)); } -static int -_lines_intersect (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_intersection) +static const cairo_fixed_64_64_t +_det32_64 (cairo_fixed_32_32_t a, + cairo_fixed_32_32_t b, + cairo_fixed_32_32_t c, + cairo_fixed_32_32_t d) { - double dx1 = cairo_fixed_to_double (l1->p1.x - l1->p2.x); - double dy1 = cairo_fixed_to_double (l1->p1.y - l1->p2.y); - - double dx2 = cairo_fixed_to_double (l2->p1.x - l2->p2.x); - double dy2 = cairo_fixed_to_double (l2->p1.y - l2->p2.y); - - double l1_det, l2_det; + return _cairo_int128_sub (_cairo_int64x64_128_mul (a, d), + _cairo_int64x64_128_mul (b, c)); +} - double den_det = _det (dx1, dy1, dx2, dy2); +static const cairo_fixed_32_32_t +_fixed_16_16_to_fixed_32_32 (cairo_fixed_16_16_t a) +{ + return _cairo_int64_lsl (_cairo_int32_to_int64 (a), 16); +} - if (den_det == 0) +static int +_line_segs_intersect_ceil (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_intersection) +{ + cairo_fixed_16_16_t dx1, dx2, dy1, dy2; + cairo_fixed_32_32_t den_det; + cairo_fixed_32_32_t l1_det, l2_det; + cairo_fixed_64_64_t num_det; + cairo_fixed_32_32_t intersect_32_32; + cairo_fixed_48_16_t intersect_48_16; + cairo_fixed_16_16_t intersect_16_16; + cairo_quorem128_t qr; + + dx1 = l1->p1.x - l1->p2.x; + dy1 = l1->p1.y - l1->p2.y; + dx2 = l2->p1.x - l2->p2.x; + dy2 = l2->p1.y - l2->p2.y; + den_det = _det16_32 (dx1, dy1, + dx2, dy2); + + if (_cairo_int64_eq (den_det, _cairo_int32_to_int64(0))) return 0; - l1_det = _det (l1->p1.x, l1->p1.y, - l1->p2.x, l1->p2.y); - l2_det = _det (l2->p1.x, l2->p1.y, - l2->p2.x, l2->p2.y); + l1_det = _det16_32 (l1->p1.x, l1->p1.y, + l1->p2.x, l1->p2.y); + l2_det = _det16_32 (l2->p1.x, l2->p1.y, + l2->p2.x, l2->p2.y); - *y_intersection = _det (l1_det, dy1, - l2_det, dy2) / den_det; + + num_det = _det32_64 (l1_det, _fixed_16_16_to_fixed_32_32 (dy1), + l2_det, _fixed_16_16_to_fixed_32_32 (dy2)); + + /* + * Ok, this one is a bit tricky in fixed point, the denominator + * needs to be left with 32-bits of fraction so that the + * result of the divide ends up with 32-bits of fraction (64 - 32 = 32) + */ + qr = _cairo_int128_divrem (num_det, _cairo_int64_to_int128 (den_det)); + + intersect_32_32 = _cairo_int128_to_int64 (qr.quo); + + /* + * Find the ceiling of the quotient -- divrem returns + * the quotient truncated towards zero, so if the + * quotient should be positive (num_den and den_det have same sign) + * bump the quotient up by one. + */ + + if (_cairo_int128_ne (qr.rem, _cairo_int32_to_int128 (0)) && + (_cairo_int128_ge (num_det, _cairo_int32_to_int128 (0)) == + _cairo_int64_ge (den_det, _cairo_int32_to_int64 (0)))) + { + intersect_32_32 = _cairo_int64_add (intersect_32_32, + _cairo_int32_to_int64 (1)); + } + + /* + * Now convert from 32.32 to 48.16 and take the ceiling; + * this requires adding in 15 1 bits and shifting the result + */ + + intersect_32_32 = _cairo_int64_add (intersect_32_32, + _cairo_int32_to_int64 ((1 << 16) - 1)); + intersect_48_16 = _cairo_int64_rsa (intersect_32_32, 16); + + /* + * And drop the top bits + */ + intersect_16_16 = _cairo_int64_to_int32 (intersect_48_16); + + *y_intersection = intersect_16_16; return 1; } -*/ +#endif /* CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE */ + static cairo_fixed_16_16_t _compute_x (cairo_line_t *line, cairo_fixed_t y) { @@ -371,6 +493,7 @@ _compute_x (cairo_line_t *line, cairo_fixed_t y) return line->p1.x + (ex / dy); } +#if ! CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE static double _compute_inverse_slope (cairo_line_t *l) { @@ -460,6 +583,7 @@ _line_segs_intersect_ceil (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_ return 1; } +#endif /* CAIRO_TRAPS_USE_NEW_INTERSECTION_CODE */ /* The algorithm here is pretty simple: @@ -567,32 +691,32 @@ _cairo_traps_tessellate_polygon (cairo_traps_t *traps, return CAIRO_STATUS_SUCCESS; } -static int +static cairo_bool_t _cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt) { cairo_slope_t slope_left, slope_pt, slope_right; if (t->top > pt->y) - return 0; + return FALSE; if (t->bottom < pt->y) - return 0; + return FALSE; _cairo_slope_init (&slope_left, &t->left.p1, &t->left.p2); _cairo_slope_init (&slope_pt, &t->left.p1, pt); if (_cairo_slope_compare (&slope_left, &slope_pt) < 0) - return 0; + return FALSE; _cairo_slope_init (&slope_right, &t->right.p1, &t->right.p2); _cairo_slope_init (&slope_pt, &t->right.p1, pt); if (_cairo_slope_compare (&slope_pt, &slope_right) < 0) - return 0; + return FALSE; - return 1; + return TRUE; } -int +cairo_bool_t _cairo_traps_contain (cairo_traps_t *traps, double x, double y) { int i; @@ -603,45 +727,14 @@ _cairo_traps_contain (cairo_traps_t *traps, double x, double y) for (i = 0; i < traps->num_traps; i++) { if (_cairo_trap_contains (&traps->traps[i], &point)) - return 1; + return TRUE; } - return 0; -} - -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#define MAX(a,b) ((a) > (b) ? (a) : (b)) - -static void -_cairo_trap_extents (cairo_trapezoid_t *t, cairo_box_t *extents) -{ - cairo_fixed_t x; - - if (t->top < extents->p1.y) - extents->p1.y = t->top; - - if (t->bottom > extents->p2.y) - extents->p2.y = t->bottom; - - x = MIN (_compute_x (&t->left, t->top), - _compute_x (&t->left, t->bottom)); - if (x < extents->p1.x) - extents->p1.x = x; - - x = MAX (_compute_x (&t->right, t->top), - _compute_x (&t->right, t->bottom)); - if (x > extents->p2.x) - extents->p2.x = x; + return FALSE; } void _cairo_traps_extents (cairo_traps_t *traps, cairo_box_t *extents) { - int i; - - extents->p1.x = extents->p1.y = CAIRO_MAXSHORT << 16; - extents->p2.x = extents->p2.y = CAIRO_MINSHORT << 16; - - for (i = 0; i < traps->num_traps; i++) - _cairo_trap_extents (&traps->traps[i], extents); + *extents = traps->extents; } diff --git a/src/cairo_unicode.c b/src/cairo_unicode.c new file mode 100644 index 000000000..92201391a --- /dev/null +++ b/src/cairo_unicode.c @@ -0,0 +1,340 @@ +/* cairo_unicode.c: Unicode conversion routines + * + * The code in this file is derived from GLib's gutf8.c and + * ultimately from libunicode. It is relicensed under the + * dual LGPL/MPL with permission of the original authors. + * + * Copyright © 1999 Tom Tromey + * Copyright © 2005 Red Hat, Inc + * + * 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 cairo_unicode.c as distributed with the + * cairo graphics library. + * + * The Initial Developer of the Original Code is Tom Tromey. + * and Red Hat, Inc. + * + * Contributor(s): + * Owen Taylor <otaylor@redhat.com> + */ + +#include <limits.h> + +#include <cairoint.h> + +#define UTF8_COMPUTE(Char, Mask, Len) \ + if (Char < 128) \ + { \ + Len = 1; \ + Mask = 0x7f; \ + } \ + else if ((Char & 0xe0) == 0xc0) \ + { \ + Len = 2; \ + Mask = 0x1f; \ + } \ + else if ((Char & 0xf0) == 0xe0) \ + { \ + Len = 3; \ + Mask = 0x0f; \ + } \ + else if ((Char & 0xf8) == 0xf0) \ + { \ + Len = 4; \ + Mask = 0x07; \ + } \ + else if ((Char & 0xfc) == 0xf8) \ + { \ + Len = 5; \ + Mask = 0x03; \ + } \ + else if ((Char & 0xfe) == 0xfc) \ + { \ + Len = 6; \ + Mask = 0x01; \ + } \ + else \ + Len = -1; + +#define UTF8_LENGTH(Char) \ + ((Char) < 0x80 ? 1 : \ + ((Char) < 0x800 ? 2 : \ + ((Char) < 0x10000 ? 3 : \ + ((Char) < 0x200000 ? 4 : \ + ((Char) < 0x4000000 ? 5 : 6))))) + + +#define UTF8_GET(Result, Chars, Count, Mask, Len) \ + (Result) = (Chars)[0] & (Mask); \ + for ((Count) = 1; (Count) < (Len); ++(Count)) \ + { \ + if (((Chars)[(Count)] & 0xc0) != 0x80) \ + { \ + (Result) = -1; \ + break; \ + } \ + (Result) <<= 6; \ + (Result) |= ((Chars)[(Count)] & 0x3f); \ + } + +#define UNICODE_VALID(Char) \ + ((Char) < 0x110000 && \ + (((Char) & 0xFFFFF800) != 0xD800) && \ + ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ + ((Char) & 0xFFFE) != 0xFFFE) + + +static const char utf8_skip_data[256] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +#define UTF8_NEXT_CHAR(p) (char *)((p) + utf8_skip_data[*(unsigned char *)(p)]) + +/* Converts a sequence of bytes encoded as UTF-8 to a Unicode character. + * If @p does not point to a valid UTF-8 encoded character, results are + * undefined. + **/ +static uint32_t +_utf8_get_char (const char *p) +{ + int i, mask = 0, len; + uint32_t result; + unsigned char c = (unsigned char) *p; + + UTF8_COMPUTE (c, mask, len); + if (len == -1) + return (uint32_t)-1; + UTF8_GET (result, p, i, mask, len); + + return result; +} + +/* Like _utf8_get_char, but take a maximum length + * and return (uint32_t)-2 on incomplete trailing character + */ +static uint32_t +_utf8_get_char_extended (const char *p, + long max_len) +{ + int i, len; + uint32_t wc = (unsigned char) *p; + + if (wc < 0x80) { + return wc; + } else if (wc < 0xc0) { + return (uint32_t)-1; + } else if (wc < 0xe0) { + len = 2; + wc &= 0x1f; + } else if (wc < 0xf0) { + len = 3; + wc &= 0x0f; + } else if (wc < 0xf8) { + len = 4; + wc &= 0x07; + } else if (wc < 0xfc) { + len = 5; + wc &= 0x03; + } else if (wc < 0xfe) { + len = 6; + wc &= 0x01; + } else { + return (uint32_t)-1; + } + + if (max_len >= 0 && len > max_len) { + for (i = 1; i < max_len; i++) { + if ((((unsigned char *)p)[i] & 0xc0) != 0x80) + return (uint32_t)-1; + } + return (uint32_t)-2; + } + + for (i = 1; i < len; ++i) { + uint32_t ch = ((unsigned char *)p)[i]; + + if ((ch & 0xc0) != 0x80) { + if (ch) + return (uint32_t)-1; + else + return (uint32_t)-2; + } + + wc <<= 6; + wc |= (ch & 0x3f); + } + + if (UTF8_LENGTH(wc) != len) + return (uint32_t)-1; + + return wc; +} + +/** + * _cairo_utf8_to_utf32: + * @str: an UTF-8 string + * @len: length of @str in bytes, or -1 if it is nul-terminated. + * If @len is supplied and the string has an embedded nul + * byte, only the portion before the nul byte is converted. + * @result: location to store a pointer to a newly allocated UTF-32 + * string (always native endian). Free with free(). A 0 + * word will be written after the last character. + * @items_written: location to store number of 32-bit words + * written. (Not including the trailing 0) + * + * Converts a UTF-8 string to UCS-4. UCS-4 is an encoding of Unicode + * with 1 32-bit word per character. The string is validated to + * consist entirely of valid Unicode characters. + * + * Return value: %CAIRO_STATUS_SUCCESS if the entire string was + * succesfully converted. %CAIRO_STATUS_INVALID_STRING if an + * an invalid sequence was found. + **/ +cairo_status_t +_cairo_utf8_to_ucs4 (const char *str, + int len, + uint32_t **result, + int *items_written) +{ + uint32_t *str32 = NULL; + int n_chars, i; + const char *in; + + in = str; + n_chars = 0; + while ((len < 0 || str + len - in > 0) && *in) + { + uint32_t wc = _utf8_get_char_extended (in, str + len - in); + if (wc & 0x80000000 || !UNICODE_VALID (wc)) + return CAIRO_STATUS_INVALID_STRING; + + n_chars++; + if (n_chars == INT_MAX) + return CAIRO_STATUS_INVALID_STRING; + + in = UTF8_NEXT_CHAR (in); + } + + str32 = malloc (sizeof (uint32_t) * (n_chars + 1)); + if (!str32) + return CAIRO_STATUS_NO_MEMORY; + + in = str; + for (i=0; i < n_chars; i++) { + str32[i] = _utf8_get_char (in); + in = UTF8_NEXT_CHAR (in); + } + str32[i] = 0; + + *result = str32; + if (items_written) + *items_written = n_chars; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_utf8_to_utf16: + * @str: an UTF-8 string + * @len: length of @str in bytes, or -1 if it is nul-terminated. + * If @len is supplied and the string has an embedded nul + * byte, only the portion before the nul byte is converted. + * @result: location to store a pointer to a newly allocated UTF-16 + * string (always native endian). Free with free(). A 0 + * word will be written after the last character. + * @items_written: location to store number of 16-bit words + * written. (Not including the trailing 0) + * + * Converts a UTF-8 string to UTF-16. UTF-16 is an encoding of Unicode + * where characters are represented either as a single 16-bit word, or + * as a pair of 16-bit "surrogates". The string is validated to + * consist entirely of valid Unicode characters. + * + * Return value: %CAIRO_STATUS_SUCCESS if the entire string was + * succesfully converted. %CAIRO_STATUS_INVALID_STRING if an + * an invalid sequence was found. + **/ +cairo_status_t +_cairo_utf8_to_utf16 (const char *str, + int len, + uint16_t **result, + int *items_written) +{ + uint16_t *str16 = NULL; + int n16, i; + const char *in; + + in = str; + n16 = 0; + while ((len < 0 || str + len - in > 0) && *in) { + uint32_t wc = _utf8_get_char_extended (in, str + len - in); + if (wc & 0x80000000 || !UNICODE_VALID (wc)) + return CAIRO_STATUS_INVALID_STRING; + + if (wc < 0x10000) + n16 += 1; + else + n16 += 2; + + if (n16 == INT_MAX - 1 || n16 == INT_MAX) + return CAIRO_STATUS_INVALID_STRING; + + in = UTF8_NEXT_CHAR (in); + } + + + str16 = malloc (sizeof (uint16_t) * (n16 + 1)); + if (!str16) + return CAIRO_STATUS_NO_MEMORY; + + in = str; + for (i = 0; i < n16;) { + uint32_t wc = _utf8_get_char (in); + + if (wc < 0x10000) { + str16[i++] = wc; + } else { + str16[i++] = (wc - 0x10000) / 0x400 + 0xd800; + str16[i++] = (wc - 0x10000) % 0x400 + 0xdc00; + } + + in = UTF8_NEXT_CHAR (in); + } + + str16[i] = 0; + + *result = str16; + if (items_written) + *items_written = n16; + + return CAIRO_STATUS_SUCCESS; +} diff --git a/src/cairo_win32_font.c b/src/cairo_win32_font.c new file mode 100644 index 000000000..02f0cffd6 --- /dev/null +++ b/src/cairo_win32_font.c @@ -0,0 +1,1252 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * 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 Red Hat, Inc. + * + * Contributor(s): + */ + +#include <string.h> +#include <stdio.h> + +#include "cairo-win32-private.h" + +#ifndef SPI_GETFONTSMOOTHINGTYPE +#define SPI_GETFONTSMOOTHINGTYPE 0x200a +#endif +#ifndef FE_FONTSMOOTHINGCLEARTYPE +#define FE_FONTSMOOTHINGCLEARTYPE 2 +#endif +#ifndef CLEARTYPE_QUALITY +#define CLEARTYPE_QUALITY 5 +#endif + +const cairo_font_backend_t cairo_win32_font_backend; + +#define LOGICAL_SCALE 32 + +typedef struct { + cairo_font_t base; + + LOGFONTW logfont; + + BYTE quality; + + /* We do drawing and metrics computation in a "logical space" which + * is similar to font space, except that it is scaled by a factor + * of the (desired font size) * (LOGICAL_SCALE). The multiplication + * by LOGICAL_SCALE allows for sub-pixel precision. + */ + double logical_scale; + + /* The size we should actually request the font at from Windows; differs + * from the logical_scale because it is quantized for orthogonal + * transformations + */ + double logical_size; + + /* Transformations from device <=> logical space + */ + cairo_matrix_t logical_to_device; + cairo_matrix_t device_to_logical; + + /* We special case combinations of 90-degree-rotations, scales and + * flips ... that is transformations that take the axes to the + * axes. If preserve_axes is true, then swap_axes/swap_x/swap_y + * encode the 8 possibilities for orientation (4 rotation angles with + * and without a flip), and scale_x, scale_y the scale components. + */ + cairo_bool_t preserve_axes; + cairo_bool_t swap_axes; + cairo_bool_t swap_x; + cairo_bool_t swap_y; + double x_scale; + double y_scale; + + /* The size of the design unit of the font + */ + int em_square; + + HFONT scaled_font; + HFONT unscaled_font; + +} cairo_win32_font_t; + +#define NEARLY_ZERO(d) (fabs(d) < (1. / 65536.)) + +static void +_compute_transform (cairo_win32_font_t *font, + cairo_font_scale_t *sc) +{ + if (NEARLY_ZERO (sc->matrix[0][1]) && NEARLY_ZERO (sc->matrix[1][0])) { + font->preserve_axes = TRUE; + font->x_scale = sc->matrix[0][0]; + font->swap_x = (sc->matrix[0][0] < 0); + font->y_scale = sc->matrix[1][1]; + font->swap_y = (sc->matrix[1][1] < 0); + font->swap_axes = FALSE; + + } else if (NEARLY_ZERO (sc->matrix[0][0]) && NEARLY_ZERO (sc->matrix[1][1])) { + font->preserve_axes = TRUE; + font->x_scale = sc->matrix[0][1]; + font->swap_x = (sc->matrix[0][1] < 0); + font->y_scale = sc->matrix[1][0]; + font->swap_y = (sc->matrix[1][0] < 0); + font->swap_axes = TRUE; + + } else { + font->preserve_axes = FALSE; + font->swap_x = font->swap_y = font->swap_axes = FALSE; + } + + if (font->preserve_axes) { + if (font->swap_x) + font->x_scale = - font->x_scale; + if (font->swap_y) + font->y_scale = - font->y_scale; + + font->logical_scale = LOGICAL_SCALE * font->y_scale; + font->logical_size = LOGICAL_SCALE * floor (font->y_scale + 0.5); + } + + /* The font matrix has x and y "scale" components which we extract and + * use as character scale values. + */ + cairo_matrix_set_affine (&font->logical_to_device, + sc->matrix[0][0], + sc->matrix[0][1], + sc->matrix[1][0], + sc->matrix[1][1], + 0, 0); + + if (!font->preserve_axes) { + _cairo_matrix_compute_scale_factors (&font->logical_to_device, + &font->x_scale, &font->y_scale, + TRUE); /* XXX: Handle vertical text */ + + font->logical_size = floor (LOGICAL_SCALE * font->y_scale + 0.5); + font->logical_scale = LOGICAL_SCALE * font->y_scale; + } + + cairo_matrix_scale (&font->logical_to_device, + 1.0 / font->logical_scale, 1.0 / font->logical_scale); + + font->device_to_logical = font->logical_to_device; + if (!CAIRO_OK (cairo_matrix_invert (&font->device_to_logical))) + cairo_matrix_set_identity (&font->device_to_logical); +} + +static BYTE +_get_system_quality (void) +{ + BOOL font_smoothing; + + if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { + _cairo_win32_print_gdi_error ("_get_system_quality"); + return FALSE; + } + + if (font_smoothing) { + OSVERSIONINFO version_info; + + version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + + if (!GetVersionEx (&version_info)) { + _cairo_win32_print_gdi_error ("_get_system_quality"); + return FALSE; + } + + if (version_info.dwMajorVersion > 5 || + (version_info.dwMajorVersion == 5 && + version_info.dwMinorVersion >= 1)) { /* XP or newer */ + UINT smoothing_type; + + if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE, + 0, &smoothing_type, 0)) { + _cairo_win32_print_gdi_error ("_get_system_quality"); + return FALSE; + } + + if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) + return CLEARTYPE_QUALITY; + } + + return ANTIALIASED_QUALITY; + } else + return DEFAULT_QUALITY; +} + +static cairo_font_t * +_win32_font_create (LOGFONTW *logfont, + cairo_font_scale_t *scale) +{ + cairo_win32_font_t *f; + + f = malloc (sizeof(cairo_win32_font_t)); + if (f == NULL) + return NULL; + + f->logfont = *logfont; + f->quality = _get_system_quality (); + f->em_square = 0; + f->scaled_font = NULL; + f->unscaled_font = NULL; + + _compute_transform (f, scale); + + _cairo_font_init ((cairo_font_t *)f, scale, &cairo_win32_font_backend); + + return (cairo_font_t *)f; +} + +static cairo_status_t +_win32_font_set_world_transform (cairo_win32_font_t *font, + HDC hdc) +{ + XFORM xform; + + xform.eM11 = font->logical_to_device.m[0][0]; + xform.eM21 = font->logical_to_device.m[1][0]; + xform.eM12 = font->logical_to_device.m[0][1]; + xform.eM22 = font->logical_to_device.m[1][1]; + xform.eDx = font->logical_to_device.m[2][0]; + xform.eDy = font->logical_to_device.m[2][1]; + + if (!SetWorldTransform (hdc, &xform)) + return _cairo_win32_print_gdi_error ("_win32_font_set_world_transform"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_win32_font_set_identity_transform (HDC hdc) +{ + if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY)) + return _cairo_win32_print_gdi_error ("_win32_font_set_identity_transform"); + + return CAIRO_STATUS_SUCCESS; +} + +static HDC +_get_global_font_dc (void) +{ + static HDC hdc; + + if (!hdc) { + hdc = CreateCompatibleDC (NULL); + if (!hdc) { + _cairo_win32_print_gdi_error ("_get_global_font_dc"); + return NULL; + } + + if (!SetGraphicsMode (hdc, GM_ADVANCED)) { + _cairo_win32_print_gdi_error ("_get_global_font_dc"); + DeleteDC (hdc); + return NULL; + } + } + + return hdc; +} + +static HFONT +_win32_font_get_scaled_font (cairo_win32_font_t *font) +{ + if (!font->scaled_font) { + LOGFONTW logfont = font->logfont; + logfont.lfHeight = font->logical_size; + logfont.lfWidth = 0; + logfont.lfEscapement = 0; + logfont.lfOrientation = 0; + logfont.lfQuality = font->quality; + + font->scaled_font = CreateFontIndirectW (&logfont); + if (!font->scaled_font) { + _cairo_win32_print_gdi_error ("_win32_font_get_scaled_font"); + return NULL; + } + } + + return font->scaled_font; +} + +static HFONT +_win32_font_get_unscaled_font (cairo_win32_font_t *font, + HDC hdc) +{ + if (!font->unscaled_font) { + OUTLINETEXTMETRIC *otm; + unsigned int otm_size; + HFONT scaled_font; + LOGFONTW logfont; + + scaled_font = _win32_font_get_scaled_font (font); + if (!scaled_font) + return NULL; + + if (!SelectObject (hdc, scaled_font)) { + _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:SelectObject"); + return NULL; + } + + otm_size = GetOutlineTextMetrics (hdc, 0, NULL); + if (!otm_size) { + _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:GetOutlineTextMetrics"); + return NULL; + } + + otm = malloc (otm_size); + if (!otm) + return NULL; + + if (!GetOutlineTextMetrics (hdc, otm_size, otm)) { + _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:GetOutlineTextMetrics"); + free (otm); + return NULL; + } + + font->em_square = otm->otmEMSquare; + free (otm); + + logfont = font->logfont; + logfont.lfHeight = font->em_square; + logfont.lfWidth = 0; + logfont.lfEscapement = 0; + logfont.lfOrientation = 0; + logfont.lfQuality = font->quality; + + font->unscaled_font = CreateFontIndirectW (&logfont); + if (!font->unscaled_font) { + _cairo_win32_print_gdi_error ("_win32_font_get_unscaled_font:CreateIndirect"); + return NULL; + } + } + + return font->unscaled_font; +} + +static cairo_status_t +_cairo_win32_font_select_unscaled_font (cairo_font_t *font, + HDC hdc) +{ + cairo_status_t status; + HFONT hfont; + HFONT old_hfont = NULL; + + hfont = _win32_font_get_unscaled_font ((cairo_win32_font_t *)font, hdc); + if (!hfont) + return CAIRO_STATUS_NO_MEMORY; + + old_hfont = SelectObject (hdc, hfont); + if (!old_hfont) + return _cairo_win32_print_gdi_error ("_cairo_win32_font_select_unscaled_font"); + + status = _win32_font_set_identity_transform (hdc); + if (!CAIRO_OK (status)) { + SelectObject (hdc, old_hfont); + return status; + } + + SetMapMode (hdc, MM_TEXT); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_font_done_unscaled_font (cairo_font_t *font) +{ +} + +/* implement the font backend interface */ + +static cairo_status_t +_cairo_win32_font_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight, + cairo_font_scale_t *scale, + cairo_font_t **font_out) +{ + LOGFONTW logfont; + cairo_font_t *font; + uint16_t *face_name; + int face_name_len; + cairo_status_t status; + + status = _cairo_utf8_to_utf16 (family, -1, &face_name, &face_name_len); + if (!CAIRO_OK (status)) + return status; + + if (face_name_len > LF_FACESIZE - 1) { + free (face_name); + return CAIRO_STATUS_INVALID_STRING; + } + + memcpy (logfont.lfFaceName, face_name, sizeof (uint16_t) * (face_name_len + 1)); + free (face_name); + + logfont.lfHeight = 0; /* filled in later */ + logfont.lfWidth = 0; /* filled in later */ + logfont.lfEscapement = 0; /* filled in later */ + logfont.lfOrientation = 0; /* filled in later */ + + switch (weight) { + case CAIRO_FONT_WEIGHT_NORMAL: + default: + logfont.lfWeight = FW_NORMAL; + break; + case CAIRO_FONT_WEIGHT_BOLD: + logfont.lfWeight = FW_BOLD; + break; + } + + switch (slant) { + case CAIRO_FONT_SLANT_NORMAL: + default: + logfont.lfItalic = FALSE; + break; + case CAIRO_FONT_SLANT_ITALIC: + case CAIRO_FONT_SLANT_OBLIQUE: + logfont.lfItalic = TRUE; + break; + } + + logfont.lfUnderline = FALSE; + logfont.lfStrikeOut = FALSE; + /* The docs for LOGFONT discourage using this, since the + * interpretation is locale-specific, but it's not clear what + * would be a better alternative. + */ + logfont.lfCharSet = DEFAULT_CHARSET; + logfont.lfOutPrecision = OUT_DEFAULT_PRECIS; + logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; + logfont.lfQuality = DEFAULT_QUALITY; /* filled in later */ + logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + + if (!logfont.lfFaceName) + return CAIRO_STATUS_NO_MEMORY; + + font = _win32_font_create (&logfont, scale); + if (!font) + return CAIRO_STATUS_NO_MEMORY; + + *font_out = font; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_font_destroy_font (void *abstract_font) +{ + cairo_win32_font_t *font = abstract_font; + + if (font->scaled_font) + DeleteObject (font->scaled_font); + + if (font->unscaled_font) + DeleteObject (font->unscaled_font); + + free (font); +} + +static void +_cairo_win32_font_destroy_unscaled_font (void *abstract_font) +{ +} + +static void +_cairo_win32_font_get_glyph_cache_key (void *abstract_font, + cairo_glyph_cache_key_t *key) +{ +} + +static cairo_status_t +_cairo_win32_font_text_to_glyphs (void *abstract_font, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) +{ + cairo_win32_font_t *font = abstract_font; + uint16_t *utf16; + int n16; + GCP_RESULTSW gcp_results; + unsigned int buffer_size, i; + WCHAR *glyph_indices = NULL; + int *dx = NULL; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + double x_pos; + HDC hdc = NULL; + + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); + if (!CAIRO_OK (status)) + return status; + + gcp_results.lStructSize = sizeof (GCP_RESULTS); + gcp_results.lpOutString = NULL; + gcp_results.lpOrder = NULL; + gcp_results.lpCaretPos = NULL; + gcp_results.lpClass = NULL; + + buffer_size = MAX (n16 * 1.2, 16); /* Initially guess number of chars plus a few */ + if (buffer_size > INT_MAX) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL1; + } + + hdc = _get_global_font_dc (); + if (!hdc) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL1; + } + + status = cairo_win32_font_select_font (&font->base, hdc); + if (!CAIRO_OK (status)) + goto FAIL1; + + while (TRUE) { + if (glyph_indices) { + free (glyph_indices); + glyph_indices = NULL; + } + if (dx) { + free (dx); + dx = NULL; + } + + glyph_indices = malloc (sizeof (WCHAR) * buffer_size); + dx = malloc (sizeof (int) * buffer_size); + if (!glyph_indices || !dx) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL2; + } + + gcp_results.nGlyphs = buffer_size; + gcp_results.lpDx = dx; + gcp_results.lpGlyphs = glyph_indices; + + if (!GetCharacterPlacementW (hdc, utf16, n16, + 0, + &gcp_results, + GCP_DIACRITIC | GCP_LIGATE | GCP_GLYPHSHAPE | GCP_REORDER)) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_font_text_to_glyphs"); + goto FAIL2; + } + + if (gcp_results.lpDx && gcp_results.lpGlyphs) + break; + + /* Too small a buffer, try again */ + + buffer_size *= 1.5; + if (buffer_size > INT_MAX) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL2; + } + } + + *num_glyphs = gcp_results.nGlyphs; + *glyphs = malloc (sizeof (cairo_glyph_t) * gcp_results.nGlyphs); + if (!*glyphs) { + status = CAIRO_STATUS_NO_MEMORY; + goto FAIL2; + } + + x_pos = 0; + for (i = 0; i < gcp_results.nGlyphs; i++) { + (*glyphs)[i].index = glyph_indices[i]; + (*glyphs)[i].x = x_pos ; + (*glyphs)[i].y = 0; + + x_pos += dx[i] / font->logical_scale; + } + + FAIL2: + if (glyph_indices) + free (glyph_indices); + if (dx) + free (dx); + + cairo_win32_font_done_font (&font->base); + + FAIL1: + free (utf16); + + return status; +} + +static cairo_status_t +_cairo_win32_font_font_extents (void *abstract_font, + cairo_font_extents_t *extents) +{ + cairo_win32_font_t *font = abstract_font; + cairo_status_t status; + TEXTMETRIC metrics; + HDC hdc; + + hdc = _get_global_font_dc (); + if (!hdc) + return CAIRO_STATUS_NO_MEMORY; + + if (font->preserve_axes) { + /* For 90-degree rotations (including 0), we get the metrics + * from the GDI in logical space, then convert back to font space + */ + status = cairo_win32_font_select_font (&font->base, hdc); + if (!CAIRO_OK (status)) + return status; + GetTextMetrics (hdc, &metrics); + cairo_win32_font_done_font (&font->base); + + extents->ascent = metrics.tmAscent / font->logical_scale; + extents->descent = metrics.tmDescent / font->logical_scale; + + extents->height = (metrics.tmHeight + metrics.tmExternalLeading) / font->logical_scale; + extents->max_x_advance = metrics.tmMaxCharWidth / font->logical_scale; + extents->max_y_advance = 0; + + } else { + /* For all other transformations, we use the design metrics + * of the font. The GDI results from GetTextMetrics() on a + * transformed font are inexplicably large and we want to + * avoid them. + */ + status = _cairo_win32_font_select_unscaled_font (&font->base, hdc); + if (!CAIRO_OK (status)) + return status; + GetTextMetrics (hdc, &metrics); + _cairo_win32_font_done_unscaled_font (&font->base); + + extents->ascent = (double)metrics.tmAscent / font->em_square; + extents->descent = metrics.tmDescent * font->em_square; + extents->height = (double)(metrics.tmHeight + metrics.tmExternalLeading) / font->em_square; + extents->max_x_advance = (double)(metrics.tmMaxCharWidth) / font->em_square; + extents->max_y_advance = 0; + + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_font_glyph_extents (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + cairo_win32_font_t *font = abstract_font; + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + GLYPHMETRICS metrics; + cairo_status_t status; + HDC hdc; + + hdc = _get_global_font_dc (); + if (!hdc) + return CAIRO_STATUS_NO_MEMORY; + + /* We handle only the case num_glyphs == 1, glyphs[i].x == glyphs[0].y == 0. + * This is all that the calling code triggers, and the backend interface + * will eventually be changed to match + */ + assert (num_glyphs == 1); + + if (font->preserve_axes) { + /* If we aren't rotating / skewing the axes, then we get the metrics + * from the GDI in device space and convert to font space. + */ + status = cairo_win32_font_select_font (&font->base, hdc); + if (!CAIRO_OK (status)) + return status; + GetGlyphOutlineW (hdc, glyphs[0].index, GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + cairo_win32_font_done_font (&font->base); + + if (font->swap_axes) { + extents->x_bearing = - metrics.gmptGlyphOrigin.y / font->y_scale; + extents->y_bearing = metrics.gmptGlyphOrigin.x / font->x_scale; + extents->width = metrics.gmBlackBoxY / font->y_scale; + extents->height = metrics.gmBlackBoxX / font->x_scale; + extents->x_advance = metrics.gmCellIncY / font->x_scale; + extents->y_advance = metrics.gmCellIncX / font->y_scale; + } else { + extents->x_bearing = metrics.gmptGlyphOrigin.x / font->x_scale; + extents->y_bearing = - metrics.gmptGlyphOrigin.y / font->y_scale; + extents->width = metrics.gmBlackBoxX / font->x_scale; + extents->height = metrics.gmBlackBoxY / font->y_scale; + extents->x_advance = metrics.gmCellIncX / font->x_scale; + extents->y_advance = metrics.gmCellIncY / font->y_scale; + } + + if (font->swap_x) { + extents->x_bearing = (- extents->x_bearing - extents->width); + extents->x_advance = - extents->x_advance; + } + + if (font->swap_y) { + extents->y_bearing = (- extents->y_bearing - extents->height); + extents->y_advance = - extents->y_advance; + } + + } else { + /* For all other transformations, we use the design metrics + * of the font. + */ + status = _cairo_win32_font_select_unscaled_font (&font->base, hdc); + GetGlyphOutlineW (hdc, glyphs[0].index, GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + _cairo_win32_font_done_unscaled_font (&font->base); + + extents->x_bearing = (double)metrics.gmptGlyphOrigin.x / font->em_square; + extents->y_bearing = (double)metrics.gmptGlyphOrigin.y / font->em_square; + extents->width = (double)metrics.gmBlackBoxX / font->em_square; + extents->height = (double)metrics.gmBlackBoxY / font->em_square; + extents->x_advance = (double)metrics.gmCellIncX / font->em_square; + extents->y_advance = (double)metrics.gmCellIncY / font->em_square; + } + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +_cairo_win32_font_glyph_bbox (void *abstract_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) +{ + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + cairo_win32_font_t *font = abstract_font; + int x1 = 0, x2 = 0, y1 = 0, y2 = 0; + + if (num_glyphs > 0) { + HDC hdc = _get_global_font_dc (); + GLYPHMETRICS metrics; + cairo_status_t status; + int i; + + if (!hdc) + return CAIRO_STATUS_NO_MEMORY; + + status = cairo_win32_font_select_font (&font->base, hdc); + if (!CAIRO_OK (status)) + return status; + + for (i = 0; i < num_glyphs; i++) { + int x = floor (0.5 + glyphs[i].x); + int y = floor (0.5 + glyphs[i].y); + + GetGlyphOutlineW (hdc, glyphs[i].index, GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + + if (i == 0 || x1 > x + metrics.gmptGlyphOrigin.x) + x1 = x + metrics.gmptGlyphOrigin.x; + if (i == 0 || y1 > y - metrics.gmptGlyphOrigin.y) + y1 = y - metrics.gmptGlyphOrigin.y; + if (i == 0 || x2 < x + metrics.gmptGlyphOrigin.x + metrics.gmBlackBoxX) + x2 = x + metrics.gmptGlyphOrigin.x + metrics.gmBlackBoxX; + if (i == 0 || y2 < y - metrics.gmptGlyphOrigin.y + metrics.gmBlackBoxY) + y2 = y - metrics.gmptGlyphOrigin.y + metrics.gmBlackBoxY; + } + + cairo_win32_font_done_font (&font->base); + } + + bbox->p1.x = _cairo_fixed_from_int (x1); + bbox->p1.y = _cairo_fixed_from_int (y1); + bbox->p2.x = _cairo_fixed_from_int (x2); + bbox->p2.y = _cairo_fixed_from_int (y2); + + return CAIRO_STATUS_SUCCESS; +} + +typedef struct { + cairo_win32_font_t *font; + HDC hdc; + + cairo_array_t glyphs; + cairo_array_t dx; + + int start_x; + int last_x; + int last_y; +} cairo_glyph_state_t; + +static void +_start_glyphs (cairo_glyph_state_t *state, + cairo_win32_font_t *font, + HDC hdc) +{ + state->hdc = hdc; + state->font = font; + + _cairo_array_init (&state->glyphs, sizeof (WCHAR)); + _cairo_array_init (&state->dx, sizeof (int)); +} + +static cairo_status_t +_flush_glyphs (cairo_glyph_state_t *state) +{ + int dx = 0; + if (!_cairo_array_append (&state->dx, &dx, 1)) + return CAIRO_STATUS_NO_MEMORY; + + if (!ExtTextOutW (state->hdc, + state->start_x, state->last_y, + ETO_GLYPH_INDEX, + NULL, + (WCHAR *)state->glyphs.elements, + state->glyphs.num_elements, + (int *)state->dx.elements)) { + return _cairo_win32_print_gdi_error ("_flush_glyphs"); + } + + _cairo_array_truncate (&state->glyphs, 0); + _cairo_array_truncate (&state->dx, 0); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_add_glyph (cairo_glyph_state_t *state, + unsigned long index, + double device_x, + double device_y) +{ + double user_x = device_x; + double user_y = device_y; + WCHAR glyph_index = index; + int logical_x, logical_y; + + cairo_matrix_transform_point (&state->font->device_to_logical, &user_x, &user_y); + + logical_x = floor (user_x + 0.5); + logical_y = floor (user_y + 0.5); + + if (state->glyphs.num_elements > 0) { + int dx; + + if (logical_y != state->last_y) { + cairo_status_t status = _flush_glyphs (state); + if (!CAIRO_OK (status)) + return status; + state->start_x = logical_x; + } + + dx = logical_x - state->last_x; + if (!_cairo_array_append (&state->dx, &dx, 1)) + return CAIRO_STATUS_NO_MEMORY; + } else { + state->start_x = logical_x; + } + + state->last_x = logical_x; + state->last_y = logical_y; + + _cairo_array_append (&state->glyphs, &glyph_index, 1); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_finish_glyphs (cairo_glyph_state_t *state) +{ + _flush_glyphs (state); + + _cairo_array_fini (&state->glyphs); + _cairo_array_fini (&state->dx); +} + +static cairo_status_t +_draw_glyphs_on_surface (cairo_win32_surface_t *surface, + cairo_win32_font_t *font, + COLORREF color, + int x_offset, + int y_offset, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_glyph_state_t state; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int i; + + if (!SaveDC (surface->dc)) + return _cairo_win32_print_gdi_error ("_draw_glyphs_on_surface:SaveDC"); + + status = cairo_win32_font_select_font (&font->base, surface->dc); + if (!CAIRO_OK (status)) + goto FAIL1; + + SetTextColor (surface->dc, color); + SetTextAlign (surface->dc, TA_BASELINE | TA_LEFT); + SetBkMode (surface->dc, TRANSPARENT); + + _start_glyphs (&state, font, surface->dc); + + for (i = 0; i < num_glyphs; i++) { + status = _add_glyph (&state, glyphs[i].index, + glyphs[i].x - x_offset, glyphs[i].y - y_offset); + if (!CAIRO_OK (status)) + goto FAIL2; + } + + FAIL2: + _finish_glyphs (&state); + cairo_win32_font_done_font (&font->base); + FAIL1: + RestoreDC (surface->dc, 1); + + return status; +} + +/* Duplicate the green channel of a 4-channel mask in the alpha channel, then + * invert the whole mask. + */ +static void +_compute_argb32_mask_alpha (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; + int i, j; + + for (i = 0; i < image->height; i++) { + uint32_t *p = (uint32_t *) (image->data + i * image->stride); + for (j = 0; j < image->width; j++) { + *p = 0xffffffff ^ (*p | ((*p & 0x0000ff00) << 16)); + p++; + } + } +} + +/* Invert a mask + */ +static void +_invert_argb32_mask (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; + int i, j; + + for (i = 0; i < image->height; i++) { + uint32_t *p = (uint32_t *) (image->data + i * image->stride); + for (j = 0; j < image->width; j++) { + *p = 0xffffffff ^ *p; + p++; + } + } +} + +/* Compute an alpha-mask from a monochrome RGB24 image + */ +static cairo_surface_t * +_compute_a8_mask (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image; + cairo_image_surface_t *image8; + int i, j; + + image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8, + image24->width, image24->height); + if (!image8) + return NULL; + + for (i = 0; i < image24->height; i++) { + uint32_t *p = (uint32_t *) (image24->data + i * image24->stride); + unsigned char *q = (unsigned char *) (image8->data + i * image8->stride); + + for (j = 0; j < image24->width; j++) { + *q = 255 - ((*p & 0x0000ff00) >> 8); + p++; + q++; + } + } + + return &image8->base; +} + +static cairo_status_t +_cairo_win32_font_show_glyphs (void *abstract_font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_surface_t *generic_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_win32_font_t *font = abstract_font; + cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; + cairo_status_t status; + + if (width == 0 || height == 0) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_surface_is_win32 (generic_surface) && + surface->format == CAIRO_FORMAT_RGB24 && + operator == CAIRO_OPERATOR_OVER && + pattern->type == CAIRO_PATTERN_SOLID && + _cairo_pattern_is_opaque (pattern)) { + + cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern; + + /* When compositing OVER on a GDI-understood surface, with a + * solid opaque color, we can just call ExtTextOut directly. + */ + COLORREF new_color; + + new_color = RGB (((int)(0xffff * solid_pattern->red)) >> 8, + ((int)(0xffff * solid_pattern->green)) >> 8, + ((int)(0xffff * solid_pattern->blue)) >> 8); + + status = _draw_glyphs_on_surface (surface, font, new_color, + 0, 0, + glyphs, num_glyphs); + + return status; + } else { + /* Otherwise, we need to draw using software fallbacks. We create a mask + * surface by drawing the the glyphs onto a DIB, black-on-white then + * inverting. GDI outputs gamma-corrected images so inverted black-on-white + * is very different from white-on-black. We favor the more common + * case where the final output is dark-on-light. + */ + cairo_win32_surface_t *tmp_surface; + cairo_surface_t *mask_surface; + cairo_surface_pattern_t mask; + RECT r; + + tmp_surface = (cairo_win32_surface_t *)_cairo_win32_surface_create_dib (CAIRO_FORMAT_ARGB32, width, height); + if (!tmp_surface) + return CAIRO_STATUS_NO_MEMORY; + + r.left = 0; + r.top = 0; + r.right = width; + r.bottom = height; + FillRect (tmp_surface->dc, &r, GetStockObject (WHITE_BRUSH)); + + _draw_glyphs_on_surface (tmp_surface, font, RGB (0, 0, 0), + dest_x, dest_y, + glyphs, num_glyphs); + + if (font->quality == CLEARTYPE_QUALITY) { + /* For ClearType, we need a 4-channel mask. If we are compositing on + * a surface with alpha, we need to compute the alpha channel of + * the mask (we just copy the green channel). But for a destination + * surface without alpha the alpha channel of the mask is ignored + */ + + if (surface->format != CAIRO_FORMAT_RGB24) + _compute_argb32_mask_alpha (tmp_surface); + else + _invert_argb32_mask (tmp_surface); + + mask_surface = &tmp_surface->base; + + /* XXX: Hacky, should expose this in cairo_image_surface */ + pixman_image_set_component_alpha (((cairo_image_surface_t *)tmp_surface->image)->pixman_image, TRUE); + + } else { + mask_surface = _compute_a8_mask (tmp_surface); + cairo_surface_destroy (&tmp_surface->base); + if (!mask_surface) + return CAIRO_STATUS_NO_MEMORY; + } + + /* For operator == OVER, no-cleartype, a possible optimization here is to + * draw onto an intermediate ARGB32 surface and alpha-blend that with the + * destination + */ + _cairo_pattern_init_for_surface (&mask, mask_surface); + + status = _cairo_surface_composite (operator, pattern, + &mask.base, + &surface->base, + source_x, source_y, + 0, 0, + dest_x, dest_y, + width, height); + + _cairo_pattern_fini (&mask.base); + + cairo_surface_destroy (mask_surface); + + return status; + } +} + +static cairo_status_t +_cairo_win32_font_glyph_path (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path) +{ + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_font_create_glyph (cairo_image_glyph_cache_entry_t *val) +{ + return CAIRO_STATUS_NO_MEMORY; +} + +const cairo_font_backend_t cairo_win32_font_backend = { + _cairo_win32_font_create, + _cairo_win32_font_destroy_font, + _cairo_win32_font_destroy_unscaled_font, + _cairo_win32_font_font_extents, + _cairo_win32_font_text_to_glyphs, + _cairo_win32_font_glyph_extents, + _cairo_win32_font_glyph_bbox, + _cairo_win32_font_show_glyphs, + _cairo_win32_font_glyph_path, + _cairo_win32_font_get_glyph_cache_key, + _cairo_win32_font_create_glyph +}; + +/* implement the platform-specific interface */ + +/** + * cairo_win32_font_create_for_logfontw: + * @logfont: A #LOGFONTW structure specifying the font to use. + * The lfHeight, lfWidth, lfOrientation and lfEscapement + * fields of this structure are ignored; information from + * @scale will be used instead. + * @scale: The scale at which this font will be used. The + * scale is given by multiplying the font matrix (see + * cairo_transform_font()) by the current transformation matrix. + * The translation elements of the resulting matrix are ignored. + * + * Creates a new font for the Win32 font backend based on a + * #LOGFONT. This font can then be used with + * cairo_set_font(), cairo_font_glyph_extents(), or FreeType backend + * specific functions like cairo_win32_font_select_font(). + * + * Return value: a newly created #cairo_font_t. Free with + * cairo_font_destroy() when you are done using it. + **/ +cairo_font_t * +cairo_win32_font_create_for_logfontw (LOGFONTW *logfont, + cairo_matrix_t *scale) +{ + cairo_font_scale_t sc; + + cairo_matrix_get_affine (scale, + &sc.matrix[0][0], &sc.matrix[0][1], + &sc.matrix[1][0], &sc.matrix[1][1], + NULL, NULL); + + return _win32_font_create (logfont, &sc); +} + +/** + * cairo_win32_font_select_font: + * @font: A #cairo_font_t from the Win32 font backend. Such an + * object can be created with cairo_win32_font_create_for_logfontw(). + * @hdc: a device context + * + * Selects the font into the given device context and changes the + * map mode and world transformation of the device context to match + * that of the font. This function is intended for use when using + * layout APIs such as Uniscribe to do text layout with the + * Cairo font. After finishing using the device context, you must call + * cairo_win32_font_done_font() to release any resources allocated + * by this function. + * + * See cairo_win32_font_get_scale_factor() for converting logical + * coordinates from the device context to font space. + * + * Normally, calls to SaveDC() and RestoreDC() would be made around + * the use of this function to preserve the original graphics state. + * + * Return value: %CAIRO_STATUS_SUCCESS if the operation succeeded. + * otherwise an error such as %CAIRO_STATUS_NO_MEMORY and + * the device context is unchanged. + **/ +cairo_status_t +cairo_win32_font_select_font (cairo_font_t *font, + HDC hdc) +{ + cairo_status_t status; + HFONT hfont; + HFONT old_hfont = NULL; + int old_mode; + + hfont = _win32_font_get_scaled_font ((cairo_win32_font_t *)font); + if (!hfont) + return CAIRO_STATUS_NO_MEMORY; + + old_hfont = SelectObject (hdc, hfont); + if (!old_hfont) + return _cairo_win32_print_gdi_error ("cairo_win32_font_select_font"); + + old_mode = SetGraphicsMode (hdc, GM_ADVANCED); + if (!old_mode) { + status = _cairo_win32_print_gdi_error ("cairo_win32_font_select_font"); + SelectObject (hdc, old_hfont); + return status; + } + + status = _win32_font_set_world_transform ((cairo_win32_font_t *)font, hdc); + if (!CAIRO_OK (status)) { + SetGraphicsMode (hdc, old_mode); + SelectObject (hdc, old_hfont); + return status; + } + + SetMapMode (hdc, MM_TEXT); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_win32_font_done_font: + * @font: A #cairo_font_t from the Win32 font backend. + * + * Releases any resources allocated by cairo_win32_font_select_font() + **/ +void +cairo_win32_font_done_font (cairo_font_t *font) +{ +} + +/** + * cairo_win32_font_get_scale_factor: + * @font: a #cairo_font_t from the Win32 font backend + * + * Gets a scale factor between logical coordinates in the coordinate + * space used by cairo_win32_font_select_font() and font space coordinates. + * + * Return value: factor to multiply logical units by to get font space + * coordinates. + **/ +double +cairo_win32_font_get_scale_factor (cairo_font_t *font) +{ + return 1. / ((cairo_win32_font_t *)font)->logical_scale; +} diff --git a/src/cairo_win32_surface.c b/src/cairo_win32_surface.c new file mode 100644 index 000000000..dcfe6d044 --- /dev/null +++ b/src/cairo_win32_surface.c @@ -0,0 +1,931 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * + * 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 Red Hat, Inc. + * + * Contributor(s): + * Owen Taylor <otaylor@redhat.com> + */ + +#include <stdio.h> + +#include "cairo-win32-private.h" + +static const cairo_surface_backend_t cairo_win32_surface_backend; + +/** + * _cairo_win32_print_gdi_error: + * @context: context string to display along with the error + * + * Helper function to dump out a human readable form of the + * current error code. + * + * Return value: A Cairo status code for the error code + **/ +cairo_status_t +_cairo_win32_print_gdi_error (const char *context) +{ + void *lpMsgBuf; + DWORD last_error = GetLastError (); + + if (!FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + last_error, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL)) { + fprintf (stderr, "%s: Unknown GDI error", context); + } else { + fprintf (stderr, "%s: %s", context, (char *)lpMsgBuf); + + LocalFree (lpMsgBuf); + } + + /* We should switch off of last_status, but we'd either return + * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there + * is no CAIRO_STATUS_UNKNOWN_ERROR. + */ + + return CAIRO_STATUS_NO_MEMORY; +} + +void +cairo_set_target_win32 (cairo_t *cr, + HDC hdc) +{ + cairo_surface_t *surface; + + if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) + return; + + surface = cairo_win32_surface_create (hdc); + if (surface == NULL) { + cr->status = CAIRO_STATUS_NO_MEMORY; + return; + } + + cairo_set_target_surface (cr, surface); + + /* cairo_set_target_surface takes a reference, so we must destroy ours */ + cairo_surface_destroy (surface); +} + +static cairo_status_t +_create_dc_and_bitmap (cairo_win32_surface_t *surface, + HDC original_dc, + cairo_format_t format, + int width, + int height, + char **bits_out, + int *rowstride_out) +{ + cairo_status_t status; + + BITMAPINFO *bitmap_info = NULL; + struct { + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[2]; + } bmi_stack; + void *bits; + + int num_palette = 0; /* Quiet GCC */ + int i; + + surface->dc = NULL; + surface->bitmap = NULL; + + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + num_palette = 0; + break; + + case CAIRO_FORMAT_A8: + num_palette = 256; + break; + + case CAIRO_FORMAT_A1: + num_palette = 2; + break; + } + + if (num_palette > 2) { + bitmap_info = malloc (sizeof (BITMAPINFOHEADER) + num_palette * sizeof (RGBQUAD)); + if (!bitmap_info) + return CAIRO_STATUS_NO_MEMORY; + } else { + bitmap_info = (BITMAPINFO *)&bmi_stack; + } + + bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + bitmap_info->bmiHeader.biWidth = width; + bitmap_info->bmiHeader.biHeight = - height; /* top-down */ + bitmap_info->bmiHeader.biSizeImage = 0; + bitmap_info->bmiHeader.biXPelsPerMeter = 72. / 0.0254; /* unused here */ + bitmap_info->bmiHeader.biYPelsPerMeter = 72. / 0.0254; /* unused here */ + bitmap_info->bmiHeader.biPlanes = 1; + + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + bitmap_info->bmiHeader.biBitCount = 32; + bitmap_info->bmiHeader.biCompression = BI_RGB; + bitmap_info->bmiHeader.biClrUsed = 0; /* unused */ + bitmap_info->bmiHeader.biClrImportant = 0; + break; + + case CAIRO_FORMAT_A8: + bitmap_info->bmiHeader.biBitCount = 8; + bitmap_info->bmiHeader.biCompression = BI_RGB; + bitmap_info->bmiHeader.biClrUsed = 256; + bitmap_info->bmiHeader.biClrImportant = 0; + + for (i = 0; i < 256; i++) { + bitmap_info->bmiColors[i].rgbBlue = i; + bitmap_info->bmiColors[i].rgbGreen = i; + bitmap_info->bmiColors[i].rgbRed = i; + bitmap_info->bmiColors[i].rgbReserved = 0; + } + + break; + + case CAIRO_FORMAT_A1: + bitmap_info->bmiHeader.biBitCount = 1; + bitmap_info->bmiHeader.biCompression = BI_RGB; + bitmap_info->bmiHeader.biClrUsed = 2; + bitmap_info->bmiHeader.biClrImportant = 0; + + for (i = 0; i < 2; i++) { + bitmap_info->bmiColors[i].rgbBlue = i * 255; + bitmap_info->bmiColors[i].rgbGreen = i * 255; + bitmap_info->bmiColors[i].rgbRed = i * 255; + bitmap_info->bmiColors[i].rgbReserved = 0; + break; + } + } + + surface->dc = CreateCompatibleDC (original_dc); + if (!surface->dc) + goto FAIL; + + surface->bitmap = CreateDIBSection (surface->dc, + bitmap_info, + DIB_RGB_COLORS, + &bits, + NULL, 0); + if (!surface->bitmap) + goto FAIL; + + surface->saved_dc_bitmap = SelectObject (surface->dc, + surface->bitmap); + if (!surface->saved_dc_bitmap) + goto FAIL; + + if (bitmap_info && num_palette > 2) + free (bitmap_info); + + if (bits_out) + *bits_out = bits; + + if (rowstride_out) { + /* Windows bitmaps are padded to 16-bit (word) boundaries */ + switch (format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + *rowstride_out = 4 * width; + break; + + case CAIRO_FORMAT_A8: + *rowstride_out = (width + 1) & -2; + break; + + case CAIRO_FORMAT_A1: + *rowstride_out = ((width + 15) & -16) / 8; + break; + } + } + + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error ("_create_dc_and_bitmap"); + + if (bitmap_info && num_palette > 2) + free (bitmap_info); + + if (surface->saved_dc_bitmap) { + SelectObject (surface->dc, surface->saved_dc_bitmap); + surface->saved_dc_bitmap = NULL; + } + + if (surface->bitmap) { + DeleteObject (surface->bitmap); + surface->bitmap = NULL; + } + + if (surface->dc) { + DeleteDC (surface->dc); + surface->dc = NULL; + } + + return status; +} + +static cairo_surface_t * +_cairo_win32_surface_create_for_dc (HDC original_dc, + cairo_format_t format, + int drawable, + int width, + int height) +{ + cairo_win32_surface_t *surface; + char *bits; + int rowstride; + + surface = malloc (sizeof (cairo_win32_surface_t)); + if (!surface) + return NULL; + + if (_create_dc_and_bitmap (surface, original_dc, format, + width, height, + &bits, &rowstride) != CAIRO_STATUS_SUCCESS) + goto FAIL; + + surface->image = cairo_image_surface_create_for_data (bits, format, + width, height, rowstride); + if (!surface->image) + goto FAIL; + + surface->format = format; + + surface->clip_rect.x = 0; + surface->clip_rect.y = 0; + surface->clip_rect.width = width; + surface->clip_rect.height = height; + + surface->set_clip = 0; + surface->saved_clip = NULL; + + _cairo_surface_init (&surface->base, &cairo_win32_surface_backend); + + return (cairo_surface_t *)surface; + + FAIL: + if (surface->bitmap) { + SelectObject (surface->dc, surface->saved_dc_bitmap); + DeleteObject (surface->bitmap); + DeleteDC (surface->dc); + } + if (surface) + free (surface); + + return NULL; + +} + +static cairo_surface_t * +_cairo_win32_surface_create_similar (void *abstract_src, + cairo_format_t format, + int drawable, + int width, + int height) +{ + cairo_win32_surface_t *src = abstract_src; + + return _cairo_win32_surface_create_for_dc (src->dc, format, drawable, + width, height); +} + +/** + * _cairo_win32_surface_create_dib: + * @format: format of pixels in the surface to create + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates a device-independent-bitmap surface not associated with + * any particular existing surface or device context. The created + * bitmap will be unititialized. + * + * Return value: the newly created surface, or %NULL if it couldn't + * be created (probably because of lack of memory) + **/ +cairo_surface_t * +_cairo_win32_surface_create_dib (cairo_format_t format, + int width, + int height) +{ + return _cairo_win32_surface_create_for_dc (NULL, format, TRUE, + width, height); +} + +static void +_cairo_win32_surface_destroy (void *abstract_surface) +{ + cairo_win32_surface_t *surface = abstract_surface; + + if (surface->image) + cairo_surface_destroy (surface->image); + + if (surface->saved_clip) + DeleteObject (surface->saved_clip); + + /* If we created the Bitmap and DC, destroy them */ + if (surface->bitmap) { + SelectObject (surface->dc, surface->saved_dc_bitmap); + DeleteObject (surface->bitmap); + DeleteDC (surface->dc); + } + + free (surface); +} + +static double +_cairo_win32_surface_pixels_per_inch (void *abstract_surface) +{ + /* XXX: We should really get this value from somewhere */ + return 96.0; +} + +static cairo_status_t +_cairo_win32_surface_get_subimage (cairo_win32_surface_t *surface, + int x, + int y, + int width, + int height, + cairo_win32_surface_t **local_out) +{ + cairo_win32_surface_t *local; + cairo_status_t status; + + local = + (cairo_win32_surface_t *) _cairo_win32_surface_create_similar (surface, + surface->format, + 0, + width, height); + if (!local) + return CAIRO_STATUS_NO_MEMORY; + + if (!BitBlt (local->dc, + 0, 0, + width, height, + surface->dc, + x, y, + SRCCOPY)) + goto FAIL; + + *local_out = local; + + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_get_subimage"); + + if (local) + cairo_surface_destroy (&local->base); + + return status; +} + +static cairo_status_t +_cairo_win32_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_surface_t *local = NULL; + cairo_status_t status; + + if (surface->image) { + *image_out = (cairo_image_surface_t *)surface->image; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0, + surface->clip_rect.width, + surface->clip_rect.height, &local); + if (CAIRO_OK (status)) { + cairo_surface_set_filter (&local->base, surface->base.filter); + cairo_surface_set_matrix (&local->base, &surface->base.matrix); + cairo_surface_set_repeat (&local->base, surface->base.repeat); + + *image_out = (cairo_image_surface_t *)local->image; + *image_extra = local; + } + + return status; +} + +static void +_cairo_win32_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_win32_surface_t *local = image_extra; + + if (local) + cairo_surface_destroy ((cairo_surface_t *)local); +} + +static cairo_status_t +_cairo_win32_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect, + void **image_extra) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_surface_t *local = NULL; + cairo_status_t status; + RECT clip_box; + int x1, y1, x2, y2; + + if (surface->image) { + image_rect->x = 0; + image_rect->y = 0; + image_rect->width = surface->clip_rect.width; + image_rect->height = surface->clip_rect.height; + + *image_out = (cairo_image_surface_t *)surface->image; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; + } + + if (GetClipBox (surface->dc, &clip_box) == ERROR) + return _cairo_win32_print_gdi_error ("_cairo_win3_surface_acquire_dest_image"); + + x1 = clip_box.left; + x2 = clip_box.right; + y1 = clip_box.top; + y2 = clip_box.bottom; + + if (interest_rect->x > x1) + x1 = interest_rect->x; + if (interest_rect->y > y1) + y1 = interest_rect->y; + if (interest_rect->x + interest_rect->width < x2) + x2 = interest_rect->x + interest_rect->width; + if (interest_rect->y + interest_rect->height < y2) + y2 = interest_rect->y + interest_rect->height; + + if (x1 >= x2 || y1 >= y2) { + *image_out = NULL; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_win32_surface_get_subimage (abstract_surface, + x1, y1, x2 - x1, y2 - y1, + &local); + if (CAIRO_OK (status)) { + *image_out = (cairo_image_surface_t *)local->image; + *image_extra = local; + + image_rect->x = x1; + image_rect->y = y1; + image_rect->width = x2 - x1; + image_rect->height = y2 - y1; + } + + return status; +} + +static void +_cairo_win32_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_surface_t *local = image_extra; + + if (!local) + return; + + if (!BitBlt (surface->dc, + image_rect->x, image_rect->y, + image_rect->width, image_rect->height, + local->dc, + 0, 0, + SRCCOPY)) + _cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image"); + + cairo_surface_destroy ((cairo_surface_t *)local); +} + +static cairo_status_t +_cairo_win32_surface_clone_similar (void *surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_composite (cairo_operator_t operator, + cairo_pattern_t *pattern, + cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) +{ + cairo_win32_surface_t *dst = abstract_dst; + cairo_win32_surface_t *src; + cairo_surface_pattern_t *src_surface_pattern; + int alpha; + int integer_transform; + int itx, ity; + + if (pattern->type != CAIRO_PATTERN_SURFACE || + pattern->extend != CAIRO_EXTEND_NONE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (mask_pattern) { + /* FIXME: When we fully support RENDER style 4-channel + * masks we need to check r/g/b != 1.0. + */ + if (mask_pattern->type != CAIRO_PATTERN_SOLID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + alpha = (int)(0xffff * pattern->alpha * mask_pattern->alpha) >> 8; + } else { + alpha = (int)(0xffff * pattern->alpha) >> 8; + } + + src_surface_pattern = (cairo_surface_pattern_t *)pattern; + src = (cairo_win32_surface_t *)src_surface_pattern->surface; + + if (src->base.backend != dst->base.backend) + return CAIRO_INT_STATUS_UNSUPPORTED; + + integer_transform = _cairo_matrix_is_integer_translation (&pattern->matrix, &itx, &ity); + if (!integer_transform) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (alpha == 255 && + src->format == dst->format && + (operator == CAIRO_OPERATOR_SRC || + (src->format == CAIRO_FORMAT_RGB24 && operator == CAIRO_OPERATOR_OVER))) { + + if (!BitBlt (dst->dc, + dst_x, dst_y, + width, height, + src->dc, + src_x + itx, src_y + ity, + SRCCOPY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite"); + + return CAIRO_STATUS_SUCCESS; + + } else if (integer_transform && + (src->format == CAIRO_FORMAT_RGB24 || src->format == CAIRO_FORMAT_ARGB32) && + dst->format == CAIRO_FORMAT_RGB24 && + !src->base.repeat && + operator == CAIRO_OPERATOR_OVER) { + + BLENDFUNCTION blend_function; + + blend_function.BlendOp = AC_SRC_OVER; + blend_function.BlendFlags = 0; + blend_function.SourceConstantAlpha = alpha; + blend_function.AlphaFormat = src->format == CAIRO_FORMAT_ARGB32 ? AC_SRC_ALPHA : 0; + + if (!AlphaBlend (dst->dc, + dst_x, dst_y, + width, height, + src->dc, + src_x + itx, src_y + ity, + width, height, + blend_function)) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite"); + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t operator, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_status_t status; + COLORREF new_color; + HBRUSH new_brush; + int i; + + /* If we have a local image, use the fallback code; it will be as fast + * as calling out to GDI. + */ + if (surface->image) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* We could support possibly support more operators for color->alpha = 0xffff. + * for CAIRO_OPERATOR_SRC, alpha doesn't matter since we know the destination + * image doesn't have alpha. (surface->pixman_image is non-NULL for all + * surfaces with alpha.) + */ + if (operator != CAIRO_OPERATOR_SRC) + return CAIRO_INT_STATUS_UNSUPPORTED; + + new_color = RGB (color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8); + + new_brush = CreateSolidBrush (new_color); + if (!new_brush) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); + + for (i = 0; i < num_rects; i++) { + RECT rect; + + rect.left = rects[i].x; + rect.top = rects[i].y; + rect.right = rects[i].x + rects[i].width; + rect.bottom = rects[i].y + rects[i].height; + + if (!FillRect (surface->dc, &rect, new_brush)) + goto FAIL; + } + + DeleteObject (new_brush); + + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); + + DeleteObject (new_brush); + + return status; +} + +static cairo_int_status_t +_cairo_win32_surface_composite_trapezoids (cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_dst, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps) + +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_copy_page (void *abstract_surface) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_show_page (void *abstract_surface) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_win32_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_status_t status; + + /* If we are in-memory, then we set the clip on the image surface + * as well as on the underlying GDI surface. + */ + if (surface->image) + _cairo_surface_set_clip_region (surface->image, region); + + /* The semantics we want is that any clip set by Cairo combines + * is intersected with the clip on device context that the + * surface was created for. To implement this, we need to + * save the original clip when first setting a clip on surface. + */ + + if (region == NULL) { + /* Clear any clip set by Cairo, return to the original */ + + if (surface->set_clip) { + if (SelectClipRgn (surface->dc, surface->saved_clip) == ERROR) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region"); + + if (surface->saved_clip) { + DeleteObject (surface->saved_clip); + surface->saved_clip = NULL; + } + + surface->set_clip = 0; + } + + + return CAIRO_STATUS_SUCCESS; + + } else { + pixman_box16_t *boxes = pixman_region_rects (region); + int num_boxes = pixman_region_num_rects (region); + pixman_box16_t *extents = pixman_region_extents (region); + RGNDATA *data; + size_t data_size; + RECT *rects; + int i; + HRGN gdi_region; + + /* Create a GDI region for the cairo region */ + + data_size = sizeof (RGNDATAHEADER) + num_boxes * sizeof (RECT); + data = malloc (data_size); + if (!data) + return CAIRO_STATUS_NO_MEMORY; + rects = (RECT *)data->Buffer; + + data->rdh.dwSize = sizeof (RGNDATAHEADER); + data->rdh.iType = RDH_RECTANGLES; + data->rdh.nCount = num_boxes; + data->rdh.nRgnSize = num_boxes * sizeof (RECT); + data->rdh.rcBound.left = extents->x1; + data->rdh.rcBound.top = extents->y1; + data->rdh.rcBound.right = extents->x2; + data->rdh.rcBound.bottom = extents->y2; + + for (i = 0; i < num_boxes; i++) { + rects[i].left = boxes[i].x1; + rects[i].top = boxes[i].y1; + rects[i].right = boxes[i].x2; + rects[i].bottom = boxes[i].y2; + } + + gdi_region = ExtCreateRegion (NULL, data_size, data); + free (data); + + if (!gdi_region) + return CAIRO_STATUS_NO_MEMORY; + + if (surface->set_clip) { + /* Combine the new region with the original clip */ + + if (surface->saved_clip) { + if (CombineRgn (gdi_region, gdi_region, surface->saved_clip, RGN_AND) == ERROR) + goto FAIL; + } + + if (SelectClipRgn (surface->dc, gdi_region) == ERROR) + goto FAIL; + + } else { + /* Save the the current region */ + + surface->saved_clip = CreateRectRgn (0, 0, 0, 0); + if (!surface->saved_clip) { + goto FAIL; } + + /* This function has no error return! */ + if (GetClipRgn (surface->dc, surface->saved_clip) == 0) { /* No clip */ + DeleteObject (surface->saved_clip); + surface->saved_clip = NULL; + } + + if (ExtSelectClipRgn (surface->dc, gdi_region, RGN_AND) == ERROR) + goto FAIL; + + surface->set_clip = 1; + } + + DeleteObject (gdi_region); + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region"); + DeleteObject (gdi_region); + return status; + } +} + +static cairo_status_t +_cairo_win32_surface_show_glyphs (cairo_font_t *font, + cairo_operator_t operator, + cairo_pattern_t *pattern, + void *abstract_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +cairo_surface_t * +cairo_win32_surface_create (HDC hdc) +{ + cairo_win32_surface_t *surface; + RECT rect; + + /* Try to figure out the drawing bounds for the Device context + */ + if (GetClipBox (hdc, &rect) == ERROR) { + _cairo_win32_print_gdi_error ("cairo_win32_surface_create"); + return NULL; + } + + surface = malloc (sizeof (cairo_win32_surface_t)); + if (!surface) + return NULL; + + surface->image = NULL; + surface->format = CAIRO_FORMAT_RGB24; + + surface->dc = hdc; + surface->bitmap = NULL; + + surface->clip_rect.x = rect.left; + surface->clip_rect.y = rect.top; + surface->clip_rect.width = rect.right - rect.left; + surface->clip_rect.height = rect.bottom - rect.top; + + surface->set_clip = 0; + surface->saved_clip = NULL; + + _cairo_surface_init (&surface->base, &cairo_win32_surface_backend); + + return (cairo_surface_t *)surface; +} + +/** + * _cairo_surface_is_win32: + * @surface: a #cairo_surface_t + * + * Checks if a surface is an #cairo_win32_surface_t + * + * Return value: True if the surface is an win32 surface + **/ +int +_cairo_surface_is_win32 (cairo_surface_t *surface) +{ + return surface->backend == &cairo_win32_surface_backend; +} + +static const cairo_surface_backend_t cairo_win32_surface_backend = { + _cairo_win32_surface_create_similar, + _cairo_win32_surface_destroy, + _cairo_win32_surface_pixels_per_inch, + _cairo_win32_surface_acquire_source_image, + _cairo_win32_surface_release_source_image, + _cairo_win32_surface_acquire_dest_image, + _cairo_win32_surface_release_dest_image, + _cairo_win32_surface_clone_similar, + _cairo_win32_surface_composite, + _cairo_win32_surface_fill_rectangles, + _cairo_win32_surface_composite_trapezoids, + _cairo_win32_surface_copy_page, + _cairo_win32_surface_show_page, + _cairo_win32_surface_set_clip_region, + _cairo_win32_surface_show_glyphs +}; diff --git a/src/cairo_xcb_surface.c b/src/cairo_xcb_surface.c index 758cf26de..0694b77a2 100644 --- a/src/cairo_xcb_surface.c +++ b/src/cairo_xcb_surface.c @@ -31,10 +31,11 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" +#include "cairo-xcb.h" cairo_surface_t * cairo_xcb_surface_create (XCBConnection *dpy, @@ -327,14 +328,17 @@ bytes_per_line(XCBConnection *c, int width, int bpp) return ((bpp * width + bitmap_pad - 1) & -bitmap_pad) >> 3; } -static cairo_image_surface_t * -_cairo_xcb_surface_get_image (void *abstract_surface) +static cairo_status_t +_get_image_surface (cairo_xcb_surface_t *surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect) { - cairo_xcb_surface_t *surface = abstract_surface; cairo_image_surface_t *image; XCBGetGeometryRep *geomrep; XCBGetImageRep *imagerep; int bpp; + int x1, y1, x2, y2; geomrep = XCBGetGeometryReply(surface->dpy, XCBGetGeometry(surface->dpy, surface->drawable), 0); if(!geomrep) @@ -344,11 +348,39 @@ _cairo_xcb_surface_get_image (void *abstract_surface) surface->height = geomrep->height; free(geomrep); + x1 = 0; + y1 = 0; + x2 = surface->width; + y2 = surface->height; + + if (interest_rect) { + if (interest_rect->x > x1) + x1 = interest_rect->x; + if (interest_rect->y > y1) + y1 = interest_rect->y; + if (interest_rect->x + interest_rect->width < x2) + x2 = interest_rect->x + interest_rect->width; + if (interest_rect->y + interest_rect->height < y2) + y2 = interest_rect->y + interest_rect->height; + + if (x1 >= x2 || y1 >= y2) { + *image_out = NULL; + return CAIRO_STATUS_SUCCESS; + } + } + + if (image_rect) { + image_rect->x = x1; + image_rect->y = y1; + image_rect->width = x2 - x1; + image_rect->height = y2 - y1; + } + imagerep = XCBGetImageReply(surface->dpy, XCBGetImage(surface->dpy, ZPixmap, surface->drawable, - 0, 0, - surface->width, surface->height, + x1, y1, + x2 - x1, y2 - y1, AllPlanes), 0); if(!imagerep) return 0; @@ -368,15 +400,15 @@ _cairo_xcb_surface_get_image (void *abstract_surface) image = _cairo_image_surface_create_with_masks (XCBGetImageData(imagerep), &masks, - surface->width, - surface->height, + x2 - x1, + y2 - y1, bytes_per_line(surface->dpy, surface->width, bpp)); } else { image = (cairo_image_surface_t *) cairo_image_surface_create_for_data (XCBGetImageData(imagerep), surface->format, - surface->width, - surface->height, + x2 - x1, + y2 - y1, bytes_per_line(surface->dpy, surface->width, bpp)); } @@ -388,7 +420,8 @@ _cairo_xcb_surface_get_image (void *abstract_surface) _cairo_image_surface_set_repeat (image, surface->base.repeat); _cairo_image_surface_set_matrix (image, &(surface->base.matrix)); - return image; + *image_out = image; + return CAIRO_STATUS_SUCCESS; } static void @@ -402,10 +435,11 @@ _cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface) } static cairo_status_t -_cairo_xcb_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +_draw_image_surface (cairo_xcb_surface_t *surface, + cairo_image_surface_t *image, + int dst_x, + int dst_y) { - cairo_xcb_surface_t *surface = abstract_surface; int bpp, data_len; _cairo_xcb_surface_ensure_gc (surface); @@ -414,7 +448,7 @@ _cairo_xcb_surface_set_image (void *abstract_surface, XCBPutImage(surface->dpy, ZPixmap, surface->drawable, surface->gc, image->width, image->height, - /* dst_x */ 0, /* dst_y */ 0, + dst_x, dst_y, /* left_pad */ 0, image->depth, data_len, image->data); @@ -422,9 +456,107 @@ _cairo_xcb_surface_set_image (void *abstract_surface, } static cairo_status_t -_cairo_xcb_surface_set_matrix (void *abstract_surface, cairo_matrix_t *matrix) +_cairo_xcb_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _get_image_surface (surface, NULL, &image, NULL); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_surface_set_filter (&image->base, surface->base.filter); + cairo_surface_set_matrix (&image->base, &surface->base.matrix); + cairo_surface_set_repeat (&image->base, surface->base.repeat); + + *image_out = image; + } + + return status; +} + +static void +_cairo_xcb_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_xcb_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect_out, + void **image_extra) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _get_image_surface (surface, interest_rect, &image, image_rect_out); + if (status == CAIRO_STATUS_SUCCESS) + *image_out = image; + + return status; +} + +static void +_cairo_xcb_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ + cairo_xcb_surface_t *surface = abstract_surface; + + /* ignore errors */ + _draw_image_surface (surface, image, image_rect->x, image_rect->y); + + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_xcb_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { cairo_xcb_surface_t *surface = abstract_surface; + cairo_xcb_surface_t *clone; + + if (src->backend == surface->base.backend ) { + cairo_xcb_surface_t *xcb_src = (cairo_xcb_surface_t *)src; + + if (xcb_src->dpy == surface->dpy) { + *clone_out = src; + cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } + } else if (_cairo_surface_is_image (src)) { + cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; + + clone = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_similar (surface, image_src->format, 0, + image_src->width, image_src->height); + if (clone == NULL) + return CAIRO_STATUS_NO_MEMORY; + + _draw_image_surface (clone, image_src, 0, 0); + + *clone_out = &clone->base; + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +_cairo_xcb_surface_set_matrix (cairo_xcb_surface_t *surface, + cairo_matrix_t *matrix) +{ XCBRenderTRANSFORM xtransform; if (!surface->picture.xid) @@ -442,27 +574,42 @@ _cairo_xcb_surface_set_matrix (void *abstract_surface, cairo_matrix_t *matrix) xtransform.matrix32 = 0; xtransform.matrix33 = _cairo_fixed_from_double (1); - if (CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) + if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) { - XCBRenderSetPictureTransform (surface->dpy, surface->picture, xtransform); - } else { - /* XXX: Need support here if using an old RENDER without support - for SetPictureTransform */ + static const XCBRenderTRANSFORM identity = { + 1 << 16, 0x00000, 0x00000, + 0x00000, 1 << 16, 0x00000, + 0x00000, 0x00000, 1 << 16 + }; + + if (memcmp (&xtransform, &identity, sizeof (XCBRenderTRANSFORM)) == 0) + return CAIRO_STATUS_SUCCESS; + + return CAIRO_INT_STATUS_UNSUPPORTED; } + + XCBRenderSetPictureTransform (surface->dpy, surface->picture, xtransform); return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_xcb_surface_set_filter (void *abstract_surface, cairo_filter_t filter) +_cairo_xcb_surface_set_filter (cairo_xcb_surface_t *surface, + cairo_filter_t filter) { - cairo_xcb_surface_t *surface = abstract_surface; char *render_filter; - if (!(surface->picture.xid - && CAIRO_SURFACE_RENDER_HAS_FILTERS(surface))) + if (!surface->picture.xid) return CAIRO_STATUS_SUCCESS; - + + if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) + { + if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) + return CAIRO_STATUS_SUCCESS; + + return CAIRO_INT_STATUS_UNSUPPORTED; + } + switch (filter) { case CAIRO_FILTER_FAST: render_filter = "fast"; @@ -491,10 +638,8 @@ _cairo_xcb_surface_set_filter (void *abstract_surface, cairo_filter_t filter) } static cairo_status_t -_cairo_xcb_surface_set_repeat (void *abstract_surface, int repeat) +_cairo_xcb_surface_set_repeat (cairo_xcb_surface_t *surface, int repeat) { - cairo_xcb_surface_t *surface = abstract_surface; - CARD32 mask = XCBRenderCPRepeat; CARD32 pa[] = { repeat }; @@ -506,33 +651,32 @@ _cairo_xcb_surface_set_repeat (void *abstract_surface, int repeat) return CAIRO_STATUS_SUCCESS; } -static cairo_xcb_surface_t * -_cairo_xcb_surface_clone_similar (cairo_surface_t *src, - cairo_xcb_surface_t *template, - cairo_format_t format, - int depth) +static cairo_int_status_t +_cairo_xcb_surface_set_attributes (cairo_xcb_surface_t *surface, + cairo_surface_attributes_t *attributes) { - cairo_xcb_surface_t *clone; - cairo_image_surface_t *src_image; - - src_image = _cairo_surface_get_image (src); - - clone = (cairo_xcb_surface_t *) - _cairo_xcb_surface_create_similar (template, format, 0, - src_image->width, - src_image->height); - if (clone == NULL) - return NULL; - - _cairo_xcb_surface_set_filter (clone, cairo_surface_get_filter(src)); - - _cairo_xcb_surface_set_image (clone, src_image); + cairo_int_status_t status; - _cairo_xcb_surface_set_matrix (clone, &(src_image->base.matrix)); + status = _cairo_xcb_surface_set_matrix (surface, &attributes->matrix); + if (status) + return status; + + switch (attributes->extend) { + case CAIRO_EXTEND_NONE: + _cairo_xcb_surface_set_repeat (surface, 0); + break; + case CAIRO_EXTEND_REPEAT: + _cairo_xcb_surface_set_repeat (surface, 1); + break; + case CAIRO_EXTEND_REFLECT: + return CAIRO_INT_STATUS_UNSUPPORTED; + } - cairo_surface_destroy (&src_image->base); + status = _cairo_xcb_surface_set_filter (surface, attributes->filter); + if (status) + return status; - return clone; + return CAIRO_STATUS_SUCCESS; } static int @@ -574,65 +718,80 @@ _render_operator (cairo_operator_t operator) static cairo_int_status_t _cairo_xcb_surface_composite (cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height) + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height) { - cairo_xcb_surface_t *dst = abstract_dst; - cairo_xcb_surface_t *src = (cairo_xcb_surface_t *) generic_src; - cairo_xcb_surface_t *mask = (cairo_xcb_surface_t *) generic_mask; - cairo_xcb_surface_t *src_clone = NULL; - cairo_xcb_surface_t *mask_clone = NULL; - XCBRenderPICTURE maskpict = { 0 }; - + cairo_surface_attributes_t src_attr, mask_attr; + cairo_xcb_surface_t *dst = abstract_dst; + cairo_xcb_surface_t *src; + cairo_xcb_surface_t *mask; + cairo_int_status_t status; if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; - if (generic_src->backend != dst->base.backend || src->dpy != dst->dpy) { - src_clone = _cairo_xcb_surface_clone_similar (generic_src, dst, - CAIRO_FORMAT_ARGB32, 32); - if (!src_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; - src = src_clone; - } - if (generic_mask && (generic_mask->backend != dst->base.backend || mask->dpy != dst->dpy)) { - mask_clone = _cairo_xcb_surface_clone_similar (generic_mask, dst, - CAIRO_FORMAT_A8, 8); - if (!mask_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; - mask = mask_clone; + status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, + &dst->base, + src_x, src_y, + mask_x, mask_y, + width, height, + (cairo_surface_t **) &src, + (cairo_surface_t **) &mask, + &src_attr, &mask_attr); + if (status) + return status; + + status = _cairo_xcb_surface_set_attributes (src, &src_attr); + if (CAIRO_OK (status)) + { + if (mask) + { + status = _cairo_xcb_surface_set_attributes (mask, &mask_attr); + if (CAIRO_OK (status)) + XCBRenderComposite (dst->dpy, + _render_operator (operator), + src->picture, + mask->picture, + dst->picture, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + mask_x + mask_attr.x_offset, + mask_y + mask_attr.y_offset, + dst_x, dst_y, + width, height); + } + else + { + static XCBRenderPICTURE maskpict = { 0 }; + + XCBRenderComposite (dst->dpy, + _render_operator (operator), + src->picture, + maskpict, + dst->picture, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + } } - if(mask) - maskpict = mask->picture; - - XCBRenderComposite (dst->dpy, - _render_operator (operator), - src->picture, - maskpict, - dst->picture, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); - - /* XXX: This is messed up. If I can xcb_surface_create, then I - should be able to xcb_surface_destroy. */ - if (src_clone) - cairo_surface_destroy (&src_clone->base); - if (mask_clone) - cairo_surface_destroy (&mask_clone->base); + if (mask) + _cairo_pattern_release_surface (&dst->base, &mask->base, &mask_attr); + + _cairo_pattern_release_surface (&dst->base, &src->base, &src_attr); - return CAIRO_STATUS_SUCCESS; + return status; } static cairo_int_status_t @@ -664,42 +823,60 @@ _cairo_xcb_surface_fill_rectangles (void *abstract_surface, static cairo_int_status_t _cairo_xcb_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *generic_src, - void *abstract_dst, - int xSrc, - int ySrc, - cairo_trapezoid_t *traps, - int num_traps) + cairo_pattern_t *pattern, + void *abstract_dst, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_trapezoid_t *traps, + int num_traps) { - cairo_xcb_surface_t *dst = abstract_dst; - cairo_xcb_surface_t *src = (cairo_xcb_surface_t *) generic_src; - cairo_xcb_surface_t *src_clone = NULL; + cairo_surface_attributes_t attributes; + cairo_xcb_surface_t *dst = abstract_dst; + cairo_xcb_surface_t *src; + cairo_int_status_t status; + int render_reference_x, render_reference_y; + int render_src_x, render_src_y; if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; - - if (generic_src->backend != dst->base.backend || src->dpy != dst->dpy) { - src_clone = _cairo_xcb_surface_clone_similar (generic_src, dst, - CAIRO_FORMAT_ARGB32, 32); - if (!src_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; - src = src_clone; + + status = _cairo_pattern_acquire_surface (pattern, &dst->base, + src_x, src_y, width, height, + (cairo_surface_t **) &src, + &attributes); + if (status) + return status; + + if (traps[0].left.p1.y < traps[0].left.p2.y) { + render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); + render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); + } else { + render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); + render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); } - /* XXX: The XCBRenderTRAP cast is evil and needs to go away somehow. */ - /* XXX: format_from_cairo is slow. should cache something. */ - XCBRenderTrapezoids (dst->dpy, - _render_operator (operator), - src->picture, dst->picture, - format_from_cairo (dst->dpy, CAIRO_FORMAT_A8), - xSrc, ySrc, num_traps, (XCBRenderTRAP *) traps); - - /* XXX: This is messed up. If I can xcb_surface_create, then I - should be able to xcb_surface_destroy. */ - if (src_clone) - cairo_surface_destroy (&src_clone->base); + render_src_x = src_x + render_reference_x - dst_x; + render_src_y = src_y + render_reference_y - dst_y; - return CAIRO_STATUS_SUCCESS; + /* XXX: The XTrapezoid cast is evil and needs to go away somehow. */ + /* XXX: format_from_cairo is slow. should cache something. */ + status = _cairo_xcb_surface_set_attributes (src, &attributes); + if (CAIRO_OK (status)) + XCBRenderTrapezoids (dst->dpy, + _render_operator (operator), + src->picture, dst->picture, + format_from_cairo (dst->dpy, CAIRO_FORMAT_A8), + render_src_x + attributes.x_offset, + render_src_y + attributes.y_offset, + num_traps, (XCBRenderTRAP *) traps); + + _cairo_pattern_release_surface (&dst->base, &src->base, &attributes); + + return status; } static cairo_int_status_t @@ -722,30 +899,21 @@ _cairo_xcb_surface_set_clip_region (void *abstract_surface, return CAIRO_INT_STATUS_UNSUPPORTED; } -static cairo_int_status_t -_cairo_xcb_surface_create_pattern (void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *extents) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - static const cairo_surface_backend_t cairo_xcb_surface_backend = { _cairo_xcb_surface_create_similar, _cairo_xcb_surface_destroy, _cairo_xcb_surface_pixels_per_inch, - _cairo_xcb_surface_get_image, - _cairo_xcb_surface_set_image, - _cairo_xcb_surface_set_matrix, - _cairo_xcb_surface_set_filter, - _cairo_xcb_surface_set_repeat, + _cairo_xcb_surface_acquire_source_image, + _cairo_xcb_surface_release_source_image, + _cairo_xcb_surface_acquire_dest_image, + _cairo_xcb_surface_release_dest_image, + _cairo_xcb_surface_clone_similar, _cairo_xcb_surface_composite, _cairo_xcb_surface_fill_rectangles, _cairo_xcb_surface_composite_trapezoids, _cairo_xcb_surface_copy_page, _cairo_xcb_surface_show_page, _cairo_xcb_surface_set_clip_region, - _cairo_xcb_surface_create_pattern, NULL /* show_glyphs */ }; diff --git a/src/cairo_xlib_surface.c b/src/cairo_xlib_surface.c index d9d74f583..3eaef57e5 100644 --- a/src/cairo_xlib_surface.c +++ b/src/cairo_xlib_surface.c @@ -31,12 +31,28 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ #include "cairoint.h" #include "cairo-xlib.h" +/** + * cairo_set_target_drawable: + * @cr: a #cairo_t + * @dpy: an X display + * @drawable: a window or pixmap on the default screen of @dpy + * + * Directs output for a #cairo_t to an Xlib drawable. @drawable must + * be a Window or Pixmap on the default screen of @dpy using the + * default colormap and visual. Using this function is slow because + * the function must retrieve information about @drawable from the X + * server. + + * The combination of cairo_xlib_surface_create() and + * cairo_set_target_surface() is somewhat more flexible, although + * it still is slow. + **/ void cairo_set_target_drawable (cairo_t *cr, Display *dpy, @@ -87,6 +103,8 @@ typedef struct _cairo_xlib_surface { #define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) #define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) +#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) + #define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) #define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) @@ -141,18 +159,13 @@ _cairo_xlib_surface_create_similar (void *abstract_src, Pixmap pix; cairo_xlib_surface_t *surface; - /* XXX: There's a pretty lame heuristic here. This assumes that - * all non-Render X servers do not support depth-32 pixmaps, (and - * that they do support depths 1, 8, and 24). Obviously, it would - * be much better to check the depths that are actually - * supported. */ - if (!dpy - || (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src) - && format == CAIRO_FORMAT_ARGB32)) - { - return NULL; + /* As a good first approximation, if the display doesn't have COMPOSITE, + * we're better off using image surfaces for all temporary operations + */ + if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE(src)) { + return cairo_image_surface_create (format, width, height); } - + scr = DefaultScreen (dpy); pix = XCreatePixmap (dpy, DefaultRootWindow (dpy), @@ -196,15 +209,17 @@ _cairo_xlib_surface_pixels_per_inch (void *abstract_surface) return 96.0; } -static cairo_image_surface_t * -_cairo_xlib_surface_get_image (void *abstract_surface) +static cairo_status_t +_get_image_surface (cairo_xlib_surface_t *surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect) { - cairo_xlib_surface_t *surface = abstract_surface; cairo_image_surface_t *image; - XImage *ximage; Window root_ignore; int x_ignore, y_ignore, bwidth_ignore, depth_ignore; + int x1, y1, x2, y2; XGetGeometry (surface->dpy, surface->drawable, @@ -212,11 +227,39 @@ _cairo_xlib_surface_get_image (void *abstract_surface) &surface->width, &surface->height, &bwidth_ignore, &depth_ignore); + x1 = 0; + y1 = 0; + x2 = surface->width; + y2 = surface->height; + + if (interest_rect) { + if (interest_rect->x > x1) + x1 = interest_rect->x; + if (interest_rect->y > y1) + y1 = interest_rect->y; + if (interest_rect->x + interest_rect->width < x2) + x2 = interest_rect->x + interest_rect->width; + if (interest_rect->y + interest_rect->height < y2) + y2 = interest_rect->y + interest_rect->height; + + if (x1 >= x2 || y1 >= y2) { + *image_out = NULL; + return CAIRO_STATUS_SUCCESS; + } + } + + if (image_rect) { + image_rect->x = x1; + image_rect->y = y1; + image_rect->width = x2 - x1; + image_rect->height = y2 - y1; + } + /* XXX: This should try to use the XShm extension if availible */ ximage = XGetImage (surface->dpy, surface->drawable, - 0, 0, - surface->width, surface->height, + x1, y1, + x2 - x1, y2 - y1, AllPlanes, ZPixmap); if (surface->visual) { @@ -253,7 +296,8 @@ _cairo_xlib_surface_get_image (void *abstract_surface) _cairo_image_surface_set_repeat (image, surface->base.repeat); _cairo_image_surface_set_matrix (image, &(surface->base.matrix)); - return image; + *image_out = image; + return CAIRO_STATUS_SUCCESS; } static void @@ -266,10 +310,11 @@ _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface) } static cairo_status_t -_cairo_xlib_surface_set_image (void *abstract_surface, - cairo_image_surface_t *image) +_draw_image_surface (cairo_xlib_surface_t *surface, + cairo_image_surface_t *image, + int dst_x, + int dst_y) { - cairo_xlib_surface_t *surface = abstract_surface; XImage *ximage; unsigned bitmap_pad; @@ -295,9 +340,8 @@ _cairo_xlib_surface_set_image (void *abstract_surface, _cairo_xlib_surface_ensure_gc (surface); XPutImage(surface->dpy, surface->drawable, surface->gc, - ximage, 0, 0, 0, 0, - surface->width, - surface->height); + ximage, 0, 0, dst_x, dst_y, + image->width, image->height); /* Foolish XDestroyImage thinks it can free my data, but I won't stand for it. */ @@ -305,17 +349,116 @@ _cairo_xlib_surface_set_image (void *abstract_surface, XDestroyImage (ximage); return CAIRO_STATUS_SUCCESS; + +} + +static cairo_status_t +_cairo_xlib_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _get_image_surface (surface, NULL, &image, NULL); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_surface_set_filter (&image->base, surface->base.filter); + cairo_surface_set_matrix (&image->base, &surface->base.matrix); + cairo_surface_set_repeat (&image->base, surface->base.repeat); + + *image_out = image; + } + + return status; +} + +static void +_cairo_xlib_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_cairo_xlib_surface_acquire_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect_out, + void **image_extra) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_status_t status; + + status = _get_image_surface (surface, interest_rect, &image, image_rect_out); + if (status == CAIRO_STATUS_SUCCESS) + *image_out = image; + + return status; +} + +static void +_cairo_xlib_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra) +{ + cairo_xlib_surface_t *surface = abstract_surface; + + /* ignore errors */ + _draw_image_surface (surface, image, image_rect->x, image_rect->y); + + cairo_surface_destroy (&image->base); } static cairo_status_t -_cairo_xlib_surface_set_matrix (void *abstract_surface, cairo_matrix_t *matrix) +_cairo_xlib_surface_clone_similar (void *abstract_surface, + cairo_surface_t *src, + cairo_surface_t **clone_out) { cairo_xlib_surface_t *surface = abstract_surface; + cairo_xlib_surface_t *clone; + + if (src->backend == surface->base.backend ) { + cairo_xlib_surface_t *xlib_src = (cairo_xlib_surface_t *)src; + + if (xlib_src->dpy == surface->dpy) { + *clone_out = src; + cairo_surface_reference (src); + + return CAIRO_STATUS_SUCCESS; + } + } else if (_cairo_surface_is_image (src)) { + cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; + + clone = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_similar (surface, image_src->format, 0, + image_src->width, image_src->height); + if (clone == NULL) + return CAIRO_STATUS_NO_MEMORY; + + _draw_image_surface (clone, image_src, 0, 0); + + *clone_out = &clone->base; + + return CAIRO_STATUS_SUCCESS; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +_cairo_xlib_surface_set_matrix (cairo_xlib_surface_t *surface, + cairo_matrix_t *matrix) +{ XTransform xtransform; if (!surface->picture) return CAIRO_STATUS_SUCCESS; - + xtransform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); xtransform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); xtransform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); @@ -328,26 +471,41 @@ _cairo_xlib_surface_set_matrix (void *abstract_surface, cairo_matrix_t *matrix) xtransform.matrix[2][1] = 0; xtransform.matrix[2][2] = _cairo_fixed_from_double (1); - if (CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) + if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) { - XRenderSetPictureTransform (surface->dpy, surface->picture, &xtransform); - } else { - /* XXX: Need support here if using an old RENDER without support - for SetPictureTransform */ + static const XTransform identity = { { + { 1 << 16, 0x00000, 0x00000 }, + { 0x00000, 1 << 16, 0x00000 }, + { 0x00000, 0x00000, 1 << 16 }, + } }; + + if (memcmp (&xtransform, &identity, sizeof (XTransform)) == 0) + return CAIRO_STATUS_SUCCESS; + + return CAIRO_INT_STATUS_UNSUPPORTED; } + XRenderSetPictureTransform (surface->dpy, surface->picture, &xtransform); + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_xlib_surface_set_filter (void *abstract_surface, cairo_filter_t filter) +_cairo_xlib_surface_set_filter (cairo_xlib_surface_t *surface, + cairo_filter_t filter) { - cairo_xlib_surface_t *surface = abstract_surface; char *render_filter; - if (!(surface->picture - && CAIRO_SURFACE_RENDER_HAS_FILTERS(surface))) + if (!surface->picture) return CAIRO_STATUS_SUCCESS; + + if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) + { + if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) + return CAIRO_STATUS_SUCCESS; + + return CAIRO_INT_STATUS_UNSUPPORTED; + } switch (filter) { case CAIRO_FILTER_FAST: @@ -377,11 +535,10 @@ _cairo_xlib_surface_set_filter (void *abstract_surface, cairo_filter_t filter) } static cairo_status_t -_cairo_xlib_surface_set_repeat (void *abstract_surface, int repeat) +_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, int repeat) { - cairo_xlib_surface_t *surface = abstract_surface; - unsigned long mask; XRenderPictureAttributes pa; + unsigned long mask; if (!surface->picture) return CAIRO_STATUS_SUCCESS; @@ -394,33 +551,32 @@ _cairo_xlib_surface_set_repeat (void *abstract_surface, int repeat) return CAIRO_STATUS_SUCCESS; } -static cairo_xlib_surface_t * -_cairo_xlib_surface_clone_similar (cairo_surface_t *src, - cairo_xlib_surface_t *template, - cairo_format_t format, - int depth) +static cairo_int_status_t +_cairo_xlib_surface_set_attributes (cairo_xlib_surface_t *surface, + cairo_surface_attributes_t *attributes) { - cairo_xlib_surface_t *clone; - cairo_image_surface_t *src_image; + cairo_int_status_t status; - src_image = _cairo_surface_get_image (src); - - clone = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_similar (template, format, 0, - src_image->width, - src_image->height); - if (clone == NULL) - return NULL; - - _cairo_xlib_surface_set_filter (clone, cairo_surface_get_filter(src)); - - _cairo_xlib_surface_set_image (clone, src_image); - - _cairo_xlib_surface_set_matrix (clone, &(src_image->base.matrix)); + status = _cairo_xlib_surface_set_matrix (surface, &attributes->matrix); + if (status) + return status; + + switch (attributes->extend) { + case CAIRO_EXTEND_NONE: + _cairo_xlib_surface_set_repeat (surface, 0); + break; + case CAIRO_EXTEND_REPEAT: + _cairo_xlib_surface_set_repeat (surface, 1); + break; + case CAIRO_EXTEND_REFLECT: + return CAIRO_INT_STATUS_UNSUPPORTED; + } - cairo_surface_destroy (&src_image->base); + status = _cairo_xlib_surface_set_filter (surface, attributes->filter); + if (status) + return status; - return clone; + return CAIRO_STATUS_SUCCESS; } static int @@ -462,8 +618,8 @@ _render_operator (cairo_operator_t operator) static cairo_int_status_t _cairo_xlib_surface_composite (cairo_operator_t operator, - cairo_surface_t *generic_src, - cairo_surface_t *generic_mask, + cairo_pattern_t *src_pattern, + cairo_pattern_t *mask_pattern, void *abstract_dst, int src_x, int src_y, @@ -474,73 +630,66 @@ _cairo_xlib_surface_composite (cairo_operator_t operator, unsigned int width, unsigned int height) { - cairo_xlib_surface_t *dst = abstract_dst; - cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) generic_src; - cairo_xlib_surface_t *mask = (cairo_xlib_surface_t *) generic_mask; - cairo_xlib_surface_t *src_clone = NULL; - cairo_xlib_surface_t *mask_clone = NULL; - XGCValues gc_values; - int src_x_off, src_y_off, dst_x_off, dst_y_off; + cairo_surface_attributes_t src_attr, mask_attr; + cairo_xlib_surface_t *dst = abstract_dst; + cairo_xlib_surface_t *src; + cairo_xlib_surface_t *mask; + cairo_int_status_t status; if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; - if (generic_src->backend != dst->base.backend || src->dpy != dst->dpy) { - src_clone = _cairo_xlib_surface_clone_similar (generic_src, dst, - CAIRO_FORMAT_ARGB32, 32); - if (!src_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; - src = src_clone; - } - if (generic_mask && (generic_mask->backend != dst->base.backend || mask->dpy != dst->dpy)) { - mask_clone = _cairo_xlib_surface_clone_similar (generic_mask, dst, - CAIRO_FORMAT_A8, 8); - if (!mask_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; - mask = mask_clone; - } - - if (operator == CAIRO_OPERATOR_SRC - && !mask - && _cairo_matrix_is_integer_translation(&(src->base.matrix), - &src_x_off, &src_y_off) - && _cairo_matrix_is_integer_translation(&(dst->base.matrix), - &dst_x_off, &dst_y_off)) { - /* Fast path for copying "raw" areas. */ - _cairo_xlib_surface_ensure_gc (dst); - XGetGCValues(dst->dpy, dst->gc, GCGraphicsExposures, &gc_values); - XSetGraphicsExposures(dst->dpy, dst->gc, False); - XCopyArea(dst->dpy, - src->drawable, - dst->drawable, - dst->gc, - src_x + src_x_off, - src_y + src_y_off, - width, height, - dst_x + dst_x_off, - dst_y + dst_y_off); - XSetGraphicsExposures(dst->dpy, dst->gc, gc_values.graphics_exposures); - - } else { - XRenderComposite (dst->dpy, - _render_operator (operator), - src->picture, - mask ? mask->picture : 0, - dst->picture, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, - width, height); + status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern, + &dst->base, + src_x, src_y, + mask_x, mask_y, + width, height, + (cairo_surface_t **) &src, + (cairo_surface_t **) &mask, + &src_attr, &mask_attr); + if (status) + return status; + + status = _cairo_xlib_surface_set_attributes (src, &src_attr); + if (CAIRO_OK (status)) + { + if (mask) + { + status = _cairo_xlib_surface_set_attributes (mask, &mask_attr); + if (CAIRO_OK (status)) + XRenderComposite (dst->dpy, + _render_operator (operator), + src->picture, + mask->picture, + dst->picture, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + mask_x + mask_attr.x_offset, + mask_y + mask_attr.y_offset, + dst_x, dst_y, + width, height); + } + else + { + XRenderComposite (dst->dpy, + _render_operator (operator), + src->picture, + 0, + dst->picture, + src_x + src_attr.x_offset, + src_y + src_attr.y_offset, + 0, 0, + dst_x, dst_y, + width, height); + } } - /* XXX: This is messed up. If I can xlib_surface_create, then I - should be able to xlib_surface_destroy. */ - if (src_clone) - cairo_surface_destroy (&src_clone->base); - if (mask_clone) - cairo_surface_destroy (&mask_clone->base); + if (mask) + _cairo_pattern_release_surface (&dst->base, &mask->base, &mask_attr); + + _cairo_pattern_release_surface (&dst->base, &src->base, &src_attr); - return CAIRO_STATUS_SUCCESS; + return status; } static cairo_int_status_t @@ -572,41 +721,59 @@ _cairo_xlib_surface_fill_rectangles (void *abstract_surface, static cairo_int_status_t _cairo_xlib_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *generic_src, + cairo_pattern_t *pattern, void *abstract_dst, - int xSrc, - int ySrc, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps) { - cairo_xlib_surface_t *dst = abstract_dst; - cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) generic_src; - cairo_xlib_surface_t *src_clone = NULL; + cairo_surface_attributes_t attributes; + cairo_xlib_surface_t *dst = abstract_dst; + cairo_xlib_surface_t *src; + cairo_int_status_t status; + int render_reference_x, render_reference_y; + int render_src_x, render_src_y; if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) return CAIRO_INT_STATUS_UNSUPPORTED; - - if (generic_src->backend != dst->base.backend || src->dpy != dst->dpy) { - src_clone = _cairo_xlib_surface_clone_similar (generic_src, dst, - CAIRO_FORMAT_ARGB32, 32); - if (!src_clone) - return CAIRO_INT_STATUS_UNSUPPORTED; - src = src_clone; + + status = _cairo_pattern_acquire_surface (pattern, &dst->base, + src_x, src_y, width, height, + (cairo_surface_t **) &src, + &attributes); + if (status) + return status; + + if (traps[0].left.p1.y < traps[0].left.p2.y) { + render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x); + render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y); + } else { + render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x); + render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y); } - /* XXX: The XTrapezoid cast is evil and needs to go away somehow. */ - XRenderCompositeTrapezoids (dst->dpy, - _render_operator (operator), - src->picture, dst->picture, - XRenderFindStandardFormat (dst->dpy, PictStandardA8), - xSrc, ySrc, (XTrapezoid *) traps, num_traps); + render_src_x = src_x + render_reference_x - dst_x; + render_src_y = src_y + render_reference_y - dst_y; - /* XXX: This is messed up. If I can xlib_surface_create, then I - should be able to xlib_surface_destroy. */ - if (src_clone) - cairo_surface_destroy (&src_clone->base); + /* XXX: The XTrapezoid cast is evil and needs to go away somehow. */ + status = _cairo_xlib_surface_set_attributes (src, &attributes); + if (CAIRO_OK (status)) + XRenderCompositeTrapezoids (dst->dpy, + _render_operator (operator), + src->picture, dst->picture, + XRenderFindStandardFormat (dst->dpy, PictStandardA8), + render_src_x + attributes.x_offset, + render_src_y + attributes.y_offset, + (XTrapezoid *) traps, num_traps); + + _cairo_pattern_release_surface (&dst->base, &src->base, &attributes); - return CAIRO_STATUS_SUCCESS; + return status; } static cairo_int_status_t @@ -685,22 +852,17 @@ _cairo_xlib_surface_set_clip_region (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_xlib_surface_create_pattern (void *abstract_surface, - cairo_pattern_t *pattern, - cairo_box_t *extents) -{ - return CAIRO_INT_STATUS_UNSUPPORTED; -} - static cairo_status_t -_cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, +_cairo_xlib_surface_show_glyphs (cairo_font_t *font, cairo_operator_t operator, - cairo_surface_t *source, + cairo_pattern_t *pattern, void *abstract_surface, int source_x, int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, const cairo_glyph_t *glyphs, int num_glyphs); @@ -708,18 +870,17 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = { _cairo_xlib_surface_create_similar, _cairo_xlib_surface_destroy, _cairo_xlib_surface_pixels_per_inch, - _cairo_xlib_surface_get_image, - _cairo_xlib_surface_set_image, - _cairo_xlib_surface_set_matrix, - _cairo_xlib_surface_set_filter, - _cairo_xlib_surface_set_repeat, + _cairo_xlib_surface_acquire_source_image, + _cairo_xlib_surface_release_source_image, + _cairo_xlib_surface_acquire_dest_image, + _cairo_xlib_surface_release_dest_image, + _cairo_xlib_surface_clone_similar, _cairo_xlib_surface_composite, _cairo_xlib_surface_fill_rectangles, _cairo_xlib_surface_composite_trapezoids, _cairo_xlib_surface_copy_page, _cairo_xlib_surface_show_page, _cairo_xlib_surface_set_clip_region, - _cairo_xlib_surface_create_pattern, _cairo_xlib_surface_show_glyphs }; @@ -827,6 +988,7 @@ typedef struct { cairo_glyph_cache_key_t key; Glyph glyph; XGlyphInfo info; + int refcount; } glyphset_cache_entry_t; static Glyph @@ -854,17 +1016,18 @@ _xlib_glyphset_cache_create_entry (void *cache, _cairo_lock_global_image_glyph_cache (); im_cache = _cairo_get_global_image_glyph_cache (); - if (g == NULL || v == NULL ||g == NULL || im_cache == NULL) { + if (g == NULL || v == NULL || im_cache == NULL) { _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_NO_MEMORY; } - status = _cairo_cache_lookup (im_cache, key, (void **) (&im)); + status = _cairo_cache_lookup (im_cache, key, (void **) (&im), NULL); if (status != CAIRO_STATUS_SUCCESS || im == NULL) { _cairo_unlock_global_image_glyph_cache (); return CAIRO_STATUS_NO_MEMORY; } + v->refcount = 1; v->key = *k; _cairo_unscaled_font_reference (v->key.unscaled); @@ -925,6 +1088,12 @@ _xlib_glyphset_cache_create_entry (void *cache, return CAIRO_STATUS_SUCCESS; } +static void +_glyphset_cache_entry_reference (glyphset_cache_entry_t *e) +{ + e->refcount++; +} + static void _xlib_glyphset_cache_destroy_cache (void *cache) { @@ -940,6 +1109,9 @@ _xlib_glyphset_cache_destroy_entry (void *cache, void *entry) g = (glyphset_cache_t *) cache; v = (glyphset_cache_entry_t *) entry; + if (--v->refcount > 0) + return; + _cairo_unscaled_font_destroy (v->key.unscaled); XRenderFreeGlyphs (g->display, g->glyphset, &(v->glyph), 1); free (v); @@ -1014,8 +1186,7 @@ _get_glyphset_cache (Display *d) #define N_STACK_BUF 1024 static cairo_status_t -_cairo_xlib_surface_show_glyphs32 (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, +_cairo_xlib_surface_show_glyphs32 (cairo_font_t *font, cairo_operator_t operator, glyphset_cache_t *g, cairo_glyph_cache_key_t *key, @@ -1092,8 +1263,7 @@ _cairo_xlib_surface_show_glyphs32 (cairo_unscaled_font_t *font, static cairo_status_t -_cairo_xlib_surface_show_glyphs16 (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, +_cairo_xlib_surface_show_glyphs16 (cairo_font_t *font, cairo_operator_t operator, glyphset_cache_t *g, cairo_glyph_cache_key_t *key, @@ -1169,10 +1339,9 @@ _cairo_xlib_surface_show_glyphs16 (cairo_unscaled_font_t *font, } static cairo_status_t -_cairo_xlib_surface_show_glyphs8 (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, +_cairo_xlib_surface_show_glyphs8 (cairo_font_t *font, cairo_operator_t operator, - glyphset_cache_t *g, + glyphset_cache_t *g, cairo_glyph_cache_key_t *key, cairo_xlib_surface_t *src, cairo_xlib_surface_t *self, @@ -1247,27 +1416,44 @@ _cairo_xlib_surface_show_glyphs8 (cairo_unscaled_font_t *font, static cairo_status_t -_cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, +_cairo_xlib_surface_show_glyphs (cairo_font_t *font, cairo_operator_t operator, - cairo_surface_t *source, + cairo_pattern_t *pattern, void *abstract_surface, int source_x, int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, const cairo_glyph_t *glyphs, int num_glyphs) { + cairo_surface_attributes_t attributes; + cairo_int_status_t status; unsigned int elt_size; cairo_xlib_surface_t *self = abstract_surface; - cairo_image_surface_t *tmp = NULL; - cairo_xlib_surface_t *src = NULL; + cairo_xlib_surface_t *src; glyphset_cache_t *g; - cairo_status_t status; cairo_glyph_cache_key_t key; glyphset_cache_entry_t **entries; glyphset_cache_entry_t *stack_entries [N_STACK_BUF]; int i; + if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (self)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_pattern_acquire_surface (pattern, &self->base, + source_x, source_y, width, height, + (cairo_surface_t **) &src, + &attributes); + if (status) + return status; + + status = _cairo_xlib_surface_set_attributes (src, &attributes); + if (status) + goto FAIL; + /* Acquire an entry array of suitable size. */ if (num_glyphs < N_STACK_BUF) { entries = stack_entries; @@ -1278,26 +1464,6 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, goto FAIL; } - /* prep the source surface. */ - if (source->backend == self->base.backend) { - src = (cairo_xlib_surface_t *) source; - - } else { - tmp = _cairo_surface_get_image (source); - if (tmp == NULL) - goto FREE_ENTRIES; - - src = (cairo_xlib_surface_t *) - _cairo_surface_create_similar_scratch (&self->base, self->format, 1, - tmp->width, tmp->height); - - if (src == NULL) - goto FREE_TMP; - - if (_cairo_surface_set_image (&(src->base), tmp) != CAIRO_STATUS_SUCCESS) - goto FREE_SRC; - } - _lock_xlib_glyphset_caches (); g = _get_glyphset_cache (self->dpy); if (g == NULL) @@ -1305,15 +1471,23 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, /* Work out the index size to use. */ elt_size = 8; - key.scale = *scale; - key.unscaled = font; + _cairo_font_get_glyph_cache_key (font, &key); for (i = 0; i < num_glyphs; ++i) { key.index = glyphs[i].index; - status = _cairo_cache_lookup (&g->base, &key, (void **) (&entries[i])); + status = _cairo_cache_lookup (&g->base, &key, (void **) (&entries[i]), NULL); if (status != CAIRO_STATUS_SUCCESS || entries[i] == NULL) goto UNLOCK; + /* Referencing the glyph entries we use prevents them from + * being freed if lookup of later entries causes them to + * be ejected from the cache. It would be more efficient + * (though more complex) to prevent them from being ejected + * from the cache at all, so they could get reused later + * in the same string. + */ + _glyphset_cache_entry_reference (entries[i]); + if (elt_size == 8 && entries[i]->glyph > 0xff) elt_size = 16; if (elt_size == 16 && entries[i]->glyph > 0xffff) { @@ -1325,43 +1499,32 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, /* Call the appropriate sub-function. */ if (elt_size == 8) - status = _cairo_xlib_surface_show_glyphs8 (font, scale, operator, g, &key, src, self, - source_x, source_y, + status = _cairo_xlib_surface_show_glyphs8 (font, operator, g, &key, src, self, + source_x + attributes.x_offset, + source_y + attributes.y_offset, glyphs, entries, num_glyphs); else if (elt_size == 16) - status = _cairo_xlib_surface_show_glyphs16 (font, scale, operator, g, &key, src, self, - source_x, source_y, + status = _cairo_xlib_surface_show_glyphs16 (font, operator, g, &key, src, self, + source_x + attributes.x_offset, + source_y + attributes.y_offset, glyphs, entries, num_glyphs); else - status = _cairo_xlib_surface_show_glyphs32 (font, scale, operator, g, &key, src, self, - source_x, source_y, + status = _cairo_xlib_surface_show_glyphs32 (font, operator, g, &key, src, self, + source_x + attributes.x_offset, + source_y + attributes.y_offset, glyphs, entries, num_glyphs); - _unlock_xlib_glyphset_caches (); - - if (tmp != NULL) { - cairo_surface_destroy (&(src->base)); - cairo_surface_destroy (&(tmp->base)); - } - - if (num_glyphs >= N_STACK_BUF) - free (entries); - - return status; + for (i = 0; i < num_glyphs; ++i) + _xlib_glyphset_cache_destroy_entry (g, entries[i]); UNLOCK: _unlock_xlib_glyphset_caches (); - FREE_SRC: - cairo_surface_destroy (&(src->base)); - - FREE_TMP: - cairo_surface_destroy (&(tmp->base)); - - FREE_ENTRIES: if (num_glyphs >= N_STACK_BUF) free (entries); FAIL: - return CAIRO_STATUS_NO_MEMORY; + _cairo_pattern_release_surface (&self->base, &src->base, &attributes); + + return status; } diff --git a/src/cairoint.h b/src/cairoint.h index 6f9d50617..50899eca8 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -31,7 +31,7 @@ * California. * * Contributor(s): - * Carl D. Worth <cworth@isi.edu> + * Carl D. Worth <cworth@cworth.org> */ /* @@ -39,7 +39,7 @@ * and constitute no kind of standard. If you need any of these * functions, please drop me a note. Either the library needs new * functionality, or there's a way to do what you need using the - * existing published interfaces. cworth@isi.edu + * existing published interfaces. cworth@cworth.org */ #ifndef _CAIROINT_H_ @@ -108,6 +108,17 @@ #define __attribute__(x) #endif +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + #include "cairo-wideint.h" typedef int32_t cairo_fixed_16_16_t; @@ -166,6 +177,8 @@ typedef enum cairo_int_status { CAIRO_INT_STATUS_UNSUPPORTED } cairo_int_status_t; +#define CAIRO_OK(status) ((status) == CAIRO_STATUS_SUCCESS) + typedef enum cairo_path_op { CAIRO_PATH_OP_MOVE_TO = 0, CAIRO_PATH_OP_LINE_TO = 1, @@ -373,43 +386,49 @@ _cairo_cache_destroy (cairo_cache_t *cache); cairo_private cairo_status_t _cairo_cache_lookup (cairo_cache_t *cache, - void *key, - void **entry_return); + void *key, + void **entry_return, + int *created_entry); + +cairo_private cairo_status_t +_cairo_cache_remove (cairo_cache_t *cache, + void *key); + +cairo_private void * +_cairo_cache_random_entry (cairo_cache_t *cache, + int (*predicate) (void*)); cairo_private unsigned long _cairo_hash_string (const char *c); #define CAIRO_IMAGE_GLYPH_CACHE_MEMORY_DEFAULT 0x100000 #define CAIRO_XLIB_GLYPH_CACHE_MEMORY_DEFAULT 0x100000 -#define CAIRO_FONT_CACHE_NUM_FONTS_DEFAULT 20 -#define CAIRO_FT_CACHE_NUM_FONTS_DEFAULT 20 typedef struct { - double matrix[2][2]; + double matrix[2][2]; } cairo_font_scale_t; struct _cairo_font_backend; +/* + * A cairo_unscaled_font_t is just an opaque handle we use in the + * glyph cache. + */ typedef struct { int refcount; const struct _cairo_font_backend *backend; } cairo_unscaled_font_t; -/* - * A cairo_font contains a pointer to a cairo_unscaled_font_t and a scale - * matrix. These are the things the user holds references to. - */ - struct _cairo_font { int refcount; - cairo_font_scale_t scale; - cairo_unscaled_font_t *unscaled; + cairo_font_scale_t scale; /* font space => device space */ + const struct _cairo_font_backend *backend; }; -/* cairo_font.c is responsible for two global caches: +/* cairo_font.c is responsible for a global glyph cache: * - * - font entries: [[[base], name, weight, slant], cairo_unscaled_font_t ] - * - glyph entries: [[[base], cairo_font_t, index], image, size, extents ] + * - glyph entries: [[[base], cairo_unscaled_font_t, scale, flags, index], + * image, size, extents] * * Surfaces may build their own glyph caches if they have surface-specific * glyph resources to maintain; those caches can feed off of the global @@ -420,6 +439,7 @@ typedef struct { cairo_cache_entry_base_t base; cairo_unscaled_font_t *unscaled; cairo_font_scale_t scale; + int flags; unsigned long index; } cairo_glyph_cache_key_t; @@ -452,49 +472,52 @@ _cairo_glyph_cache_keys_equal (void *cache, /* the font backend interface */ typedef struct _cairo_font_backend { - cairo_unscaled_font_t *(*create) (const char *family, + cairo_status_t (*create) (const char *family, cairo_font_slant_t slant, - cairo_font_weight_t weight); + cairo_font_weight_t weight, + cairo_font_scale_t *scale, + cairo_font_t **font); - void (*destroy) (void *font); + void (*destroy_font) (void *font); + void (*destroy_unscaled_font) (void *font); cairo_status_t (*font_extents) (void *font, - cairo_font_scale_t *scale, cairo_font_extents_t *extents); cairo_status_t (*text_to_glyphs) (void *font, - cairo_font_scale_t *scale, const unsigned char *utf8, cairo_glyph_t **glyphs, int *num_glyphs); cairo_status_t (*glyph_extents) (void *font, - cairo_font_scale_t *scale, cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents); cairo_status_t (*glyph_bbox) (void *font, - cairo_font_scale_t *scale, const cairo_glyph_t *glyphs, int num_glyphs, cairo_box_t *bbox); cairo_status_t (*show_glyphs) (void *font, - cairo_font_scale_t *scale, cairo_operator_t operator, - cairo_surface_t *source, + cairo_pattern_t *pattern, cairo_surface_t *surface, int source_x, int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, const cairo_glyph_t *glyphs, int num_glyphs); cairo_status_t (*glyph_path) (void *font, - cairo_font_scale_t *scale, cairo_glyph_t *glyphs, int num_glyphs, cairo_path_t *path); + void (*get_glyph_cache_key) (void *font, + cairo_glyph_cache_key_t *key); cairo_status_t (*create_glyph) (cairo_image_glyph_cache_entry_t *entry); @@ -507,6 +530,12 @@ extern const cairo_private struct _cairo_font_backend cairo_ft_font_backend; #endif +#ifdef CAIRO_HAS_WIN32_FONT + +extern const cairo_private struct _cairo_font_backend cairo_win32_font_backend; + +#endif + #ifdef CAIRO_HAS_ATSUI_FONT extern const cairo_private struct _cairo_font_backend cairo_atsui_font_backend; @@ -527,35 +556,40 @@ typedef struct _cairo_surface_backend { double (*pixels_per_inch) (void *surface); - /* XXX: We could use a better name than get_image here. Something - to suggest the fact that the function will create a new - surface, (and hence that it needs to be destroyed). Perhaps - clone_image or maybe simply clone? */ - - cairo_image_surface_t * - (*get_image) (void *surface); - cairo_status_t - (*set_image) (void *surface, - cairo_image_surface_t *image); + (* acquire_source_image) (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra); - cairo_status_t - (*set_matrix) (void *surface, - cairo_matrix_t *matrix); + void + (* release_source_image) (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra); cairo_status_t - (*set_filter) (void *surface, - cairo_filter_t filter); + (*acquire_dest_image) (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect, + void **image_extra); + void + (*release_dest_image) (void *abstract_surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra); + cairo_status_t - (*set_repeat) (void *surface, - int repeat); - + (*clone_similar) (void *surface, + cairo_surface_t *src, + cairo_surface_t **clone_out); + /* XXX: dst should be the first argument for consistency */ cairo_int_status_t (*composite) (cairo_operator_t operator, - cairo_surface_t *src, - cairo_surface_t *mask, + cairo_pattern_t *src, + cairo_pattern_t *mask, void *dst, int src_x, int src_y, @@ -576,10 +610,14 @@ typedef struct _cairo_surface_backend { /* XXX: dst should be the first argument for consistency */ cairo_int_status_t (*composite_trapezoids) (cairo_operator_t operator, - cairo_surface_t *src, + cairo_pattern_t *pattern, void *dst, - int xSrc, - int ySrc, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int num_traps); @@ -592,24 +630,22 @@ typedef struct _cairo_surface_backend { cairo_int_status_t (*set_clip_region) (void *surface, pixman_region16_t *region); - cairo_int_status_t - (*create_pattern) (void *surface, - cairo_pattern_t *pattern, - cairo_box_t *extents); - /* * This is an optional entry to let the surface manage its own glyph * resources. If null, the font will be asked to render against this * surface, using image surfaces as glyphs. */ cairo_status_t - (*show_glyphs) (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, + (*show_glyphs) (cairo_font_t *font, cairo_operator_t operator, - cairo_surface_t *source, + cairo_pattern_t *pattern, void *surface, int source_x, int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, const cairo_glyph_t *glyphs, int num_glyphs); } cairo_surface_backend_t; @@ -640,6 +676,7 @@ struct _cairo_image_surface { cairo_surface_t base; /* libic-specific fields */ + cairo_format_t format; char *data; int owns_data; @@ -681,60 +718,77 @@ typedef enum { typedef struct _cairo_color_stop { cairo_fixed_t offset; - cairo_fixed_48_16_t scale; - int id; - unsigned char color_char[4]; + cairo_color_t color; } cairo_color_stop_t; -typedef void (*cairo_shader_function_t) (unsigned char *color0, - unsigned char *color1, - cairo_fixed_t factor, - int *pixel); +struct _cairo_pattern { + cairo_pattern_type_t type; + unsigned int ref_count; + cairo_matrix_t matrix; + cairo_filter_t filter; + cairo_extend_t extend; + double alpha; +}; + +typedef struct _cairo_solid_pattern { + cairo_pattern_t base; + + double red, green, blue; +} cairo_solid_pattern_t; + +typedef struct _cairo_surface_pattern { + cairo_pattern_t base; + + cairo_surface_t *surface; +} cairo_surface_pattern_t; -typedef struct _cairo_shader_op { +typedef struct _cairo_gradient_pattern { + cairo_pattern_t base; + cairo_color_stop_t *stops; - int n_stops; - cairo_fixed_t min_offset; - cairo_fixed_t max_offset; - cairo_extend_t extend; - cairo_shader_function_t shader_function; -} cairo_shader_op_t; + int n_stops; +} cairo_gradient_pattern_t; -struct _cairo_pattern { - unsigned int ref_count; - +typedef struct _cairo_linear_pattern { + cairo_gradient_pattern_t base; + + cairo_point_double_t point0; + cairo_point_double_t point1; +} cairo_linear_pattern_t; + +typedef struct _cairo_radial_pattern { + cairo_gradient_pattern_t base; + + cairo_point_double_t center0; + cairo_point_double_t center1; + double radius0; + double radius1; +} cairo_radial_pattern_t; + +typedef union { + cairo_gradient_pattern_t base; + + cairo_linear_pattern_t linear; + cairo_radial_pattern_t radial; +} cairo_gradient_pattern_union_t; + +typedef union { + cairo_pattern_t base; + + cairo_solid_pattern_t solid; + cairo_surface_pattern_t surface; + cairo_gradient_pattern_union_t gradient; +} cairo_pattern_union_t; + +typedef struct _cairo_surface_attributes { + cairo_matrix_t matrix; cairo_extend_t extend; cairo_filter_t filter; - cairo_matrix_t matrix; - - cairo_color_stop_t *stops; - int n_stops; - - cairo_color_t color; - - cairo_surface_t *source; - cairo_point_double_t source_offset; - - cairo_pattern_type_t type; - union { - struct { - cairo_surface_t *surface; - cairo_matrix_t save_matrix; - int save_repeat; - cairo_filter_t save_filter; - } surface; - struct { - cairo_point_double_t point0; - cairo_point_double_t point1; - } linear; - struct { - cairo_point_double_t center0; - cairo_point_double_t center1; - double radius0; - double radius1; - } radial; - } u; -}; + int x_offset; + int y_offset; + cairo_bool_t acquired; + void *extra; +} cairo_surface_attributes_t; typedef struct _cairo_traps { cairo_trapezoid_t *traps; @@ -746,19 +800,21 @@ typedef struct _cairo_traps { #define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL #define CAIRO_FONT_WEIGHT_DEFAULT CAIRO_FONT_WEIGHT_NORMAL -#ifdef CAIRO_HAS_FT_FONT - -#define CAIRO_FONT_FAMILY_DEFAULT "serif" +#if defined (CAIRO_HAS_WIN32_FONT) -/* XXX: Platform-specific. Other platforms may want a different default */ -#define CAIRO_FONT_BACKEND_DEFAULT &cairo_ft_font_backend +#define CAIRO_FONT_FAMILY_DEFAULT "Arial" +#define CAIRO_FONT_BACKEND_DEFAULT &cairo_win32_font_backend -#elif defined(CAIRO_HAS_ATSUI_FONT) +#elif defined (CAIRO_HAS_ATSUI_FONT) #define CAIRO_FONT_FAMILY_DEFAULT "Monaco" - #define CAIRO_FONT_BACKEND_DEFAULT &cairo_atsui_font_backend +#elif defined (CAIRO_HAS_FT_FONT) + +#define CAIRO_FONT_FAMILY_DEFAULT "serif" +#define CAIRO_FONT_BACKEND_DEFAULT &cairo_ft_font_backend + #endif #define CAIRO_GSTATE_OPERATOR_DEFAULT CAIRO_OPERATOR_OVER @@ -772,10 +828,7 @@ typedef struct _cairo_traps { /* Need a name distinct from the cairo_clip function */ typedef struct _cairo_clip_rec { - int x; - int y; - int width; - int height; + cairo_rectangle_t rect; pixman_region16_t *region; cairo_surface_t *surface; } cairo_clip_rec_t; @@ -797,7 +850,11 @@ typedef struct _cairo_gstate { int num_dashes; double dash_offset; - cairo_unscaled_font_t *font; + char *font_family; /* NULL means CAIRO_FONT_FAMILY_DEFAULT; */ + cairo_font_slant_t font_slant; + cairo_font_weight_t font_weight; + + cairo_font_t *font; /* Specific to the current CTM */ cairo_surface_t *surface; @@ -868,7 +925,7 @@ _cairo_fixed_integer_ceil (cairo_fixed_t f); cairo_private cairo_gstate_t * _cairo_gstate_create (void); -cairo_private void +cairo_private cairo_status_t _cairo_gstate_init (cairo_gstate_t *gstate); cairo_private cairo_status_t @@ -1086,13 +1143,13 @@ cairo_private cairo_status_t _cairo_gstate_in_stroke (cairo_gstate_t *gstate, double x, double y, - int *inside_ret); + cairo_bool_t *inside_ret); cairo_private cairo_status_t _cairo_gstate_in_fill (cairo_gstate_t *gstate, double x, double y, - int *inside_ret); + cairo_bool_t *inside_ret); cairo_private cairo_status_t _cairo_gstate_init_clip (cairo_gstate_t *gstate); @@ -1119,6 +1176,10 @@ cairo_private cairo_status_t _cairo_gstate_scale_font (cairo_gstate_t *gstate, double scale); +cairo_private void +_cairo_gstate_current_font_scale (cairo_gstate_t *gstate, + cairo_font_scale_t *sc); + cairo_private cairo_status_t _cairo_gstate_transform_font (cairo_gstate_t *gstate, cairo_matrix_t *matrix); @@ -1177,26 +1238,29 @@ cairo_private void _cairo_color_set_rgb (cairo_color_t *color, double red, double green, double blue); cairo_private void -_cairo_color_get_rgb (cairo_color_t *color, double *red, double *green, double *blue); +_cairo_color_get_rgb (const cairo_color_t *color, + double *red, double *green, double *blue); cairo_private void _cairo_color_set_alpha (cairo_color_t *color, double alpha); /* cairo_font.c */ -cairo_private cairo_unscaled_font_t * -_cairo_unscaled_font_create (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight); +cairo_private cairo_status_t +_cairo_font_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight, + cairo_font_scale_t *sc, + cairo_font_t **font); cairo_private void -_cairo_font_init (cairo_font_t *scaled, - cairo_font_scale_t *scale, - cairo_unscaled_font_t *unscaled); +_cairo_font_init (cairo_font_t *font, + cairo_font_scale_t *scale, + const cairo_font_backend_t *backend); -cairo_private cairo_status_t -_cairo_unscaled_font_init (cairo_unscaled_font_t *font, - const struct _cairo_font_backend *backend); +cairo_private void +_cairo_unscaled_font_init (cairo_unscaled_font_t *font, + const struct _cairo_font_backend *backend); cairo_private void _cairo_unscaled_font_reference (cairo_unscaled_font_t *font); @@ -1205,48 +1269,56 @@ cairo_private void _cairo_unscaled_font_destroy (cairo_unscaled_font_t *font); cairo_private cairo_status_t -_cairo_unscaled_font_font_extents (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - cairo_font_extents_t *extents); +_cairo_font_font_extents (cairo_font_t *font, + cairo_font_extents_t *extents); + +cairo_private cairo_status_t +_cairo_font_text_to_glyphs (cairo_font_t *font, + const unsigned char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs); cairo_private cairo_status_t -_cairo_unscaled_font_text_to_glyphs (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - int *num_glyphs); +_cairo_font_glyph_extents (cairo_font_t *font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); cairo_private cairo_status_t -_cairo_unscaled_font_glyph_extents (cairo_unscaled_font_t *font, - cairo_font_scale_t *scale, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents); +_cairo_font_glyph_bbox (cairo_font_t *font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox); cairo_private cairo_status_t -_cairo_unscaled_font_glyph_bbox (cairo_unscaled_font_t *font, - cairo_font_scale_t *size, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_box_t *bbox); +_cairo_font_show_glyphs (cairo_font_t *font, + cairo_operator_t operator, + cairo_pattern_t *source, + cairo_surface_t *surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int widht, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs); cairo_private cairo_status_t -_cairo_unscaled_font_show_glyphs (cairo_unscaled_font_t *font, - cairo_font_scale_t *size, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - int source_x, - int source_y, - cairo_glyph_t *glyphs, - int num_glyphs); +_cairo_font_glyph_path (cairo_font_t *font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path); cairo_private cairo_status_t -_cairo_unscaled_font_glyph_path (cairo_unscaled_font_t *font, - cairo_font_scale_t *size, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_path_t *path); +_cairo_font_glyph_path (cairo_font_t *font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path); + +cairo_private void +_cairo_font_get_glyph_cache_key (cairo_font_t *font, + cairo_glyph_cache_key_t *key); /* cairo_hull.c */ cairo_private cairo_status_t @@ -1355,8 +1427,8 @@ _cairo_surface_fill_rectangle (cairo_surface_t *surface, cairo_private cairo_status_t _cairo_surface_composite (cairo_operator_t operator, - cairo_surface_t *src, - cairo_surface_t *mask, + cairo_pattern_t *src, + cairo_pattern_t *mask, cairo_surface_t *dst, int src_x, int src_y, @@ -1376,10 +1448,14 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface, cairo_private cairo_status_t _cairo_surface_composite_trapezoids (cairo_operator_t operator, - cairo_surface_t *src, + cairo_pattern_t *pattern, cairo_surface_t *dst, - int xSrc, - int ySrc, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, cairo_trapezoid_t *traps, int ntraps); @@ -1392,20 +1468,37 @@ _cairo_surface_show_page (cairo_surface_t *surface); cairo_private double _cairo_surface_pixels_per_inch (cairo_surface_t *surface); -cairo_private cairo_image_surface_t * -_cairo_surface_get_image (cairo_surface_t *surface); +cairo_private cairo_status_t +_cairo_surface_acquire_source_image (cairo_surface_t *surface, + cairo_image_surface_t **image_out, + void **image_extra); + +cairo_private void +_cairo_surface_release_source_image (cairo_surface_t *surface, + cairo_image_surface_t *image, + void *image_extra); cairo_private cairo_status_t -_cairo_surface_set_image (cairo_surface_t *surface, - cairo_image_surface_t *image); +_cairo_surface_acquire_dest_image (cairo_surface_t *surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_t *image_rect, + void **image_extra); +cairo_private void +_cairo_surface_release_dest_image (cairo_surface_t *surface, + cairo_rectangle_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_t *image_rect, + void *image_extra); + cairo_private cairo_status_t -_cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region); +_cairo_surface_clone_similar (cairo_surface_t *surface, + cairo_surface_t *src, + cairo_surface_t **clone_out); cairo_private cairo_status_t -_cairo_surface_create_pattern (cairo_surface_t *surface, - cairo_pattern_t *pattern, - cairo_box_t *extents); +_cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region); /* cairo_image_surface.c */ @@ -1435,6 +1528,9 @@ cairo_private cairo_int_status_t _cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, pixman_region16_t *region); +cairo_private int +_cairo_surface_is_image (cairo_surface_t *surface); + /* cairo_pen.c */ cairo_private cairo_status_t _cairo_pen_init (cairo_pen_t *pen, double radius, cairo_gstate_t *gstate); @@ -1540,7 +1636,7 @@ _cairo_matrix_compute_eigen_values (cairo_matrix_t *matrix, double *lambda1, dou cairo_private cairo_status_t _cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double *sy, int x_major); -cairo_private int +cairo_private cairo_bool_t _cairo_matrix_is_integer_translation(cairo_matrix_t *matrix, int *itx, int *ity); /* cairo_traps.c */ @@ -1581,18 +1677,29 @@ cairo_private int _cairo_slope_counter_clockwise (cairo_slope_t *a, cairo_slope_t *b); /* cairo_pattern.c */ -cairo_private void -_cairo_pattern_init (cairo_pattern_t *pattern); cairo_private cairo_status_t _cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other); cairo_private void -_cairo_pattern_fini (cairo_pattern_t *pattern); +_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, + double red, double green, double blue); cairo_private void -_cairo_pattern_init_solid (cairo_pattern_t *pattern, - double red, double green, double blue); +_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, + cairo_surface_t *surface); + +cairo_private void +_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern, + double x0, double y0, double x1, double y1); + +cairo_private void +_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern, + double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1); + +cairo_private void +_cairo_pattern_fini (cairo_pattern_t *pattern); cairo_private cairo_pattern_t * _cairo_pattern_create_solid (double red, double green, double blue); @@ -1605,27 +1712,56 @@ cairo_private void _cairo_pattern_set_alpha (cairo_pattern_t *pattern, double alpha); cairo_private void -_cairo_pattern_set_source_offset (cairo_pattern_t *pattern, - double x, double y); - -cairo_private void _cairo_pattern_transform (cairo_pattern_t *pattern, cairo_matrix_t *ctm_inverse); -cairo_private void -_cairo_pattern_prepare_surface (cairo_pattern_t *pattern); +cairo_private cairo_bool_t +_cairo_pattern_is_opaque (cairo_pattern_t *pattern); -cairo_private void -_cairo_pattern_shader_init (cairo_pattern_t *pattern, - cairo_shader_op_t *op); +cairo_private cairo_int_status_t +_cairo_pattern_acquire_surface (cairo_pattern_t *pattern, + cairo_surface_t *dst, + int x, + int y, + unsigned int width, + unsigned int height, + cairo_surface_t **surface_out, + cairo_surface_attributes_t *attributes); cairo_private void -_cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op, - cairo_fixed_t factor, - int *pixel); +_cairo_pattern_release_surface (cairo_surface_t *dst, + cairo_surface_t *surface, + cairo_surface_attributes_t *attributes); -cairo_private cairo_image_surface_t * -_cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box); +cairo_private cairo_int_status_t +_cairo_pattern_acquire_surfaces (cairo_pattern_t *src, + cairo_pattern_t *mask, + cairo_surface_t *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + unsigned int width, + unsigned int height, + cairo_surface_t **src_out, + cairo_surface_t **mask_out, + cairo_surface_attributes_t *src_attributes, + cairo_surface_attributes_t *mask_attributes); + + +/* cairo_unicode.c */ + +cairo_private cairo_status_t +_cairo_utf8_to_ucs4 (const char *str, + int len, + uint32_t **result, + int *items_written); + +cairo_private cairo_status_t +_cairo_utf8_to_utf16 (const char *str, + int len, + uint16_t **result, + int *items_written); /* Avoid unnecessary PLT entries. */ |