diff options
Diffstat (limited to 'src')
42 files changed, 7771 insertions, 2134 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 2ed7341b..518e69f2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,12 +1,29 @@ lib_LTLIBRARIES = libcairo.la include_HEADERS = cairo.h cairo-features.h +if CAIRO_HAS_PS_SURFACE +libcairo_ps_sources = cairo_ps_surface.c +endif + +if CAIRO_HAS_PNG_SURFACE +libcairo_png_sources = cairo_png_surface.c +endif + if CAIRO_HAS_XLIB_SURFACE libcairo_xlib_sources = cairo_xlib_surface.c -else -libcairo_xlib_sources = endif +if CAIRO_HAS_XCB_SURFACE +libcairo_xcb_sources = cairo_xcb_surface.c +endif + +# These names match automake style variable definition conventions so +# without these lines, automake will complain during the handling of +# the libcairo_la_LIBADD below. (The INCLUDES is an autoconf only +# term and automake does not care about it) +FONTCONFIG_LIBS=@FONTCONFIG_LIBS@ +XRENDER_LIBS=@XRENDER_LIBS@ + libcairo_la_SOURCES = \ cairo.c \ cairo.h \ @@ -24,16 +41,19 @@ libcairo_la_SOURCES = \ cairo_path_stroke.c \ cairo_pen.c \ cairo_polygon.c \ - cairo_ps_surface.c \ cairo_slope.c \ cairo_spline.c \ cairo_surface.c \ cairo_traps.c \ + cairo_pattern.c \ + $(libcairo_ps_sources) \ + $(libcairo_png_sources) \ $(libcairo_xlib_sources)\ + $(libcairo_xcb_sources) \ cairoint.h -libcairo_la_LDFLAGS = -version-info @VERSION_INFO@ +libcairo_la_LDFLAGS = -version-info @VERSION_INFO@ -no-undefined -INCLUDES = $(CAIRO_CFLAGS) $(FONTCONFIG_CFLAGS) $(XRENDER_CFLAGS) $(X_CFLAGS) +INCLUDES = -I$(srcdir) $(CAIRO_CFLAGS) $(FONTCONFIG_CFLAGS) $(XRENDER_CFLAGS) $(XCB_CFLAGS) $(PNG_CFLAGS) -libcairo_la_LIBADD = $(CAIRO_LIBS) $(FONTCONFIG_LIBS) $(XRENDER_LIBS) $(X_LIBS) -lm +libcairo_la_LIBADD = $(CAIRO_LIBS) $(FONTCONFIG_LIBS) $(XRENDER_LIBS) $(XCB_LIBS) $(PS_LIBS) $(PNG_LIBS) -lm diff --git a/src/cairo-features.h.in b/src/cairo-features.h.in index 9d83d070..e9dacad5 100644 --- a/src/cairo-features.h.in +++ b/src/cairo-features.h.in @@ -28,6 +28,12 @@ #ifndef _CAIRO_CONFIG_H_ #define _CAIRO_CONFIG_H_ +#define @PS_SURFACE_FEATURE@ + +#define @PNG_SURFACE_FEATURE@ + #define @XLIB_SURFACE_FEATURE@ +#define @XCB_SURFACE_FEATURE@ + #endif diff --git a/src/cairo-fixed.c b/src/cairo-fixed.c index 33088546..9a7e7bc4 100644 --- a/src/cairo-fixed.c +++ b/src/cairo-fixed.c @@ -39,8 +39,15 @@ _cairo_fixed_from_double (double d) return (cairo_fixed_t) (d * 65536); } +cairo_fixed_t +_cairo_fixed_from_26_6 (uint32_t i) +{ + return i << 10; +} + double _cairo_fixed_to_double (cairo_fixed_t f) { return ((double) f) / 65536.0; } + diff --git a/src/cairo-font.c b/src/cairo-font.c index f6bf390a..157ebedb 100644 --- a/src/cairo-font.c +++ b/src/cairo-font.c @@ -28,12 +28,18 @@ #include "cairoint.h" cairo_font_t * -_cairo_font_create (char *family, +_cairo_font_create (const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { const struct cairo_font_backend *backend = CAIRO_FONT_BACKEND_DEFAULT; + /* 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). */ + return backend->create (family, slant, weight); } @@ -118,31 +124,30 @@ _cairo_font_show_glyphs (cairo_font_t *font, cairo_operator_t operator, cairo_surface_t *source, cairo_surface_t *surface, - double x, - double y, cairo_glyph_t *glyphs, int num_glyphs) { return font->backend->show_glyphs(font, operator, source, - surface, x, y, glyphs, num_glyphs); + surface, glyphs, num_glyphs); } cairo_status_t _cairo_font_text_path (cairo_font_t *font, - cairo_path_t *path, - const unsigned char *utf8) + double x, + double y, + const unsigned char *utf8, + cairo_path_t *path) { - return font->backend->text_path(font, path, utf8); + return font->backend->text_path(font, x, y, utf8, path); } cairo_status_t _cairo_font_glyph_path (cairo_font_t *font, - cairo_path_t *path, cairo_glyph_t *glyphs, - int num_glyphs) + int num_glyphs, + cairo_path_t *path) { - return font->backend->glyph_path(font, path, - glyphs, num_glyphs); + return font->backend->glyph_path(font, glyphs, num_glyphs, path); } cairo_status_t diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index c3e4306d..77cf59b0 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -25,7 +25,10 @@ #include "cairoint.h" #include <fontconfig/fontconfig.h> #include <fontconfig/fcfreetype.h> -#include <freetype/freetype.h> + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_OUTLINE_H typedef struct { cairo_font_t base; @@ -39,13 +42,10 @@ typedef struct { FcPattern *pattern; } cairo_ft_font_t; - -#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 63.0)) -#define DOUBLE_FROM_26_6(t) (((double)((t) >> 6)) \ - + ((double)((t) & 0x3F) / 63.0)) -#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65535.0)) -#define DOUBLE_FROM_16_16(t) (((double)((t) >> 16)) \ - + ((double)((t) & 0xFFFF) / 65535.0)) +#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0)) +#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0) +#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) +#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) /* implement the platform-specific interface */ @@ -101,29 +101,31 @@ cairo_ft_font_create (FT_Library ft_library, FcPattern *pattern) } FT_Face -cairo_ft_font_face (cairo_font_t *font) +cairo_ft_font_face (cairo_font_t *abstract_font) { + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + if (font == NULL) return NULL; - return ((cairo_ft_font_t *) font)->face; + return font->face; } FcPattern * -cairo_ft_font_pattern (cairo_font_t *font) +cairo_ft_font_pattern (cairo_font_t *abstract_font) { + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + if (font == NULL) return NULL; - return ((cairo_ft_font_t *) font)->pattern; + return font->pattern; } - - /* implement the backend interface */ static cairo_font_t * -_cairo_ft_font_create (char *family, +_cairo_ft_font_create (const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { @@ -175,6 +177,9 @@ _cairo_ft_font_create (char *family, } font = cairo_ft_font_create (ft_library, pat); + if (font == NULL) + return NULL; + ft_font = (cairo_ft_font_t *) font; ft_font->owns_ft_library = 1; @@ -188,45 +193,43 @@ _cairo_ft_font_create (char *family, return font; } - static cairo_font_t * -_cairo_ft_font_copy (cairo_font_t *font) +_cairo_ft_font_copy (void *abstract_font) { - cairo_ft_font_t * ft_font_new = NULL; - cairo_ft_font_t * ft_font = NULL; + cairo_ft_font_t * font_new = NULL; + cairo_ft_font_t * font = abstract_font; - ft_font = (cairo_ft_font_t *)font; + if (font->base.backend != &cairo_ft_font_backend) + return NULL; - ft_font_new = (cairo_ft_font_t *)cairo_ft_font_create_for_ft_face (ft_font->face); - if (ft_font_new == NULL) + font_new = (cairo_ft_font_t *) cairo_ft_font_create_for_ft_face (font->face); + if (font_new == NULL) return NULL; - if (ft_font_new != NULL && ft_font->pattern != NULL) - ft_font_new->pattern = FcPatternDuplicate (ft_font->pattern); + if (font_new != NULL && font->pattern != NULL) + font_new->pattern = FcPatternDuplicate (font->pattern); - return (cairo_font_t *)ft_font_new; + return (cairo_font_t *) font_new; } static void -_cairo_ft_font_destroy (cairo_font_t *font) +_cairo_ft_font_destroy (void *abstract_font) { - cairo_ft_font_t * ft_font = NULL; + cairo_ft_font_t * font = abstract_font; if (font == NULL) return; - ft_font = (cairo_ft_font_t *)font; - - if (ft_font->face != NULL && ft_font->owns_face) - FT_Done_Face (ft_font->face); + if (font->face != NULL && font->owns_face) + FT_Done_Face (font->face); - if (ft_font->pattern != NULL) - FcPatternDestroy (ft_font->pattern); + if (font->pattern != NULL) + FcPatternDestroy (font->pattern); - if (ft_font->ft_library && ft_font->owns_ft_library) - FT_Done_FreeType (ft_font->ft_library); + if (font->ft_library && font->owns_ft_library) + FT_Done_FreeType (font->ft_library); - free (ft_font); + free (font); } static void @@ -260,21 +263,7 @@ _utf8_to_ucs4 (char const *utf8, len -= step; utf8 += step; } - *nchars = alloc; -} - -static void -_get_scale_factors(cairo_matrix_t *matrix, double *sx, double *sy) -{ - double e0, e1; - e1 = 1.; e0 = 0.; - - cairo_matrix_transform_distance (matrix, &e1, &e0); - *sx = sqrt(e1*e1 + e0*e0); - - e1 = 1.; e0 = 0.; - cairo_matrix_transform_distance (matrix, &e0, &e1); - *sy = sqrt(e1*e1 + e0*e0); + *nchars = n; } static void @@ -292,7 +281,7 @@ _install_font_matrix(cairo_matrix_t *matrix, FT_Face face) * transformation. */ - _get_scale_factors(matrix, &scale_x, &scale_y); + _cairo_matrix_compute_scale_factors (matrix, &scale_x, &scale_y); cairo_matrix_copy (&normalized, matrix); @@ -314,23 +303,19 @@ _install_font_matrix(cairo_matrix_t *matrix, FT_Face face) 0, 0); } - static int -_utf8_to_glyphs (cairo_font_t *font, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - size_t *nglyphs) +_utf8_to_glyphs (cairo_ft_font_t *font, + const unsigned char *utf8, + double x0, + double y0, + cairo_glyph_t **glyphs, + size_t *nglyphs) { - cairo_ft_font_t *ft; + FT_Face face = font->face; double x = 0., y = 0.; size_t i; FT_ULong *ucs4 = NULL; - if (font == NULL) - return 0; - - ft = (cairo_ft_font_t *)font; - _utf8_to_ucs4 (utf8, &ucs4, nglyphs); if (ucs4 == NULL) @@ -343,18 +328,18 @@ _utf8_to_glyphs (cairo_font_t *font, return 0; } - _install_font_matrix (&font->matrix, ft->face); + _install_font_matrix (&font->base.matrix, face); for (i = 0; i < *nglyphs; i++) { - (*glyphs)[i].index = FT_Get_Char_Index (ft->face, ucs4[i]); - (*glyphs)[i].x = x; - (*glyphs)[i].y = y; + (*glyphs)[i].index = FT_Get_Char_Index (face, ucs4[i]); + (*glyphs)[i].x = x0 + x; + (*glyphs)[i].y = y0 + y; - FT_Load_Glyph (ft->face, (*glyphs)[i].index, FT_LOAD_DEFAULT); + FT_Load_Glyph (face, (*glyphs)[i].index, FT_LOAD_DEFAULT); - x += DOUBLE_FROM_26_6 (ft->face->glyph->advance.x); - y -= DOUBLE_FROM_26_6 (ft->face->glyph->advance.y); + x += DOUBLE_FROM_26_6 (face->glyph->advance.x); + y -= DOUBLE_FROM_26_6 (face->glyph->advance.y); } free (ucs4); @@ -362,51 +347,114 @@ _utf8_to_glyphs (cairo_font_t *font, } static cairo_status_t -_cairo_ft_font_font_extents (cairo_font_t *font, - cairo_font_extents_t *extents) +_cairo_ft_font_font_extents (void *abstract_font, + cairo_font_extents_t *extents) { + cairo_ft_font_t *font = abstract_font; + FT_Face face = font->face; double scale_x, scale_y; - cairo_ft_font_t *ft = (cairo_ft_font_t *)font; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - _get_scale_factors(&font->matrix, &scale_x, &scale_y); + double upm = face->units_per_EM; -#define FONT_UNIT_TO_DEV(x) ((double)(x) / (double)(ft->face->units_per_EM)) + _cairo_matrix_compute_scale_factors (&font->base.matrix, &scale_x, &scale_y); - extents->ascent = FONT_UNIT_TO_DEV(ft->face->ascender) * scale_y; - extents->descent = FONT_UNIT_TO_DEV(ft->face->descender) * scale_y; - extents->height = FONT_UNIT_TO_DEV(ft->face->height) * scale_y; - extents->max_x_advance = FONT_UNIT_TO_DEV(ft->face->max_advance_width) * scale_x; - extents->max_y_advance = FONT_UNIT_TO_DEV(ft->face->max_advance_height) * scale_y; - return status; + 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; + + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_ft_font_glyph_extents (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) +_cairo_ft_font_glyph_extents (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) { - cairo_ft_font_t *ft; - cairo_status_t status = CAIRO_STATUS_SUCCESS; + int i; + cairo_ft_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; - ft = (cairo_ft_font_t *)font; + 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; + } - /* FIXME: lift code from xft to do this */ + origin.x = glyphs[0].x; + origin.y = glyphs[0].y; - return status; + _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_ft_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; + + return CAIRO_STATUS_SUCCESS; } + static cairo_status_t -_cairo_ft_font_text_extents (cairo_font_t *font, - const unsigned char *utf8, - cairo_text_extents_t *extents) +_cairo_ft_font_text_extents (void *abstract_font, + const unsigned char *utf8, + cairo_text_extents_t *extents) { + cairo_ft_font_t *font = abstract_font; cairo_glyph_t *glyphs; size_t nglyphs; cairo_status_t status = CAIRO_STATUS_SUCCESS; - if (_utf8_to_glyphs (font, utf8, &glyphs, &nglyphs)) + if (_utf8_to_glyphs (font, utf8, 0, 0, &glyphs, &nglyphs)) { status = _cairo_ft_font_glyph_extents (font, glyphs, nglyphs, extents); @@ -414,24 +462,22 @@ _cairo_ft_font_text_extents (cairo_font_t *font, } return status; } - - static cairo_status_t -_cairo_ft_font_show_glyphs (cairo_font_t *font, +_cairo_ft_font_show_glyphs (void *abstract_font, cairo_operator_t operator, cairo_surface_t *source, cairo_surface_t *surface, - double x0, - double y0, const cairo_glyph_t *glyphs, int num_glyphs) { + cairo_ft_font_t *font = abstract_font; cairo_status_t status; int i; cairo_ft_font_t *ft = NULL; FT_GlyphSlot glyphslot; cairo_surface_t *mask = NULL; + cairo_point_double_t origin; double x, y; int width, height, stride; @@ -444,22 +490,27 @@ _cairo_ft_font_show_glyphs (cairo_font_t *font, ft = (cairo_ft_font_t *)font; glyphslot = ft->face->glyph; - _install_font_matrix (&font->matrix, ft->face); + _install_font_matrix (&font->base.matrix, ft->face); for (i = 0; i < num_glyphs; i++) { unsigned char *bitmap; FT_Load_Glyph (ft->face, glyphs[i].index, FT_LOAD_DEFAULT); - FT_Render_Glyph (glyphslot, FT_RENDER_MODE_NORMAL); + FT_Render_Glyph (glyphslot, ft_render_mode_normal); width = glyphslot->bitmap.width; height = glyphslot->bitmap.rows; stride = glyphslot->bitmap.pitch; bitmap = glyphslot->bitmap.buffer; - x = x0 + glyphs[i].x; - y = y0 + glyphs[i].y; + x = glyphs[i].x; + y = glyphs[i].y; + + if (i == 0) { + origin.x = x; + origin.y = y; + } /* X gets upset with zero-sized images (such as whitespace) */ if (width * height == 0) @@ -502,11 +553,14 @@ _cairo_ft_font_show_glyphs (cairo_font_t *font, return CAIRO_STATUS_NO_MEMORY; } - status = _cairo_surface_composite (operator, source, mask, surface, - 0, 0, 0, 0, - x + glyphslot->bitmap_left, - y - glyphslot->bitmap_top, - (double)width, (double)height); + status = + _cairo_surface_composite (operator, source, mask, surface, + -origin.x + x + glyphslot->bitmap_left, + -origin.y + y - glyphslot->bitmap_top, + 0, 0, + x + glyphslot->bitmap_left, + y - glyphslot->bitmap_top, + (double) width, (double) height); cairo_surface_destroy (mask); if (bitmap != glyphslot->bitmap.buffer) @@ -519,7 +573,7 @@ _cairo_ft_font_show_glyphs (cairo_font_t *font, } static cairo_status_t -_cairo_ft_font_show_text (cairo_font_t *font, +_cairo_ft_font_show_text (void *abstract_font, cairo_operator_t operator, cairo_surface_t *source, cairo_surface_t *surface, @@ -527,15 +581,16 @@ _cairo_ft_font_show_text (cairo_font_t *font, double y0, const unsigned char *utf8) { + cairo_ft_font_t *font = abstract_font; cairo_glyph_t *glyphs; - size_t nglyphs; + int num_glyphs; - if (_utf8_to_glyphs (font, utf8, &glyphs, &nglyphs)) + if (_utf8_to_glyphs (font, utf8, x0, y0, &glyphs, &num_glyphs)) { cairo_status_t res; res = _cairo_ft_font_show_glyphs (font, operator, - source, surface, x0, y0, - glyphs, nglyphs); + source, surface, + glyphs, num_glyphs); free (glyphs); return res; } @@ -543,35 +598,147 @@ _cairo_ft_font_show_text (cairo_font_t *font, return CAIRO_STATUS_NO_MEMORY; } +static int +_move_to (FT_Vector *to, void *closure) +{ + cairo_path_t *path = closure; + cairo_point_t point; + + point.x = _cairo_fixed_from_26_6 (to->x); + point.y = _cairo_fixed_from_26_6 (to->y); + + _cairo_path_close_path (path); + _cairo_path_move_to (path, &point); + + return 0; +} + +static int +_line_to (FT_Vector *to, void *closure) +{ + cairo_path_t *path = closure; + cairo_point_t point; + + point.x = _cairo_fixed_from_26_6 (to->x); + point.y = _cairo_fixed_from_26_6 (to->y); + + _cairo_path_line_to (path, &point); + + return 0; +} + +static int +_conic_to (FT_Vector *control, FT_Vector *to, void *closure) +{ + cairo_path_t *path = closure; + + cairo_point_t p0, p1, p2, p3; + cairo_point_t conic; + + _cairo_path_current_point (path, &p0); + + conic.x = _cairo_fixed_from_26_6 (control->x); + conic.y = _cairo_fixed_from_26_6 (control->y); + + p3.x = _cairo_fixed_from_26_6 (to->x); + p3.y = _cairo_fixed_from_26_6 (to->y); + + p1.x = p0.x + 2.0/3.0 * (conic.x - p0.x); + p1.y = p0.y + 2.0/3.0 * (conic.y - p0.y); + + p2.x = p3.x + 2.0/3.0 * (conic.x - p3.x); + p2.y = p3.y + 2.0/3.0 * (conic.y - p3.y); + + _cairo_path_curve_to (path, + &p1, &p2, &p3); + + return 0; +} + +static int +_cubic_to (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *closure) +{ + cairo_path_t *path = closure; + cairo_point_t p0, p1, p2; + + p0.x = _cairo_fixed_from_26_6 (control1->x); + p0.y = _cairo_fixed_from_26_6 (control1->y); + + p1.x = _cairo_fixed_from_26_6 (control2->x); + p1.y = _cairo_fixed_from_26_6 (control2->y); + + p2.x = _cairo_fixed_from_26_6 (to->x); + p2.y = _cairo_fixed_from_26_6 (to->y); + + _cairo_path_curve_to (path, &p0, &p1, &p2); + + return 0; +} static cairo_status_t -_cairo_ft_font_glyph_path (cairo_font_t *font, - cairo_path_t *path, - cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_ft_font_glyph_path (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_ft_font_t *ft; - - ft = (cairo_ft_font_t *)font; - - /* FIXME: lift code from xft to do this */ + int i; + cairo_ft_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); - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_ft_font_text_path (cairo_font_t *font, - cairo_path_t *path, - const unsigned char *utf8) +_cairo_ft_font_text_path (void *abstract_font, + double x, + double y, + const unsigned char *utf8, + cairo_path_t *path) { + cairo_ft_font_t *font = abstract_font; cairo_glyph_t *glyphs; size_t nglyphs; - if (_utf8_to_glyphs (font, utf8, &glyphs, &nglyphs)) + if (_utf8_to_glyphs (font, utf8, x, y, &glyphs, &nglyphs)) { cairo_status_t res; - res = _cairo_ft_font_glyph_path (font, path, glyphs, nglyphs); + res = _cairo_ft_font_glyph_path (font, glyphs, nglyphs, path); free (glyphs); return res; } @@ -579,7 +746,6 @@ _cairo_ft_font_text_path (cairo_font_t *font, return CAIRO_STATUS_NO_MEMORY; } - cairo_font_t * cairo_ft_font_create_for_ft_face (FT_Face face) { @@ -602,16 +768,15 @@ cairo_ft_font_create_for_ft_face (FT_Face face) return (cairo_font_t *) f; } - const struct cairo_font_backend cairo_ft_font_backend = { - font_extents: (void *) _cairo_ft_font_font_extents, - text_extents: (void *) _cairo_ft_font_text_extents, - glyph_extents: (void *) _cairo_ft_font_glyph_extents, - show_text: (void *) _cairo_ft_font_show_text, - show_glyphs: (void *) _cairo_ft_font_show_glyphs, - text_path: (void *) _cairo_ft_font_text_path, - glyph_path: (void *) _cairo_ft_font_glyph_path, - create: (void *) _cairo_ft_font_create, - copy: (void *) _cairo_ft_font_copy, - destroy: (void *) _cairo_ft_font_destroy + _cairo_ft_font_create, + _cairo_ft_font_copy, + _cairo_ft_font_destroy, + _cairo_ft_font_font_extents, + _cairo_ft_font_text_extents, + _cairo_ft_font_glyph_extents, + _cairo_ft_font_show_text, + _cairo_ft_font_show_glyphs, + _cairo_ft_font_text_path, + _cairo_ft_font_glyph_path, }; diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 8bc6e704..ed8c8a1a 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -30,15 +30,9 @@ #include "cairoint.h" -static void -_cairo_gstate_set_current_point (cairo_gstate_t *gstate, double x, double y); - -static cairo_status_t -_cairo_gstate_ensure_source (cairo_gstate_t *gstate); - static cairo_status_t _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, - cairo_surface_t *src, + cairo_pattern_t *src, cairo_operator_t operator, cairo_surface_t *dst, cairo_traps_t *traps); @@ -79,25 +73,20 @@ _cairo_gstate_init (cairo_gstate_t *gstate) CAIRO_FONT_WEIGHT_DEFAULT); gstate->surface = NULL; - gstate->source = NULL; - gstate->source_offset.x = 0.0; - gstate->source_offset.y = 0.0; - gstate->source_is_solid = 1; + gstate->clip.region = NULL; gstate->clip.surface = NULL; - + + gstate->pattern = _cairo_pattern_create_solid (1.0, 1.0, 1.0); + gstate->pattern_offset.x = 0.0; + gstate->pattern_offset.y = 0.0; gstate->alpha = 1.0; - _cairo_color_init (&gstate->color); gstate->pixels_per_inch = CAIRO_GSTATE_PIXELS_PER_INCH_DEFAULT; _cairo_gstate_default_matrix (gstate); _cairo_path_init (&gstate->path); - gstate->current_point.x = 0.0; - gstate->current_point.y = 0.0; - gstate->has_current_point = 0; - _cairo_pen_init_empty (&gstate->pen_regular); gstate->next = NULL; @@ -130,9 +119,16 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) } } + if (other->clip.region) + { + gstate->clip.region = pixman_region_create (); + pixman_region_copy (gstate->clip.region, other->clip.region); + } + cairo_surface_reference (gstate->surface); - cairo_surface_reference (gstate->source); cairo_surface_reference (gstate->clip.surface); + + cairo_pattern_reference (gstate->pattern); status = _cairo_path_init_copy (&gstate->path, &other->path); if (status) @@ -164,16 +160,15 @@ _cairo_gstate_fini (cairo_gstate_t *gstate) cairo_surface_destroy (gstate->surface); gstate->surface = NULL; - if (gstate->source) - cairo_surface_destroy (gstate->source); - gstate->source = NULL; - gstate->source_is_solid = 1; - if (gstate->clip.surface) cairo_surface_destroy (gstate->clip.surface); gstate->clip.surface = NULL; - _cairo_color_fini (&gstate->color); + if (gstate->clip.region) + pixman_region_destroy (gstate->clip.region); + gstate->clip.region = NULL; + + cairo_pattern_destroy (gstate->pattern); _cairo_matrix_fini (&gstate->ctm); _cairo_matrix_fini (&gstate->ctm_inverse); @@ -358,22 +353,37 @@ _cairo_gstate_current_target_surface (cairo_gstate_t *gstate) } cairo_status_t -_cairo_gstate_set_pattern (cairo_gstate_t *gstate, cairo_surface_t *pattern) +_cairo_gstate_set_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern) { - cairo_surface_destroy (gstate->source); - - gstate->source = pattern; - gstate->source_is_solid = 0; + if (pattern == NULL) + return CAIRO_STATUS_NULL_POINTER; - cairo_surface_reference (gstate->source); + if (gstate->pattern) + cairo_pattern_destroy (gstate->pattern); + + gstate->pattern = pattern; + cairo_pattern_reference (pattern); _cairo_gstate_current_point (gstate, - &gstate->source_offset.x, - &gstate->source_offset.y); - + &gstate->pattern_offset.x, + &gstate->pattern_offset.y); + return CAIRO_STATUS_SUCCESS; } +cairo_pattern_t * +_cairo_gstate_current_pattern (cairo_gstate_t *gstate) +{ + if (gstate == NULL) + return NULL; + +/* XXX: Do we want this? + cairo_pattern_reference (gstate->pattern); +*/ + + return gstate->pattern; +} + cairo_status_t _cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t operator) { @@ -391,25 +401,19 @@ _cairo_gstate_current_operator (cairo_gstate_t *gstate) cairo_status_t _cairo_gstate_set_rgb_color (cairo_gstate_t *gstate, double red, double green, double blue) { - _cairo_color_set_rgb (&gstate->color, red, green, blue); - - if (gstate->source) - cairo_surface_destroy (gstate->source); - - gstate->source = NULL; - gstate->source_offset.x = 0; - gstate->source_offset.y = 0; - gstate->source_is_solid = 1; - + cairo_pattern_destroy (gstate->pattern); + + gstate->pattern = _cairo_pattern_create_solid (red, green, blue); + gstate->pattern_offset.x = 0.0; + gstate->pattern_offset.y = 0.0; + return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_current_rgb_color (cairo_gstate_t *gstate, double *red, double *green, double *blue) { - _cairo_color_get_rgb (&gstate->color, red, green, blue); - - return CAIRO_STATUS_SUCCESS; + return _cairo_pattern_get_rgb (gstate->pattern, red, green, blue); } cairo_status_t @@ -426,20 +430,11 @@ _cairo_gstate_current_tolerance (cairo_gstate_t *gstate) return gstate->tolerance; } -/* XXX: Need to fix this so it does the right thing after set_pattern. */ cairo_status_t _cairo_gstate_set_alpha (cairo_gstate_t *gstate, double alpha) { gstate->alpha = alpha; - _cairo_color_set_alpha (&gstate->color, alpha); - - cairo_surface_destroy (gstate->source); - - gstate->source = NULL; - gstate->source_offset.x = 0; - gstate->source_offset.y = 0; - return CAIRO_STATUS_SUCCESS; } @@ -680,20 +675,10 @@ _cairo_gstate_inverse_transform_distance (cairo_gstate_t *gstate, double *dx, do return CAIRO_STATUS_SUCCESS; } -static void -_cairo_gstate_set_current_point (cairo_gstate_t *gstate, double x, double y) -{ - gstate->current_point.x = x; - gstate->current_point.y = y; - - gstate->has_current_point = 1; -} - cairo_status_t _cairo_gstate_new_path (cairo_gstate_t *gstate) { _cairo_path_fini (&gstate->path); - gstate->has_current_point = 0; return CAIRO_STATUS_SUCCESS; } @@ -701,53 +686,51 @@ _cairo_gstate_new_path (cairo_gstate_t *gstate) cairo_status_t _cairo_gstate_move_to (cairo_gstate_t *gstate, double x, double y) { - cairo_status_t status; + cairo_point_t point; cairo_matrix_transform_point (&gstate->ctm, &x, &y); - status = _cairo_path_move_to (&gstate->path, x, y); - - _cairo_gstate_set_current_point (gstate, x, y); - - gstate->last_move_point = gstate->current_point; + point.x = _cairo_fixed_from_double (x); + point.y = _cairo_fixed_from_double (y); - return status; + return _cairo_path_move_to (&gstate->path, &point); } cairo_status_t _cairo_gstate_line_to (cairo_gstate_t *gstate, double x, double y) { - cairo_status_t status; + cairo_point_t point; cairo_matrix_transform_point (&gstate->ctm, &x, &y); - status = _cairo_path_line_to (&gstate->path, x, y); - - _cairo_gstate_set_current_point (gstate, x, y); + point.x = _cairo_fixed_from_double (x); + point.y = _cairo_fixed_from_double (y); - return status; + return _cairo_path_line_to (&gstate->path, &point); } cairo_status_t _cairo_gstate_curve_to (cairo_gstate_t *gstate, + double x0, double y0, double x1, double y1, - double x2, double y2, - double x3, double y3) + double x2, double y2) { - cairo_status_t status; + cairo_point_t p0, p1, p2; + cairo_matrix_transform_point (&gstate->ctm, &x0, &y0); cairo_matrix_transform_point (&gstate->ctm, &x1, &y1); cairo_matrix_transform_point (&gstate->ctm, &x2, &y2); - cairo_matrix_transform_point (&gstate->ctm, &x3, &y3); - status = _cairo_path_curve_to (&gstate->path, - x1, y1, - x2, y2, - x3, y3); + p0.x = _cairo_fixed_from_double (x0); + p0.y = _cairo_fixed_from_double (y0); - _cairo_gstate_set_current_point (gstate, x3, y3); + p1.x = _cairo_fixed_from_double (x1); + p1.y = _cairo_fixed_from_double (y1); - return status; + p2.x = _cairo_fixed_from_double (x2); + p2.y = _cairo_fixed_from_double (y2); + + return _cairo_path_curve_to (&gstate->path, &p0, &p1, &p2); } /* Spline deviation from the circle in radius would be given by: @@ -1025,63 +1008,54 @@ _cairo_gstate_arc_to (cairo_gstate_t *gstate, cairo_status_t _cairo_gstate_rel_move_to (cairo_gstate_t *gstate, double dx, double dy) { - cairo_status_t status; - double x, y; + cairo_distance_t distance; cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); - x = gstate->current_point.x + dx; - y = gstate->current_point.y + dy; - - status = _cairo_path_move_to (&gstate->path, x, y); - - _cairo_gstate_set_current_point (gstate, x, y); + distance.dx = _cairo_fixed_from_double (dx); + distance.dy = _cairo_fixed_from_double (dy); - gstate->last_move_point = gstate->current_point; - - return status; + return _cairo_path_rel_move_to (&gstate->path, &distance); } cairo_status_t _cairo_gstate_rel_line_to (cairo_gstate_t *gstate, double dx, double dy) { - cairo_status_t status; - double x, y; + cairo_distance_t distance; cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); - x = gstate->current_point.x + dx; - y = gstate->current_point.y + dy; - - status = _cairo_path_line_to (&gstate->path, x, y); - - _cairo_gstate_set_current_point (gstate, x, y); + distance.dx = _cairo_fixed_from_double (dx); + distance.dy = _cairo_fixed_from_double (dy); - return status; + return _cairo_path_rel_line_to (&gstate->path, &distance); } cairo_status_t _cairo_gstate_rel_curve_to (cairo_gstate_t *gstate, + double dx0, double dy0, double dx1, double dy1, - double dx2, double dy2, - double dx3, double dy3) + double dx2, double dy2) { - cairo_status_t status; + cairo_distance_t distance[3]; + cairo_matrix_transform_distance (&gstate->ctm, &dx0, &dy0); cairo_matrix_transform_distance (&gstate->ctm, &dx1, &dy1); cairo_matrix_transform_distance (&gstate->ctm, &dx2, &dy2); - cairo_matrix_transform_distance (&gstate->ctm, &dx3, &dy3); - status = _cairo_path_curve_to (&gstate->path, - gstate->current_point.x + dx1, gstate->current_point.y + dy1, - gstate->current_point.x + dx2, gstate->current_point.y + dy2, - gstate->current_point.x + dx3, gstate->current_point.y + dy3); + distance[0].dx = _cairo_fixed_from_double (dx0); + distance[0].dy = _cairo_fixed_from_double (dy0); - _cairo_gstate_set_current_point (gstate, - gstate->current_point.x + dx3, - gstate->current_point.y + dy3); + distance[1].dx = _cairo_fixed_from_double (dx1); + distance[1].dy = _cairo_fixed_from_double (dy1); - return status; + distance[2].dx = _cairo_fixed_from_double (dx2); + distance[2].dy = _cairo_fixed_from_double (dy2); + + return _cairo_path_rel_curve_to (&gstate->path, + &distance[0], + &distance[1], + &distance[2]); } /* XXX: NYI @@ -1098,29 +1072,24 @@ _cairo_gstate_stroke_path (cairo_gstate_t *gstate) cairo_status_t _cairo_gstate_close_path (cairo_gstate_t *gstate) { - cairo_status_t status; - - status = _cairo_path_close_path (&gstate->path); - - _cairo_gstate_set_current_point (gstate, - gstate->last_move_point.x, - gstate->last_move_point.y); - - return status; + return _cairo_path_close_path (&gstate->path); } cairo_status_t _cairo_gstate_current_point (cairo_gstate_t *gstate, double *x_ret, double *y_ret) { + cairo_status_t status; + cairo_point_t point; double x, y; - if (gstate->has_current_point) { - x = gstate->current_point.x; - y = gstate->current_point.y; - cairo_matrix_transform_point (&gstate->ctm_inverse, &x, &y); - } else { + status = _cairo_path_current_point (&gstate->path, &point); + if (status == CAIRO_STATUS_NO_CURRENT_POINT) { x = 0.0; y = 0.0; + } else { + x = _cairo_fixed_to_double (point.x); + y = _cairo_fixed_to_double (point.y); + cairo_matrix_transform_point (&gstate->ctm_inverse, &x, &y); } *x_ret = x; @@ -1129,24 +1098,201 @@ _cairo_gstate_current_point (cairo_gstate_t *gstate, double *x_ret, double *y_re return CAIRO_STATUS_SUCCESS; } +typedef struct gstate_path_interpreter { + cairo_matrix_t ctm_inverse; + double tolerance; + cairo_point_t current_point; + + cairo_move_to_func_t *move_to; + cairo_line_to_func_t *line_to; + cairo_curve_to_func_t *curve_to; + cairo_close_path_func_t *close_path; + + void *closure; +} gpi_t; + static cairo_status_t -_cairo_gstate_ensure_source (cairo_gstate_t *gstate) +_gpi_move_to (void *closure, cairo_point_t *point) { - if (gstate->source) - return CAIRO_STATUS_SUCCESS; + gpi_t *gpi = closure; + double x, y; - if (gstate->surface == NULL) + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + + cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); + + gpi->move_to (gpi->closure, x, y); + gpi->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_gpi_line_to (void *closure, cairo_point_t *point) +{ + gpi_t *gpi = closure; + double x, y; + + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + + cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); + + gpi->line_to (gpi->closure, x, y); + gpi->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_gpi_curve_to (void *closure, + cairo_point_t *p1, + cairo_point_t *p2, + cairo_point_t *p3) +{ + gpi_t *gpi = closure; + cairo_status_t status; + cairo_spline_t spline; + double x1, y1, x2, y2, x3, y3; + + if (gpi->curve_to) { + x1 = _cairo_fixed_to_double (p1->x); + y1 = _cairo_fixed_to_double (p1->y); + cairo_matrix_transform_point (&gpi->ctm_inverse, &x1, &y1); + + x2 = _cairo_fixed_to_double (p2->x); + y2 = _cairo_fixed_to_double (p2->y); + cairo_matrix_transform_point (&gpi->ctm_inverse, &x2, &y2); + + x3 = _cairo_fixed_to_double (p3->x); + y3 = _cairo_fixed_to_double (p3->y); + cairo_matrix_transform_point (&gpi->ctm_inverse, &x3, &y3); + + gpi->curve_to (gpi->closure, x1, y1, x2, y2, x3, y3); + } else { + cairo_point_t *p0 = &gpi->current_point; + int i; + double x, y; + + status = _cairo_spline_init (&spline, p0, p1, p2, p3); + if (status == CAIRO_INT_STATUS_DEGENERATE) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_spline_decompose (&spline, gpi->tolerance); + if (status) + return status; + + for (i=1; i < spline.num_points; i++) { + x = _cairo_fixed_to_double (spline.points[i].x); + y = _cairo_fixed_to_double (spline.points[i].y); + + cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); + + gpi->line_to (gpi->closure, x, y); + } + } + + gpi->current_point = *p3; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_gpi_close_path (void *closure) +{ + gpi_t *gpi = closure; + + gpi->close_path (gpi->closure); + + gpi->current_point.x = 0; + gpi->current_point.y = 0; + + return CAIRO_STATUS_SUCCESS; +} + +/* It's OK for curve_path to be NULL. In that case, all curves in the + path will be decomposed into one or more calls to the line_to + function, (according to the current tolerance). */ +cairo_status_t +_cairo_gstate_interpret_path (cairo_gstate_t *gstate, + cairo_move_to_func_t *move_to, + cairo_line_to_func_t *line_to, + cairo_curve_to_func_t *curve_to, + cairo_close_path_func_t *close_path, + void *closure) +{ + cairo_path_t path; + gpi_t gpi; + + /* Anything we want from gstate must be copied. We must not retain + pointers into gstate. */ + _cairo_path_init_copy (&path, &gstate->path); + + cairo_matrix_copy (&gpi.ctm_inverse, &gstate->ctm_inverse); + gpi.tolerance = gstate->tolerance; + + gpi.move_to = move_to; + gpi.line_to = line_to; + gpi.curve_to = curve_to; + gpi.close_path = close_path; + gpi.closure = closure; + + gpi.current_point.x = 0; + gpi.current_point.y = 0; + + return _cairo_path_interpret (&path, + CAIRO_DIRECTION_FORWARD, + _gpi_move_to, + _gpi_line_to, + _gpi_curve_to, + _gpi_close_path, + &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) +{ + cairo_int_status_t status; + + if (gstate->surface == NULL) { + _cairo_pattern_fini (pattern); return CAIRO_STATUS_NO_TARGET_SURFACE; + } - gstate->source = _cairo_surface_create_similar_solid (gstate->surface, - CAIRO_FORMAT_ARGB32, - 1, 1, - &gstate->color); - if (gstate->source == NULL) - return CAIRO_STATUS_NO_MEMORY; + 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) + pattern->color = pattern->stops->color; + } + } + + _cairo_pattern_set_alpha (pattern, gstate->alpha); + _cairo_pattern_transform (pattern, &gstate->ctm, &gstate->ctm_inverse); - cairo_surface_set_repeat (gstate->source, 1); + 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); + _cairo_pattern_add_source_offset (pattern, + gstate->pattern_offset.x, + gstate->pattern_offset.y); + return CAIRO_STATUS_SUCCESS; } @@ -1155,15 +1301,10 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate) { cairo_status_t status; cairo_traps_t traps; - cairo_matrix_t user_to_source, device_to_source; if (gstate->line_width <= 0.0) return CAIRO_STATUS_SUCCESS; - status = _cairo_gstate_ensure_source (gstate); - if (status) - return status; - _cairo_pen_init (&gstate->pen_regular, gstate->line_width / 2.0, gstate); _cairo_traps_init (&traps); @@ -1174,21 +1315,11 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate) return status; } - if (! gstate->source_is_solid) { - cairo_surface_get_matrix (gstate->source, &user_to_source); - cairo_matrix_multiply (&device_to_source, &gstate->ctm_inverse, &user_to_source); - cairo_surface_set_matrix (gstate->source, &device_to_source); - } - _cairo_gstate_clip_and_composite_trapezoids (gstate, - gstate->source, - gstate->operator, - gstate->surface, - &traps); - - /* restore the matrix originally in the source surface */ - if (! gstate->source_is_solid) - cairo_surface_set_matrix (gstate->source, &user_to_source); + gstate->pattern, + gstate->operator, + gstate->surface, + &traps); _cairo_traps_fini (&traps); @@ -1227,12 +1358,14 @@ BAIL: /* Warning: This call modifies the coordinates of traps */ static cairo_status_t _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, - cairo_surface_t *src, + cairo_pattern_t *src, cairo_operator_t operator, cairo_surface_t *dst, cairo_traps_t *traps) { cairo_status_t status; + cairo_pattern_t pattern; + cairo_box_t extents; if (traps->num_traps == 0) return CAIRO_STATUS_SUCCESS; @@ -1241,19 +1374,8 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, cairo_fixed_t xoff, yoff; cairo_trapezoid_t *t; int i; - - cairo_surface_t *white, *intermediate; - cairo_color_t white_color, empty_color; - - _cairo_color_init (&white_color); - white = _cairo_surface_create_similar_solid (gstate->surface, CAIRO_FORMAT_A8, - 1, 1, - &white_color); - if (white == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL0; - } - cairo_surface_set_repeat (white, 1); + cairo_surface_t *intermediate; + cairo_color_t empty_color; _cairo_color_init (&empty_color); _cairo_color_set_alpha (&empty_color, 0.); @@ -1261,10 +1383,10 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, CAIRO_FORMAT_A8, gstate->clip.width, gstate->clip.height, - &empty_color); + &empty_color); if (intermediate == NULL) { status = CAIRO_STATUS_NO_MEMORY; - goto BAIL1; + goto BAIL0; } /* Ugh. The cairo_composite/(Render) interface doesn't allow @@ -1286,8 +1408,16 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, t->right.p2.y -= yoff; } + _cairo_pattern_init_solid (&pattern, 1.0, 1.0, 1.0); + _cairo_pattern_set_alpha (&pattern, 1.0); + + _cairo_traps_extents (traps, &extents); + status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); + if (status) + goto BAIL1; + status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD, - white, intermediate, + pattern.source, intermediate, 0, 0, traps->traps, traps->num_traps); @@ -1302,9 +1432,23 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, gstate->clip.width, gstate->clip.height); if (status) goto BAIL2; + + _cairo_pattern_fini (&pattern); + + _cairo_pattern_init_copy (&pattern, src); + + extents.p1.x = _cairo_fixed_from_int (gstate->clip.x); + extents.p1.y = _cairo_fixed_from_int (gstate->clip.y); + extents.p2.x = + _cairo_fixed_from_int (gstate->clip.x + gstate->clip.width); + extents.p2.y = + _cairo_fixed_from_int (gstate->clip.y + gstate->clip.height); + status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); + if (status) + goto BAIL2; status = _cairo_surface_composite (operator, - src, intermediate, dst, + pattern.source, intermediate, dst, 0, 0, 0, 0, gstate->clip.x, @@ -1315,11 +1459,12 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, BAIL2: cairo_surface_destroy (intermediate); BAIL1: - cairo_surface_destroy (white); + _cairo_pattern_fini (&pattern); BAIL0: + if (status) return status; - + } else { int xoff, yoff; @@ -1331,16 +1476,26 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, yoff = _cairo_fixed_to_double (traps->traps[0].left.p2.y); } + _cairo_pattern_init_copy (&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, - src, dst, - xoff - gstate->source_offset.x, - yoff - gstate->source_offset.y, + pattern.source, dst, + xoff - pattern.source_offset.x, + yoff - pattern.source_offset.y, traps->traps, traps->num_traps); + + _cairo_pattern_fini (&pattern); + if (status) return status; } - + return CAIRO_STATUS_SUCCESS; } @@ -1349,11 +1504,6 @@ _cairo_gstate_fill (cairo_gstate_t *gstate) { cairo_status_t status; cairo_traps_t traps; - cairo_matrix_t user_to_source, device_to_source; - - status = _cairo_gstate_ensure_source (gstate); - if (status) - return status; _cairo_traps_init (&traps); @@ -1363,21 +1513,11 @@ _cairo_gstate_fill (cairo_gstate_t *gstate) return status; } - if (! gstate->source_is_solid) { - cairo_surface_get_matrix (gstate->source, &user_to_source); - cairo_matrix_multiply (&device_to_source, &gstate->ctm_inverse, &user_to_source); - cairo_surface_set_matrix (gstate->source, &device_to_source); - } - _cairo_gstate_clip_and_composite_trapezoids (gstate, - gstate->source, - gstate->operator, - gstate->surface, - &traps); - - /* restore the matrix originally in the source surface */ - if (! gstate->source_is_solid) - cairo_surface_set_matrix (gstate->source, &user_to_source); + gstate->pattern, + gstate->operator, + gstate->surface, + &traps); _cairo_traps_fini (&traps); @@ -1404,7 +1544,7 @@ _cairo_gstate_in_fill (cairo_gstate_t *gstate, goto BAIL; *inside_ret = _cairo_traps_contain (&traps, x, y); - + BAIL: _cairo_traps_fini (&traps); @@ -1430,39 +1570,137 @@ _cairo_gstate_show_page (cairo_gstate_t *gstate) } cairo_status_t -_cairo_gstate_clip (cairo_gstate_t *gstate) +_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, + double *x1, double *y1, + double *x2, double *y2) { cairo_status_t status; - cairo_surface_t *alpha_one; cairo_traps_t traps; - cairo_color_t white_color; + cairo_box_t extents; + + _cairo_traps_init (&traps); + + status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps); + if (status) + goto BAIL; - _cairo_color_init (&white_color); + _cairo_traps_extents (&traps, &extents); - if (gstate->clip.surface == NULL) { - double x1, y1, x2, y2; - _cairo_path_bounds (&gstate->path, - &x1, &y1, &x2, &y2); - gstate->clip.x = floor (x1); - gstate->clip.y = floor (y1); - gstate->clip.width = ceil (x2 - gstate->clip.x); - gstate->clip.height = ceil (y2 - gstate->clip.y); - gstate->clip.surface = _cairo_surface_create_similar_solid (gstate->surface, - CAIRO_FORMAT_A8, - gstate->clip.width, - gstate->clip.height, - &white_color); - if (gstate->clip.surface == NULL) - return CAIRO_STATUS_NO_MEMORY; + *x1 = _cairo_fixed_to_double (extents.p1.x); + *y1 = _cairo_fixed_to_double (extents.p1.y); + *x2 = _cairo_fixed_to_double (extents.p2.x); + *y2 = _cairo_fixed_to_double (extents.p2.y); + + cairo_matrix_transform_point (&gstate->ctm_inverse, x1, y1); + cairo_matrix_transform_point (&gstate->ctm_inverse, x2, y2); + +BAIL: + _cairo_traps_fini (&traps); + + return status; +} + +cairo_status_t +_cairo_gstate_fill_extents (cairo_gstate_t *gstate, + double *x1, double *y1, + double *x2, double *y2) +{ + cairo_status_t status; + cairo_traps_t traps; + cairo_box_t extents; + + _cairo_traps_init (&traps); + + status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); + if (status) + goto BAIL; + + _cairo_traps_extents (&traps, &extents); + + *x1 = _cairo_fixed_to_double (extents.p1.x); + *y1 = _cairo_fixed_to_double (extents.p1.y); + *x2 = _cairo_fixed_to_double (extents.p2.x); + *y2 = _cairo_fixed_to_double (extents.p2.y); + + cairo_matrix_transform_point (&gstate->ctm_inverse, x1, y1); + cairo_matrix_transform_point (&gstate->ctm_inverse, x2, y2); + +BAIL: + _cairo_traps_fini (&traps); + + return status; +} + +cairo_status_t +_cairo_gstate_init_clip (cairo_gstate_t *gstate) +{ + /* destroy any existing clip-region artifacts */ + if (gstate->clip.surface) + cairo_surface_destroy (gstate->clip.surface); + gstate->clip.surface = NULL; + + if (gstate->clip.region) + pixman_region_destroy (gstate->clip.region); + gstate->clip.region = NULL; + + /* reset the surface's clip to the whole surface */ + _cairo_surface_set_clip_region (gstate->surface, + gstate->clip.region); + + return CAIRO_STATUS_SUCCESS; +} + +static int +extract_transformed_rectangle(cairo_matrix_t *mat, + cairo_traps_t *tr, + pixman_box16_t *box) +{ +#define CAIRO_FIXED_IS_INTEGER(x) (((x) & 0xFFFF) == 0) +#define CAIRO_FIXED_INTEGER_PART(x) ((x) >> 16) + + double a, b, c, d, tx, ty; + cairo_status_t st; + + st = cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty); + if (!(st == CAIRO_STATUS_SUCCESS && b == 0. && c == 0.)) + return 0; + + if (tr->num_traps == 1 + && tr->traps[0].left.p1.x == tr->traps[0].left.p2.x + && tr->traps[0].right.p1.x == tr->traps[0].right.p2.x + && tr->traps[0].left.p1.y == tr->traps[0].right.p1.y + && tr->traps[0].left.p2.y == tr->traps[0].right.p2.y + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].left.p1.x) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].left.p1.y) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].left.p2.x) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].left.p2.y) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].right.p1.x) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].right.p1.y) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].right.p2.x) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].right.p2.y)) { + + box->x1 = (short) CAIRO_FIXED_INTEGER_PART(tr->traps[0].left.p1.x); + box->x2 = (short) CAIRO_FIXED_INTEGER_PART(tr->traps[0].right.p1.x); + box->y1 = (short) CAIRO_FIXED_INTEGER_PART(tr->traps[0].left.p1.y); + box->y2 = (short) CAIRO_FIXED_INTEGER_PART(tr->traps[0].left.p2.y); + return 1; } + return 0; - alpha_one = _cairo_surface_create_similar_solid (gstate->surface, CAIRO_FORMAT_A8, - 1, 1, - &white_color); - if (alpha_one == NULL) - return CAIRO_STATUS_NO_MEMORY; +#undef CAIRO_FIXED_IS_INTEGER +#undef CAIRO_FIXED_INTEGER_PART +} - cairo_surface_set_repeat (alpha_one, 1); +cairo_status_t +_cairo_gstate_clip (cairo_gstate_t *gstate) +{ + cairo_status_t status; + cairo_pattern_t pattern; + cairo_traps_t traps; + cairo_color_t white_color; + pixman_box16_t box; + + /* Fill the clip region as traps. */ _cairo_traps_init (&traps); status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); @@ -1471,16 +1709,82 @@ _cairo_gstate_clip (cairo_gstate_t *gstate) return status; } + /* Check to see if we can represent these traps as a PixRegion. */ + + if (extract_transformed_rectangle (&gstate->ctm, &traps, &box)) { + + pixman_region16_t *rect = NULL; + pixman_region16_t *intersection = NULL; + + status = CAIRO_STATUS_SUCCESS; + rect = pixman_region_create_simple (&box); + + if (rect == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + + } else { + + if (gstate->clip.region == NULL) { + gstate->clip.region = rect; + } else { + intersection = pixman_region_create(); + if (pixman_region_intersect (intersection, + gstate->clip.region, rect) + == PIXMAN_REGION_STATUS_SUCCESS) { + pixman_region_destroy (gstate->clip.region); + gstate->clip.region = intersection; + } else { + status = CAIRO_STATUS_NO_MEMORY; + } + pixman_region_destroy (rect); + } + + if (!status) + status = _cairo_surface_set_clip_region (gstate->surface, + gstate->clip.region); + } + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + _cairo_traps_fini (&traps); + return status; + } + } + + /* Otherwise represent the clip as a mask surface. */ + + _cairo_color_init (&white_color); + + if (gstate->clip.surface == NULL) { + double x1, y1, x2, y2; + _cairo_path_bounds (&gstate->path, + &x1, &y1, &x2, &y2); + gstate->clip.x = floor (x1); + gstate->clip.y = floor (y1); + gstate->clip.width = ceil (x2 - gstate->clip.x); + gstate->clip.height = ceil (y2 - gstate->clip.y); + gstate->clip.surface = + _cairo_surface_create_similar_solid (gstate->surface, + CAIRO_FORMAT_A8, + gstate->clip.width, + gstate->clip.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, - alpha_one, + &pattern, CAIRO_OPERATOR_IN, gstate->clip.surface, &traps); - + + _cairo_pattern_fini (&pattern); + _cairo_traps_fini (&traps); - cairo_surface_destroy (alpha_one); - return status; } @@ -1491,27 +1795,12 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, int height) { cairo_status_t status; - cairo_surface_t *mask; cairo_matrix_t user_to_image, image_to_user; cairo_matrix_t image_to_device, device_to_image; double device_x, device_y; double device_width, device_height; - cairo_color_t alpha_color; - - if (gstate->alpha != 1.0) { - _cairo_color_init (&alpha_color); - _cairo_color_set_alpha (&alpha_color, gstate->alpha); - mask = _cairo_surface_create_similar_solid (gstate->surface, - CAIRO_FORMAT_A8, - 1, 1, - &alpha_color); - if (mask == NULL) - return CAIRO_STATUS_NO_MEMORY; - - cairo_surface_set_repeat (mask, 1); - } else { - mask = NULL; - } + cairo_pattern_t pattern; + cairo_box_t extents; cairo_surface_get_matrix (surface, &user_to_image); cairo_matrix_multiply (&device_to_image, &gstate->ctm_inverse, &user_to_image); @@ -1527,32 +1816,47 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, _cairo_matrix_transform_bounding_box (&image_to_device, &device_x, &device_y, &device_width, &device_height); + + _cairo_pattern_init (&pattern); + + 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); + extents.p1.x = _cairo_fixed_from_double (device_x); + extents.p1.y = _cairo_fixed_from_double (device_y); + extents.p2.x = _cairo_fixed_from_double (device_x + device_width); + extents.p2.y = _cairo_fixed_from_double (device_y + device_height); + status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); + if (status) + return status; + } /* XXX: The rendered size is sometimes 1 or 2 pixels short from what I expect. Need to fix this. */ status = _cairo_surface_composite (gstate->operator, - surface, mask, gstate->surface, + surface, pattern.source, gstate->surface, device_x, device_y, 0, 0, device_x, device_y, device_width, device_height); - - if (mask) - cairo_surface_destroy (mask); - if (status) - return status; + _cairo_pattern_fini (&pattern); /* restore the matrix originally in the surface */ cairo_surface_set_matrix (surface, &user_to_image); + + if (status) + return status; return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_select_font (cairo_gstate_t *gstate, - char *family, + const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { @@ -1600,6 +1904,7 @@ _cairo_gstate_current_font_extents (cairo_gstate_t *gstate, status = _cairo_font_font_extents (gstate->font, extents); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + return status; } @@ -1622,14 +1927,24 @@ _cairo_gstate_text_extents (cairo_gstate_t *gstate, { cairo_matrix_t saved_font_matrix; cairo_status_t status; + double scale_x, scale_y; cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); + _cairo_matrix_compute_scale_factors (&gstate->ctm, &scale_x, &scale_y); + cairo_matrix_scale (&gstate->font->matrix, scale_x, scale_y); status = _cairo_font_text_extents (gstate->font, utf8, extents); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + + extents->x_bearing /= scale_x; + extents->y_bearing /= scale_y; + extents->width /= scale_x; + extents->height /= scale_y; + extents->x_advance /= scale_x; + extents->y_advance /= scale_y; + return status; } @@ -1640,120 +1955,91 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, cairo_text_extents_t *extents) { cairo_status_t status; - int i; - cairo_glyph_t *transformed_glyphs = NULL; cairo_matrix_t saved_font_matrix; - - transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); - if (transformed_glyphs == NULL) - return CAIRO_STATUS_NO_MEMORY; - - for (i = 0; i < num_glyphs; ++i) - { - transformed_glyphs[i] = glyphs[i]; - cairo_matrix_transform_point (&gstate->ctm, - &(transformed_glyphs[i].x), - &(transformed_glyphs[i].y)); - } + double scale_x, scale_y; cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); + _cairo_matrix_compute_scale_factors (&gstate->ctm, &scale_x, &scale_y); + cairo_matrix_scale (&gstate->font->matrix, scale_x, scale_y); status = _cairo_font_glyph_extents (gstate->font, - transformed_glyphs, num_glyphs, + glyphs, num_glyphs, extents); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - free (transformed_glyphs); - return status; -} - + extents->x_bearing /= scale_x; + extents->y_bearing /= scale_y; + extents->width /= scale_x; + extents->height /= scale_y; + extents->x_advance /= scale_x; + extents->y_advance /= scale_y; -static cairo_status_t -setup_text_rendering_context(cairo_gstate_t *gstate, - double *x, double *y, - cairo_matrix_t *user_to_source) -{ - cairo_status_t status; - cairo_matrix_t device_to_source; - - /* XXX: I believe this is correct, but it would be much more clear - to have some explicit current_point accesor functions, (one for - user- and one for device-space). */ - - if (gstate->has_current_point) { - *x = gstate->current_point.x; - *y = gstate->current_point.y; - } else { - *x = 0; - *y = 0; - cairo_matrix_transform_point (&gstate->ctm, x, y); - } - - status = _cairo_gstate_ensure_source (gstate); - if (status) - return status; - - /* XXX: This same source matrix manipulation code shows up in - about 3 or 4 places. We should move that into a shared function - or two. */ - if (! gstate->source_is_solid) { - cairo_surface_get_matrix (gstate->source, user_to_source); - cairo_matrix_multiply (&device_to_source, &gstate->ctm_inverse, user_to_source); - cairo_surface_set_matrix (gstate->source, &device_to_source); - } - return CAIRO_STATUS_SUCCESS; -} - -static void -restore_text_rendering_context(cairo_gstate_t *gstate, - cairo_matrix_t *user_to_source) -{ - /* restore the matrix originally in the source surface */ - if (! gstate->source_is_solid) - cairo_surface_set_matrix (gstate->source, user_to_source); + return status; } - cairo_status_t _cairo_gstate_show_text (cairo_gstate_t *gstate, const unsigned char *utf8) { cairo_status_t status; + cairo_point_t point; double x, y; - cairo_matrix_t user_to_source; cairo_matrix_t saved_font_matrix; - - status = setup_text_rendering_context(gstate, &x, &y, &user_to_source); - if (status) - return status; + cairo_pattern_t pattern; + cairo_text_extents_t text_extents; + cairo_box_t extents; + + status = _cairo_path_current_point (&gstate->path, &point); + if (status == CAIRO_STATUS_NO_CURRENT_POINT) { + x = 0; + y = 0; + cairo_matrix_transform_point (&gstate->ctm, &x, &y); + } else { + x = _cairo_fixed_to_double (point.x); + y = _cairo_fixed_to_double (point.y); + } cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); + _cairo_pattern_init_copy (&pattern, gstate->pattern); + + status = _cairo_gstate_text_extents (gstate, utf8, &text_extents); + if (status) + return status; + + extents.p1.x = _cairo_fixed_from_double (x); + extents.p1.y = _cairo_fixed_from_double (y); + extents.p2.x = _cairo_fixed_from_double (x + text_extents.width); + extents.p2.y = _cairo_fixed_from_double (y + text_extents.height); + status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); + if (status) + return status; + status = _cairo_font_show_text (gstate->font, - gstate->operator, gstate->source, + gstate->operator, pattern.source, gstate->surface, x, y, utf8); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - restore_text_rendering_context (gstate, &user_to_source); + + _cairo_pattern_fini (&pattern); return status; } - cairo_status_t _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, cairo_glyph_t *glyphs, int num_glyphs) { cairo_status_t status; - double x, y; - cairo_matrix_t user_to_source; cairo_matrix_t saved_font_matrix; int i; cairo_glyph_t *transformed_glyphs = NULL; + cairo_pattern_t pattern; + cairo_text_extents_t text_extents; + cairo_box_t extents; transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); if (transformed_glyphs == NULL) @@ -1767,25 +2053,37 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, &(transformed_glyphs[i].y)); } - status = setup_text_rendering_context (gstate, &x, &y, &user_to_source); - if (status) - return status; - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); + _cairo_pattern_init_copy (&pattern, gstate->pattern); + _cairo_gstate_glyph_extents (gstate, transformed_glyphs, num_glyphs, + &text_extents); + if (status) + return status; + + extents.p1.x = _cairo_fixed_from_double (transformed_glyphs[0].x); + extents.p1.y = _cairo_fixed_from_double (transformed_glyphs[0].y); + extents.p2.x = _cairo_fixed_from_double (transformed_glyphs[0].x + + text_extents.width); + extents.p2.y = _cairo_fixed_from_double (transformed_glyphs[0].y + + text_extents.height); + status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); + if (status) + return status; + status = _cairo_font_show_glyphs (gstate->font, - gstate->operator, gstate->source, - gstate->surface, x, y, + gstate->operator, pattern.source, + gstate->surface, transformed_glyphs, num_glyphs); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - restore_text_rendering_context (gstate, &user_to_source); + + _cairo_pattern_fini (&pattern); free (transformed_glyphs); return status; - } @@ -1795,21 +2093,35 @@ _cairo_gstate_text_path (cairo_gstate_t *gstate, { cairo_status_t status; cairo_matrix_t saved_font_matrix; + cairo_point_t point; + double x, y; + + status = _cairo_path_current_point (&gstate->path, &point); + if (status == CAIRO_STATUS_NO_CURRENT_POINT) { + x = 0; + y = 0; + cairo_matrix_transform_point (&gstate->ctm, &x, &y); + } else { + x = _cairo_fixed_to_double (point.x); + y = _cairo_fixed_to_double (point.y); + } cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); status = _cairo_font_text_path (gstate->font, - &gstate->path, - utf8); + x, y, + utf8, + &gstate->path); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + return status; } cairo_status_t -_cairo_gstate_glyph_path (cairo_gstate_t *gstate, +_cairo_gstate_glyph_path (cairo_gstate_t *gstate, cairo_glyph_t *glyphs, int num_glyphs) { @@ -1834,11 +2146,11 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); status = _cairo_font_glyph_path (gstate->font, - &gstate->path, - transformed_glyphs, num_glyphs); + transformed_glyphs, num_glyphs, + &gstate->path); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - + free (transformed_glyphs); return status; } diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index efa54d26..a11a07ee 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -45,7 +45,7 @@ _cairo_format_bpp (cairo_format_t format) } static cairo_image_surface_t * -_cairo_image_surface_create_for_ic_image (IcImage *ic_image) +_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image) { cairo_image_surface_t *surface; @@ -55,15 +55,15 @@ _cairo_image_surface_create_for_ic_image (IcImage *ic_image) _cairo_surface_init (&surface->base, &cairo_image_surface_backend); - surface->ic_image = ic_image; + surface->pixman_image = pixman_image; - surface->data = (char *) IcImageGetData (ic_image); + surface->data = (char *) pixman_image_get_data (pixman_image); surface->owns_data = 0; - surface->width = IcImageGetWidth (ic_image); - surface->height = IcImageGetHeight (ic_image); - surface->stride = IcImageGetStride (ic_image); - surface->depth = IcImageGetDepth (ic_image); + surface->width = pixman_image_get_width (pixman_image); + surface->height = pixman_image_get_height (pixman_image); + surface->stride = pixman_image_get_stride (pixman_image); + surface->depth = pixman_image_get_depth (pixman_image); return surface; } @@ -76,47 +76,47 @@ _cairo_image_surface_create_with_masks (char *data, int stride) { cairo_image_surface_t *surface; - IcFormat *ic_format; - IcImage *ic_image; + pixman_format_t *pixman_format; + pixman_image_t *pixman_image; - ic_format = IcFormatCreateMasks (format->bpp, - format->alpha_mask, - format->red_mask, - format->green_mask, - format->blue_mask); + pixman_format = pixman_format_create_masks (format->bpp, + format->alpha_mask, + format->red_mask, + format->green_mask, + format->blue_mask); - if (ic_format == NULL) + if (pixman_format == NULL) return NULL; - ic_image = IcImageCreateForData ((IcBits *) data, ic_format, - width, height, format->bpp, stride); + pixman_image = pixman_image_create_for_data ((pixman_bits_t *) data, pixman_format, + width, height, format->bpp, stride); - IcFormatDestroy (ic_format); + pixman_format_destroy (pixman_format); - if (ic_image == NULL) + if (pixman_image == NULL) return NULL; - surface = _cairo_image_surface_create_for_ic_image (ic_image); + surface = _cairo_image_surface_create_for_pixman_image (pixman_image); return surface; } -static IcFormat * -_create_ic_format (cairo_format_t format) +static pixman_format_t * +_create_pixman_format (cairo_format_t format) { switch (format) { case CAIRO_FORMAT_A1: - return IcFormatCreate (IcFormatNameA1); + return pixman_format_create (PIXMAN_FORMAT_NAME_A1); break; case CAIRO_FORMAT_A8: - return IcFormatCreate (IcFormatNameA8); + return pixman_format_create (PIXMAN_FORMAT_NAME_A8); break; case CAIRO_FORMAT_RGB24: - return IcFormatCreate (IcFormatNameRGB24); + return pixman_format_create (PIXMAN_FORMAT_NAME_RG_B24); break; case CAIRO_FORMAT_ARGB32: default: - return IcFormatCreate (IcFormatNameARGB32); + return pixman_format_create (PIXMAN_FORMAT_NAME_AR_GB32); break; } } @@ -127,21 +127,21 @@ cairo_image_surface_create (cairo_format_t format, int height) { cairo_image_surface_t *surface; - IcFormat *ic_format; - IcImage *ic_image; + pixman_format_t *pixman_format; + pixman_image_t *pixman_image; - ic_format = _create_ic_format (format); - if (ic_format == NULL) + pixman_format = _create_pixman_format (format); + if (pixman_format == NULL) return NULL; - ic_image = IcImageCreate (ic_format, width, height); + pixman_image = pixman_image_create (pixman_format, width, height); - IcFormatDestroy (ic_format); + pixman_format_destroy (pixman_format); - if (ic_image == NULL) + if (pixman_image == NULL) return NULL; - surface = _cairo_image_surface_create_for_ic_image (ic_image); + surface = _cairo_image_surface_create_for_pixman_image (pixman_image); return &surface->base; } @@ -154,24 +154,24 @@ cairo_image_surface_create_for_data (char *data, int stride) { cairo_image_surface_t *surface; - IcFormat *ic_format; - IcImage *ic_image; + pixman_format_t *pixman_format; + pixman_image_t *pixman_image; - ic_format = _create_ic_format (format); - if (ic_format == NULL) + pixman_format = _create_pixman_format (format); + if (pixman_format == NULL) return NULL; - ic_image = IcImageCreateForData ((IcBits *) data, ic_format, - width, height, - _cairo_format_bpp (format), - stride); + pixman_image = pixman_image_create_for_data ((pixman_bits_t *) data, pixman_format, + width, height, + _cairo_format_bpp (format), + stride); - IcFormatDestroy (ic_format); + pixman_format_destroy (pixman_format); - if (ic_image == NULL) + if (pixman_image == NULL) return NULL; - surface = _cairo_image_surface_create_for_ic_image (ic_image); + surface = _cairo_image_surface_create_for_pixman_image (pixman_image); return &surface->base; } @@ -190,8 +190,8 @@ _cairo_image_abstract_surface_destroy (void *abstract_surface) { cairo_image_surface_t *surface = abstract_surface; - if (surface->ic_image) - IcImageDestroy (surface->ic_image); + if (surface->pixman_image) + pixman_image_destroy (surface->pixman_image); if (surface->owns_data) { free (surface->data); @@ -247,21 +247,21 @@ cairo_status_t _cairo_image_surface_set_matrix (cairo_image_surface_t *surface, cairo_matrix_t *matrix) { - IcTransform ic_transform; + pixman_transform_t pixman_transform; - ic_transform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); - ic_transform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); - ic_transform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); + pixman_transform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); + pixman_transform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); + pixman_transform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); - ic_transform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); - ic_transform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); - ic_transform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); + pixman_transform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); + pixman_transform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); + pixman_transform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); - ic_transform.matrix[2][0] = 0; - ic_transform.matrix[2][1] = 0; - ic_transform.matrix[2][2] = _cairo_fixed_from_double (1); + pixman_transform.matrix[2][0] = 0; + pixman_transform.matrix[2][1] = 0; + pixman_transform.matrix[2][2] = _cairo_fixed_from_double (1); - IcImageSetTransform (surface->ic_image, &ic_transform); + pixman_image_set_transform (surface->pixman_image, &pixman_transform); return CAIRO_STATUS_SUCCESS; } @@ -277,29 +277,29 @@ _cairo_image_abstract_surface_set_filter (void *abstract_surface, cairo_filter_t cairo_status_t _cairo_image_surface_set_filter (cairo_image_surface_t *surface, cairo_filter_t filter) { - IcFilter ic_filter; + pixman_filter_t pixman_filter; switch (filter) { case CAIRO_FILTER_FAST: - ic_filter = IcFilterFast; + pixman_filter = PIXMAN_FILTER_FAST; break; case CAIRO_FILTER_GOOD: - ic_filter = IcFilterGood; + pixman_filter = PIXMAN_FILTER_GOOD; break; case CAIRO_FILTER_BEST: - ic_filter = IcFilterBest; + pixman_filter = PIXMAN_FILTER_BEST; break; case CAIRO_FILTER_NEAREST: - ic_filter = IcFilterNearest; + pixman_filter = PIXMAN_FILTER_NEAREST; break; case CAIRO_FILTER_BILINEAR: - ic_filter = IcFilterBilinear; + pixman_filter = PIXMAN_FILTER_BILINEAR; break; default: - ic_filter = IcFilterBest; + pixman_filter = PIXMAN_FILTER_BEST; } - IcImageSetFilter (surface->ic_image, ic_filter); + pixman_image_set_filter (surface->pixman_image, pixman_filter); return CAIRO_STATUS_SUCCESS; } @@ -314,45 +314,45 @@ _cairo_image_abstract_surface_set_repeat (void *abstract_surface, int repeat) cairo_status_t _cairo_image_surface_set_repeat (cairo_image_surface_t *surface, int repeat) { - IcImageSetRepeat (surface->ic_image, repeat); + pixman_image_set_repeat (surface->pixman_image, repeat); return CAIRO_STATUS_SUCCESS; } -static IcOperator -_ic_operator (cairo_operator_t operator) +static pixman_operator_t +_pixman_operator (cairo_operator_t operator) { switch (operator) { case CAIRO_OPERATOR_CLEAR: - return IcOperatorClear; + return PIXMAN_OPERATOR_CLEAR; case CAIRO_OPERATOR_SRC: - return IcOperatorSrc; + return PIXMAN_OPERATOR_SRC; case CAIRO_OPERATOR_DST: - return IcOperatorDst; + return PIXMAN_OPERATOR_DST; case CAIRO_OPERATOR_OVER: - return IcOperatorOver; + return PIXMAN_OPERATOR_OVER; case CAIRO_OPERATOR_OVER_REVERSE: - return IcOperatorOverReverse; + return PIXMAN_OPERATOR_OVER_REVERSE; case CAIRO_OPERATOR_IN: - return IcOperatorIn; + return PIXMAN_OPERATOR_IN; case CAIRO_OPERATOR_IN_REVERSE: - return IcOperatorInReverse; + return PIXMAN_OPERATOR_IN_REVERSE; case CAIRO_OPERATOR_OUT: - return IcOperatorOut; + return PIXMAN_OPERATOR_OUT; case CAIRO_OPERATOR_OUT_REVERSE: - return IcOperatorOutReverse; + return PIXMAN_OPERATOR_OUT_REVERSE; case CAIRO_OPERATOR_ATOP: - return IcOperatorAtop; + return PIXMAN_OPERATOR_ATOP; case CAIRO_OPERATOR_ATOP_REVERSE: - return IcOperatorAtopReverse; + return PIXMAN_OPERATOR_ATOP_REVERSE; case CAIRO_OPERATOR_XOR: - return IcOperatorXor; + return PIXMAN_OPERATOR_XOR; case CAIRO_OPERATOR_ADD: - return IcOperatorAdd; + return PIXMAN_OPERATOR_ADD; case CAIRO_OPERATOR_SATURATE: - return IcOperatorSaturate; + return PIXMAN_OPERATOR_SATURATE; default: - return IcOperatorOver; + return PIXMAN_OPERATOR_OVER; } } @@ -380,14 +380,14 @@ _cairo_image_surface_composite (cairo_operator_t operator, return CAIRO_INT_STATUS_UNSUPPORTED; } - IcComposite (_ic_operator (operator), - src->ic_image, - mask ? mask->ic_image : NULL, - dst->ic_image, - src_x, src_y, - mask_x, mask_y, - 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; } @@ -401,16 +401,16 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, { cairo_image_surface_t *surface = abstract_surface; - IcColor ic_color; + pixman_color_t pixman_color; - ic_color.red = color->red_short; - ic_color.green = color->green_short; - ic_color.blue = color->blue_short; - ic_color.alpha = color->alpha_short; + pixman_color.red = color->red_short; + pixman_color.green = color->green_short; + pixman_color.blue = color->blue_short; + pixman_color.alpha = color->alpha_short; - /* XXX: The IcRectangle cast is evil... it needs to go away somehow. */ - IcFillRectangles (_ic_operator(operator), surface->ic_image, - &ic_color, (IcRectangle *) rects, num_rects); + /* XXX: The pixman_rectangle_t cast is evil... it needs to go away somehow. */ + pixman_fill_rectangles (_pixman_operator(operator), surface->pixman_image, + &pixman_color, (pixman_rectangle_t *) rects, num_rects); return CAIRO_STATUS_SUCCESS; } @@ -430,9 +430,9 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t operator, if (generic_src->backend != dst->base.backend) return CAIRO_INT_STATUS_UNSUPPORTED; - /* XXX: The IcTrapezoid cast is evil and needs to go away somehow. */ - IcCompositeTrapezoids (operator, src->ic_image, dst->ic_image, - x_src, y_src, (IcTrapezoid *) traps, num_traps); + /* 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); return CAIRO_STATUS_SUCCESS; } @@ -449,6 +449,44 @@ _cairo_image_surface_show_page (void *abstract_surface) return CAIRO_INT_STATUS_UNSUPPORTED; } +static cairo_int_status_t +_cairo_image_abstract_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + cairo_image_surface_t *surface = (cairo_image_surface_t *) abstract_surface; + + return _cairo_image_surface_set_clip_region (surface, region); +} + +cairo_int_status_t +_cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, + pixman_region16_t *region) +{ + pixman_image_set_clip_region (surface->pixman_image, region); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_image_abstract_surface_create_pattern (void *abstract_surface, + cairo_pattern_t *pattern, + cairo_box_t *box) +{ + 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; +} + static const cairo_surface_backend_t cairo_image_surface_backend = { _cairo_image_surface_create_similar, _cairo_image_abstract_surface_destroy, @@ -462,5 +500,7 @@ static const cairo_surface_backend_t cairo_image_surface_backend = { _cairo_image_surface_fill_rectangles, _cairo_image_surface_composite_trapezoids, _cairo_image_surface_copy_page, - _cairo_image_surface_show_page + _cairo_image_surface_show_page, + _cairo_image_abstract_surface_set_clip_region, + _cairo_image_abstract_surface_create_pattern }; diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c index 655bcde8..de89c0fd 100644 --- a/src/cairo-matrix.c +++ b/src/cairo-matrix.c @@ -386,3 +386,22 @@ _cairo_matrix_compute_eigen_values (cairo_matrix_t *matrix, double *lambda1, dou return CAIRO_STATUS_SUCCESS; } + +/* Compute the amount that each basis vector is scaled by. */ +cairo_status_t +_cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double *sy) +{ + double x, y; + + x = 1.0; + y = 0.0; + cairo_matrix_transform_distance (matrix, &x, &y); + *sx = sqrt(x*x + y*y); + + x = 0.0; + y = 1.0; + cairo_matrix_transform_distance (matrix, &x, &y); + *sy = sqrt(x*x + y*y); + + return CAIRO_STATUS_SUCCESS; +} diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c index 40b64c3d..6a02b9ac 100644 --- a/src/cairo-path-bounds.c +++ b/src/cairo-path-bounds.c @@ -46,18 +46,19 @@ static cairo_status_t _cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, cairo_point_t *point); static cairo_status_t -_cairo_path_bounder_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2); +_cairo_path_bounder_move_to (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_path_bounder_add_spline (void *closure, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d); +_cairo_path_bounder_line_to (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_path_bounder_done_sub_path (void *closure, cairo_sub_path_done_t done); +_cairo_path_bounder_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d); static cairo_status_t -_cairo_path_bounder_done_path (void *closure); +_cairo_path_bounder_close_path (void *closure); static void _cairo_path_bounder_init (cairo_path_bounder_t *bounder) @@ -99,39 +100,42 @@ _cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, cairo_point_t *poi } static cairo_status_t -_cairo_path_bounder_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2) +_cairo_path_bounder_move_to (void *closure, cairo_point_t *point) { cairo_path_bounder_t *bounder = closure; - _cairo_path_bounder_add_point (bounder, p1); - _cairo_path_bounder_add_point (bounder, p2); + _cairo_path_bounder_add_point (bounder, point); return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_path_bounder_add_spline (void *closure, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d) +_cairo_path_bounder_line_to (void *closure, cairo_point_t *point) { cairo_path_bounder_t *bounder = closure; - _cairo_path_bounder_add_point (bounder, a); - _cairo_path_bounder_add_point (bounder, b); - _cairo_path_bounder_add_point (bounder, c); - _cairo_path_bounder_add_point (bounder, d); + _cairo_path_bounder_add_point (bounder, point); return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_path_bounder_done_sub_path (void *closure, cairo_sub_path_done_t done) +_cairo_path_bounder_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d) { + cairo_path_bounder_t *bounder = closure; + + _cairo_path_bounder_add_point (bounder, b); + _cairo_path_bounder_add_point (bounder, c); + _cairo_path_bounder_add_point (bounder, d); + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_path_bounder_done_path (void *closure) +_cairo_path_bounder_close_path (void *closure) { return CAIRO_STATUS_SUCCESS; } @@ -141,18 +145,17 @@ cairo_status_t _cairo_path_bounds (cairo_path_t *path, double *x1, double *y1, double *x2, double *y2) { cairo_status_t status; - static cairo_path_callbacks_t const cb = { - _cairo_path_bounder_add_edge, - _cairo_path_bounder_add_spline, - _cairo_path_bounder_done_sub_path, - _cairo_path_bounder_done_path - }; cairo_path_bounder_t bounder; _cairo_path_bounder_init (&bounder); - status = _cairo_path_interpret (path, CAIRO_DIRECTION_FORWARD, &cb, &bounder); + status = _cairo_path_interpret (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to, + _cairo_path_bounder_close_path, + &bounder); if (status) { *x1 = *y1 = *x2 = *y2 = 0.0; _cairo_path_bounder_fini (&bounder); diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c index 8f34af85..fba5bb09 100644 --- a/src/cairo-path-fill.c +++ b/src/cairo-path-fill.c @@ -31,6 +31,8 @@ typedef struct cairo_filler { cairo_gstate_t *gstate; cairo_traps_t *traps; + cairo_point_t current_point; + cairo_polygon_t polygon; } cairo_filler_t; @@ -41,18 +43,19 @@ static void _cairo_filler_fini (cairo_filler_t *filler); static cairo_status_t -_cairo_filler_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2); +_cairo_filler_move_to (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_filler_add_spline (void *closure, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d); +_cairo_filler_line_to (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_filler_done_sub_path (void *closure, cairo_sub_path_done_t done); +_cairo_filler_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d); static cairo_status_t -_cairo_filler_done_path (void *closure); +_cairo_filler_close_path (void *closure); static void _cairo_filler_init (cairo_filler_t *filler, cairo_gstate_t *gstate, cairo_traps_t *traps) @@ -60,6 +63,9 @@ _cairo_filler_init (cairo_filler_t *filler, cairo_gstate_t *gstate, cairo_traps_ filler->gstate = gstate; filler->traps = traps; + filler->current_point.x = 0; + filler->current_point.y = 0; + _cairo_polygon_init (&filler->polygon); } @@ -70,18 +76,46 @@ _cairo_filler_fini (cairo_filler_t *filler) } static cairo_status_t -_cairo_filler_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2) +_cairo_filler_move_to (void *closure, cairo_point_t *point) { + cairo_status_t status; cairo_filler_t *filler = closure; cairo_polygon_t *polygon = &filler->polygon; - return _cairo_polygon_add_edge (polygon, p1, p2); + status = _cairo_polygon_close (polygon); + if (status) + return status; + + status = _cairo_polygon_move_to (polygon, point); + if (status) + return status; + + filler->current_point = *point; + + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_filler_add_spline (void *closure, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d) +_cairo_filler_line_to (void *closure, cairo_point_t *point) +{ + cairo_status_t status; + cairo_filler_t *filler = closure; + cairo_polygon_t *polygon = &filler->polygon; + + status = _cairo_polygon_line_to (polygon, point); + if (status) + return status; + + filler->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_filler_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d) { int i; cairo_status_t status = CAIRO_STATUS_SUCCESS; @@ -90,7 +124,8 @@ _cairo_filler_add_spline (void *closure, cairo_gstate_t *gstate = filler->gstate; cairo_spline_t spline; - status = _cairo_spline_init (&spline, a, b, c, d); + status = _cairo_spline_init (&spline, &filler->current_point, b, c, d); + if (status == CAIRO_INT_STATUS_DEGENERATE) return CAIRO_STATUS_SUCCESS; @@ -98,65 +133,65 @@ _cairo_filler_add_spline (void *closure, if (status) goto CLEANUP_SPLINE; - for (i = 0; i < spline.num_points - 1; i++) { - status = _cairo_polygon_add_edge (polygon, &spline.points[i], &spline.points[i+1]); + for (i = 1; i < spline.num_points; i++) { + status = _cairo_polygon_line_to (polygon, &spline.points[i]); if (status) - goto CLEANUP_SPLINE; + break; } CLEANUP_SPLINE: _cairo_spline_fini (&spline); + filler->current_point = *d; + return status; } static cairo_status_t -_cairo_filler_done_sub_path (void *closure, cairo_sub_path_done_t done) +_cairo_filler_close_path (void *closure) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_status_t status; cairo_filler_t *filler = closure; cairo_polygon_t *polygon = &filler->polygon; - _cairo_polygon_close (polygon); - - return status; -} - -static cairo_status_t -_cairo_filler_done_path (void *closure) -{ - cairo_filler_t *filler = closure; + status = _cairo_polygon_close (polygon); + if (status) + return status; - return _cairo_traps_tessellate_polygon (filler->traps, - &filler->polygon, - filler->gstate->fill_rule); + return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_path_fill_to_traps (cairo_path_t *path, cairo_gstate_t *gstate, cairo_traps_t *traps) { - static const cairo_path_callbacks_t filler_callbacks = { - _cairo_filler_add_edge, - _cairo_filler_add_spline, - _cairo_filler_done_sub_path, - _cairo_filler_done_path - }; - - cairo_status_t status; + cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_filler_t filler; _cairo_filler_init (&filler, gstate, traps); status = _cairo_path_interpret (path, CAIRO_DIRECTION_FORWARD, - &filler_callbacks, &filler); - if (status) { - _cairo_filler_fini (&filler); - return status; - } + _cairo_filler_move_to, + _cairo_filler_line_to, + _cairo_filler_curve_to, + _cairo_filler_close_path, + &filler); + if (status) + goto BAIL; + status = _cairo_polygon_close (&filler.polygon); + if (status) + goto BAIL; + + status = _cairo_traps_tessellate_polygon (filler.traps, + &filler.polygon, + filler.gstate->fill_rule); + if (status) + goto BAIL; + +BAIL: _cairo_filler_fini (&filler); - return CAIRO_STATUS_SUCCESS; + return status; } diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c index 35e7b1b7..c2412579 100644 --- a/src/cairo-path-stroke.c +++ b/src/cairo-path-stroke.c @@ -31,11 +31,17 @@ typedef struct cairo_stroker { cairo_gstate_t *gstate; cairo_traps_t *traps; - int have_prev; - int have_first; - int is_first; - cairo_stroke_face_t prev; - cairo_stroke_face_t first; + int has_current_point; + cairo_point_t current_point; + cairo_point_t first_point; + + int has_current_face; + cairo_stroke_face_t current_face; + + int has_first_face; + cairo_stroke_face_t first_face; + + int dashed; int dash_index; int dash_on; double dash_remain; @@ -49,21 +55,22 @@ static void _cairo_stroker_fini (cairo_stroker_t *stroker); static cairo_status_t -_cairo_stroker_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2); +_cairo_stroker_move_to (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_stroker_add_edge_dashed (void *closure, cairo_point_t *p1, cairo_point_t *p2); +_cairo_stroker_line_to (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_stroker_add_spline (void *closure, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d); +_cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_stroker_done_sub_path (void *closure, cairo_sub_path_done_t done); +_cairo_stroker_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d); static cairo_status_t -_cairo_stroker_done_path (void *closure); +_cairo_stroker_close_path (void *closure); static void _translate_point (cairo_point_t *point, cairo_point_t *offset); @@ -89,6 +96,7 @@ _cairo_stroker_start_dash (cairo_stroker_t *stroker) if (++i == gstate->num_dashes) i = 0; } + stroker->dashed = 1; stroker->dash_index = i; stroker->dash_on = on; stroker->dash_remain = gstate->dash[i] - offset; @@ -113,11 +121,15 @@ _cairo_stroker_init (cairo_stroker_t *stroker, cairo_gstate_t *gstate, cairo_tra { stroker->gstate = gstate; stroker->traps = traps; - stroker->have_prev = 0; - stroker->have_first = 0; - stroker->is_first = 1; + + stroker->has_current_point = 0; + stroker->has_current_face = 0; + stroker->has_first_face = 0; + if (gstate->dash) _cairo_stroker_start_dash (stroker); + else + stroker->dashed = 0; } static void @@ -286,10 +298,11 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st outer.x = _cairo_fixed_from_double (mx); outer.y = _cairo_fixed_from_double (my); _cairo_polygon_init (&polygon); - _cairo_polygon_add_edge (&polygon, &in->point, inpt); - _cairo_polygon_add_edge (&polygon, inpt, &outer); - _cairo_polygon_add_edge (&polygon, &outer, outpt); - _cairo_polygon_add_edge (&polygon, outpt, &in->point); + _cairo_polygon_move_to (&polygon, &in->point); + _cairo_polygon_line_to (&polygon, inpt); + _cairo_polygon_line_to (&polygon, &outer); + _cairo_polygon_line_to (&polygon, outpt); + _cairo_polygon_close (&polygon); status = _cairo_traps_tessellate_polygon (stroker->traps, &polygon, CAIRO_FILL_RULE_WINDING); @@ -351,8 +364,6 @@ _cairo_stroker_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f) cairo_point_t occw, ocw; cairo_polygon_t polygon; - _cairo_polygon_init (&polygon); - dx = f->usr_vector.x; dy = f->usr_vector.y; dx *= gstate->line_width / 2.0; @@ -365,10 +376,12 @@ _cairo_stroker_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f) ocw.x = f->cw.x + fvector.dx; ocw.y = f->cw.y + fvector.dy; - _cairo_polygon_add_edge (&polygon, &f->cw, &ocw); - _cairo_polygon_add_edge (&polygon, &ocw, &occw); - _cairo_polygon_add_edge (&polygon, &occw, &f->ccw); - _cairo_polygon_add_edge (&polygon, &f->ccw, &f->cw); + _cairo_polygon_init (&polygon); + _cairo_polygon_move_to (&polygon, &f->cw); + _cairo_polygon_line_to (&polygon, &ocw); + _cairo_polygon_line_to (&polygon, &occw); + _cairo_polygon_line_to (&polygon, &f->ccw); + _cairo_polygon_close (&polygon); status = _cairo_traps_tessellate_polygon (stroker->traps, &polygon, CAIRO_FILL_RULE_WINDING); _cairo_polygon_fini (&polygon); @@ -454,8 +467,9 @@ static cairo_status_t _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, cairo_point_t *p1, cairo_point_t *p2, cairo_stroke_face_t *start, cairo_stroke_face_t *end) { + cairo_status_t status; cairo_gstate_t *gstate = stroker->gstate; - cairo_point_t quad[4]; + cairo_polygon_t polygon; cairo_slope_t slope; if (p1->x == p2->x && p1->y == p2->y) { @@ -473,20 +487,58 @@ _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, cairo_point_t *p1, cairo_ fields from start. */ _compute_face (p2, &slope, gstate, end); - quad[0] = start->cw; - quad[1] = start->ccw; - quad[2] = end->ccw; - quad[3] = end->cw; + /* XXX: I should really check the return value of the + move_to/line_to functions here to catch out of memory + conditions. But since that would be ugly, I'd prefer to add a + status flag to the polygon object that I could check only once + at then end of this sequence, (like we do with cairo_t + already). */ + _cairo_polygon_init (&polygon); + _cairo_polygon_move_to (&polygon, &start->cw); + _cairo_polygon_line_to (&polygon, &start->ccw); + _cairo_polygon_line_to (&polygon, &end->ccw); + _cairo_polygon_line_to (&polygon, &end->cw); + _cairo_polygon_close (&polygon); + + /* XXX: We can't use tessellate_rectangle as the matrix may have + skewed this into a non-rectangular shape. Perhaps it would be + worth checking the matrix for skew so that the common case + could use the faster tessellate_rectangle rather than + tessellate_polygon? */ + status = _cairo_traps_tessellate_polygon (stroker->traps, + &polygon, CAIRO_FILL_RULE_WINDING); + + _cairo_polygon_fini (&polygon); - return _cairo_traps_tessellate_rectangle (stroker->traps, quad); + return status; } static cairo_status_t -_cairo_stroker_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2) +_cairo_stroker_move_to (void *closure, cairo_point_t *point) +{ + cairo_stroker_t *stroker = closure; + + stroker->first_point = *point; + stroker->current_point = *point; + stroker->has_current_point = 1; + + stroker->has_first_face = 0; + stroker->has_current_face = 0; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_stroker_line_to (void *closure, cairo_point_t *point) { cairo_status_t status; cairo_stroker_t *stroker = closure; cairo_stroke_face_t start, end; + cairo_point_t *p1 = &stroker->current_point; + cairo_point_t *p2 = point; + + if (!stroker->has_current_point) + return _cairo_stroker_move_to (stroker, point); if (p1->x == p2->x && p1->y == p2->y) { /* XXX: Need to rethink how this case should be handled, (both @@ -500,19 +552,20 @@ _cairo_stroker_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2) if (status) return status; - if (stroker->have_prev) { - status = _cairo_stroker_join (stroker, &stroker->prev, &start); + if (stroker->has_current_face) { + status = _cairo_stroker_join (stroker, &stroker->current_face, &start); if (status) return status; } else { - stroker->have_prev = 1; - if (stroker->is_first) { - stroker->have_first = 1; - stroker->first = start; + if (!stroker->has_first_face) { + stroker->first_face = start; + stroker->has_first_face = 1; } } - stroker->prev = end; - stroker->is_first = 0; + stroker->current_face = end; + stroker->has_current_face = 1; + + stroker->current_point = *point; return CAIRO_STATUS_SUCCESS; } @@ -521,7 +574,7 @@ _cairo_stroker_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2) * Dashed lines. Cap each dash end, join around turns when on */ static cairo_status_t -_cairo_stroker_add_edge_dashed (void *closure, cairo_point_t *p1, cairo_point_t *p2) +_cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_stroker_t *stroker = closure; @@ -532,6 +585,11 @@ _cairo_stroker_add_edge_dashed (void *closure, cairo_point_t *p1, cairo_point_t cairo_point_t fd1, fd2; int first = 1; cairo_stroke_face_t sub_start, sub_end; + cairo_point_t *p1 = &stroker->current_point; + cairo_point_t *p2 = point; + + if (!stroker->has_current_point) + return _cairo_stroker_move_to (stroker, point); dx = _cairo_fixed_to_double (p2->x - p1->x); dy = _cairo_fixed_to_double (p2->y - p1->y); @@ -569,18 +627,18 @@ _cairo_stroker_add_edge_dashed (void *closure, cairo_point_t *p1, cairo_point_t return status; } else { /* - * First in this segment, join to any prev, else + * First in this segment, join to any current_face, else * if at start of sub-path, mark position, else * cap */ - if (stroker->have_prev) { - status = _cairo_stroker_join (stroker, &stroker->prev, &sub_start); + if (stroker->has_current_face) { + status = _cairo_stroker_join (stroker, &stroker->current_face, &sub_start); if (status) return status; } else { - if (stroker->is_first) { - stroker->have_first = 1; - stroker->first = sub_start; + if (!stroker->has_first_face) { + stroker->first_face = sub_start; + stroker->has_first_face = 1; } else { status = _cairo_stroker_cap (stroker, &sub_start); if (status) @@ -600,8 +658,8 @@ _cairo_stroker_add_edge_dashed (void *closure, cairo_point_t *p1, cairo_point_t * Mark previous line face and fix up next time * through */ - stroker->prev = sub_end; - stroker->have_prev = 1; + stroker->current_face = sub_end; + stroker->has_current_face = 1; } } else { /* @@ -609,27 +667,30 @@ _cairo_stroker_add_edge_dashed (void *closure, cairo_point_t *p1, cairo_point_t * and cap if necessary */ if (first) { - if (stroker->have_prev) { - status = _cairo_stroker_cap (stroker, &stroker->prev); + if (stroker->has_current_face) { + status = _cairo_stroker_cap (stroker, &stroker->current_face); if (status) return status; } } if (!remain) - stroker->have_prev = 0; + stroker->has_current_face = 0; } _cairo_stroker_step_dash (stroker, tmp); fd1 = fd2; first = 0; } - stroker->is_first = 0; + + stroker->current_point = *point; + return status; } static cairo_status_t -_cairo_stroker_add_spline (void *closure, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d) +_cairo_stroker_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_stroker_t *stroker = closure; @@ -638,6 +699,7 @@ _cairo_stroker_add_spline (void *closure, cairo_pen_t pen; cairo_stroke_face_t start, end; cairo_point_t extra_points[4]; + cairo_point_t *a = &stroker->current_point; status = _cairo_spline_init (&spline, a, b, c, d); if (status == CAIRO_INT_STATUS_DEGENERATE) @@ -650,19 +712,18 @@ _cairo_stroker_add_spline (void *closure, _compute_face (a, &spline.initial_slope, gstate, &start); _compute_face (d, &spline.final_slope, gstate, &end); - if (stroker->have_prev) { - status = _cairo_stroker_join (stroker, &stroker->prev, &start); + if (stroker->has_current_face) { + status = _cairo_stroker_join (stroker, &stroker->current_face, &start); if (status) return status; } else { - stroker->have_prev = 1; - if (stroker->is_first) { - stroker->have_first = 1; - stroker->first = start; + if (!stroker->has_first_face) { + stroker->first_face = start; + stroker->has_first_face = 1; } } - stroker->prev = end; - stroker->is_first = 0; + stroker->current_face = end; + stroker->has_current_face = 1; extra_points[0] = start.cw; extra_points[0].x -= start.point.x; @@ -690,91 +751,89 @@ _cairo_stroker_add_spline (void *closure, CLEANUP_SPLINE: _cairo_spline_fini (&spline); + stroker->current_point = *d; + return status; } static cairo_status_t -_cairo_stroker_done_sub_path (void *closure, cairo_sub_path_done_t done) +_cairo_stroker_close_path (void *closure) { cairo_status_t status; cairo_stroker_t *stroker = closure; - switch (done) { - case CAIRO_SUB_PATH_DONE_JOIN: - if (stroker->have_first && stroker->have_prev) { - status = _cairo_stroker_join (stroker, &stroker->prev, &stroker->first); - if (status) - return status; - break; - } - /* fall through... */ - case CAIRO_SUB_PATH_DONE_CAP: - if (stroker->have_first) { - cairo_point_t t; - /* The initial cap needs an outward facing vector. Reverse everything */ - stroker->first.usr_vector.x = -stroker->first.usr_vector.x; - stroker->first.usr_vector.y = -stroker->first.usr_vector.y; - stroker->first.dev_vector.dx = -stroker->first.dev_vector.dx; - stroker->first.dev_vector.dy = -stroker->first.dev_vector.dy; - t = stroker->first.cw; - stroker->first.cw = stroker->first.ccw; - stroker->first.ccw = t; - status = _cairo_stroker_cap (stroker, &stroker->first); - if (status) - return status; - } - if (stroker->have_prev) { - status = _cairo_stroker_cap (stroker, &stroker->prev); - if (status) - return status; - } - break; + if (stroker->has_current_point) { + if (stroker->dashed) + status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point); + else + status = _cairo_stroker_line_to (stroker, &stroker->first_point); + if (status) + return status; } - stroker->have_prev = 0; - stroker->have_first = 0; - stroker->is_first = 1; + if (stroker->has_first_face && stroker->has_current_face) { + status = _cairo_stroker_join (stroker, &stroker->current_face, &stroker->first_face); + if (status) + return status; + } - return CAIRO_STATUS_SUCCESS; -} + stroker->has_first_face = 0; + stroker->has_current_face = 0; + stroker->has_current_point = 0; -static cairo_status_t -_cairo_stroker_done_path (void *closure) -{ return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_path_stroke_to_traps (cairo_path_t *path, cairo_gstate_t *gstate, cairo_traps_t *traps) { - static const cairo_path_callbacks_t stroker_solid_cb = { - _cairo_stroker_add_edge, - _cairo_stroker_add_spline, - _cairo_stroker_done_sub_path, - _cairo_stroker_done_path - }; - static const cairo_path_callbacks_t stroker_dashed_cb = { - _cairo_stroker_add_edge_dashed, - _cairo_stroker_add_spline, - _cairo_stroker_done_sub_path, - _cairo_stroker_done_path - }; - const cairo_path_callbacks_t *callbacks = gstate->dash ? &stroker_dashed_cb : &stroker_solid_cb; - - cairo_status_t status; + cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_stroker_t stroker; _cairo_stroker_init (&stroker, gstate, traps); - status = _cairo_path_interpret (path, - CAIRO_DIRECTION_FORWARD, - callbacks, &stroker); - if (status) { - _cairo_stroker_fini (&stroker); - return status; + if (gstate->dash) + status = _cairo_path_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_stroker_move_to, + _cairo_stroker_line_to_dashed, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); + else + status = _cairo_path_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_stroker_move_to, + _cairo_stroker_line_to, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); + if (status) + goto BAIL; + + if (stroker.has_first_face) { + cairo_point_t t; + /* The initial cap needs an outward facing vector. Reverse everything */ + stroker.first_face.usr_vector.x = -stroker.first_face.usr_vector.x; + stroker.first_face.usr_vector.y = -stroker.first_face.usr_vector.y; + stroker.first_face.dev_vector.dx = -stroker.first_face.dev_vector.dx; + stroker.first_face.dev_vector.dy = -stroker.first_face.dev_vector.dy; + t = stroker.first_face.cw; + stroker.first_face.cw = stroker.first_face.ccw; + stroker.first_face.ccw = t; + status = _cairo_stroker_cap (&stroker, &stroker.first_face); + if (status) + goto BAIL; } + if (stroker.has_current_face) { + status = _cairo_stroker_cap (&stroker, &stroker.current_face); + if (status) + goto BAIL; + } + +BAIL: _cairo_stroker_fini (&stroker); - return CAIRO_STATUS_SUCCESS; + return status; } diff --git a/src/cairo-path.c b/src/cairo-path.c index 9366394d..91142c8f 100644 --- a/src/cairo-path.c +++ b/src/cairo-path.c @@ -70,6 +70,11 @@ _cairo_path_init (cairo_path_t *path) path->arg_head = NULL; path->arg_tail = NULL; + + path->current_point.x = 0; + path->current_point.y = 0; + path->has_current_point = 0; + path->last_move_point = path->current_point; } cairo_status_t @@ -79,6 +84,9 @@ _cairo_path_init_copy (cairo_path_t *path, cairo_path_t *other) cairo_path_arg_buf_t *arg, *other_arg; _cairo_path_init (path); + path->current_point = other->current_point; + path->has_current_point = other->has_current_point; + path->last_move_point = other->last_move_point; for (other_op = other->op_head; other_op; other_op = other_op->next) { op = _cairo_path_op_buf_create (); @@ -120,54 +128,131 @@ _cairo_path_fini (cairo_path_t *path) _cairo_path_arg_buf_destroy (arg); } path->arg_tail = NULL; + + path->has_current_point = 0; +} + +cairo_status_t +_cairo_path_move_to (cairo_path_t *path, cairo_point_t *point) +{ + cairo_status_t status; + + status = _cairo_path_add (path, CAIRO_PATH_OP_MOVE_TO, point, 1); + if (status) + return status; + + path->current_point = *point; + path->has_current_point = 1; + path->last_move_point = path->current_point; + + return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_path_move_to (cairo_path_t *path, double x, double y) +_cairo_path_rel_move_to (cairo_path_t *path, cairo_distance_t *distance) { cairo_point_t point; - point.x = _cairo_fixed_from_double (x); - point.y = _cairo_fixed_from_double (y); + point.x = path->current_point.x + distance->dx; + point.y = path->current_point.y + distance->dy; + + return _cairo_path_move_to (path, &point); +} + +cairo_status_t +_cairo_path_line_to (cairo_path_t *path, cairo_point_t *point) +{ + cairo_status_t status; + + status = _cairo_path_add (path, CAIRO_PATH_OP_LINE_TO, point, 1); + if (status) + return status; - return _cairo_path_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1); + path->current_point = *point; + path->has_current_point = 1; + + return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_path_line_to (cairo_path_t *path, double x, double y) +_cairo_path_rel_line_to (cairo_path_t *path, cairo_distance_t *distance) { cairo_point_t point; - point.x = _cairo_fixed_from_double (x); - point.y = _cairo_fixed_from_double (y); + point.x = path->current_point.x + distance->dx; + point.y = path->current_point.y + distance->dy; - return _cairo_path_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1); + return _cairo_path_line_to (path, &point); } cairo_status_t _cairo_path_curve_to (cairo_path_t *path, - double x1, double y1, - double x2, double y2, - double x3, double y3) + cairo_point_t *p0, + cairo_point_t *p1, + cairo_point_t *p2) { + cairo_status_t status; cairo_point_t point[3]; - point[0].x = _cairo_fixed_from_double (x1); - point[0].y = _cairo_fixed_from_double (y1); + point[0] = *p0; + point[1] = *p1; + point[2] = *p2; - point[1].x = _cairo_fixed_from_double (x2); - point[1].y = _cairo_fixed_from_double (y2); + status = _cairo_path_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); + if (status) + return status; - point[2].x = _cairo_fixed_from_double (x3); - point[2].y = _cairo_fixed_from_double (y3); + path->current_point = *p2; + path->has_current_point = 1; - return _cairo_path_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_path_rel_curve_to (cairo_path_t *path, + cairo_distance_t *d0, + cairo_distance_t *d1, + cairo_distance_t *d2) +{ + cairo_point_t p0, p1, p2; + + p0.x = path->current_point.x + d0->dx; + p0.y = path->current_point.y + d0->dy; + + p1.x = path->current_point.x + d1->dx; + p1.y = path->current_point.y + d1->dy; + + p2.x = path->current_point.x + d2->dx; + p2.y = path->current_point.y + d2->dy; + + return _cairo_path_curve_to (path, &p0, &p1, &p2); } cairo_status_t _cairo_path_close_path (cairo_path_t *path) { - return _cairo_path_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); + cairo_status_t status; + + status = _cairo_path_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); + if (status) + return status; + + path->current_point.x = path->last_move_point.x; + path->current_point.y = path->last_move_point.y; + path->has_current_point = 1; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_path_current_point (cairo_path_t *path, cairo_point_t *point) +{ + if (! path->has_current_point) + return CAIRO_STATUS_NO_CURRENT_POINT; + + *point = path->current_point; + + return CAIRO_STATUS_SUCCESS; } static cairo_status_t @@ -320,7 +405,13 @@ static int const num_args[] = }; cairo_status_t -_cairo_path_interpret (cairo_path_t *path, cairo_direction_t dir, const cairo_path_callbacks_t *cb, void *closure) +_cairo_path_interpret (cairo_path_t *path, + cairo_direction_t dir, + cairo_path_move_to_func_t *move_to, + cairo_path_line_to_func_t *line_to, + cairo_path_curve_to_func_t *curve_to, + cairo_path_close_path_func_t *close_path, + void *closure) { cairo_status_t status; int i, arg; @@ -329,10 +420,6 @@ _cairo_path_interpret (cairo_path_t *path, cairo_direction_t dir, const cairo_pa cairo_path_arg_buf_t *arg_buf = path->arg_head; int buf_i = 0; cairo_point_t point[CAIRO_PATH_OP_MAX_ARGS]; - cairo_point_t current = {0, 0}; - cairo_point_t first = {0, 0}; - int has_current = 0; - int has_edge = 0; int step = (dir == CAIRO_DIRECTION_FORWARD) ? 1 : -1; for (op_buf = (dir == CAIRO_DIRECTION_FORWARD) ? path->op_head : path->op_tail; @@ -374,62 +461,24 @@ _cairo_path_interpret (cairo_path_t *path, cairo_direction_t dir, const cairo_pa switch (op) { case CAIRO_PATH_OP_MOVE_TO: - if (has_edge) { - status = (*cb->done_sub_path) (closure, CAIRO_SUB_PATH_DONE_CAP); - if (status) - return status; - } - first = point[0]; - current = point[0]; - has_current = 1; - has_edge = 0; + status = (*move_to) (closure, &point[0]); break; case CAIRO_PATH_OP_LINE_TO: - if (has_current) { - status = (*cb->add_edge) (closure, ¤t, &point[0]); - if (status) - return status; - current = point[0]; - has_edge = 1; - } else { - first = point[0]; - current = point[0]; - has_current = 1; - has_edge = 0; - } + status = (*line_to) (closure, &point[0]); break; case CAIRO_PATH_OP_CURVE_TO: - if (has_current) { - status = (*cb->add_spline) (closure, ¤t, &point[0], &point[1], &point[2]); - if (status) - return status; - current = point[2]; - has_edge = 1; - } else { - first = point[2]; - current = point[2]; - has_current = 1; - has_edge = 0; - } + status = (*curve_to) (closure, &point[0], &point[1], &point[2]); break; case CAIRO_PATH_OP_CLOSE_PATH: - if (has_edge) { - (*cb->add_edge) (closure, ¤t, &first); - (*cb->done_sub_path) (closure, CAIRO_SUB_PATH_DONE_JOIN); - } - current.x = 0; - current.y = 0; - first.x = 0; - first.y = 0; - has_current = 0; - has_edge = 0; + default: + status = (*close_path) (closure); break; } + if (status) + return status; } } - if (has_edge) - (*cb->done_sub_path) (closure, CAIRO_SUB_PATH_DONE_CAP); - return (*cb->done_path) (closure); + return CAIRO_STATUS_SUCCESS; } diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c new file mode 100644 index 00000000..dcdb1566 --- /dev/null +++ b/src/cairo-pattern.c @@ -0,0 +1,716 @@ +/* + * Copyright © 2002 University of Southern California + * + * 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 the + * University of Southern California not be used in advertising or + * publicity pertaining to distribution of the software without + * specific, written prior permission. The University of Southern + * California makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF + * SOUTHERN CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <c99drn@cs.umu.se> + */ + +#include "cairoint.h" + +void +_cairo_pattern_init (cairo_pattern_t *pattern) +{ + pattern->ref_count = 1; + + pattern->extend = CAIRO_EXTEND_DEFAULT; + pattern->filter = CAIRO_FILTER_DEFAULT; + + _cairo_color_init (&pattern->color); + + _cairo_matrix_init (&pattern->matrix); + + pattern->stops = NULL; + pattern->n_stops = 0; + + pattern->type = CAIRO_PATTERN_SOLID; + + pattern->source = NULL; + pattern->source_offset.x = 0.0; + pattern->source_offset.y = 0.0; +} + +cairo_status_t +_cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other) +{ + *pattern = *other; + + pattern->ref_count = 1; + + 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); + } + + if (pattern->source) + cairo_surface_reference (other->source); + + if (pattern->type == CAIRO_PATTERN_SURFACE) + cairo_surface_reference (other->u.surface.surface); + + return CAIRO_STATUS_SUCCESS; +} + +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); + } + + if (pattern->source) + cairo_surface_destroy (pattern->source); +} + +void +_cairo_pattern_init_solid (cairo_pattern_t *pattern, + double red, double green, double blue) +{ + _cairo_pattern_init (pattern); + + pattern->type = CAIRO_PATTERN_SOLID; + _cairo_color_set_rgb (&pattern->color, red, green, blue); +} + +cairo_pattern_t * +_cairo_pattern_create_solid (double red, double green, double blue) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_pattern_t)); + if (pattern == NULL) + return NULL; + + _cairo_pattern_init_solid (pattern, red, green, blue); + + return pattern; +} + +cairo_pattern_t * +cairo_pattern_create_for_surface (cairo_surface_t *surface) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_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); + + return pattern; +} + +cairo_pattern_t * +cairo_pattern_create_linear (double x0, double y0, double x1, double y1) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_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; + + return pattern; +} + +cairo_pattern_t * +cairo_pattern_create_radial (double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_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.dx = radius0; + pattern->u.radial.radius0.dy = radius0; + pattern->u.radial.center1.x = cx1; + pattern->u.radial.center1.y = cy1; + pattern->u.radial.radius1.dx = radius1; + pattern->u.radial.radius1.dy = radius1; + + return pattern; +} + +void +cairo_pattern_reference (cairo_pattern_t *pattern) +{ + if (pattern == NULL) + return; + + pattern->ref_count++; +} + +void +cairo_pattern_destroy (cairo_pattern_t *pattern) +{ + if (pattern == NULL) + return; + + pattern->ref_count--; + if (pattern->ref_count) + return; + + _cairo_pattern_fini (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) +{ + cairo_color_stop_t *stop; + + _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); + if (pattern->stops == NULL) { + pattern->n_stops = 0; + + return CAIRO_STATUS_NO_MEMORY; + } + + stop = &pattern->stops[pattern->n_stops - 1]; + + stop->offset = offset; + stop->id = pattern->n_stops; + _cairo_color_init (&stop->color); + _cairo_color_set_rgb (&stop->color, red, green, blue); + _cairo_color_set_alpha (&stop->color, alpha); + stop->color_char[0] = stop->color.red_short / 256; + stop->color_char[1] = stop->color.green_short / 256; + stop->color_char[2] = stop->color.blue_short / 256; + stop->color_char[3] = stop->color.alpha_short / 256; + + /* sort stops in ascending order */ + qsort (pattern->stops, pattern->n_stops, sizeof (cairo_color_stop_t), + _cairo_pattern_stop_compare); + + return CAIRO_STATUS_SUCCESS; +} + +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; +} + +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; +} + +cairo_status_t +cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter) +{ + pattern->filter = filter; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_filter_t +cairo_pattern_get_filter (cairo_pattern_t *pattern) +{ + return pattern->filter; +} + +cairo_status_t +cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) +{ + pattern->extend = extend; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_extend_t +cairo_pattern_get_extend (cairo_pattern_t *pattern) +{ + return pattern->extend; +} + +cairo_status_t +_cairo_pattern_get_rgb (cairo_pattern_t *pattern, + double *red, double *green, double *blue) +{ + _cairo_color_get_rgb (&pattern->color, red, green, blue); + + return CAIRO_STATUS_SUCCESS; +} + +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++) { + cairo_color_stop_t *stop = &pattern->stops[i]; + + _cairo_color_set_alpha (&stop->color, stop->color.alpha * alpha); + + stop->color_char[0] = stop->color.red_short / 256; + stop->color_char[1] = stop->color.green_short / 256; + stop->color_char[2] = stop->color.blue_short / 256; + stop->color_char[3] = stop->color.alpha_short / 256; + } +} + +void +_cairo_pattern_add_source_offset (cairo_pattern_t *pattern, + double x, double y) +{ + pattern->source_offset.x += x; + pattern->source_offset.y += y; +} + +void +_cairo_pattern_transform (cairo_pattern_t *pattern, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse) +{ + cairo_matrix_t matrix; + + switch (pattern->type) { + case CAIRO_PATTERN_SURFACE: + /* hmm, maybe we should instead multiply with the inverse of the + pattern matrix here? */ + cairo_matrix_multiply (&pattern->matrix, ctm_inverse, + &pattern->matrix); + break; + case CAIRO_PATTERN_LINEAR: + cairo_matrix_multiply (&matrix, &pattern->matrix, ctm); + cairo_matrix_transform_point (&matrix, + &pattern->u.linear.point0.x, + &pattern->u.linear.point0.y); + cairo_matrix_transform_point (&matrix, + &pattern->u.linear.point1.x, + &pattern->u.linear.point1.y); + break; + case CAIRO_PATTERN_RADIAL: + cairo_matrix_multiply (&matrix, &pattern->matrix, ctm); + cairo_matrix_transform_point (&matrix, + &pattern->u.radial.center0.x, + &pattern->u.radial.center0.y); + cairo_matrix_transform_distance (&matrix, + &pattern->u.radial.radius0.dx, + &pattern->u.radial.radius0.dy); + cairo_matrix_transform_point (&matrix, + &pattern->u.radial.center1.x, + &pattern->u.radial.center1.y); + cairo_matrix_transform_distance (&matrix, + &pattern->u.radial.radius1.dx, + &pattern->u.radial.radius1.dy); + break; + case CAIRO_PATTERN_SOLID: + break; + } +} + +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); +} + +typedef void (*cairo_shader_function_t) (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color); + +#define INTERPOLATE_COLOR_NEAREST(c1, c2, factor) \ + ((unsigned char) ((factor < 0.5)? c1: c2)) + +static void +_cairo_pattern_shader_nearest (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color) +{ + result_color[0] = INTERPOLATE_COLOR_NEAREST (color0[0], color1[0], factor); + result_color[1] = INTERPOLATE_COLOR_NEAREST (color0[1], color1[1], factor); + result_color[2] = INTERPOLATE_COLOR_NEAREST (color0[2], color1[2], factor); + result_color[3] = INTERPOLATE_COLOR_NEAREST (color0[3], color1[3], factor); +} + +#undef INTERPOLATE_COLOR_NEAREST + +#define INTERPOLATE_COLOR_LINEAR(c1, c2, factor) \ + ((unsigned char) ((c2 * factor) + (c1 * (1.0 - factor)))) + +static void +_cairo_pattern_shader_linear (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color) +{ + result_color[0] = INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor); + result_color[1] = INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor); + result_color[2] = INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor); + result_color[3] = INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor); +} + +static void +_cairo_pattern_shader_gaussian (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color) +{ + factor = (exp (factor * factor) - 1.0) / (M_E - 1.0); + + result_color[0] = INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor); + result_color[1] = INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor); + result_color[2] = INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor); + result_color[3] = INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor); +} + +#undef INTERPOLATE_COLOR_LINEAR + +void +_cairo_pattern_calc_color_at_pixel (cairo_pattern_t *pattern, + double factor, + int *pixel) +{ + int p, colorstop; + double factorscale; + unsigned char result_color[4]; + cairo_shader_function_t shader_function; + + switch (pattern->filter) { + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + shader_function = _cairo_pattern_shader_nearest; + break; + case CAIRO_FILTER_GAUSSIAN: + shader_function = _cairo_pattern_shader_gaussian; + break; + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + shader_function = _cairo_pattern_shader_linear; + break; + } + + if (factor > 1.0 || factor < 0.0) { + switch (pattern->extend) { + case CAIRO_EXTEND_REPEAT: + factor -= floor (factor); + break; + case CAIRO_EXTEND_REFLECT: + if (factor >= 0.0) { + if (((int) factor) % 2) + factor = 1.0 - (factor - floor (factor)); + else + factor -= floor (factor); + } else { + if (((int) factor) % 2) + factor -= floor (factor); + else + factor = 1.0 - (factor - floor (factor)); + } + break; + case CAIRO_EXTEND_NONE: + break; + } + } + + if (factor < pattern->stops[0].offset) + factor = pattern->stops[0].offset; + + if (factor > pattern->stops[pattern->n_stops - 1].offset) + factor = pattern->stops[pattern->n_stops - 1].offset; + + for (colorstop = 0; colorstop < pattern->n_stops - 1; colorstop++) { + if (factor <= pattern->stops[colorstop + 1].offset) { + factorscale = fabs (pattern->stops[colorstop].offset - + pattern->stops[colorstop + 1].offset); + + /* abrubt change, difference between two offsets == 0.0 */ + if (factorscale == 0) + break; + + factor -= pattern->stops[colorstop].offset; + + /* take offset as new 0 of coordinate system */ + factor /= factorscale; + + shader_function (pattern->stops[colorstop].color_char, + pattern->stops[colorstop + 1].color_char, + factor, result_color); + + p = ((result_color[3] << 24) | + (result_color[0] << 16) | + (result_color[1] << 8) | (result_color[2] << 0)); + *pixel = p; + break; + } + } +} + +static void +_cairo_image_data_set_linear (cairo_pattern_t *pattern, + double offset_x, + double offset_y, + char *data, + int width, + int height) +{ + int x, y; + cairo_point_double_t point0, point1, angle; + double a, length, start, end; + double factor; + + point0.x = pattern->u.linear.point0.x - offset_x; + point0.y = pattern->u.linear.point0.y - offset_y; + point1.x = pattern->u.linear.point1.x - offset_x; + point1.y = pattern->u.linear.point1.y - offset_y; + + length = sqrt ((point1.x - point0.x) * (point1.x - point0.x) + + (point1.y - point0.y) * (point1.y - point0.y)); + length = (length) ? 1.0 / length : INT_MAX; + + a = -atan2 (point1.y - point0.y, point1.x - point0.x); + angle.x = cos (a); + angle.y = -sin (a); + + start = angle.x * point0.x; + start += angle.y * point0.y; + + end = angle.x * point1.x; + end += angle.y * point1.y; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + + factor = angle.x * (double) x; + factor += angle.y * (double) y; + + factor = factor - start; + factor *= length; + + _cairo_pattern_calc_color_at_pixel (pattern, factor, (int *) + &data[y * width * 4 + x * 4]); + } + } +} + +/* TODO: Inner circle is currently ignored. */ +static void +_cairo_image_data_set_radial (cairo_pattern_t *pattern, + double offset_x, + double offset_y, + char *data, + int width, + int height) +{ + int x, y; + cairo_point_double_t center1, pos; + cairo_distance_double_t length; + double factor; + double min_length; + + center1.x = pattern->u.radial.center1.x - offset_x; + center1.y = pattern->u.radial.center1.y - offset_y; + + min_length = + fabs ((pattern->u.radial.radius1.dx < pattern->u.radial.radius1.dy) ? + pattern->u.radial.radius1.dx : pattern->u.radial.radius1.dy); + + /* ugly */ + if (min_length == 0.0) + min_length = 0.000001; + + length.dx = min_length / pattern->u.radial.radius1.dx; + length.dy = min_length / pattern->u.radial.radius1.dy; + + min_length = 1.0 / min_length; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + pos.x = x - center1.x; + pos.y = y - center1.y; + + pos.x *= length.dx; + pos.y *= length.dy; + + factor = sqrt (pos.x * pos.x + pos.y * pos.y) * min_length; + + _cairo_pattern_calc_color_at_pixel (pattern, factor, (int *) + &data[y * width * 4 + x * 4]); + } + } +} + +cairo_image_surface_t * +_cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box) +{ + cairo_surface_t *surface; + + switch (pattern->type) { + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: { + char *data; + int width = ceil (_cairo_fixed_to_double (box->p2.x) - + _cairo_fixed_to_double (box->p1.x)); + int height = ceil (_cairo_fixed_to_double (box->p2.y) - + _cairo_fixed_to_double (box->p1.y)); + + data = malloc (width * height * 4); + if (!data) + return NULL; + + _cairo_pattern_add_source_offset (pattern, + _cairo_fixed_to_double (box->p1.x), + _cairo_fixed_to_double (box->p1.y)); + + if (pattern->type == CAIRO_PATTERN_RADIAL) + _cairo_image_data_set_radial (pattern, + pattern->source_offset.x, + pattern->source_offset.y, + data, width, height); + else + _cairo_image_data_set_linear (pattern, + pattern->source_offset.x, + pattern->source_offset.y, + data, width, height); + + surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_ARGB32, + width, height, + width * 4); + + if (surface) + _cairo_image_surface_assume_ownership_of_data ( + (cairo_image_surface_t *) surface); + } + 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); + } + break; + case CAIRO_PATTERN_SURFACE: { + cairo_image_surface_t *image; + + image = _cairo_surface_get_image (pattern->u.surface.surface); + if (image) + surface = &image->base; + else + surface = NULL; + + } + break; + } + + return (cairo_image_surface_t *) surface; +} diff --git a/src/cairo-pen.c b/src/cairo-pen.c index dd054372..0bb5debd 100644 --- a/src/cairo-pen.c +++ b/src/cairo-pen.c @@ -288,7 +288,7 @@ _cairo_pen_stroke_spline_half (cairo_pen_t *pen, while (i != stop) { hull_point.x = point[i].x + pen->vertices[active].point.x; hull_point.y = point[i].y + pen->vertices[active].point.y; - status = _cairo_polygon_add_point (polygon, &hull_point); + status = _cairo_polygon_line_to (polygon, &hull_point); if (status) return status; diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c index 97f074a3..fbda7321 100644 --- a/src/cairo-polygon.c +++ b/src/cairo-polygon.c @@ -35,9 +35,6 @@ static cairo_status_t _cairo_polygon_grow_by (cairo_polygon_t *polygon, int additional); -static void -_cairo_polygon_set_last_point (cairo_polygon_t *polygon, cairo_point_t *point); - void _cairo_polygon_init (cairo_polygon_t *polygon) { @@ -46,10 +43,7 @@ _cairo_polygon_init (cairo_polygon_t *polygon) polygon->edges_size = 0; polygon->edges = NULL; - polygon->first_point_defined = 0; - polygon->last_point_defined = 0; - - polygon->closed = 0; + polygon->has_current_point = 0; } void @@ -62,10 +56,7 @@ _cairo_polygon_fini (cairo_polygon_t *polygon) polygon->num_edges = 0; } - polygon->first_point_defined = 0; - polygon->last_point_defined = 0; - - polygon->closed = 0; + polygon->has_current_point = 0; } static cairo_status_t @@ -92,25 +83,12 @@ _cairo_polygon_grow_by (cairo_polygon_t *polygon, int additional) return CAIRO_STATUS_SUCCESS; } -static void -_cairo_polygon_set_last_point (cairo_polygon_t *polygon, cairo_point_t *point) -{ - polygon->last_point = *point; - polygon->last_point_defined = 1; -} - cairo_status_t _cairo_polygon_add_edge (cairo_polygon_t *polygon, cairo_point_t *p1, cairo_point_t *p2) { cairo_status_t status; cairo_edge_t *edge; - if (! polygon->first_point_defined) { - polygon->first_point = *p1; - polygon->first_point_defined = 1; - polygon->closed = 0; - } - /* drop horizontal edges */ if (p1->y == p2->y) { goto DONE; @@ -137,20 +115,31 @@ _cairo_polygon_add_edge (cairo_polygon_t *polygon, cairo_point_t *p1, cairo_poin polygon->num_edges++; DONE: - _cairo_polygon_set_last_point (polygon, p2); + _cairo_polygon_move_to (polygon, p2); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_polygon_move_to (cairo_polygon_t *polygon, cairo_point_t *point) +{ + if (! polygon->has_current_point) + polygon->first_point = *point; + polygon->current_point = *point; + polygon->has_current_point = 1; return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_polygon_add_point (cairo_polygon_t *polygon, cairo_point_t *point) +_cairo_polygon_line_to (cairo_polygon_t *polygon, cairo_point_t *point) { cairo_status_t status = CAIRO_STATUS_SUCCESS; - if (polygon->last_point_defined) { - status = _cairo_polygon_add_edge (polygon, &polygon->last_point, point); + if (polygon->has_current_point) { + status = _cairo_polygon_add_edge (polygon, &polygon->current_point, point); } else { - _cairo_polygon_set_last_point (polygon, point); + _cairo_polygon_move_to (polygon, point); } return status; @@ -161,13 +150,14 @@ _cairo_polygon_close (cairo_polygon_t *polygon) { cairo_status_t status; - if (polygon->closed == 0 && polygon->last_point_defined) { - status = _cairo_polygon_add_edge (polygon, &polygon->last_point, &polygon->first_point); + if (polygon->has_current_point) { + status = _cairo_polygon_add_edge (polygon, + &polygon->current_point, + &polygon->first_point); if (status) return status; - polygon->closed = 1; - polygon->first_point_defined = 0; + polygon->has_current_point = 0; } return CAIRO_STATUS_SUCCESS; diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index c66b7098..98d34e44 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -286,19 +286,19 @@ _cairo_ps_surface_copy_page (void *abstract_surface) int i, x, y; cairo_surface_t *white_surface; - char *bgr, *compressed; - long bgr_size, compressed_size; + char *rgb, *compressed; + long rgb_size, compressed_size; cairo_color_t white; - bgr_size = 3 * width * height; - bgr = malloc (bgr_size); - if (bgr == NULL) { + rgb_size = 3 * width * height; + rgb = malloc (rgb_size); + if (rgb == NULL) { status = CAIRO_STATUS_NO_MEMORY; goto BAIL0; } - compressed_size = (int) (1.0 + 1.1 * bgr_size); + compressed_size = (int) (1.0 + 1.1 * rgb_size); compressed = malloc (compressed_size); if (compressed == NULL) { status = CAIRO_STATUS_NO_MEMORY; @@ -330,16 +330,15 @@ _cairo_ps_surface_copy_page (void *abstract_surface) i = 0; for (y = 0; y < height; y++) { - char *line = surface->image->data + y * surface->image->stride; - for (x = 0; x < width; x++) { - unsigned char *pixel = (unsigned char *) line + x * 4; - bgr[i++] = *(pixel+2); - bgr[i++] = *(pixel+1); - bgr[i++] = *(pixel); + pixman_bits_t *pixel = (pixman_bits_t *) (surface->image->data + y * surface->image->stride); + for (x = 0; x < width; x++, pixel++) { + rgb[i++] = (*pixel & 0x00ff0000) >> 16; + rgb[i++] = (*pixel & 0x0000ff00) >> 8; + rgb[i++] = (*pixel & 0x000000ff) >> 0; } } - compress (compressed, &compressed_size, bgr, bgr_size); + compress (compressed, &compressed_size, rgb, rgb_size); /* Page header */ fprintf (file, "%%%%Page: %d\n", ++surface->pages); @@ -375,7 +374,7 @@ _cairo_ps_surface_copy_page (void *abstract_surface) BAIL2: free (compressed); BAIL1: - free (bgr); + free (rgb); BAIL0: return status; } @@ -395,6 +394,23 @@ _cairo_ps_surface_show_page (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_ps_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + cairo_ps_surface_t *surface = 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, @@ -408,5 +424,7 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = { _cairo_ps_surface_fill_rectangles, _cairo_ps_surface_composite_trapezoids, _cairo_ps_surface_copy_page, - _cairo_ps_surface_show_page + _cairo_ps_surface_show_page, + _cairo_ps_surface_set_clip_region, + _cairo_ps_surface_create_pattern }; diff --git a/src/cairo-surface.c b/src/cairo-surface.c index ff4f39c1..91ab8aba 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -38,6 +38,7 @@ _cairo_surface_init (cairo_surface_t *surface, surface->ref_count = 1; _cairo_matrix_init (&surface->matrix); + surface->filter = CAIRO_FILTER_NEAREST; surface->repeat = 0; } @@ -165,9 +166,16 @@ cairo_surface_set_filter (cairo_surface_t *surface, cairo_filter_t filter) if (surface == NULL) return CAIRO_STATUS_NULL_POINTER; + surface->filter = filter; return surface->backend->set_filter (surface, filter); } +cairo_filter_t +cairo_surface_get_filter (cairo_surface_t *surface) +{ + return surface->filter; +} + /* XXX: NYI cairo_status_t cairo_surface_clip_rectangle (cairo_surface_t *surface, @@ -364,3 +372,116 @@ _cairo_surface_show_page (cairo_surface_t *surface) return CAIRO_STATUS_SUCCESS; } +cairo_status_t +_cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region) +{ + 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) { + int width = ceil (_cairo_fixed_to_double (box->p2.x) - + _cairo_fixed_to_double (box->p1.x)); + int height = ceil (_cairo_fixed_to_double (box->p2.y) - + _cairo_fixed_to_double (box->p1.y)); + 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_add_source_offset (pattern, + _cairo_fixed_to_double (box->p1.x), + _cairo_fixed_to_double (box->p1.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 63df3ea4..9b44d38e 100644 --- a/src/cairo-traps.c +++ b/src/cairo-traps.c @@ -58,7 +58,7 @@ _compute_inverse_slope (cairo_line_t *l); static double _compute_x_intercept (cairo_line_t *l, double inverse_slope); -static cairo_fixed_t +static int _line_segs_intersect_ceil (cairo_line_t *left, cairo_line_t *right, cairo_fixed_t *y_ret); void @@ -490,7 +490,7 @@ _line_segs_intersect_ceil (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_ */ cairo_status_t _cairo_traps_tessellate_polygon (cairo_traps_t *traps, - cairo_polygon_t *poly, + cairo_polygon_t *poly, cairo_fill_rule_t fill_rule) { cairo_status_t status; @@ -608,3 +608,40 @@ _cairo_traps_contain (cairo_traps_t *traps, double x, double y) 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; +} + +void +_cairo_traps_extents (cairo_traps_t *traps, cairo_box_t *extents) +{ + int i; + + extents->p1.x = extents->p1.y = SHRT_MAX << 16; + extents->p2.x = extents->p2.y = SHRT_MIN << 16; + + for (i = 0; i < traps->num_traps; i++) + _cairo_trap_extents (&traps->traps[i], extents); +} diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c new file mode 100644 index 00000000..ea1f72e1 --- /dev/null +++ b/src/cairo-xcb-surface.c @@ -0,0 +1,799 @@ +/* + * Copyright © 2002 University of Southern California + * + * 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 the + * University of Southern California not be used in advertising or + * publicity pertaining to distribution of the software without + * specific, written prior permission. The University of Southern + * California makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF + * SOUTHERN CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Carl D. Worth <cworth@isi.edu> + */ + +#include "cairoint.h" + +cairo_surface_t * +cairo_xcb_surface_create (XCBConnection *dpy, + XCBDRAWABLE drawable, + XCBVISUALTYPE *visual, + cairo_format_t format); + +#define AllPlanes ((unsigned long)~0L) + +static XCBRenderPICTFORMAT +format_from_visual(XCBConnection *c, XCBVISUALID visual) +{ + static const XCBRenderPICTFORMAT nil = { 0 }; + XCBRenderQueryPictFormatsRep *r; + XCBRenderPICTSCREENIter si; + XCBRenderPICTDEPTHIter di; + XCBRenderPICTVISUALIter vi; + + r = XCBRenderQueryPictFormatsReply(c, XCBRenderQueryPictFormats(c), 0); + if(!r) + return nil; + + for(si = XCBRenderQueryPictFormatsScreens(r); si.rem; XCBRenderPICTSCREENNext(&si)) + for(di = XCBRenderPICTSCREENDepths(si.data); di.rem; XCBRenderPICTDEPTHNext(&di)) + for(vi = XCBRenderPICTDEPTHVisuals(di.data); vi.rem; XCBRenderPICTVISUALNext(&vi)) + if(vi.data->visual.id == visual.id) + { + XCBRenderPICTFORMAT ret = vi.data->format; + free(r); + return ret; + } + + return nil; +} + +static XCBRenderPICTFORMAT +format_from_cairo(XCBConnection *c, cairo_format_t fmt) +{ + XCBRenderPICTFORMAT ret = { 0 }; + struct tmpl_t { + XCBRenderDIRECTFORMAT direct; + CARD8 depth; + }; + static const struct tmpl_t templates[] = { + /* CAIRO_FORMAT_ARGB32 */ + { + { + 16, 0xff, + 8, 0xff, + 0, 0xff, + 24, 0xff + }, + 32 + }, + /* CAIRO_FORMAT_RGB24 */ + { + { + 16, 0xff, + 8, 0xff, + 0, 0xff, + 0, 0x00 + }, + 24 + }, + /* CAIRO_FORMAT_A8 */ + { + { + 0, 0x00, + 0, 0x00, + 0, 0x00, + 0, 0xff + }, + 8 + }, + /* CAIRO_FORMAT_A1 */ + { + { + 0, 0x00, + 0, 0x00, + 0, 0x00, + 0, 0x01 + }, + 1 + }, + }; + const struct tmpl_t *tmpl; + XCBRenderQueryPictFormatsRep *r; + XCBRenderPICTFORMINFOIter fi; + + if(fmt < 0 || fmt >= (sizeof(templates) / sizeof(*templates))) + return ret; + tmpl = templates + fmt; + + r = XCBRenderQueryPictFormatsReply(c, XCBRenderQueryPictFormats(c), 0); + if(!r) + return ret; + + for(fi = XCBRenderQueryPictFormatsFormats(r); fi.rem; XCBRenderPICTFORMINFONext(&fi)) + { + const XCBRenderDIRECTFORMAT *t, *f; + if(fi.data->type != XCBRenderPictTypeDirect) + continue; + if(fi.data->depth != tmpl->depth) + continue; + t = &tmpl->direct; + f = &fi.data->direct; + if(t->red_mask && (t->red_mask != f->red_mask || t->red_shift != f->red_shift)) + continue; + if(t->green_mask && (t->green_mask != f->green_mask || t->green_shift != f->green_shift)) + continue; + if(t->blue_mask && (t->blue_mask != f->blue_mask || t->blue_shift != f->blue_shift)) + continue; + if(t->alpha_mask && (t->alpha_mask != f->alpha_mask || t->alpha_shift != f->alpha_shift)) + continue; + + ret = fi.data->id; + } + + free(r); + return ret; +} + +void +cairo_set_target_xcb (cairo_t *cr, + XCBConnection *dpy, + XCBDRAWABLE drawable, + XCBVISUALTYPE *visual, + cairo_format_t format) +{ + cairo_surface_t *surface; + + if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) + return; + + surface = cairo_xcb_surface_create (dpy, drawable, visual, format); + 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_xcb_surface { + cairo_surface_t base; + + XCBConnection *dpy; + XCBGCONTEXT gc; + XCBDRAWABLE drawable; + int owns_pixmap; + XCBVISUALTYPE *visual; + cairo_format_t format; + + int render_major; + int render_minor; + + int width; + int height; + + XCBRenderPICTURE picture; +} cairo_xcb_surface_t; + +#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ + (((surface)->render_major > major) || \ + (((surface)->render_major == major) && ((surface)->render_minor >= minor))) + +#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_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) + +#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) +#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) + +#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) +#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) +#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) +#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) + +#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) +#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) + +static int +_CAIRO_FORMAT_DEPTH (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_A1: + return 1; + case CAIRO_FORMAT_A8: + return 8; + case CAIRO_FORMAT_RGB24: + return 24; + case CAIRO_FORMAT_ARGB32: + default: + return 32; + } +} + +static cairo_surface_t * +_cairo_xcb_surface_create_similar (void *abstract_src, + cairo_format_t format, + int width, + int height) +{ + cairo_xcb_surface_t *src = abstract_src; + XCBConnection *dpy = src->dpy; + XCBDRAWABLE d; + cairo_xcb_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; + } + + d.pixmap = XCBPIXMAPNew (dpy); + XCBCreatePixmap (dpy, _CAIRO_FORMAT_DEPTH (format), + d.pixmap, src->drawable, + width, height); + + surface = (cairo_xcb_surface_t *) + cairo_xcb_surface_create (dpy, d, NULL, format); + surface->owns_pixmap = 1; + + surface->width = width; + surface->height = height; + + return &surface->base; +} + +static void +_cairo_xcb_surface_destroy (void *abstract_surface) +{ + cairo_xcb_surface_t *surface = abstract_surface; + if (surface->picture.xid) + XCBRenderFreePicture (surface->dpy, surface->picture); + + if (surface->owns_pixmap) + XCBFreePixmap (surface->dpy, surface->drawable.pixmap); + + if (surface->gc.xid) + XCBFreeGC (surface->dpy, surface->gc); + + surface->dpy = 0; + + free (surface); +} + +static double +_cairo_xcb_surface_pixels_per_inch (void *abstract_surface) +{ + /* XXX: We should really get this value from somewhere like Xft.dpy */ + return 96.0; +} + +static int +bits_per_pixel(XCBConnection *c, int depth) +{ + XCBFORMAT *fmt = XCBConnSetupSuccessRepPixmapFormats(XCBGetSetup(c)); + XCBFORMAT *fmtend = fmt + XCBConnSetupSuccessRepPixmapFormatsLength(XCBGetSetup(c)); + + for(; fmt != fmtend; ++fmt) + if(fmt->depth == depth) + return fmt->bits_per_pixel; + + if(depth <= 4) + return 4; + if(depth <= 8) + return 8; + if(depth <= 16) + return 16; + return 32; +} + +static int +bytes_per_line(XCBConnection *c, int width, int bpp) +{ + int bitmap_pad = XCBGetSetup(c)->bitmap_format_scanline_pad; + return (bpp * width + bitmap_pad - 1) & -bitmap_pad; +} + +static cairo_image_surface_t * +_cairo_xcb_surface_get_image (void *abstract_surface) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + XCBGetGeometryRep *geomrep; + XCBGetImageRep *imagerep; + int bpp; + + geomrep = XCBGetGeometryReply(surface->dpy, XCBGetGeometry(surface->dpy, surface->drawable), 0); + if(!geomrep) + return 0; + + surface->width = geomrep->width; + surface->height = geomrep->height; + free(geomrep); + + imagerep = XCBGetImageReply(surface->dpy, + XCBGetImage(surface->dpy, ZPixmap, + surface->drawable, + 0, 0, + surface->width, surface->height, + AllPlanes), 0); + if(!imagerep) + return 0; + + bpp = bits_per_pixel(surface->dpy, imagerep->depth); + + if (surface->visual) { + cairo_format_masks_t masks; + + /* XXX: Add support here for pictures with external alpha? */ + + masks.bpp = bpp; + masks.alpha_mask = 0; + masks.red_mask = surface->visual->red_mask; + masks.green_mask = surface->visual->green_mask; + masks.blue_mask = surface->visual->blue_mask; + + image = _cairo_image_surface_create_with_masks (XCBGetImageData(imagerep), + &masks, + surface->width, + surface->height, + 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, + bytes_per_line(surface->dpy, surface->width, bpp)); + } + + /* Let the surface take ownership of the data */ + /* XXX: Can probably come up with a cleaner API here. */ + _cairo_image_surface_assume_ownership_of_data (image); + /* FIXME: imagerep can't be freed correctly, I think. must copy. :-( */ + + _cairo_image_surface_set_repeat (image, surface->base.repeat); + _cairo_image_surface_set_matrix (image, &(surface->base.matrix)); + + return image; +} + +static void +_cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface) +{ + if (surface->gc.xid) + return; + + surface->gc = XCBGCONTEXTNew(surface->dpy); + XCBCreateGC (surface->dpy, surface->gc, surface->drawable, 0, 0); +} + +static cairo_status_t +_cairo_xcb_surface_set_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_xcb_surface_t *surface = abstract_surface; + int bpp, data_len; + + _cairo_xcb_surface_ensure_gc (surface); + bpp = bits_per_pixel(surface->dpy, image->depth); + data_len = bytes_per_line(surface->dpy, surface->width, bpp) * surface->height; + XCBPutImage(surface->dpy, ZPixmap, surface->drawable, surface->gc, + image->width, + image->height, + /* dst_x */ 0, /* dst_y */ 0, + /* left_pad */ 0, image->depth, + data_len, image->data); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_set_matrix (void *abstract_surface, cairo_matrix_t *matrix) +{ + cairo_xcb_surface_t *surface = abstract_surface; + XCBRenderTRANSFORM xtransform; + + if (!surface->picture.xid) + return CAIRO_STATUS_SUCCESS; + + xtransform.matrix11 = _cairo_fixed_from_double (matrix->m[0][0]); + xtransform.matrix12 = _cairo_fixed_from_double (matrix->m[1][0]); + xtransform.matrix13 = _cairo_fixed_from_double (matrix->m[2][0]); + + xtransform.matrix21 = _cairo_fixed_from_double (matrix->m[0][1]); + xtransform.matrix22 = _cairo_fixed_from_double (matrix->m[1][1]); + xtransform.matrix23 = _cairo_fixed_from_double (matrix->m[2][1]); + + xtransform.matrix31 = 0; + xtransform.matrix32 = 0; + xtransform.matrix33 = _cairo_fixed_from_double (1); + + 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 */ + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_set_filter (void *abstract_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))) + return CAIRO_STATUS_SUCCESS; + + switch (filter) { + case CAIRO_FILTER_FAST: + render_filter = "fast"; + break; + case CAIRO_FILTER_GOOD: + render_filter = "good"; + break; + case CAIRO_FILTER_BEST: + render_filter = "best"; + break; + case CAIRO_FILTER_NEAREST: + render_filter = "nearest"; + break; + case CAIRO_FILTER_BILINEAR: + render_filter = "bilinear"; + break; + default: + render_filter = "best"; + break; + } + + XCBRenderSetPictureFilter(surface->dpy, surface->picture, + strlen(render_filter), 0, render_filter, NULL); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_set_repeat (void *abstract_surface, int repeat) +{ + cairo_xcb_surface_t *surface = abstract_surface; + + CARD32 mask = XCBRenderCPRepeat; + CARD32 pa[] = { repeat }; + + if (!surface->picture.xid) + return CAIRO_STATUS_SUCCESS; + + XCBRenderChangePicture (surface->dpy, surface->picture, mask, pa); + + 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) +{ + 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, + 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_xcb_surface_set_matrix (clone, &(src_image->base.matrix)); + + cairo_surface_destroy (&src_image->base); + + return clone; +} + +static int +_render_operator (cairo_operator_t operator) +{ + switch (operator) { + case CAIRO_OPERATOR_CLEAR: + return XCBRenderPictOpClear; + case CAIRO_OPERATOR_SRC: + return XCBRenderPictOpSrc; + case CAIRO_OPERATOR_DST: + return XCBRenderPictOpDst; + case CAIRO_OPERATOR_OVER: + return XCBRenderPictOpOver; + case CAIRO_OPERATOR_OVER_REVERSE: + return XCBRenderPictOpOverReverse; + case CAIRO_OPERATOR_IN: + return XCBRenderPictOpIn; + case CAIRO_OPERATOR_IN_REVERSE: + return XCBRenderPictOpInReverse; + case CAIRO_OPERATOR_OUT: + return XCBRenderPictOpOut; + case CAIRO_OPERATOR_OUT_REVERSE: + return XCBRenderPictOpOutReverse; + case CAIRO_OPERATOR_ATOP: + return XCBRenderPictOpAtop; + case CAIRO_OPERATOR_ATOP_REVERSE: + return XCBRenderPictOpAtopReverse; + case CAIRO_OPERATOR_XOR: + return XCBRenderPictOpXor; + case CAIRO_OPERATOR_ADD: + return XCBRenderPictOpAdd; + case CAIRO_OPERATOR_SATURATE: + return XCBRenderPictOpSaturate; + default: + return XCBRenderPictOpOver; + } +} + +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_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 }; + + + 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; + } + + 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); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xcb_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t operator, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) +{ + cairo_xcb_surface_t *surface = abstract_surface; + XCBRenderCOLOR render_color; + + if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE (surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + render_color.red = color->red_short; + render_color.green = color->green_short; + render_color.blue = color->blue_short; + render_color.alpha = color->alpha_short; + + /* XXX: This XCBRECTANGLE cast is evil... it needs to go away somehow. */ + XCBRenderFillRectangles (surface->dpy, + _render_operator (operator), + surface->picture, + render_color, num_rects, (XCBRECTANGLE *) rects); + + return CAIRO_STATUS_SUCCESS; +} + +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_xcb_surface_t *dst = abstract_dst; + cairo_xcb_surface_t *src = (cairo_xcb_surface_t *) generic_src; + cairo_xcb_surface_t *src_clone = NULL; + + 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; + } + + /* 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); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xcb_surface_copy_page (void *abstract_surface) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_xcb_surface_show_page (void *abstract_surface) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_xcb_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + /* FIXME */ + 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 struct cairo_surface_backend 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_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 +}; + +static void +query_render_version (XCBConnection *c, cairo_xcb_surface_t *surface) +{ + XCBRenderQueryVersionRep *r; + + surface->render_major = -1; + surface->render_minor = -1; + + if (!XCBRenderInit(c)) + return; + + r = XCBRenderQueryVersionReply(c, XCBRenderQueryVersion(c, 0, 6), 0); + if (!r) + return; + + surface->render_major = r->major_version; + surface->render_minor = r->minor_version; + free(r); +} + +cairo_surface_t * +cairo_xcb_surface_create (XCBConnection *dpy, + XCBDRAWABLE drawable, + XCBVISUALTYPE *visual, + cairo_format_t format) +{ + cairo_xcb_surface_t *surface; + + surface = malloc (sizeof (cairo_xcb_surface_t)); + if (surface == NULL) + return NULL; + + _cairo_surface_init (&surface->base, &cairo_xcb_surface_backend); + + surface->dpy = dpy; + surface->gc.xid = 0; + surface->drawable = drawable; + surface->owns_pixmap = 0; + surface->visual = visual; + surface->format = format; + + query_render_version(dpy, surface); + + if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) + { + XCBRenderPICTFORMAT fmt; + if(visual) + fmt = format_from_visual (dpy, visual->visual_id); + else + fmt = format_from_cairo (dpy, format); + surface->picture = XCBRenderPICTURENew(dpy); + XCBRenderCreatePicture (dpy, surface->picture, drawable, + fmt, 0, NULL); + } + else + surface->picture.xid = 0; + + return (cairo_surface_t *) surface; +} diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index 49abdb56..19dfde50 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -90,6 +90,7 @@ typedef struct cairo_xlib_surface { #define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) #define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) +#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) static int _CAIRO_FORMAT_DEPTH (cairo_format_t format) @@ -257,7 +258,7 @@ _cairo_xlib_surface_set_image (void *abstract_surface, ximage = XCreateImage (surface->dpy, DefaultVisual(surface->dpy, DefaultScreen(surface->dpy)), - image->depth == 32 ? 24 : image->depth, + image->depth, ZPixmap, 0, image->data, @@ -320,26 +321,29 @@ _cairo_xlib_surface_set_filter (void *abstract_surface, cairo_filter_t filter) cairo_xlib_surface_t *surface = abstract_surface; char *render_filter; - if (!surface->picture) + if (!(surface->picture + && CAIRO_SURFACE_RENDER_HAS_FILTERS(surface))) return CAIRO_STATUS_SUCCESS; - - /* XXX: The Render specification has capitalized versions of these - strings. However, the current implementation is - case-sensitive and expects lowercase versions. - */ + switch (filter) { case CAIRO_FILTER_FAST: - render_filter = "fast"; + render_filter = FilterFast; + break; case CAIRO_FILTER_GOOD: - render_filter = "good"; + render_filter = FilterGood; + break; case CAIRO_FILTER_BEST: - render_filter = "best"; + render_filter = FilterBest; + break; case CAIRO_FILTER_NEAREST: - render_filter = "nearest"; + render_filter = FilterNearest; + break; case CAIRO_FILTER_BILINEAR: - render_filter = "bilinear"; + render_filter = FilterBilinear; + break; default: - render_filter = "best"; + render_filter = FilterBest; + break; } XRenderSetPictureFilter (surface->dpy, surface->picture, @@ -384,6 +388,8 @@ _cairo_xlib_surface_clone_similar (cairo_surface_t *src, 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)); @@ -567,6 +573,60 @@ _cairo_xlib_surface_show_page (void *abstract_surface) return CAIRO_INT_STATUS_UNSUPPORTED; } +static cairo_int_status_t +_cairo_xlib_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + Region xregion; + XRectangle xr; + pixman_box16_t *box; + cairo_xlib_surface_t *surf; + int n, m; + + surf = (cairo_xlib_surface_t *) abstract_surface; + + if (region == NULL) { + /* NULL region == reset the clip */ + xregion = XCreateRegion(); + xr.x = 0; + xr.y = 0; + xr.width = surf->width; + xr.height = surf->height; + XUnionRectWithRegion (&xr, xregion, xregion); + } else { + n = pixman_region_num_rects (region); + /* XXX: Are we sure these are the semantics we want for an + * empty, (not null) region? */ + if (n == 0) + return CAIRO_STATUS_SUCCESS; + + box = pixman_region_rects (region); + xregion = XCreateRegion(); + + m = n; + for (; n > 0; --n, ++box) { + xr.x = (short) box->x1; + xr.y = (short) box->y1; + xr.width = (unsigned short) (box->x2 - box->x1); + xr.height = (unsigned short) (box->y2 - box->y1); + XUnionRectWithRegion (&xr, xregion, xregion); + } + } + + XRenderSetPictureClipRegion (surf->dpy, surf->picture, xregion); + XDestroyRegion(xregion); + + 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 const struct cairo_surface_backend cairo_xlib_surface_backend = { _cairo_xlib_surface_create_similar, _cairo_xlib_surface_destroy, @@ -580,7 +640,9 @@ static const struct cairo_surface_backend cairo_xlib_surface_backend = { _cairo_xlib_surface_fill_rectangles, _cairo_xlib_surface_composite_trapezoids, _cairo_xlib_surface_copy_page, - _cairo_xlib_surface_show_page + _cairo_xlib_surface_show_page, + _cairo_xlib_surface_set_clip_region, + _cairo_xlib_surface_create_pattern }; cairo_surface_t * diff --git a/src/cairo.c b/src/cairo.c index 1149337b..d173ca76 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -29,9 +29,6 @@ #define CAIRO_TOLERANCE_MINIMUM 0.0002 /* We're limited by 16 bits of sub-pixel precision */ -static void -_cairo_restrict_value (double *value, double min, double max); - cairo_t * cairo_create (void) { @@ -220,7 +217,7 @@ cairo_set_rgb_color (cairo_t *cr, double red, double green, double blue) } void -cairo_set_pattern (cairo_t *cr, cairo_surface_t *pattern) +cairo_set_pattern (cairo_t *cr, cairo_pattern_t *pattern) { if (cr->status) return; @@ -228,6 +225,12 @@ cairo_set_pattern (cairo_t *cr, cairo_surface_t *pattern) cr->status = _cairo_gstate_set_pattern (cr->gstate, pattern); } +cairo_pattern_t * +cairo_current_pattern (cairo_t *cr) +{ + return _cairo_gstate_current_pattern (cr->gstate); +} + void cairo_set_tolerance (cairo_t *cr, double tolerance) { @@ -636,6 +639,35 @@ cairo_in_fill (cairo_t *cr, double x, double y) } void +cairo_stroke_extents (cairo_t *cr, + double *x1, double *y1, double *x2, double *y2) +{ + if (cr->status) + return; + + cr->status = _cairo_gstate_stroke_extents (cr->gstate, x1, y1, x2, y2); +} + +void +cairo_fill_extents (cairo_t *cr, + double *x1, double *y1, double *x2, double *y2) +{ + if (cr->status) + return; + + cr->status = _cairo_gstate_fill_extents (cr->gstate, x1, y1, x2, y2); +} + +void +cairo_init_clip (cairo_t *cr) +{ + if (cr->status) + return; + + cr->status = _cairo_gstate_init_clip (cr->gstate); +} + +void cairo_clip (cairo_t *cr) { if (cr->status) @@ -646,7 +678,7 @@ cairo_clip (cairo_t *cr) void cairo_select_font (cairo_t *cr, - char *family, + const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { @@ -706,8 +738,6 @@ cairo_transform_font (cairo_t *cr, cairo_matrix_t *matrix) cr->status = _cairo_gstate_transform_font (cr->gstate, matrix); } - -/* XXX: NYI void cairo_text_extents (cairo_t *cr, const unsigned char *utf8, @@ -731,7 +761,6 @@ cairo_glyph_extents (cairo_t *cr, cr->status = _cairo_gstate_glyph_extents (cr->gstate, glyphs, num_glyphs, extents); } -*/ void cairo_show_text (cairo_t *cr, const unsigned char *utf8) @@ -751,7 +780,6 @@ cairo_show_glyphs (cairo_t *cr, cairo_glyph_t *glyphs, int num_glyphs) cr->status = _cairo_gstate_show_glyphs (cr->gstate, glyphs, num_glyphs); } -/* XXX: NYI void cairo_text_path (cairo_t *cr, const unsigned char *utf8) { @@ -769,7 +797,6 @@ cairo_glyph_path (cairo_t *cr, cairo_glyph_t *glyphs, int num_glyphs) cr->status = _cairo_gstate_glyph_path (cr->gstate, glyphs, num_glyphs); } -*/ void cairo_show_surface (cairo_t *cr, @@ -868,6 +895,43 @@ cairo_current_target_surface (cairo_t *cr) } DEPRECATE (cairo_get_target_surface, cairo_current_target_surface); +void +cairo_current_path (cairo_t *cr, + cairo_move_to_func_t *move_to, + cairo_line_to_func_t *line_to, + cairo_curve_to_func_t *curve_to, + cairo_close_path_func_t *close_path, + void *closure) +{ + if (cr->status) + return; + + cr->status = _cairo_gstate_interpret_path (cr->gstate, + move_to, + line_to, + curve_to, + close_path, + closure); +} + +void +cairo_current_path_flat (cairo_t *cr, + cairo_move_to_func_t *move_to, + cairo_line_to_func_t *line_to, + cairo_close_path_func_t *close_path, + void *closure) +{ + if (cr->status) + return; + + cr->status = _cairo_gstate_interpret_path (cr->gstate, + move_to, + line_to, + NULL, + close_path, + closure); +} + cairo_status_t cairo_status (cairo_t *cr) { @@ -901,7 +965,7 @@ cairo_status_string (cairo_t *cr) } DEPRECATE (cairo_get_status_string, cairo_status_string); -static void +void _cairo_restrict_value (double *value, double min, double max) { if (*value < min) diff --git a/src/cairo.h b/src/cairo.h index 6df5f8cf..ab3c80e8 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -30,52 +30,48 @@ #include <cairo-features.h> -#include <ic.h> +#include <pixman.h> #include <stdio.h> -#ifdef _CAIROINT_H_ -#include <slim_export.h> -#else -#include <slim_import.h> -#endif - typedef struct cairo cairo_t; typedef struct cairo_surface cairo_surface_t; typedef struct cairo_matrix cairo_matrix_t; +typedef struct cairo_pattern cairo_pattern_t; #ifdef __cplusplus extern "C" { #endif /* Functions for manipulating state objects */ -extern cairo_t * __external_linkage +cairo_t * cairo_create (void); -extern void __external_linkage +void cairo_reference (cairo_t *cr); -extern void __external_linkage +void cairo_destroy (cairo_t *cr); -extern void __external_linkage +void cairo_save (cairo_t *cr); -extern void __external_linkage +void cairo_restore (cairo_t *cr); -extern void __external_linkage +/* XXX: Replace with cairo_current_gstate/cairo_set_gstate */ +void cairo_copy (cairo_t *dest, cairo_t *src); /* XXX: I want to rethink this API -extern void __external_linkage +void cairo_push_group (cairo_t *cr); -extern void __external_linkage +void cairo_pop_group (cairo_t *cr); */ /* Modify state */ -extern void __external_linkage +void cairo_set_target_surface (cairo_t *cr, cairo_surface_t *surface); typedef enum cairo_format { @@ -85,7 +81,7 @@ typedef enum cairo_format { CAIRO_FORMAT_A1 } cairo_format_t; -extern void __external_linkage +void cairo_set_target_image (cairo_t *cr, char *data, cairo_format_t format, @@ -93,7 +89,11 @@ cairo_set_target_image (cairo_t *cr, int height, int stride); -extern void __external_linkage +#ifdef CAIRO_HAS_PS_SURFACE + +#include <stdio.h> + +void cairo_set_target_ps (cairo_t *cr, FILE *file, double width_inches, @@ -101,18 +101,46 @@ cairo_set_target_ps (cairo_t *cr, double x_pixels_per_inch, double y_pixels_per_inch); +#endif /* CAIRO_HAS_PS_SURFACE */ + +#ifdef CAIRO_HAS_PNG_SURFACE + +#include <stdio.h> + +void +cairo_set_target_png (cairo_t *cr, + FILE *file, + cairo_format_t format, + int width, + int height); + +#endif /* CAIRO_HAS_PNG_SURFACE */ + #ifdef CAIRO_HAS_XLIB_SURFACE #include <X11/extensions/Xrender.h> /* XXX: This shold be renamed to cairo_set_target_xlib to match the * other backends */ -extern void __external_linkage +void cairo_set_target_drawable (cairo_t *cr, Display *dpy, Drawable drawable); #endif /* CAIRO_HAS_XLIB_SURFACE */ +#ifdef CAIRO_HAS_XCB_SURFACE + +#include <X11/XCB/xcb.h> +#include <X11/XCB/render.h> + +void +cairo_set_target_xcb (cairo_t *cr, + XCBConnection *dpy, + XCBDRAWABLE drawable, + XCBVISUALTYPE *visual, + cairo_format_t format); +#endif /* CAIRO_HAS_XCB_SURFACE */ + typedef enum cairo_operator { CAIRO_OPERATOR_CLEAR, CAIRO_OPERATOR_SRC, @@ -127,10 +155,10 @@ typedef enum cairo_operator { CAIRO_OPERATOR_ATOP_REVERSE, CAIRO_OPERATOR_XOR, CAIRO_OPERATOR_ADD, - CAIRO_OPERATOR_SATURATE, + CAIRO_OPERATOR_SATURATE } cairo_operator_t; -extern void __external_linkage +void cairo_set_operator (cairo_t *cr, cairo_operator_t op); /* XXX: Probably want to bite the bullet and expose a cairo_color_t object */ @@ -150,16 +178,20 @@ cairo_set_operator (cairo_t *cr, cairo_operator_t op); the behavior of cairo_show_surface. */ -extern void __external_linkage +void cairo_set_rgb_color (cairo_t *cr, double red, double green, double blue); -extern void __external_linkage -cairo_set_alpha (cairo_t *cr, double alpha); +void +cairo_set_pattern (cairo_t *cr, cairo_pattern_t *pattern); -extern void __external_linkage -cairo_set_pattern (cairo_t *cr, cairo_surface_t *pattern); +void +cairo_set_alpha (cairo_t *cr, double alpha); -extern void __external_linkage +/* XXX: Currently, the tolerance value is specified by the user in + terms of device-space units. If I'm not mistaken, this is the only + value in this API that is not expressed in user-space units. I + should think whether this value should be user-space instead. */ +void cairo_set_tolerance (cairo_t *cr, double tolerance); typedef enum cairo_fill_rule { @@ -167,10 +199,10 @@ typedef enum cairo_fill_rule { CAIRO_FILL_RULE_EVEN_ODD } cairo_fill_rule_t; -extern void __external_linkage +void cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule); -extern void __external_linkage +void cairo_set_line_width (cairo_t *cr, double width); typedef enum cairo_line_cap { @@ -179,7 +211,7 @@ typedef enum cairo_line_cap { CAIRO_LINE_CAP_SQUARE } cairo_line_cap_t; -extern void __external_linkage +void cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap); typedef enum cairo_line_join { @@ -188,101 +220,101 @@ typedef enum cairo_line_join { CAIRO_LINE_JOIN_BEVEL } cairo_line_join_t; -extern void __external_linkage +void cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join); -extern void __external_linkage +void cairo_set_dash (cairo_t *cr, double *dashes, int ndash, double offset); -extern void __external_linkage +void cairo_set_miter_limit (cairo_t *cr, double limit); -extern void __external_linkage +void cairo_translate (cairo_t *cr, double tx, double ty); -extern void __external_linkage +void cairo_scale (cairo_t *cr, double sx, double sy); -extern void __external_linkage +void cairo_rotate (cairo_t *cr, double angle); -extern void __external_linkage +void cairo_concat_matrix (cairo_t *cr, - cairo_matrix_t *matrix); + cairo_matrix_t *matrix); -extern void __external_linkage +void cairo_set_matrix (cairo_t *cr, - cairo_matrix_t *matrix); + cairo_matrix_t *matrix); -extern void __external_linkage +void cairo_default_matrix (cairo_t *cr); /* XXX: There's been a proposal to add cairo_default_matrix_exact */ -extern void __external_linkage +void cairo_identity_matrix (cairo_t *cr); -extern void __external_linkage +void cairo_transform_point (cairo_t *cr, double *x, double *y); -extern void __external_linkage +void cairo_transform_distance (cairo_t *cr, double *dx, double *dy); -extern void __external_linkage +void cairo_inverse_transform_point (cairo_t *cr, double *x, double *y); -extern void __external_linkage +void cairo_inverse_transform_distance (cairo_t *cr, double *dx, double *dy); /* Path creation functions */ -extern void __external_linkage +void cairo_new_path (cairo_t *cr); -extern void __external_linkage +void cairo_move_to (cairo_t *cr, double x, double y); -extern void __external_linkage +void cairo_line_to (cairo_t *cr, double x, double y); -extern void __external_linkage +void cairo_curve_to (cairo_t *cr, - double x1, double y1, - double x2, double y2, - double x3, double y3); + double x1, double y1, + double x2, double y2, + double x3, double y3); -extern void __external_linkage +void cairo_arc (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2); -extern void __external_linkage +void cairo_arc_negative (cairo_t *cr, double xc, double yc, double radius, double angle1, double angle2); /* XXX: NYI -extern void __external_linkage +void cairo_arc_to (cairo_t *cr, double x1, double y1, double x2, double y2, double radius); */ -extern void __external_linkage +void cairo_rel_move_to (cairo_t *cr, double dx, double dy); -extern void __external_linkage +void cairo_rel_line_to (cairo_t *cr, double dx, double dy); -extern void __external_linkage +void cairo_rel_curve_to (cairo_t *cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3); -extern void __external_linkage +void cairo_rectangle (cairo_t *cr, double x, double y, double width, double height); @@ -295,35 +327,49 @@ cairo_rectangle (cairo_t *cr, Maybe we could use something like "cairo_outline_path (cairo_t *)"? */ /* XXX: NYI -extern void __external_linkage +void cairo_stroke_path (cairo_t *cr); */ -extern void __external_linkage +void cairo_close_path (cairo_t *cr); /* Painting functions */ -extern void __external_linkage +void cairo_stroke (cairo_t *cr); -extern void __external_linkage +void cairo_fill (cairo_t *cr); -extern void __external_linkage +void cairo_copy_page (cairo_t *cr); -extern void __external_linkage +void cairo_show_page (cairo_t *cr); /* Insideness testing */ -extern int __external_linkage +int cairo_in_stroke (cairo_t *cr, double x, double y); -extern int __external_linkage +int cairo_in_fill (cairo_t *cr, double x, double y); +/* Rectangular extents */ +void +cairo_stroke_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2); + +void +cairo_fill_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2); + /* Clipping */ -extern void __external_linkage +void +cairo_init_clip (cairo_t *cr); + +void cairo_clip (cairo_t *cr); /* Font/Text functions */ @@ -337,10 +383,10 @@ typedef struct { } cairo_glyph_t; typedef struct { - double left_side_bearing; - double right_side_bearing; - double ascent; - double descent; + double x_bearing; + double y_bearing; + double width; + double height; double x_advance; double y_advance; } cairo_text_extents_t; @@ -353,115 +399,106 @@ typedef struct { double max_y_advance; } cairo_font_extents_t; -typedef enum cairo_font_weight { - CAIRO_FONT_WEIGHT_NORMAL, - CAIRO_FONT_WEIGHT_BOLD -} cairo_font_weight_t; - typedef enum cairo_font_slant { CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_SLANT_OBLIQUE } cairo_font_slant_t; - +typedef enum cairo_font_weight { + CAIRO_FONT_WEIGHT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD +} cairo_font_weight_t; + /* This interface is for dealing with text as text, not caring about the font object inside the the cairo_t. */ -extern void __external_linkage +void cairo_select_font (cairo_t *ct, - char *family, + const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight); -extern void __external_linkage +void cairo_scale_font (cairo_t *cr, double scale); -extern void __external_linkage +void cairo_transform_font (cairo_t *cr, cairo_matrix_t *matrix); -extern void __external_linkage +void cairo_show_text (cairo_t *ct, const unsigned char *utf8); -extern void __external_linkage +void cairo_show_glyphs (cairo_t *ct, cairo_glyph_t *glyphs, int num_glyphs); -extern cairo_font_t * __external_linkage +cairo_font_t * cairo_current_font (cairo_t *ct); -extern void __external_linkage +void cairo_current_font_extents (cairo_t *ct, cairo_font_extents_t *extents); -extern void __external_linkage +void cairo_set_font (cairo_t *ct, cairo_font_t *font); - -/* XXX: NYI - -extern void __external_linkage +void cairo_text_extents (cairo_t *ct, const unsigned char *utf8, cairo_text_extents_t *extents); -extern void __external_linkage +void cairo_glyph_extents (cairo_t *ct, cairo_glyph_t *glyphs, int num_glyphs, cairo_text_extents_t *extents); -extern void __external_linkage +void cairo_text_path (cairo_t *ct, const unsigned char *utf8); -extern void __external_linkage +void cairo_glyph_path (cairo_t *ct, cairo_glyph_t *glyphs, int num_glyphs); -*/ - - /* Portable interface to general font features. */ -extern void __external_linkage +void cairo_font_reference (cairo_font_t *font); -extern void __external_linkage +void cairo_font_destroy (cairo_font_t *font); -extern void __external_linkage +void cairo_font_set_transform (cairo_font_t *font, cairo_matrix_t *matrix); -extern void __external_linkage +void cairo_font_current_transform (cairo_font_t *font, cairo_matrix_t *matrix); - /* Fontconfig/Freetype platform-specific font interface */ #include <fontconfig/fontconfig.h> #include <ft2build.h> #include FT_FREETYPE_H -extern cairo_font_t * __external_linkage +cairo_font_t * cairo_ft_font_create (FT_Library ft_library, FcPattern *pattern); -extern cairo_font_t * __external_linkage +cairo_font_t * cairo_ft_font_create_for_ft_face (FT_Face face); -extern void __external_linkage +void cairo_ft_font_destroy (cairo_font_t *ft_font); -extern FT_Face __external_linkage +FT_Face cairo_ft_font_face (cairo_font_t *ft_font); -extern FcPattern * __external_linkage +FcPattern * cairo_ft_font_pattern (cairo_font_t *ft_font); - - /* Image functions */ -extern void __external_linkage +/* XXX: Eliminate width/height here */ +void cairo_show_surface (cairo_t *cr, cairo_surface_t *surface, int width, @@ -475,48 +512,78 @@ cairo_show_surface (cairo_t *cr, into one file and be done with it. For now, I've got a little more typing than that. */ -extern cairo_operator_t __external_linkage +cairo_operator_t cairo_current_operator (cairo_t *cr); -extern void __external_linkage +void cairo_current_rgb_color (cairo_t *cr, double *red, double *green, double *blue); +cairo_pattern_t * +cairo_current_pattern (cairo_t *cr); -extern double __external_linkage +double cairo_current_alpha (cairo_t *cr); /* XXX: Do we want cairo_current_pattern as well? */ -extern double __external_linkage +double cairo_current_tolerance (cairo_t *cr); -extern void __external_linkage +void cairo_current_point (cairo_t *cr, double *x, double *y); -extern cairo_fill_rule_t __external_linkage +cairo_fill_rule_t cairo_current_fill_rule (cairo_t *cr); -extern double __external_linkage +double cairo_current_line_width (cairo_t *cr); -extern cairo_line_cap_t __external_linkage +cairo_line_cap_t cairo_current_line_cap (cairo_t *cr); -extern cairo_line_join_t __external_linkage +cairo_line_join_t cairo_current_line_join (cairo_t *cr); -extern double __external_linkage +double cairo_current_miter_limit (cairo_t *cr); /* XXX: How to do cairo_current_dash??? Do we want to switch to a cairo_dash object? */ -extern void __external_linkage +void cairo_current_matrix (cairo_t *cr, cairo_matrix_t *matrix); /* XXX: Need to decide the memory mangement semantics of this function. Should it reference the surface again? */ -extern cairo_surface_t * __external_linkage +cairo_surface_t * cairo_current_target_surface (cairo_t *cr); +typedef void (cairo_move_to_func_t) (void *closure, + double x, double y); + +typedef void (cairo_line_to_func_t) (void *closure, + double x, double y); + +typedef void (cairo_curve_to_func_t) (void *closure, + double x1, double y1, + double x2, double y2, + double x3, double y3); + +typedef void (cairo_close_path_func_t) (void *closure); + +extern void +cairo_current_path (cairo_t *cr, + cairo_move_to_func_t *move_to, + cairo_line_to_func_t *line_to, + cairo_curve_to_func_t *curve_to, + cairo_close_path_func_t *close_path, + void *closure); + +extern void +cairo_current_path_flat (cairo_t *cr, + cairo_move_to_func_t *move_to, + cairo_line_to_func_t *line_to, + cairo_close_path_func_t *close_path, + void *closure); + /* Error status queries */ typedef enum cairo_status { @@ -530,16 +597,16 @@ typedef enum cairo_status { CAIRO_STATUS_NULL_POINTER } cairo_status_t; -extern cairo_status_t __external_linkage +cairo_status_t cairo_status (cairo_t *cr); -extern const char * __external_linkage +const char * cairo_status_string (cairo_t *cr); /* Surface manipulation */ /* XXX: We may want to rename this function in light of the new virtualized surface backends... */ -extern cairo_surface_t * __external_linkage +cairo_surface_t * cairo_surface_create_for_image (char *data, cairo_format_t format, int width, @@ -548,26 +615,26 @@ cairo_surface_create_for_image (char *data, /* XXX: I want to remove this function, (replace with cairo_set_target_scratch or similar). */ -extern cairo_surface_t * __external_linkage +cairo_surface_t * cairo_surface_create_similar (cairo_surface_t *other, cairo_format_t format, int width, int height); -extern void __external_linkage +void cairo_surface_reference (cairo_surface_t *surface); -extern void __external_linkage +void cairo_surface_destroy (cairo_surface_t *surface); /* XXX: NYI -extern cairo_status_t __external_linkage +cairo_status_t cairo_surface_clip_restore (cairo_surface_t *surface); -extern cairo_status_t __external_linkage +cairo_status_t cairo_surface_clip_begin (cairo_surface_t *surface); -extern cairo_status_t __external_linkage +cairo_status_t cairo_surface_clip_rectangle (cairo_surface_t *surface, int x, int y, int width, int height); @@ -576,52 +643,120 @@ cairo_surface_clip_rectangle (cairo_surface_t *surface, /* XXX: Note: The current Render/Ic implementations don't do the right thing with repeat when the surface has a non-identity matrix. */ /* XXX: Rework this as a cairo function with an enum: cairo_set_pattern_extend */ -extern cairo_status_t __external_linkage +cairo_status_t cairo_surface_set_repeat (cairo_surface_t *surface, int repeat); /* XXX: Rework this as a cairo function: cairo_set_pattern_transform */ -extern cairo_status_t __external_linkage +cairo_status_t cairo_surface_set_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix); /* XXX: Rework this as a cairo function: cairo_current_pattern_transform */ -extern cairo_status_t __external_linkage +cairo_status_t cairo_surface_get_matrix (cairo_surface_t *surface, cairo_matrix_t *matrix); -typedef enum cairo_filter { +typedef enum { CAIRO_FILTER_FAST, CAIRO_FILTER_GOOD, CAIRO_FILTER_BEST, CAIRO_FILTER_NEAREST, CAIRO_FILTER_BILINEAR, + CAIRO_FILTER_GAUSSIAN } cairo_filter_t; - + /* XXX: Rework this as a cairo function: cairo_set_pattern_filter */ -extern cairo_status_t __external_linkage +cairo_status_t cairo_surface_set_filter (cairo_surface_t *surface, cairo_filter_t filter); +cairo_filter_t +cairo_surface_get_filter (cairo_surface_t *surface); + /* Image-surface functions */ -extern cairo_surface_t * __external_linkage +cairo_surface_t * cairo_image_surface_create (cairo_format_t format, int width, int height); -extern cairo_surface_t * __external_linkage +cairo_surface_t * cairo_image_surface_create_for_data (char *data, cairo_format_t format, int width, int height, int stride); +/* Pattern creation functions */ +cairo_pattern_t * +cairo_pattern_create_for_surface (cairo_surface_t *surface); + +cairo_pattern_t * +cairo_pattern_create_linear (double x0, double y0, + double x1, double y1); + +cairo_pattern_t * +cairo_pattern_create_radial (double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1); + +void +cairo_pattern_reference (cairo_pattern_t *pattern); + +void +cairo_pattern_destroy (cairo_pattern_t *pattern); + +cairo_status_t +cairo_pattern_add_color_stop (cairo_pattern_t *pattern, + double offset, + double red, double green, double blue, + double alpha); + +cairo_status_t +cairo_pattern_set_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix); + +cairo_status_t +cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix); + +typedef enum { + CAIRO_EXTEND_NONE, + CAIRO_EXTEND_REPEAT, + CAIRO_EXTEND_REFLECT +} cairo_extend_t; + +cairo_status_t +cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend); + +cairo_extend_t +cairo_pattern_get_extend (cairo_pattern_t *pattern); + +cairo_status_t +cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter); + +cairo_filter_t +cairo_pattern_get_filter (cairo_pattern_t *pattern); + +#ifdef CAIRO_HAS_PS_SURFACE + /* PS-surface functions */ -extern cairo_surface_t * __external_linkage +cairo_surface_t * cairo_ps_surface_create (FILE *file, double width_inches, double height_inches, double x_pixels_per_inch, double y_pixels_per_inch); +#endif /* CAIRO_HAS_PS_SURFACE */ + +#ifdef CAIRO_HAS_PNG_SURFACE + +/* PNG-surface functions */ + +cairo_surface_t * +cairo_png_surface_create (FILE *file, + cairo_format_t format, + int width, + int height); + +#endif /* CAIRO_HAS_PNG_SURFACE */ + #ifdef CAIRO_HAS_XLIB_SURFACE /* XXX: This is a mess from the user's POV. Should the Visual or the @@ -629,12 +764,17 @@ cairo_ps_surface_create (FILE *file, cairo_surface_create_for_window with a visual, and cairo_surface_create_for_pixmap with a cairo_format_t. Would that work? */ -extern cairo_surface_t * __external_linkage +cairo_surface_t * cairo_xlib_surface_create (Display *dpy, Drawable drawable, Visual *visual, cairo_format_t format, Colormap colormap); + +/* XXX: This has been proposed +cairo_status_t +cairo_xlib_surface_set_size (cairo_surface_t *surface, int width, int height); +*/ #endif /* CAIRO_HAS_XLIB_SURFACE */ @@ -642,49 +782,49 @@ cairo_xlib_surface_create (Display *dpy, /* XXX: Rename all of these to cairo_transform_t */ -extern cairo_matrix_t * __external_linkage +cairo_matrix_t * cairo_matrix_create (void); -extern void __external_linkage +void cairo_matrix_destroy (cairo_matrix_t *matrix); -extern cairo_status_t __external_linkage +cairo_status_t cairo_matrix_copy (cairo_matrix_t *matrix, const cairo_matrix_t *other); -extern cairo_status_t __external_linkage +cairo_status_t cairo_matrix_set_identity (cairo_matrix_t *matrix); -extern cairo_status_t __external_linkage +cairo_status_t cairo_matrix_set_affine (cairo_matrix_t *cr, double a, double b, double c, double d, double tx, double ty); -extern cairo_status_t __external_linkage +cairo_status_t cairo_matrix_get_affine (cairo_matrix_t *matrix, double *a, double *b, double *c, double *d, double *tx, double *ty); -extern cairo_status_t __external_linkage +cairo_status_t cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty); -extern cairo_status_t __external_linkage +cairo_status_t cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy); -extern cairo_status_t __external_linkage +cairo_status_t cairo_matrix_rotate (cairo_matrix_t *matrix, double radians); -extern cairo_status_t __external_linkage +cairo_status_t cairo_matrix_invert (cairo_matrix_t *matrix); -extern cairo_status_t __external_linkage +cairo_status_t cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b); -extern cairo_status_t __external_linkage +cairo_status_t cairo_matrix_transform_distance (cairo_matrix_t *matrix, double *dx, double *dy); -extern cairo_status_t __external_linkage +cairo_status_t cairo_matrix_transform_point (cairo_matrix_t *matrix, double *x, double *y); /* Deprecated functions. We've made some effort to allow the @@ -712,6 +852,4 @@ cairo_matrix_transform_point (cairo_matrix_t *matrix, double *x, double *y); } #endif -#undef __external_linkage - #endif diff --git a/src/cairo_fixed.c b/src/cairo_fixed.c index 33088546..9a7e7bc4 100644 --- a/src/cairo_fixed.c +++ b/src/cairo_fixed.c @@ -39,8 +39,15 @@ _cairo_fixed_from_double (double d) return (cairo_fixed_t) (d * 65536); } +cairo_fixed_t +_cairo_fixed_from_26_6 (uint32_t i) +{ + return i << 10; +} + double _cairo_fixed_to_double (cairo_fixed_t f) { return ((double) f) / 65536.0; } + diff --git a/src/cairo_font.c b/src/cairo_font.c index f6bf390a..157ebedb 100644 --- a/src/cairo_font.c +++ b/src/cairo_font.c @@ -28,12 +28,18 @@ #include "cairoint.h" cairo_font_t * -_cairo_font_create (char *family, +_cairo_font_create (const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { const struct cairo_font_backend *backend = CAIRO_FONT_BACKEND_DEFAULT; + /* 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). */ + return backend->create (family, slant, weight); } @@ -118,31 +124,30 @@ _cairo_font_show_glyphs (cairo_font_t *font, cairo_operator_t operator, cairo_surface_t *source, cairo_surface_t *surface, - double x, - double y, cairo_glyph_t *glyphs, int num_glyphs) { return font->backend->show_glyphs(font, operator, source, - surface, x, y, glyphs, num_glyphs); + surface, glyphs, num_glyphs); } cairo_status_t _cairo_font_text_path (cairo_font_t *font, - cairo_path_t *path, - const unsigned char *utf8) + double x, + double y, + const unsigned char *utf8, + cairo_path_t *path) { - return font->backend->text_path(font, path, utf8); + return font->backend->text_path(font, x, y, utf8, path); } cairo_status_t _cairo_font_glyph_path (cairo_font_t *font, - cairo_path_t *path, cairo_glyph_t *glyphs, - int num_glyphs) + int num_glyphs, + cairo_path_t *path) { - return font->backend->glyph_path(font, path, - glyphs, num_glyphs); + return font->backend->glyph_path(font, glyphs, num_glyphs, path); } cairo_status_t diff --git a/src/cairo_ft_font.c b/src/cairo_ft_font.c index c3e4306d..77cf59b0 100644 --- a/src/cairo_ft_font.c +++ b/src/cairo_ft_font.c @@ -25,7 +25,10 @@ #include "cairoint.h" #include <fontconfig/fontconfig.h> #include <fontconfig/fcfreetype.h> -#include <freetype/freetype.h> + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_OUTLINE_H typedef struct { cairo_font_t base; @@ -39,13 +42,10 @@ typedef struct { FcPattern *pattern; } cairo_ft_font_t; - -#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 63.0)) -#define DOUBLE_FROM_26_6(t) (((double)((t) >> 6)) \ - + ((double)((t) & 0x3F) / 63.0)) -#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65535.0)) -#define DOUBLE_FROM_16_16(t) (((double)((t) >> 16)) \ - + ((double)((t) & 0xFFFF) / 65535.0)) +#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0)) +#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0) +#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0)) +#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0) /* implement the platform-specific interface */ @@ -101,29 +101,31 @@ cairo_ft_font_create (FT_Library ft_library, FcPattern *pattern) } FT_Face -cairo_ft_font_face (cairo_font_t *font) +cairo_ft_font_face (cairo_font_t *abstract_font) { + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + if (font == NULL) return NULL; - return ((cairo_ft_font_t *) font)->face; + return font->face; } FcPattern * -cairo_ft_font_pattern (cairo_font_t *font) +cairo_ft_font_pattern (cairo_font_t *abstract_font) { + cairo_ft_font_t *font = (cairo_ft_font_t *) abstract_font; + if (font == NULL) return NULL; - return ((cairo_ft_font_t *) font)->pattern; + return font->pattern; } - - /* implement the backend interface */ static cairo_font_t * -_cairo_ft_font_create (char *family, +_cairo_ft_font_create (const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { @@ -175,6 +177,9 @@ _cairo_ft_font_create (char *family, } font = cairo_ft_font_create (ft_library, pat); + if (font == NULL) + return NULL; + ft_font = (cairo_ft_font_t *) font; ft_font->owns_ft_library = 1; @@ -188,45 +193,43 @@ _cairo_ft_font_create (char *family, return font; } - static cairo_font_t * -_cairo_ft_font_copy (cairo_font_t *font) +_cairo_ft_font_copy (void *abstract_font) { - cairo_ft_font_t * ft_font_new = NULL; - cairo_ft_font_t * ft_font = NULL; + cairo_ft_font_t * font_new = NULL; + cairo_ft_font_t * font = abstract_font; - ft_font = (cairo_ft_font_t *)font; + if (font->base.backend != &cairo_ft_font_backend) + return NULL; - ft_font_new = (cairo_ft_font_t *)cairo_ft_font_create_for_ft_face (ft_font->face); - if (ft_font_new == NULL) + font_new = (cairo_ft_font_t *) cairo_ft_font_create_for_ft_face (font->face); + if (font_new == NULL) return NULL; - if (ft_font_new != NULL && ft_font->pattern != NULL) - ft_font_new->pattern = FcPatternDuplicate (ft_font->pattern); + if (font_new != NULL && font->pattern != NULL) + font_new->pattern = FcPatternDuplicate (font->pattern); - return (cairo_font_t *)ft_font_new; + return (cairo_font_t *) font_new; } static void -_cairo_ft_font_destroy (cairo_font_t *font) +_cairo_ft_font_destroy (void *abstract_font) { - cairo_ft_font_t * ft_font = NULL; + cairo_ft_font_t * font = abstract_font; if (font == NULL) return; - ft_font = (cairo_ft_font_t *)font; - - if (ft_font->face != NULL && ft_font->owns_face) - FT_Done_Face (ft_font->face); + if (font->face != NULL && font->owns_face) + FT_Done_Face (font->face); - if (ft_font->pattern != NULL) - FcPatternDestroy (ft_font->pattern); + if (font->pattern != NULL) + FcPatternDestroy (font->pattern); - if (ft_font->ft_library && ft_font->owns_ft_library) - FT_Done_FreeType (ft_font->ft_library); + if (font->ft_library && font->owns_ft_library) + FT_Done_FreeType (font->ft_library); - free (ft_font); + free (font); } static void @@ -260,21 +263,7 @@ _utf8_to_ucs4 (char const *utf8, len -= step; utf8 += step; } - *nchars = alloc; -} - -static void -_get_scale_factors(cairo_matrix_t *matrix, double *sx, double *sy) -{ - double e0, e1; - e1 = 1.; e0 = 0.; - - cairo_matrix_transform_distance (matrix, &e1, &e0); - *sx = sqrt(e1*e1 + e0*e0); - - e1 = 1.; e0 = 0.; - cairo_matrix_transform_distance (matrix, &e0, &e1); - *sy = sqrt(e1*e1 + e0*e0); + *nchars = n; } static void @@ -292,7 +281,7 @@ _install_font_matrix(cairo_matrix_t *matrix, FT_Face face) * transformation. */ - _get_scale_factors(matrix, &scale_x, &scale_y); + _cairo_matrix_compute_scale_factors (matrix, &scale_x, &scale_y); cairo_matrix_copy (&normalized, matrix); @@ -314,23 +303,19 @@ _install_font_matrix(cairo_matrix_t *matrix, FT_Face face) 0, 0); } - static int -_utf8_to_glyphs (cairo_font_t *font, - const unsigned char *utf8, - cairo_glyph_t **glyphs, - size_t *nglyphs) +_utf8_to_glyphs (cairo_ft_font_t *font, + const unsigned char *utf8, + double x0, + double y0, + cairo_glyph_t **glyphs, + size_t *nglyphs) { - cairo_ft_font_t *ft; + FT_Face face = font->face; double x = 0., y = 0.; size_t i; FT_ULong *ucs4 = NULL; - if (font == NULL) - return 0; - - ft = (cairo_ft_font_t *)font; - _utf8_to_ucs4 (utf8, &ucs4, nglyphs); if (ucs4 == NULL) @@ -343,18 +328,18 @@ _utf8_to_glyphs (cairo_font_t *font, return 0; } - _install_font_matrix (&font->matrix, ft->face); + _install_font_matrix (&font->base.matrix, face); for (i = 0; i < *nglyphs; i++) { - (*glyphs)[i].index = FT_Get_Char_Index (ft->face, ucs4[i]); - (*glyphs)[i].x = x; - (*glyphs)[i].y = y; + (*glyphs)[i].index = FT_Get_Char_Index (face, ucs4[i]); + (*glyphs)[i].x = x0 + x; + (*glyphs)[i].y = y0 + y; - FT_Load_Glyph (ft->face, (*glyphs)[i].index, FT_LOAD_DEFAULT); + FT_Load_Glyph (face, (*glyphs)[i].index, FT_LOAD_DEFAULT); - x += DOUBLE_FROM_26_6 (ft->face->glyph->advance.x); - y -= DOUBLE_FROM_26_6 (ft->face->glyph->advance.y); + x += DOUBLE_FROM_26_6 (face->glyph->advance.x); + y -= DOUBLE_FROM_26_6 (face->glyph->advance.y); } free (ucs4); @@ -362,51 +347,114 @@ _utf8_to_glyphs (cairo_font_t *font, } static cairo_status_t -_cairo_ft_font_font_extents (cairo_font_t *font, - cairo_font_extents_t *extents) +_cairo_ft_font_font_extents (void *abstract_font, + cairo_font_extents_t *extents) { + cairo_ft_font_t *font = abstract_font; + FT_Face face = font->face; double scale_x, scale_y; - cairo_ft_font_t *ft = (cairo_ft_font_t *)font; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - _get_scale_factors(&font->matrix, &scale_x, &scale_y); + double upm = face->units_per_EM; -#define FONT_UNIT_TO_DEV(x) ((double)(x) / (double)(ft->face->units_per_EM)) + _cairo_matrix_compute_scale_factors (&font->base.matrix, &scale_x, &scale_y); - extents->ascent = FONT_UNIT_TO_DEV(ft->face->ascender) * scale_y; - extents->descent = FONT_UNIT_TO_DEV(ft->face->descender) * scale_y; - extents->height = FONT_UNIT_TO_DEV(ft->face->height) * scale_y; - extents->max_x_advance = FONT_UNIT_TO_DEV(ft->face->max_advance_width) * scale_x; - extents->max_y_advance = FONT_UNIT_TO_DEV(ft->face->max_advance_height) * scale_y; - return status; + 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; + + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_ft_font_glyph_extents (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents) +_cairo_ft_font_glyph_extents (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) { - cairo_ft_font_t *ft; - cairo_status_t status = CAIRO_STATUS_SUCCESS; + int i; + cairo_ft_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; - ft = (cairo_ft_font_t *)font; + 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; + } - /* FIXME: lift code from xft to do this */ + origin.x = glyphs[0].x; + origin.y = glyphs[0].y; - return status; + _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_ft_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; + + return CAIRO_STATUS_SUCCESS; } + static cairo_status_t -_cairo_ft_font_text_extents (cairo_font_t *font, - const unsigned char *utf8, - cairo_text_extents_t *extents) +_cairo_ft_font_text_extents (void *abstract_font, + const unsigned char *utf8, + cairo_text_extents_t *extents) { + cairo_ft_font_t *font = abstract_font; cairo_glyph_t *glyphs; size_t nglyphs; cairo_status_t status = CAIRO_STATUS_SUCCESS; - if (_utf8_to_glyphs (font, utf8, &glyphs, &nglyphs)) + if (_utf8_to_glyphs (font, utf8, 0, 0, &glyphs, &nglyphs)) { status = _cairo_ft_font_glyph_extents (font, glyphs, nglyphs, extents); @@ -414,24 +462,22 @@ _cairo_ft_font_text_extents (cairo_font_t *font, } return status; } - - static cairo_status_t -_cairo_ft_font_show_glyphs (cairo_font_t *font, +_cairo_ft_font_show_glyphs (void *abstract_font, cairo_operator_t operator, cairo_surface_t *source, cairo_surface_t *surface, - double x0, - double y0, const cairo_glyph_t *glyphs, int num_glyphs) { + cairo_ft_font_t *font = abstract_font; cairo_status_t status; int i; cairo_ft_font_t *ft = NULL; FT_GlyphSlot glyphslot; cairo_surface_t *mask = NULL; + cairo_point_double_t origin; double x, y; int width, height, stride; @@ -444,22 +490,27 @@ _cairo_ft_font_show_glyphs (cairo_font_t *font, ft = (cairo_ft_font_t *)font; glyphslot = ft->face->glyph; - _install_font_matrix (&font->matrix, ft->face); + _install_font_matrix (&font->base.matrix, ft->face); for (i = 0; i < num_glyphs; i++) { unsigned char *bitmap; FT_Load_Glyph (ft->face, glyphs[i].index, FT_LOAD_DEFAULT); - FT_Render_Glyph (glyphslot, FT_RENDER_MODE_NORMAL); + FT_Render_Glyph (glyphslot, ft_render_mode_normal); width = glyphslot->bitmap.width; height = glyphslot->bitmap.rows; stride = glyphslot->bitmap.pitch; bitmap = glyphslot->bitmap.buffer; - x = x0 + glyphs[i].x; - y = y0 + glyphs[i].y; + x = glyphs[i].x; + y = glyphs[i].y; + + if (i == 0) { + origin.x = x; + origin.y = y; + } /* X gets upset with zero-sized images (such as whitespace) */ if (width * height == 0) @@ -502,11 +553,14 @@ _cairo_ft_font_show_glyphs (cairo_font_t *font, return CAIRO_STATUS_NO_MEMORY; } - status = _cairo_surface_composite (operator, source, mask, surface, - 0, 0, 0, 0, - x + glyphslot->bitmap_left, - y - glyphslot->bitmap_top, - (double)width, (double)height); + status = + _cairo_surface_composite (operator, source, mask, surface, + -origin.x + x + glyphslot->bitmap_left, + -origin.y + y - glyphslot->bitmap_top, + 0, 0, + x + glyphslot->bitmap_left, + y - glyphslot->bitmap_top, + (double) width, (double) height); cairo_surface_destroy (mask); if (bitmap != glyphslot->bitmap.buffer) @@ -519,7 +573,7 @@ _cairo_ft_font_show_glyphs (cairo_font_t *font, } static cairo_status_t -_cairo_ft_font_show_text (cairo_font_t *font, +_cairo_ft_font_show_text (void *abstract_font, cairo_operator_t operator, cairo_surface_t *source, cairo_surface_t *surface, @@ -527,15 +581,16 @@ _cairo_ft_font_show_text (cairo_font_t *font, double y0, const unsigned char *utf8) { + cairo_ft_font_t *font = abstract_font; cairo_glyph_t *glyphs; - size_t nglyphs; + int num_glyphs; - if (_utf8_to_glyphs (font, utf8, &glyphs, &nglyphs)) + if (_utf8_to_glyphs (font, utf8, x0, y0, &glyphs, &num_glyphs)) { cairo_status_t res; res = _cairo_ft_font_show_glyphs (font, operator, - source, surface, x0, y0, - glyphs, nglyphs); + source, surface, + glyphs, num_glyphs); free (glyphs); return res; } @@ -543,35 +598,147 @@ _cairo_ft_font_show_text (cairo_font_t *font, return CAIRO_STATUS_NO_MEMORY; } +static int +_move_to (FT_Vector *to, void *closure) +{ + cairo_path_t *path = closure; + cairo_point_t point; + + point.x = _cairo_fixed_from_26_6 (to->x); + point.y = _cairo_fixed_from_26_6 (to->y); + + _cairo_path_close_path (path); + _cairo_path_move_to (path, &point); + + return 0; +} + +static int +_line_to (FT_Vector *to, void *closure) +{ + cairo_path_t *path = closure; + cairo_point_t point; + + point.x = _cairo_fixed_from_26_6 (to->x); + point.y = _cairo_fixed_from_26_6 (to->y); + + _cairo_path_line_to (path, &point); + + return 0; +} + +static int +_conic_to (FT_Vector *control, FT_Vector *to, void *closure) +{ + cairo_path_t *path = closure; + + cairo_point_t p0, p1, p2, p3; + cairo_point_t conic; + + _cairo_path_current_point (path, &p0); + + conic.x = _cairo_fixed_from_26_6 (control->x); + conic.y = _cairo_fixed_from_26_6 (control->y); + + p3.x = _cairo_fixed_from_26_6 (to->x); + p3.y = _cairo_fixed_from_26_6 (to->y); + + p1.x = p0.x + 2.0/3.0 * (conic.x - p0.x); + p1.y = p0.y + 2.0/3.0 * (conic.y - p0.y); + + p2.x = p3.x + 2.0/3.0 * (conic.x - p3.x); + p2.y = p3.y + 2.0/3.0 * (conic.y - p3.y); + + _cairo_path_curve_to (path, + &p1, &p2, &p3); + + return 0; +} + +static int +_cubic_to (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *closure) +{ + cairo_path_t *path = closure; + cairo_point_t p0, p1, p2; + + p0.x = _cairo_fixed_from_26_6 (control1->x); + p0.y = _cairo_fixed_from_26_6 (control1->y); + + p1.x = _cairo_fixed_from_26_6 (control2->x); + p1.y = _cairo_fixed_from_26_6 (control2->y); + + p2.x = _cairo_fixed_from_26_6 (to->x); + p2.y = _cairo_fixed_from_26_6 (to->y); + + _cairo_path_curve_to (path, &p0, &p1, &p2); + + return 0; +} static cairo_status_t -_cairo_ft_font_glyph_path (cairo_font_t *font, - cairo_path_t *path, - cairo_glyph_t *glyphs, - int num_glyphs) +_cairo_ft_font_glyph_path (void *abstract_font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_ft_font_t *ft; - - ft = (cairo_ft_font_t *)font; - - /* FIXME: lift code from xft to do this */ + int i; + cairo_ft_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); - return status; + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_ft_font_text_path (cairo_font_t *font, - cairo_path_t *path, - const unsigned char *utf8) +_cairo_ft_font_text_path (void *abstract_font, + double x, + double y, + const unsigned char *utf8, + cairo_path_t *path) { + cairo_ft_font_t *font = abstract_font; cairo_glyph_t *glyphs; size_t nglyphs; - if (_utf8_to_glyphs (font, utf8, &glyphs, &nglyphs)) + if (_utf8_to_glyphs (font, utf8, x, y, &glyphs, &nglyphs)) { cairo_status_t res; - res = _cairo_ft_font_glyph_path (font, path, glyphs, nglyphs); + res = _cairo_ft_font_glyph_path (font, glyphs, nglyphs, path); free (glyphs); return res; } @@ -579,7 +746,6 @@ _cairo_ft_font_text_path (cairo_font_t *font, return CAIRO_STATUS_NO_MEMORY; } - cairo_font_t * cairo_ft_font_create_for_ft_face (FT_Face face) { @@ -602,16 +768,15 @@ cairo_ft_font_create_for_ft_face (FT_Face face) return (cairo_font_t *) f; } - const struct cairo_font_backend cairo_ft_font_backend = { - font_extents: (void *) _cairo_ft_font_font_extents, - text_extents: (void *) _cairo_ft_font_text_extents, - glyph_extents: (void *) _cairo_ft_font_glyph_extents, - show_text: (void *) _cairo_ft_font_show_text, - show_glyphs: (void *) _cairo_ft_font_show_glyphs, - text_path: (void *) _cairo_ft_font_text_path, - glyph_path: (void *) _cairo_ft_font_glyph_path, - create: (void *) _cairo_ft_font_create, - copy: (void *) _cairo_ft_font_copy, - destroy: (void *) _cairo_ft_font_destroy + _cairo_ft_font_create, + _cairo_ft_font_copy, + _cairo_ft_font_destroy, + _cairo_ft_font_font_extents, + _cairo_ft_font_text_extents, + _cairo_ft_font_glyph_extents, + _cairo_ft_font_show_text, + _cairo_ft_font_show_glyphs, + _cairo_ft_font_text_path, + _cairo_ft_font_glyph_path, }; diff --git a/src/cairo_gstate.c b/src/cairo_gstate.c index 8bc6e704..ed8c8a1a 100644 --- a/src/cairo_gstate.c +++ b/src/cairo_gstate.c @@ -30,15 +30,9 @@ #include "cairoint.h" -static void -_cairo_gstate_set_current_point (cairo_gstate_t *gstate, double x, double y); - -static cairo_status_t -_cairo_gstate_ensure_source (cairo_gstate_t *gstate); - static cairo_status_t _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, - cairo_surface_t *src, + cairo_pattern_t *src, cairo_operator_t operator, cairo_surface_t *dst, cairo_traps_t *traps); @@ -79,25 +73,20 @@ _cairo_gstate_init (cairo_gstate_t *gstate) CAIRO_FONT_WEIGHT_DEFAULT); gstate->surface = NULL; - gstate->source = NULL; - gstate->source_offset.x = 0.0; - gstate->source_offset.y = 0.0; - gstate->source_is_solid = 1; + gstate->clip.region = NULL; gstate->clip.surface = NULL; - + + gstate->pattern = _cairo_pattern_create_solid (1.0, 1.0, 1.0); + gstate->pattern_offset.x = 0.0; + gstate->pattern_offset.y = 0.0; gstate->alpha = 1.0; - _cairo_color_init (&gstate->color); gstate->pixels_per_inch = CAIRO_GSTATE_PIXELS_PER_INCH_DEFAULT; _cairo_gstate_default_matrix (gstate); _cairo_path_init (&gstate->path); - gstate->current_point.x = 0.0; - gstate->current_point.y = 0.0; - gstate->has_current_point = 0; - _cairo_pen_init_empty (&gstate->pen_regular); gstate->next = NULL; @@ -130,9 +119,16 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) } } + if (other->clip.region) + { + gstate->clip.region = pixman_region_create (); + pixman_region_copy (gstate->clip.region, other->clip.region); + } + cairo_surface_reference (gstate->surface); - cairo_surface_reference (gstate->source); cairo_surface_reference (gstate->clip.surface); + + cairo_pattern_reference (gstate->pattern); status = _cairo_path_init_copy (&gstate->path, &other->path); if (status) @@ -164,16 +160,15 @@ _cairo_gstate_fini (cairo_gstate_t *gstate) cairo_surface_destroy (gstate->surface); gstate->surface = NULL; - if (gstate->source) - cairo_surface_destroy (gstate->source); - gstate->source = NULL; - gstate->source_is_solid = 1; - if (gstate->clip.surface) cairo_surface_destroy (gstate->clip.surface); gstate->clip.surface = NULL; - _cairo_color_fini (&gstate->color); + if (gstate->clip.region) + pixman_region_destroy (gstate->clip.region); + gstate->clip.region = NULL; + + cairo_pattern_destroy (gstate->pattern); _cairo_matrix_fini (&gstate->ctm); _cairo_matrix_fini (&gstate->ctm_inverse); @@ -358,22 +353,37 @@ _cairo_gstate_current_target_surface (cairo_gstate_t *gstate) } cairo_status_t -_cairo_gstate_set_pattern (cairo_gstate_t *gstate, cairo_surface_t *pattern) +_cairo_gstate_set_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern) { - cairo_surface_destroy (gstate->source); - - gstate->source = pattern; - gstate->source_is_solid = 0; + if (pattern == NULL) + return CAIRO_STATUS_NULL_POINTER; - cairo_surface_reference (gstate->source); + if (gstate->pattern) + cairo_pattern_destroy (gstate->pattern); + + gstate->pattern = pattern; + cairo_pattern_reference (pattern); _cairo_gstate_current_point (gstate, - &gstate->source_offset.x, - &gstate->source_offset.y); - + &gstate->pattern_offset.x, + &gstate->pattern_offset.y); + return CAIRO_STATUS_SUCCESS; } +cairo_pattern_t * +_cairo_gstate_current_pattern (cairo_gstate_t *gstate) +{ + if (gstate == NULL) + return NULL; + +/* XXX: Do we want this? + cairo_pattern_reference (gstate->pattern); +*/ + + return gstate->pattern; +} + cairo_status_t _cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t operator) { @@ -391,25 +401,19 @@ _cairo_gstate_current_operator (cairo_gstate_t *gstate) cairo_status_t _cairo_gstate_set_rgb_color (cairo_gstate_t *gstate, double red, double green, double blue) { - _cairo_color_set_rgb (&gstate->color, red, green, blue); - - if (gstate->source) - cairo_surface_destroy (gstate->source); - - gstate->source = NULL; - gstate->source_offset.x = 0; - gstate->source_offset.y = 0; - gstate->source_is_solid = 1; - + cairo_pattern_destroy (gstate->pattern); + + gstate->pattern = _cairo_pattern_create_solid (red, green, blue); + gstate->pattern_offset.x = 0.0; + gstate->pattern_offset.y = 0.0; + return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_current_rgb_color (cairo_gstate_t *gstate, double *red, double *green, double *blue) { - _cairo_color_get_rgb (&gstate->color, red, green, blue); - - return CAIRO_STATUS_SUCCESS; + return _cairo_pattern_get_rgb (gstate->pattern, red, green, blue); } cairo_status_t @@ -426,20 +430,11 @@ _cairo_gstate_current_tolerance (cairo_gstate_t *gstate) return gstate->tolerance; } -/* XXX: Need to fix this so it does the right thing after set_pattern. */ cairo_status_t _cairo_gstate_set_alpha (cairo_gstate_t *gstate, double alpha) { gstate->alpha = alpha; - _cairo_color_set_alpha (&gstate->color, alpha); - - cairo_surface_destroy (gstate->source); - - gstate->source = NULL; - gstate->source_offset.x = 0; - gstate->source_offset.y = 0; - return CAIRO_STATUS_SUCCESS; } @@ -680,20 +675,10 @@ _cairo_gstate_inverse_transform_distance (cairo_gstate_t *gstate, double *dx, do return CAIRO_STATUS_SUCCESS; } -static void -_cairo_gstate_set_current_point (cairo_gstate_t *gstate, double x, double y) -{ - gstate->current_point.x = x; - gstate->current_point.y = y; - - gstate->has_current_point = 1; -} - cairo_status_t _cairo_gstate_new_path (cairo_gstate_t *gstate) { _cairo_path_fini (&gstate->path); - gstate->has_current_point = 0; return CAIRO_STATUS_SUCCESS; } @@ -701,53 +686,51 @@ _cairo_gstate_new_path (cairo_gstate_t *gstate) cairo_status_t _cairo_gstate_move_to (cairo_gstate_t *gstate, double x, double y) { - cairo_status_t status; + cairo_point_t point; cairo_matrix_transform_point (&gstate->ctm, &x, &y); - status = _cairo_path_move_to (&gstate->path, x, y); - - _cairo_gstate_set_current_point (gstate, x, y); - - gstate->last_move_point = gstate->current_point; + point.x = _cairo_fixed_from_double (x); + point.y = _cairo_fixed_from_double (y); - return status; + return _cairo_path_move_to (&gstate->path, &point); } cairo_status_t _cairo_gstate_line_to (cairo_gstate_t *gstate, double x, double y) { - cairo_status_t status; + cairo_point_t point; cairo_matrix_transform_point (&gstate->ctm, &x, &y); - status = _cairo_path_line_to (&gstate->path, x, y); - - _cairo_gstate_set_current_point (gstate, x, y); + point.x = _cairo_fixed_from_double (x); + point.y = _cairo_fixed_from_double (y); - return status; + return _cairo_path_line_to (&gstate->path, &point); } cairo_status_t _cairo_gstate_curve_to (cairo_gstate_t *gstate, + double x0, double y0, double x1, double y1, - double x2, double y2, - double x3, double y3) + double x2, double y2) { - cairo_status_t status; + cairo_point_t p0, p1, p2; + cairo_matrix_transform_point (&gstate->ctm, &x0, &y0); cairo_matrix_transform_point (&gstate->ctm, &x1, &y1); cairo_matrix_transform_point (&gstate->ctm, &x2, &y2); - cairo_matrix_transform_point (&gstate->ctm, &x3, &y3); - status = _cairo_path_curve_to (&gstate->path, - x1, y1, - x2, y2, - x3, y3); + p0.x = _cairo_fixed_from_double (x0); + p0.y = _cairo_fixed_from_double (y0); - _cairo_gstate_set_current_point (gstate, x3, y3); + p1.x = _cairo_fixed_from_double (x1); + p1.y = _cairo_fixed_from_double (y1); - return status; + p2.x = _cairo_fixed_from_double (x2); + p2.y = _cairo_fixed_from_double (y2); + + return _cairo_path_curve_to (&gstate->path, &p0, &p1, &p2); } /* Spline deviation from the circle in radius would be given by: @@ -1025,63 +1008,54 @@ _cairo_gstate_arc_to (cairo_gstate_t *gstate, cairo_status_t _cairo_gstate_rel_move_to (cairo_gstate_t *gstate, double dx, double dy) { - cairo_status_t status; - double x, y; + cairo_distance_t distance; cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); - x = gstate->current_point.x + dx; - y = gstate->current_point.y + dy; - - status = _cairo_path_move_to (&gstate->path, x, y); - - _cairo_gstate_set_current_point (gstate, x, y); + distance.dx = _cairo_fixed_from_double (dx); + distance.dy = _cairo_fixed_from_double (dy); - gstate->last_move_point = gstate->current_point; - - return status; + return _cairo_path_rel_move_to (&gstate->path, &distance); } cairo_status_t _cairo_gstate_rel_line_to (cairo_gstate_t *gstate, double dx, double dy) { - cairo_status_t status; - double x, y; + cairo_distance_t distance; cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); - x = gstate->current_point.x + dx; - y = gstate->current_point.y + dy; - - status = _cairo_path_line_to (&gstate->path, x, y); - - _cairo_gstate_set_current_point (gstate, x, y); + distance.dx = _cairo_fixed_from_double (dx); + distance.dy = _cairo_fixed_from_double (dy); - return status; + return _cairo_path_rel_line_to (&gstate->path, &distance); } cairo_status_t _cairo_gstate_rel_curve_to (cairo_gstate_t *gstate, + double dx0, double dy0, double dx1, double dy1, - double dx2, double dy2, - double dx3, double dy3) + double dx2, double dy2) { - cairo_status_t status; + cairo_distance_t distance[3]; + cairo_matrix_transform_distance (&gstate->ctm, &dx0, &dy0); cairo_matrix_transform_distance (&gstate->ctm, &dx1, &dy1); cairo_matrix_transform_distance (&gstate->ctm, &dx2, &dy2); - cairo_matrix_transform_distance (&gstate->ctm, &dx3, &dy3); - status = _cairo_path_curve_to (&gstate->path, - gstate->current_point.x + dx1, gstate->current_point.y + dy1, - gstate->current_point.x + dx2, gstate->current_point.y + dy2, - gstate->current_point.x + dx3, gstate->current_point.y + dy3); + distance[0].dx = _cairo_fixed_from_double (dx0); + distance[0].dy = _cairo_fixed_from_double (dy0); - _cairo_gstate_set_current_point (gstate, - gstate->current_point.x + dx3, - gstate->current_point.y + dy3); + distance[1].dx = _cairo_fixed_from_double (dx1); + distance[1].dy = _cairo_fixed_from_double (dy1); - return status; + distance[2].dx = _cairo_fixed_from_double (dx2); + distance[2].dy = _cairo_fixed_from_double (dy2); + + return _cairo_path_rel_curve_to (&gstate->path, + &distance[0], + &distance[1], + &distance[2]); } /* XXX: NYI @@ -1098,29 +1072,24 @@ _cairo_gstate_stroke_path (cairo_gstate_t *gstate) cairo_status_t _cairo_gstate_close_path (cairo_gstate_t *gstate) { - cairo_status_t status; - - status = _cairo_path_close_path (&gstate->path); - - _cairo_gstate_set_current_point (gstate, - gstate->last_move_point.x, - gstate->last_move_point.y); - - return status; + return _cairo_path_close_path (&gstate->path); } cairo_status_t _cairo_gstate_current_point (cairo_gstate_t *gstate, double *x_ret, double *y_ret) { + cairo_status_t status; + cairo_point_t point; double x, y; - if (gstate->has_current_point) { - x = gstate->current_point.x; - y = gstate->current_point.y; - cairo_matrix_transform_point (&gstate->ctm_inverse, &x, &y); - } else { + status = _cairo_path_current_point (&gstate->path, &point); + if (status == CAIRO_STATUS_NO_CURRENT_POINT) { x = 0.0; y = 0.0; + } else { + x = _cairo_fixed_to_double (point.x); + y = _cairo_fixed_to_double (point.y); + cairo_matrix_transform_point (&gstate->ctm_inverse, &x, &y); } *x_ret = x; @@ -1129,24 +1098,201 @@ _cairo_gstate_current_point (cairo_gstate_t *gstate, double *x_ret, double *y_re return CAIRO_STATUS_SUCCESS; } +typedef struct gstate_path_interpreter { + cairo_matrix_t ctm_inverse; + double tolerance; + cairo_point_t current_point; + + cairo_move_to_func_t *move_to; + cairo_line_to_func_t *line_to; + cairo_curve_to_func_t *curve_to; + cairo_close_path_func_t *close_path; + + void *closure; +} gpi_t; + static cairo_status_t -_cairo_gstate_ensure_source (cairo_gstate_t *gstate) +_gpi_move_to (void *closure, cairo_point_t *point) { - if (gstate->source) - return CAIRO_STATUS_SUCCESS; + gpi_t *gpi = closure; + double x, y; - if (gstate->surface == NULL) + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + + cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); + + gpi->move_to (gpi->closure, x, y); + gpi->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_gpi_line_to (void *closure, cairo_point_t *point) +{ + gpi_t *gpi = closure; + double x, y; + + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + + cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); + + gpi->line_to (gpi->closure, x, y); + gpi->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_gpi_curve_to (void *closure, + cairo_point_t *p1, + cairo_point_t *p2, + cairo_point_t *p3) +{ + gpi_t *gpi = closure; + cairo_status_t status; + cairo_spline_t spline; + double x1, y1, x2, y2, x3, y3; + + if (gpi->curve_to) { + x1 = _cairo_fixed_to_double (p1->x); + y1 = _cairo_fixed_to_double (p1->y); + cairo_matrix_transform_point (&gpi->ctm_inverse, &x1, &y1); + + x2 = _cairo_fixed_to_double (p2->x); + y2 = _cairo_fixed_to_double (p2->y); + cairo_matrix_transform_point (&gpi->ctm_inverse, &x2, &y2); + + x3 = _cairo_fixed_to_double (p3->x); + y3 = _cairo_fixed_to_double (p3->y); + cairo_matrix_transform_point (&gpi->ctm_inverse, &x3, &y3); + + gpi->curve_to (gpi->closure, x1, y1, x2, y2, x3, y3); + } else { + cairo_point_t *p0 = &gpi->current_point; + int i; + double x, y; + + status = _cairo_spline_init (&spline, p0, p1, p2, p3); + if (status == CAIRO_INT_STATUS_DEGENERATE) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_spline_decompose (&spline, gpi->tolerance); + if (status) + return status; + + for (i=1; i < spline.num_points; i++) { + x = _cairo_fixed_to_double (spline.points[i].x); + y = _cairo_fixed_to_double (spline.points[i].y); + + cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); + + gpi->line_to (gpi->closure, x, y); + } + } + + gpi->current_point = *p3; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_gpi_close_path (void *closure) +{ + gpi_t *gpi = closure; + + gpi->close_path (gpi->closure); + + gpi->current_point.x = 0; + gpi->current_point.y = 0; + + return CAIRO_STATUS_SUCCESS; +} + +/* It's OK for curve_path to be NULL. In that case, all curves in the + path will be decomposed into one or more calls to the line_to + function, (according to the current tolerance). */ +cairo_status_t +_cairo_gstate_interpret_path (cairo_gstate_t *gstate, + cairo_move_to_func_t *move_to, + cairo_line_to_func_t *line_to, + cairo_curve_to_func_t *curve_to, + cairo_close_path_func_t *close_path, + void *closure) +{ + cairo_path_t path; + gpi_t gpi; + + /* Anything we want from gstate must be copied. We must not retain + pointers into gstate. */ + _cairo_path_init_copy (&path, &gstate->path); + + cairo_matrix_copy (&gpi.ctm_inverse, &gstate->ctm_inverse); + gpi.tolerance = gstate->tolerance; + + gpi.move_to = move_to; + gpi.line_to = line_to; + gpi.curve_to = curve_to; + gpi.close_path = close_path; + gpi.closure = closure; + + gpi.current_point.x = 0; + gpi.current_point.y = 0; + + return _cairo_path_interpret (&path, + CAIRO_DIRECTION_FORWARD, + _gpi_move_to, + _gpi_line_to, + _gpi_curve_to, + _gpi_close_path, + &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) +{ + cairo_int_status_t status; + + if (gstate->surface == NULL) { + _cairo_pattern_fini (pattern); return CAIRO_STATUS_NO_TARGET_SURFACE; + } - gstate->source = _cairo_surface_create_similar_solid (gstate->surface, - CAIRO_FORMAT_ARGB32, - 1, 1, - &gstate->color); - if (gstate->source == NULL) - return CAIRO_STATUS_NO_MEMORY; + 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) + pattern->color = pattern->stops->color; + } + } + + _cairo_pattern_set_alpha (pattern, gstate->alpha); + _cairo_pattern_transform (pattern, &gstate->ctm, &gstate->ctm_inverse); - cairo_surface_set_repeat (gstate->source, 1); + 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); + _cairo_pattern_add_source_offset (pattern, + gstate->pattern_offset.x, + gstate->pattern_offset.y); + return CAIRO_STATUS_SUCCESS; } @@ -1155,15 +1301,10 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate) { cairo_status_t status; cairo_traps_t traps; - cairo_matrix_t user_to_source, device_to_source; if (gstate->line_width <= 0.0) return CAIRO_STATUS_SUCCESS; - status = _cairo_gstate_ensure_source (gstate); - if (status) - return status; - _cairo_pen_init (&gstate->pen_regular, gstate->line_width / 2.0, gstate); _cairo_traps_init (&traps); @@ -1174,21 +1315,11 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate) return status; } - if (! gstate->source_is_solid) { - cairo_surface_get_matrix (gstate->source, &user_to_source); - cairo_matrix_multiply (&device_to_source, &gstate->ctm_inverse, &user_to_source); - cairo_surface_set_matrix (gstate->source, &device_to_source); - } - _cairo_gstate_clip_and_composite_trapezoids (gstate, - gstate->source, - gstate->operator, - gstate->surface, - &traps); - - /* restore the matrix originally in the source surface */ - if (! gstate->source_is_solid) - cairo_surface_set_matrix (gstate->source, &user_to_source); + gstate->pattern, + gstate->operator, + gstate->surface, + &traps); _cairo_traps_fini (&traps); @@ -1227,12 +1358,14 @@ BAIL: /* Warning: This call modifies the coordinates of traps */ static cairo_status_t _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, - cairo_surface_t *src, + cairo_pattern_t *src, cairo_operator_t operator, cairo_surface_t *dst, cairo_traps_t *traps) { cairo_status_t status; + cairo_pattern_t pattern; + cairo_box_t extents; if (traps->num_traps == 0) return CAIRO_STATUS_SUCCESS; @@ -1241,19 +1374,8 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, cairo_fixed_t xoff, yoff; cairo_trapezoid_t *t; int i; - - cairo_surface_t *white, *intermediate; - cairo_color_t white_color, empty_color; - - _cairo_color_init (&white_color); - white = _cairo_surface_create_similar_solid (gstate->surface, CAIRO_FORMAT_A8, - 1, 1, - &white_color); - if (white == NULL) { - status = CAIRO_STATUS_NO_MEMORY; - goto BAIL0; - } - cairo_surface_set_repeat (white, 1); + cairo_surface_t *intermediate; + cairo_color_t empty_color; _cairo_color_init (&empty_color); _cairo_color_set_alpha (&empty_color, 0.); @@ -1261,10 +1383,10 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, CAIRO_FORMAT_A8, gstate->clip.width, gstate->clip.height, - &empty_color); + &empty_color); if (intermediate == NULL) { status = CAIRO_STATUS_NO_MEMORY; - goto BAIL1; + goto BAIL0; } /* Ugh. The cairo_composite/(Render) interface doesn't allow @@ -1286,8 +1408,16 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, t->right.p2.y -= yoff; } + _cairo_pattern_init_solid (&pattern, 1.0, 1.0, 1.0); + _cairo_pattern_set_alpha (&pattern, 1.0); + + _cairo_traps_extents (traps, &extents); + status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); + if (status) + goto BAIL1; + status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD, - white, intermediate, + pattern.source, intermediate, 0, 0, traps->traps, traps->num_traps); @@ -1302,9 +1432,23 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, gstate->clip.width, gstate->clip.height); if (status) goto BAIL2; + + _cairo_pattern_fini (&pattern); + + _cairo_pattern_init_copy (&pattern, src); + + extents.p1.x = _cairo_fixed_from_int (gstate->clip.x); + extents.p1.y = _cairo_fixed_from_int (gstate->clip.y); + extents.p2.x = + _cairo_fixed_from_int (gstate->clip.x + gstate->clip.width); + extents.p2.y = + _cairo_fixed_from_int (gstate->clip.y + gstate->clip.height); + status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); + if (status) + goto BAIL2; status = _cairo_surface_composite (operator, - src, intermediate, dst, + pattern.source, intermediate, dst, 0, 0, 0, 0, gstate->clip.x, @@ -1315,11 +1459,12 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, BAIL2: cairo_surface_destroy (intermediate); BAIL1: - cairo_surface_destroy (white); + _cairo_pattern_fini (&pattern); BAIL0: + if (status) return status; - + } else { int xoff, yoff; @@ -1331,16 +1476,26 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, yoff = _cairo_fixed_to_double (traps->traps[0].left.p2.y); } + _cairo_pattern_init_copy (&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, - src, dst, - xoff - gstate->source_offset.x, - yoff - gstate->source_offset.y, + pattern.source, dst, + xoff - pattern.source_offset.x, + yoff - pattern.source_offset.y, traps->traps, traps->num_traps); + + _cairo_pattern_fini (&pattern); + if (status) return status; } - + return CAIRO_STATUS_SUCCESS; } @@ -1349,11 +1504,6 @@ _cairo_gstate_fill (cairo_gstate_t *gstate) { cairo_status_t status; cairo_traps_t traps; - cairo_matrix_t user_to_source, device_to_source; - - status = _cairo_gstate_ensure_source (gstate); - if (status) - return status; _cairo_traps_init (&traps); @@ -1363,21 +1513,11 @@ _cairo_gstate_fill (cairo_gstate_t *gstate) return status; } - if (! gstate->source_is_solid) { - cairo_surface_get_matrix (gstate->source, &user_to_source); - cairo_matrix_multiply (&device_to_source, &gstate->ctm_inverse, &user_to_source); - cairo_surface_set_matrix (gstate->source, &device_to_source); - } - _cairo_gstate_clip_and_composite_trapezoids (gstate, - gstate->source, - gstate->operator, - gstate->surface, - &traps); - - /* restore the matrix originally in the source surface */ - if (! gstate->source_is_solid) - cairo_surface_set_matrix (gstate->source, &user_to_source); + gstate->pattern, + gstate->operator, + gstate->surface, + &traps); _cairo_traps_fini (&traps); @@ -1404,7 +1544,7 @@ _cairo_gstate_in_fill (cairo_gstate_t *gstate, goto BAIL; *inside_ret = _cairo_traps_contain (&traps, x, y); - + BAIL: _cairo_traps_fini (&traps); @@ -1430,39 +1570,137 @@ _cairo_gstate_show_page (cairo_gstate_t *gstate) } cairo_status_t -_cairo_gstate_clip (cairo_gstate_t *gstate) +_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, + double *x1, double *y1, + double *x2, double *y2) { cairo_status_t status; - cairo_surface_t *alpha_one; cairo_traps_t traps; - cairo_color_t white_color; + cairo_box_t extents; + + _cairo_traps_init (&traps); + + status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps); + if (status) + goto BAIL; - _cairo_color_init (&white_color); + _cairo_traps_extents (&traps, &extents); - if (gstate->clip.surface == NULL) { - double x1, y1, x2, y2; - _cairo_path_bounds (&gstate->path, - &x1, &y1, &x2, &y2); - gstate->clip.x = floor (x1); - gstate->clip.y = floor (y1); - gstate->clip.width = ceil (x2 - gstate->clip.x); - gstate->clip.height = ceil (y2 - gstate->clip.y); - gstate->clip.surface = _cairo_surface_create_similar_solid (gstate->surface, - CAIRO_FORMAT_A8, - gstate->clip.width, - gstate->clip.height, - &white_color); - if (gstate->clip.surface == NULL) - return CAIRO_STATUS_NO_MEMORY; + *x1 = _cairo_fixed_to_double (extents.p1.x); + *y1 = _cairo_fixed_to_double (extents.p1.y); + *x2 = _cairo_fixed_to_double (extents.p2.x); + *y2 = _cairo_fixed_to_double (extents.p2.y); + + cairo_matrix_transform_point (&gstate->ctm_inverse, x1, y1); + cairo_matrix_transform_point (&gstate->ctm_inverse, x2, y2); + +BAIL: + _cairo_traps_fini (&traps); + + return status; +} + +cairo_status_t +_cairo_gstate_fill_extents (cairo_gstate_t *gstate, + double *x1, double *y1, + double *x2, double *y2) +{ + cairo_status_t status; + cairo_traps_t traps; + cairo_box_t extents; + + _cairo_traps_init (&traps); + + status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); + if (status) + goto BAIL; + + _cairo_traps_extents (&traps, &extents); + + *x1 = _cairo_fixed_to_double (extents.p1.x); + *y1 = _cairo_fixed_to_double (extents.p1.y); + *x2 = _cairo_fixed_to_double (extents.p2.x); + *y2 = _cairo_fixed_to_double (extents.p2.y); + + cairo_matrix_transform_point (&gstate->ctm_inverse, x1, y1); + cairo_matrix_transform_point (&gstate->ctm_inverse, x2, y2); + +BAIL: + _cairo_traps_fini (&traps); + + return status; +} + +cairo_status_t +_cairo_gstate_init_clip (cairo_gstate_t *gstate) +{ + /* destroy any existing clip-region artifacts */ + if (gstate->clip.surface) + cairo_surface_destroy (gstate->clip.surface); + gstate->clip.surface = NULL; + + if (gstate->clip.region) + pixman_region_destroy (gstate->clip.region); + gstate->clip.region = NULL; + + /* reset the surface's clip to the whole surface */ + _cairo_surface_set_clip_region (gstate->surface, + gstate->clip.region); + + return CAIRO_STATUS_SUCCESS; +} + +static int +extract_transformed_rectangle(cairo_matrix_t *mat, + cairo_traps_t *tr, + pixman_box16_t *box) +{ +#define CAIRO_FIXED_IS_INTEGER(x) (((x) & 0xFFFF) == 0) +#define CAIRO_FIXED_INTEGER_PART(x) ((x) >> 16) + + double a, b, c, d, tx, ty; + cairo_status_t st; + + st = cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty); + if (!(st == CAIRO_STATUS_SUCCESS && b == 0. && c == 0.)) + return 0; + + if (tr->num_traps == 1 + && tr->traps[0].left.p1.x == tr->traps[0].left.p2.x + && tr->traps[0].right.p1.x == tr->traps[0].right.p2.x + && tr->traps[0].left.p1.y == tr->traps[0].right.p1.y + && tr->traps[0].left.p2.y == tr->traps[0].right.p2.y + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].left.p1.x) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].left.p1.y) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].left.p2.x) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].left.p2.y) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].right.p1.x) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].right.p1.y) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].right.p2.x) + && CAIRO_FIXED_IS_INTEGER(tr->traps[0].right.p2.y)) { + + box->x1 = (short) CAIRO_FIXED_INTEGER_PART(tr->traps[0].left.p1.x); + box->x2 = (short) CAIRO_FIXED_INTEGER_PART(tr->traps[0].right.p1.x); + box->y1 = (short) CAIRO_FIXED_INTEGER_PART(tr->traps[0].left.p1.y); + box->y2 = (short) CAIRO_FIXED_INTEGER_PART(tr->traps[0].left.p2.y); + return 1; } + return 0; - alpha_one = _cairo_surface_create_similar_solid (gstate->surface, CAIRO_FORMAT_A8, - 1, 1, - &white_color); - if (alpha_one == NULL) - return CAIRO_STATUS_NO_MEMORY; +#undef CAIRO_FIXED_IS_INTEGER +#undef CAIRO_FIXED_INTEGER_PART +} - cairo_surface_set_repeat (alpha_one, 1); +cairo_status_t +_cairo_gstate_clip (cairo_gstate_t *gstate) +{ + cairo_status_t status; + cairo_pattern_t pattern; + cairo_traps_t traps; + cairo_color_t white_color; + pixman_box16_t box; + + /* Fill the clip region as traps. */ _cairo_traps_init (&traps); status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); @@ -1471,16 +1709,82 @@ _cairo_gstate_clip (cairo_gstate_t *gstate) return status; } + /* Check to see if we can represent these traps as a PixRegion. */ + + if (extract_transformed_rectangle (&gstate->ctm, &traps, &box)) { + + pixman_region16_t *rect = NULL; + pixman_region16_t *intersection = NULL; + + status = CAIRO_STATUS_SUCCESS; + rect = pixman_region_create_simple (&box); + + if (rect == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + + } else { + + if (gstate->clip.region == NULL) { + gstate->clip.region = rect; + } else { + intersection = pixman_region_create(); + if (pixman_region_intersect (intersection, + gstate->clip.region, rect) + == PIXMAN_REGION_STATUS_SUCCESS) { + pixman_region_destroy (gstate->clip.region); + gstate->clip.region = intersection; + } else { + status = CAIRO_STATUS_NO_MEMORY; + } + pixman_region_destroy (rect); + } + + if (!status) + status = _cairo_surface_set_clip_region (gstate->surface, + gstate->clip.region); + } + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + _cairo_traps_fini (&traps); + return status; + } + } + + /* Otherwise represent the clip as a mask surface. */ + + _cairo_color_init (&white_color); + + if (gstate->clip.surface == NULL) { + double x1, y1, x2, y2; + _cairo_path_bounds (&gstate->path, + &x1, &y1, &x2, &y2); + gstate->clip.x = floor (x1); + gstate->clip.y = floor (y1); + gstate->clip.width = ceil (x2 - gstate->clip.x); + gstate->clip.height = ceil (y2 - gstate->clip.y); + gstate->clip.surface = + _cairo_surface_create_similar_solid (gstate->surface, + CAIRO_FORMAT_A8, + gstate->clip.width, + gstate->clip.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, - alpha_one, + &pattern, CAIRO_OPERATOR_IN, gstate->clip.surface, &traps); - + + _cairo_pattern_fini (&pattern); + _cairo_traps_fini (&traps); - cairo_surface_destroy (alpha_one); - return status; } @@ -1491,27 +1795,12 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, int height) { cairo_status_t status; - cairo_surface_t *mask; cairo_matrix_t user_to_image, image_to_user; cairo_matrix_t image_to_device, device_to_image; double device_x, device_y; double device_width, device_height; - cairo_color_t alpha_color; - - if (gstate->alpha != 1.0) { - _cairo_color_init (&alpha_color); - _cairo_color_set_alpha (&alpha_color, gstate->alpha); - mask = _cairo_surface_create_similar_solid (gstate->surface, - CAIRO_FORMAT_A8, - 1, 1, - &alpha_color); - if (mask == NULL) - return CAIRO_STATUS_NO_MEMORY; - - cairo_surface_set_repeat (mask, 1); - } else { - mask = NULL; - } + cairo_pattern_t pattern; + cairo_box_t extents; cairo_surface_get_matrix (surface, &user_to_image); cairo_matrix_multiply (&device_to_image, &gstate->ctm_inverse, &user_to_image); @@ -1527,32 +1816,47 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, _cairo_matrix_transform_bounding_box (&image_to_device, &device_x, &device_y, &device_width, &device_height); + + _cairo_pattern_init (&pattern); + + 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); + extents.p1.x = _cairo_fixed_from_double (device_x); + extents.p1.y = _cairo_fixed_from_double (device_y); + extents.p2.x = _cairo_fixed_from_double (device_x + device_width); + extents.p2.y = _cairo_fixed_from_double (device_y + device_height); + status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); + if (status) + return status; + } /* XXX: The rendered size is sometimes 1 or 2 pixels short from what I expect. Need to fix this. */ status = _cairo_surface_composite (gstate->operator, - surface, mask, gstate->surface, + surface, pattern.source, gstate->surface, device_x, device_y, 0, 0, device_x, device_y, device_width, device_height); - - if (mask) - cairo_surface_destroy (mask); - if (status) - return status; + _cairo_pattern_fini (&pattern); /* restore the matrix originally in the surface */ cairo_surface_set_matrix (surface, &user_to_image); + + if (status) + return status; return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_gstate_select_font (cairo_gstate_t *gstate, - char *family, + const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight) { @@ -1600,6 +1904,7 @@ _cairo_gstate_current_font_extents (cairo_gstate_t *gstate, status = _cairo_font_font_extents (gstate->font, extents); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + return status; } @@ -1622,14 +1927,24 @@ _cairo_gstate_text_extents (cairo_gstate_t *gstate, { cairo_matrix_t saved_font_matrix; cairo_status_t status; + double scale_x, scale_y; cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); + _cairo_matrix_compute_scale_factors (&gstate->ctm, &scale_x, &scale_y); + cairo_matrix_scale (&gstate->font->matrix, scale_x, scale_y); status = _cairo_font_text_extents (gstate->font, utf8, extents); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + + extents->x_bearing /= scale_x; + extents->y_bearing /= scale_y; + extents->width /= scale_x; + extents->height /= scale_y; + extents->x_advance /= scale_x; + extents->y_advance /= scale_y; + return status; } @@ -1640,120 +1955,91 @@ _cairo_gstate_glyph_extents (cairo_gstate_t *gstate, cairo_text_extents_t *extents) { cairo_status_t status; - int i; - cairo_glyph_t *transformed_glyphs = NULL; cairo_matrix_t saved_font_matrix; - - transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); - if (transformed_glyphs == NULL) - return CAIRO_STATUS_NO_MEMORY; - - for (i = 0; i < num_glyphs; ++i) - { - transformed_glyphs[i] = glyphs[i]; - cairo_matrix_transform_point (&gstate->ctm, - &(transformed_glyphs[i].x), - &(transformed_glyphs[i].y)); - } + double scale_x, scale_y; cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); - cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); + _cairo_matrix_compute_scale_factors (&gstate->ctm, &scale_x, &scale_y); + cairo_matrix_scale (&gstate->font->matrix, scale_x, scale_y); status = _cairo_font_glyph_extents (gstate->font, - transformed_glyphs, num_glyphs, + glyphs, num_glyphs, extents); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - free (transformed_glyphs); - return status; -} - + extents->x_bearing /= scale_x; + extents->y_bearing /= scale_y; + extents->width /= scale_x; + extents->height /= scale_y; + extents->x_advance /= scale_x; + extents->y_advance /= scale_y; -static cairo_status_t -setup_text_rendering_context(cairo_gstate_t *gstate, - double *x, double *y, - cairo_matrix_t *user_to_source) -{ - cairo_status_t status; - cairo_matrix_t device_to_source; - - /* XXX: I believe this is correct, but it would be much more clear - to have some explicit current_point accesor functions, (one for - user- and one for device-space). */ - - if (gstate->has_current_point) { - *x = gstate->current_point.x; - *y = gstate->current_point.y; - } else { - *x = 0; - *y = 0; - cairo_matrix_transform_point (&gstate->ctm, x, y); - } - - status = _cairo_gstate_ensure_source (gstate); - if (status) - return status; - - /* XXX: This same source matrix manipulation code shows up in - about 3 or 4 places. We should move that into a shared function - or two. */ - if (! gstate->source_is_solid) { - cairo_surface_get_matrix (gstate->source, user_to_source); - cairo_matrix_multiply (&device_to_source, &gstate->ctm_inverse, user_to_source); - cairo_surface_set_matrix (gstate->source, &device_to_source); - } - return CAIRO_STATUS_SUCCESS; -} - -static void -restore_text_rendering_context(cairo_gstate_t *gstate, - cairo_matrix_t *user_to_source) -{ - /* restore the matrix originally in the source surface */ - if (! gstate->source_is_solid) - cairo_surface_set_matrix (gstate->source, user_to_source); + return status; } - cairo_status_t _cairo_gstate_show_text (cairo_gstate_t *gstate, const unsigned char *utf8) { cairo_status_t status; + cairo_point_t point; double x, y; - cairo_matrix_t user_to_source; cairo_matrix_t saved_font_matrix; - - status = setup_text_rendering_context(gstate, &x, &y, &user_to_source); - if (status) - return status; + cairo_pattern_t pattern; + cairo_text_extents_t text_extents; + cairo_box_t extents; + + status = _cairo_path_current_point (&gstate->path, &point); + if (status == CAIRO_STATUS_NO_CURRENT_POINT) { + x = 0; + y = 0; + cairo_matrix_transform_point (&gstate->ctm, &x, &y); + } else { + x = _cairo_fixed_to_double (point.x); + y = _cairo_fixed_to_double (point.y); + } cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); + _cairo_pattern_init_copy (&pattern, gstate->pattern); + + status = _cairo_gstate_text_extents (gstate, utf8, &text_extents); + if (status) + return status; + + extents.p1.x = _cairo_fixed_from_double (x); + extents.p1.y = _cairo_fixed_from_double (y); + extents.p2.x = _cairo_fixed_from_double (x + text_extents.width); + extents.p2.y = _cairo_fixed_from_double (y + text_extents.height); + status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); + if (status) + return status; + status = _cairo_font_show_text (gstate->font, - gstate->operator, gstate->source, + gstate->operator, pattern.source, gstate->surface, x, y, utf8); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - restore_text_rendering_context (gstate, &user_to_source); + + _cairo_pattern_fini (&pattern); return status; } - cairo_status_t _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, cairo_glyph_t *glyphs, int num_glyphs) { cairo_status_t status; - double x, y; - cairo_matrix_t user_to_source; cairo_matrix_t saved_font_matrix; int i; cairo_glyph_t *transformed_glyphs = NULL; + cairo_pattern_t pattern; + cairo_text_extents_t text_extents; + cairo_box_t extents; transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); if (transformed_glyphs == NULL) @@ -1767,25 +2053,37 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, &(transformed_glyphs[i].y)); } - status = setup_text_rendering_context (gstate, &x, &y, &user_to_source); - if (status) - return status; - cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); + _cairo_pattern_init_copy (&pattern, gstate->pattern); + _cairo_gstate_glyph_extents (gstate, transformed_glyphs, num_glyphs, + &text_extents); + if (status) + return status; + + extents.p1.x = _cairo_fixed_from_double (transformed_glyphs[0].x); + extents.p1.y = _cairo_fixed_from_double (transformed_glyphs[0].y); + extents.p2.x = _cairo_fixed_from_double (transformed_glyphs[0].x + + text_extents.width); + extents.p2.y = _cairo_fixed_from_double (transformed_glyphs[0].y + + text_extents.height); + status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); + if (status) + return status; + status = _cairo_font_show_glyphs (gstate->font, - gstate->operator, gstate->source, - gstate->surface, x, y, + gstate->operator, pattern.source, + gstate->surface, transformed_glyphs, num_glyphs); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - restore_text_rendering_context (gstate, &user_to_source); + + _cairo_pattern_fini (&pattern); free (transformed_glyphs); return status; - } @@ -1795,21 +2093,35 @@ _cairo_gstate_text_path (cairo_gstate_t *gstate, { cairo_status_t status; cairo_matrix_t saved_font_matrix; + cairo_point_t point; + double x, y; + + status = _cairo_path_current_point (&gstate->path, &point); + if (status == CAIRO_STATUS_NO_CURRENT_POINT) { + x = 0; + y = 0; + cairo_matrix_transform_point (&gstate->ctm, &x, &y); + } else { + x = _cairo_fixed_to_double (point.x); + y = _cairo_fixed_to_double (point.y); + } cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); status = _cairo_font_text_path (gstate->font, - &gstate->path, - utf8); + x, y, + utf8, + &gstate->path); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); + return status; } cairo_status_t -_cairo_gstate_glyph_path (cairo_gstate_t *gstate, +_cairo_gstate_glyph_path (cairo_gstate_t *gstate, cairo_glyph_t *glyphs, int num_glyphs) { @@ -1834,11 +2146,11 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); status = _cairo_font_glyph_path (gstate->font, - &gstate->path, - transformed_glyphs, num_glyphs); + transformed_glyphs, num_glyphs, + &gstate->path); cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); - + free (transformed_glyphs); return status; } diff --git a/src/cairo_image_surface.c b/src/cairo_image_surface.c index efa54d26..a11a07ee 100644 --- a/src/cairo_image_surface.c +++ b/src/cairo_image_surface.c @@ -45,7 +45,7 @@ _cairo_format_bpp (cairo_format_t format) } static cairo_image_surface_t * -_cairo_image_surface_create_for_ic_image (IcImage *ic_image) +_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image) { cairo_image_surface_t *surface; @@ -55,15 +55,15 @@ _cairo_image_surface_create_for_ic_image (IcImage *ic_image) _cairo_surface_init (&surface->base, &cairo_image_surface_backend); - surface->ic_image = ic_image; + surface->pixman_image = pixman_image; - surface->data = (char *) IcImageGetData (ic_image); + surface->data = (char *) pixman_image_get_data (pixman_image); surface->owns_data = 0; - surface->width = IcImageGetWidth (ic_image); - surface->height = IcImageGetHeight (ic_image); - surface->stride = IcImageGetStride (ic_image); - surface->depth = IcImageGetDepth (ic_image); + surface->width = pixman_image_get_width (pixman_image); + surface->height = pixman_image_get_height (pixman_image); + surface->stride = pixman_image_get_stride (pixman_image); + surface->depth = pixman_image_get_depth (pixman_image); return surface; } @@ -76,47 +76,47 @@ _cairo_image_surface_create_with_masks (char *data, int stride) { cairo_image_surface_t *surface; - IcFormat *ic_format; - IcImage *ic_image; + pixman_format_t *pixman_format; + pixman_image_t *pixman_image; - ic_format = IcFormatCreateMasks (format->bpp, - format->alpha_mask, - format->red_mask, - format->green_mask, - format->blue_mask); + pixman_format = pixman_format_create_masks (format->bpp, + format->alpha_mask, + format->red_mask, + format->green_mask, + format->blue_mask); - if (ic_format == NULL) + if (pixman_format == NULL) return NULL; - ic_image = IcImageCreateForData ((IcBits *) data, ic_format, - width, height, format->bpp, stride); + pixman_image = pixman_image_create_for_data ((pixman_bits_t *) data, pixman_format, + width, height, format->bpp, stride); - IcFormatDestroy (ic_format); + pixman_format_destroy (pixman_format); - if (ic_image == NULL) + if (pixman_image == NULL) return NULL; - surface = _cairo_image_surface_create_for_ic_image (ic_image); + surface = _cairo_image_surface_create_for_pixman_image (pixman_image); return surface; } -static IcFormat * -_create_ic_format (cairo_format_t format) +static pixman_format_t * +_create_pixman_format (cairo_format_t format) { switch (format) { case CAIRO_FORMAT_A1: - return IcFormatCreate (IcFormatNameA1); + return pixman_format_create (PIXMAN_FORMAT_NAME_A1); break; case CAIRO_FORMAT_A8: - return IcFormatCreate (IcFormatNameA8); + return pixman_format_create (PIXMAN_FORMAT_NAME_A8); break; case CAIRO_FORMAT_RGB24: - return IcFormatCreate (IcFormatNameRGB24); + return pixman_format_create (PIXMAN_FORMAT_NAME_RG_B24); break; case CAIRO_FORMAT_ARGB32: default: - return IcFormatCreate (IcFormatNameARGB32); + return pixman_format_create (PIXMAN_FORMAT_NAME_AR_GB32); break; } } @@ -127,21 +127,21 @@ cairo_image_surface_create (cairo_format_t format, int height) { cairo_image_surface_t *surface; - IcFormat *ic_format; - IcImage *ic_image; + pixman_format_t *pixman_format; + pixman_image_t *pixman_image; - ic_format = _create_ic_format (format); - if (ic_format == NULL) + pixman_format = _create_pixman_format (format); + if (pixman_format == NULL) return NULL; - ic_image = IcImageCreate (ic_format, width, height); + pixman_image = pixman_image_create (pixman_format, width, height); - IcFormatDestroy (ic_format); + pixman_format_destroy (pixman_format); - if (ic_image == NULL) + if (pixman_image == NULL) return NULL; - surface = _cairo_image_surface_create_for_ic_image (ic_image); + surface = _cairo_image_surface_create_for_pixman_image (pixman_image); return &surface->base; } @@ -154,24 +154,24 @@ cairo_image_surface_create_for_data (char *data, int stride) { cairo_image_surface_t *surface; - IcFormat *ic_format; - IcImage *ic_image; + pixman_format_t *pixman_format; + pixman_image_t *pixman_image; - ic_format = _create_ic_format (format); - if (ic_format == NULL) + pixman_format = _create_pixman_format (format); + if (pixman_format == NULL) return NULL; - ic_image = IcImageCreateForData ((IcBits *) data, ic_format, - width, height, - _cairo_format_bpp (format), - stride); + pixman_image = pixman_image_create_for_data ((pixman_bits_t *) data, pixman_format, + width, height, + _cairo_format_bpp (format), + stride); - IcFormatDestroy (ic_format); + pixman_format_destroy (pixman_format); - if (ic_image == NULL) + if (pixman_image == NULL) return NULL; - surface = _cairo_image_surface_create_for_ic_image (ic_image); + surface = _cairo_image_surface_create_for_pixman_image (pixman_image); return &surface->base; } @@ -190,8 +190,8 @@ _cairo_image_abstract_surface_destroy (void *abstract_surface) { cairo_image_surface_t *surface = abstract_surface; - if (surface->ic_image) - IcImageDestroy (surface->ic_image); + if (surface->pixman_image) + pixman_image_destroy (surface->pixman_image); if (surface->owns_data) { free (surface->data); @@ -247,21 +247,21 @@ cairo_status_t _cairo_image_surface_set_matrix (cairo_image_surface_t *surface, cairo_matrix_t *matrix) { - IcTransform ic_transform; + pixman_transform_t pixman_transform; - ic_transform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); - ic_transform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); - ic_transform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); + pixman_transform.matrix[0][0] = _cairo_fixed_from_double (matrix->m[0][0]); + pixman_transform.matrix[0][1] = _cairo_fixed_from_double (matrix->m[1][0]); + pixman_transform.matrix[0][2] = _cairo_fixed_from_double (matrix->m[2][0]); - ic_transform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); - ic_transform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); - ic_transform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); + pixman_transform.matrix[1][0] = _cairo_fixed_from_double (matrix->m[0][1]); + pixman_transform.matrix[1][1] = _cairo_fixed_from_double (matrix->m[1][1]); + pixman_transform.matrix[1][2] = _cairo_fixed_from_double (matrix->m[2][1]); - ic_transform.matrix[2][0] = 0; - ic_transform.matrix[2][1] = 0; - ic_transform.matrix[2][2] = _cairo_fixed_from_double (1); + pixman_transform.matrix[2][0] = 0; + pixman_transform.matrix[2][1] = 0; + pixman_transform.matrix[2][2] = _cairo_fixed_from_double (1); - IcImageSetTransform (surface->ic_image, &ic_transform); + pixman_image_set_transform (surface->pixman_image, &pixman_transform); return CAIRO_STATUS_SUCCESS; } @@ -277,29 +277,29 @@ _cairo_image_abstract_surface_set_filter (void *abstract_surface, cairo_filter_t cairo_status_t _cairo_image_surface_set_filter (cairo_image_surface_t *surface, cairo_filter_t filter) { - IcFilter ic_filter; + pixman_filter_t pixman_filter; switch (filter) { case CAIRO_FILTER_FAST: - ic_filter = IcFilterFast; + pixman_filter = PIXMAN_FILTER_FAST; break; case CAIRO_FILTER_GOOD: - ic_filter = IcFilterGood; + pixman_filter = PIXMAN_FILTER_GOOD; break; case CAIRO_FILTER_BEST: - ic_filter = IcFilterBest; + pixman_filter = PIXMAN_FILTER_BEST; break; case CAIRO_FILTER_NEAREST: - ic_filter = IcFilterNearest; + pixman_filter = PIXMAN_FILTER_NEAREST; break; case CAIRO_FILTER_BILINEAR: - ic_filter = IcFilterBilinear; + pixman_filter = PIXMAN_FILTER_BILINEAR; break; default: - ic_filter = IcFilterBest; + pixman_filter = PIXMAN_FILTER_BEST; } - IcImageSetFilter (surface->ic_image, ic_filter); + pixman_image_set_filter (surface->pixman_image, pixman_filter); return CAIRO_STATUS_SUCCESS; } @@ -314,45 +314,45 @@ _cairo_image_abstract_surface_set_repeat (void *abstract_surface, int repeat) cairo_status_t _cairo_image_surface_set_repeat (cairo_image_surface_t *surface, int repeat) { - IcImageSetRepeat (surface->ic_image, repeat); + pixman_image_set_repeat (surface->pixman_image, repeat); return CAIRO_STATUS_SUCCESS; } -static IcOperator -_ic_operator (cairo_operator_t operator) +static pixman_operator_t +_pixman_operator (cairo_operator_t operator) { switch (operator) { case CAIRO_OPERATOR_CLEAR: - return IcOperatorClear; + return PIXMAN_OPERATOR_CLEAR; case CAIRO_OPERATOR_SRC: - return IcOperatorSrc; + return PIXMAN_OPERATOR_SRC; case CAIRO_OPERATOR_DST: - return IcOperatorDst; + return PIXMAN_OPERATOR_DST; case CAIRO_OPERATOR_OVER: - return IcOperatorOver; + return PIXMAN_OPERATOR_OVER; case CAIRO_OPERATOR_OVER_REVERSE: - return IcOperatorOverReverse; + return PIXMAN_OPERATOR_OVER_REVERSE; case CAIRO_OPERATOR_IN: - return IcOperatorIn; + return PIXMAN_OPERATOR_IN; case CAIRO_OPERATOR_IN_REVERSE: - return IcOperatorInReverse; + return PIXMAN_OPERATOR_IN_REVERSE; case CAIRO_OPERATOR_OUT: - return IcOperatorOut; + return PIXMAN_OPERATOR_OUT; case CAIRO_OPERATOR_OUT_REVERSE: - return IcOperatorOutReverse; + return PIXMAN_OPERATOR_OUT_REVERSE; case CAIRO_OPERATOR_ATOP: - return IcOperatorAtop; + return PIXMAN_OPERATOR_ATOP; case CAIRO_OPERATOR_ATOP_REVERSE: - return IcOperatorAtopReverse; + return PIXMAN_OPERATOR_ATOP_REVERSE; case CAIRO_OPERATOR_XOR: - return IcOperatorXor; + return PIXMAN_OPERATOR_XOR; case CAIRO_OPERATOR_ADD: - return IcOperatorAdd; + return PIXMAN_OPERATOR_ADD; case CAIRO_OPERATOR_SATURATE: - return IcOperatorSaturate; + return PIXMAN_OPERATOR_SATURATE; default: - return IcOperatorOver; + return PIXMAN_OPERATOR_OVER; } } @@ -380,14 +380,14 @@ _cairo_image_surface_composite (cairo_operator_t operator, return CAIRO_INT_STATUS_UNSUPPORTED; } - IcComposite (_ic_operator (operator), - src->ic_image, - mask ? mask->ic_image : NULL, - dst->ic_image, - src_x, src_y, - mask_x, mask_y, - 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; } @@ -401,16 +401,16 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, { cairo_image_surface_t *surface = abstract_surface; - IcColor ic_color; + pixman_color_t pixman_color; - ic_color.red = color->red_short; - ic_color.green = color->green_short; - ic_color.blue = color->blue_short; - ic_color.alpha = color->alpha_short; + pixman_color.red = color->red_short; + pixman_color.green = color->green_short; + pixman_color.blue = color->blue_short; + pixman_color.alpha = color->alpha_short; - /* XXX: The IcRectangle cast is evil... it needs to go away somehow. */ - IcFillRectangles (_ic_operator(operator), surface->ic_image, - &ic_color, (IcRectangle *) rects, num_rects); + /* XXX: The pixman_rectangle_t cast is evil... it needs to go away somehow. */ + pixman_fill_rectangles (_pixman_operator(operator), surface->pixman_image, + &pixman_color, (pixman_rectangle_t *) rects, num_rects); return CAIRO_STATUS_SUCCESS; } @@ -430,9 +430,9 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t operator, if (generic_src->backend != dst->base.backend) return CAIRO_INT_STATUS_UNSUPPORTED; - /* XXX: The IcTrapezoid cast is evil and needs to go away somehow. */ - IcCompositeTrapezoids (operator, src->ic_image, dst->ic_image, - x_src, y_src, (IcTrapezoid *) traps, num_traps); + /* 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); return CAIRO_STATUS_SUCCESS; } @@ -449,6 +449,44 @@ _cairo_image_surface_show_page (void *abstract_surface) return CAIRO_INT_STATUS_UNSUPPORTED; } +static cairo_int_status_t +_cairo_image_abstract_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + cairo_image_surface_t *surface = (cairo_image_surface_t *) abstract_surface; + + return _cairo_image_surface_set_clip_region (surface, region); +} + +cairo_int_status_t +_cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, + pixman_region16_t *region) +{ + pixman_image_set_clip_region (surface->pixman_image, region); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_image_abstract_surface_create_pattern (void *abstract_surface, + cairo_pattern_t *pattern, + cairo_box_t *box) +{ + 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; +} + static const cairo_surface_backend_t cairo_image_surface_backend = { _cairo_image_surface_create_similar, _cairo_image_abstract_surface_destroy, @@ -462,5 +500,7 @@ static const cairo_surface_backend_t cairo_image_surface_backend = { _cairo_image_surface_fill_rectangles, _cairo_image_surface_composite_trapezoids, _cairo_image_surface_copy_page, - _cairo_image_surface_show_page + _cairo_image_surface_show_page, + _cairo_image_abstract_surface_set_clip_region, + _cairo_image_abstract_surface_create_pattern }; diff --git a/src/cairo_matrix.c b/src/cairo_matrix.c index 655bcde8..de89c0fd 100644 --- a/src/cairo_matrix.c +++ b/src/cairo_matrix.c @@ -386,3 +386,22 @@ _cairo_matrix_compute_eigen_values (cairo_matrix_t *matrix, double *lambda1, dou return CAIRO_STATUS_SUCCESS; } + +/* Compute the amount that each basis vector is scaled by. */ +cairo_status_t +_cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double *sy) +{ + double x, y; + + x = 1.0; + y = 0.0; + cairo_matrix_transform_distance (matrix, &x, &y); + *sx = sqrt(x*x + y*y); + + x = 0.0; + y = 1.0; + cairo_matrix_transform_distance (matrix, &x, &y); + *sy = sqrt(x*x + y*y); + + return CAIRO_STATUS_SUCCESS; +} diff --git a/src/cairo_path.c b/src/cairo_path.c index 9366394d..91142c8f 100644 --- a/src/cairo_path.c +++ b/src/cairo_path.c @@ -70,6 +70,11 @@ _cairo_path_init (cairo_path_t *path) path->arg_head = NULL; path->arg_tail = NULL; + + path->current_point.x = 0; + path->current_point.y = 0; + path->has_current_point = 0; + path->last_move_point = path->current_point; } cairo_status_t @@ -79,6 +84,9 @@ _cairo_path_init_copy (cairo_path_t *path, cairo_path_t *other) cairo_path_arg_buf_t *arg, *other_arg; _cairo_path_init (path); + path->current_point = other->current_point; + path->has_current_point = other->has_current_point; + path->last_move_point = other->last_move_point; for (other_op = other->op_head; other_op; other_op = other_op->next) { op = _cairo_path_op_buf_create (); @@ -120,54 +128,131 @@ _cairo_path_fini (cairo_path_t *path) _cairo_path_arg_buf_destroy (arg); } path->arg_tail = NULL; + + path->has_current_point = 0; +} + +cairo_status_t +_cairo_path_move_to (cairo_path_t *path, cairo_point_t *point) +{ + cairo_status_t status; + + status = _cairo_path_add (path, CAIRO_PATH_OP_MOVE_TO, point, 1); + if (status) + return status; + + path->current_point = *point; + path->has_current_point = 1; + path->last_move_point = path->current_point; + + return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_path_move_to (cairo_path_t *path, double x, double y) +_cairo_path_rel_move_to (cairo_path_t *path, cairo_distance_t *distance) { cairo_point_t point; - point.x = _cairo_fixed_from_double (x); - point.y = _cairo_fixed_from_double (y); + point.x = path->current_point.x + distance->dx; + point.y = path->current_point.y + distance->dy; + + return _cairo_path_move_to (path, &point); +} + +cairo_status_t +_cairo_path_line_to (cairo_path_t *path, cairo_point_t *point) +{ + cairo_status_t status; + + status = _cairo_path_add (path, CAIRO_PATH_OP_LINE_TO, point, 1); + if (status) + return status; - return _cairo_path_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1); + path->current_point = *point; + path->has_current_point = 1; + + return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_path_line_to (cairo_path_t *path, double x, double y) +_cairo_path_rel_line_to (cairo_path_t *path, cairo_distance_t *distance) { cairo_point_t point; - point.x = _cairo_fixed_from_double (x); - point.y = _cairo_fixed_from_double (y); + point.x = path->current_point.x + distance->dx; + point.y = path->current_point.y + distance->dy; - return _cairo_path_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1); + return _cairo_path_line_to (path, &point); } cairo_status_t _cairo_path_curve_to (cairo_path_t *path, - double x1, double y1, - double x2, double y2, - double x3, double y3) + cairo_point_t *p0, + cairo_point_t *p1, + cairo_point_t *p2) { + cairo_status_t status; cairo_point_t point[3]; - point[0].x = _cairo_fixed_from_double (x1); - point[0].y = _cairo_fixed_from_double (y1); + point[0] = *p0; + point[1] = *p1; + point[2] = *p2; - point[1].x = _cairo_fixed_from_double (x2); - point[1].y = _cairo_fixed_from_double (y2); + status = _cairo_path_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); + if (status) + return status; - point[2].x = _cairo_fixed_from_double (x3); - point[2].y = _cairo_fixed_from_double (y3); + path->current_point = *p2; + path->has_current_point = 1; - return _cairo_path_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_path_rel_curve_to (cairo_path_t *path, + cairo_distance_t *d0, + cairo_distance_t *d1, + cairo_distance_t *d2) +{ + cairo_point_t p0, p1, p2; + + p0.x = path->current_point.x + d0->dx; + p0.y = path->current_point.y + d0->dy; + + p1.x = path->current_point.x + d1->dx; + p1.y = path->current_point.y + d1->dy; + + p2.x = path->current_point.x + d2->dx; + p2.y = path->current_point.y + d2->dy; + + return _cairo_path_curve_to (path, &p0, &p1, &p2); } cairo_status_t _cairo_path_close_path (cairo_path_t *path) { - return _cairo_path_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); + cairo_status_t status; + + status = _cairo_path_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0); + if (status) + return status; + + path->current_point.x = path->last_move_point.x; + path->current_point.y = path->last_move_point.y; + path->has_current_point = 1; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_path_current_point (cairo_path_t *path, cairo_point_t *point) +{ + if (! path->has_current_point) + return CAIRO_STATUS_NO_CURRENT_POINT; + + *point = path->current_point; + + return CAIRO_STATUS_SUCCESS; } static cairo_status_t @@ -320,7 +405,13 @@ static int const num_args[] = }; cairo_status_t -_cairo_path_interpret (cairo_path_t *path, cairo_direction_t dir, const cairo_path_callbacks_t *cb, void *closure) +_cairo_path_interpret (cairo_path_t *path, + cairo_direction_t dir, + cairo_path_move_to_func_t *move_to, + cairo_path_line_to_func_t *line_to, + cairo_path_curve_to_func_t *curve_to, + cairo_path_close_path_func_t *close_path, + void *closure) { cairo_status_t status; int i, arg; @@ -329,10 +420,6 @@ _cairo_path_interpret (cairo_path_t *path, cairo_direction_t dir, const cairo_pa cairo_path_arg_buf_t *arg_buf = path->arg_head; int buf_i = 0; cairo_point_t point[CAIRO_PATH_OP_MAX_ARGS]; - cairo_point_t current = {0, 0}; - cairo_point_t first = {0, 0}; - int has_current = 0; - int has_edge = 0; int step = (dir == CAIRO_DIRECTION_FORWARD) ? 1 : -1; for (op_buf = (dir == CAIRO_DIRECTION_FORWARD) ? path->op_head : path->op_tail; @@ -374,62 +461,24 @@ _cairo_path_interpret (cairo_path_t *path, cairo_direction_t dir, const cairo_pa switch (op) { case CAIRO_PATH_OP_MOVE_TO: - if (has_edge) { - status = (*cb->done_sub_path) (closure, CAIRO_SUB_PATH_DONE_CAP); - if (status) - return status; - } - first = point[0]; - current = point[0]; - has_current = 1; - has_edge = 0; + status = (*move_to) (closure, &point[0]); break; case CAIRO_PATH_OP_LINE_TO: - if (has_current) { - status = (*cb->add_edge) (closure, ¤t, &point[0]); - if (status) - return status; - current = point[0]; - has_edge = 1; - } else { - first = point[0]; - current = point[0]; - has_current = 1; - has_edge = 0; - } + status = (*line_to) (closure, &point[0]); break; case CAIRO_PATH_OP_CURVE_TO: - if (has_current) { - status = (*cb->add_spline) (closure, ¤t, &point[0], &point[1], &point[2]); - if (status) - return status; - current = point[2]; - has_edge = 1; - } else { - first = point[2]; - current = point[2]; - has_current = 1; - has_edge = 0; - } + status = (*curve_to) (closure, &point[0], &point[1], &point[2]); break; case CAIRO_PATH_OP_CLOSE_PATH: - if (has_edge) { - (*cb->add_edge) (closure, ¤t, &first); - (*cb->done_sub_path) (closure, CAIRO_SUB_PATH_DONE_JOIN); - } - current.x = 0; - current.y = 0; - first.x = 0; - first.y = 0; - has_current = 0; - has_edge = 0; + default: + status = (*close_path) (closure); break; } + if (status) + return status; } } - if (has_edge) - (*cb->done_sub_path) (closure, CAIRO_SUB_PATH_DONE_CAP); - return (*cb->done_path) (closure); + return CAIRO_STATUS_SUCCESS; } diff --git a/src/cairo_path_bounds.c b/src/cairo_path_bounds.c index 40b64c3d..6a02b9ac 100644 --- a/src/cairo_path_bounds.c +++ b/src/cairo_path_bounds.c @@ -46,18 +46,19 @@ static cairo_status_t _cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, cairo_point_t *point); static cairo_status_t -_cairo_path_bounder_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2); +_cairo_path_bounder_move_to (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_path_bounder_add_spline (void *closure, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d); +_cairo_path_bounder_line_to (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_path_bounder_done_sub_path (void *closure, cairo_sub_path_done_t done); +_cairo_path_bounder_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d); static cairo_status_t -_cairo_path_bounder_done_path (void *closure); +_cairo_path_bounder_close_path (void *closure); static void _cairo_path_bounder_init (cairo_path_bounder_t *bounder) @@ -99,39 +100,42 @@ _cairo_path_bounder_add_point (cairo_path_bounder_t *bounder, cairo_point_t *poi } static cairo_status_t -_cairo_path_bounder_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2) +_cairo_path_bounder_move_to (void *closure, cairo_point_t *point) { cairo_path_bounder_t *bounder = closure; - _cairo_path_bounder_add_point (bounder, p1); - _cairo_path_bounder_add_point (bounder, p2); + _cairo_path_bounder_add_point (bounder, point); return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_path_bounder_add_spline (void *closure, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d) +_cairo_path_bounder_line_to (void *closure, cairo_point_t *point) { cairo_path_bounder_t *bounder = closure; - _cairo_path_bounder_add_point (bounder, a); - _cairo_path_bounder_add_point (bounder, b); - _cairo_path_bounder_add_point (bounder, c); - _cairo_path_bounder_add_point (bounder, d); + _cairo_path_bounder_add_point (bounder, point); return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_path_bounder_done_sub_path (void *closure, cairo_sub_path_done_t done) +_cairo_path_bounder_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d) { + cairo_path_bounder_t *bounder = closure; + + _cairo_path_bounder_add_point (bounder, b); + _cairo_path_bounder_add_point (bounder, c); + _cairo_path_bounder_add_point (bounder, d); + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_path_bounder_done_path (void *closure) +_cairo_path_bounder_close_path (void *closure) { return CAIRO_STATUS_SUCCESS; } @@ -141,18 +145,17 @@ cairo_status_t _cairo_path_bounds (cairo_path_t *path, double *x1, double *y1, double *x2, double *y2) { cairo_status_t status; - static cairo_path_callbacks_t const cb = { - _cairo_path_bounder_add_edge, - _cairo_path_bounder_add_spline, - _cairo_path_bounder_done_sub_path, - _cairo_path_bounder_done_path - }; cairo_path_bounder_t bounder; _cairo_path_bounder_init (&bounder); - status = _cairo_path_interpret (path, CAIRO_DIRECTION_FORWARD, &cb, &bounder); + status = _cairo_path_interpret (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to, + _cairo_path_bounder_close_path, + &bounder); if (status) { *x1 = *y1 = *x2 = *y2 = 0.0; _cairo_path_bounder_fini (&bounder); diff --git a/src/cairo_path_fill.c b/src/cairo_path_fill.c index 8f34af85..fba5bb09 100644 --- a/src/cairo_path_fill.c +++ b/src/cairo_path_fill.c @@ -31,6 +31,8 @@ typedef struct cairo_filler { cairo_gstate_t *gstate; cairo_traps_t *traps; + cairo_point_t current_point; + cairo_polygon_t polygon; } cairo_filler_t; @@ -41,18 +43,19 @@ static void _cairo_filler_fini (cairo_filler_t *filler); static cairo_status_t -_cairo_filler_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2); +_cairo_filler_move_to (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_filler_add_spline (void *closure, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d); +_cairo_filler_line_to (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_filler_done_sub_path (void *closure, cairo_sub_path_done_t done); +_cairo_filler_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d); static cairo_status_t -_cairo_filler_done_path (void *closure); +_cairo_filler_close_path (void *closure); static void _cairo_filler_init (cairo_filler_t *filler, cairo_gstate_t *gstate, cairo_traps_t *traps) @@ -60,6 +63,9 @@ _cairo_filler_init (cairo_filler_t *filler, cairo_gstate_t *gstate, cairo_traps_ filler->gstate = gstate; filler->traps = traps; + filler->current_point.x = 0; + filler->current_point.y = 0; + _cairo_polygon_init (&filler->polygon); } @@ -70,18 +76,46 @@ _cairo_filler_fini (cairo_filler_t *filler) } static cairo_status_t -_cairo_filler_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2) +_cairo_filler_move_to (void *closure, cairo_point_t *point) { + cairo_status_t status; cairo_filler_t *filler = closure; cairo_polygon_t *polygon = &filler->polygon; - return _cairo_polygon_add_edge (polygon, p1, p2); + status = _cairo_polygon_close (polygon); + if (status) + return status; + + status = _cairo_polygon_move_to (polygon, point); + if (status) + return status; + + filler->current_point = *point; + + return CAIRO_STATUS_SUCCESS; } static cairo_status_t -_cairo_filler_add_spline (void *closure, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d) +_cairo_filler_line_to (void *closure, cairo_point_t *point) +{ + cairo_status_t status; + cairo_filler_t *filler = closure; + cairo_polygon_t *polygon = &filler->polygon; + + status = _cairo_polygon_line_to (polygon, point); + if (status) + return status; + + filler->current_point = *point; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_filler_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d) { int i; cairo_status_t status = CAIRO_STATUS_SUCCESS; @@ -90,7 +124,8 @@ _cairo_filler_add_spline (void *closure, cairo_gstate_t *gstate = filler->gstate; cairo_spline_t spline; - status = _cairo_spline_init (&spline, a, b, c, d); + status = _cairo_spline_init (&spline, &filler->current_point, b, c, d); + if (status == CAIRO_INT_STATUS_DEGENERATE) return CAIRO_STATUS_SUCCESS; @@ -98,65 +133,65 @@ _cairo_filler_add_spline (void *closure, if (status) goto CLEANUP_SPLINE; - for (i = 0; i < spline.num_points - 1; i++) { - status = _cairo_polygon_add_edge (polygon, &spline.points[i], &spline.points[i+1]); + for (i = 1; i < spline.num_points; i++) { + status = _cairo_polygon_line_to (polygon, &spline.points[i]); if (status) - goto CLEANUP_SPLINE; + break; } CLEANUP_SPLINE: _cairo_spline_fini (&spline); + filler->current_point = *d; + return status; } static cairo_status_t -_cairo_filler_done_sub_path (void *closure, cairo_sub_path_done_t done) +_cairo_filler_close_path (void *closure) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_status_t status; cairo_filler_t *filler = closure; cairo_polygon_t *polygon = &filler->polygon; - _cairo_polygon_close (polygon); - - return status; -} - -static cairo_status_t -_cairo_filler_done_path (void *closure) -{ - cairo_filler_t *filler = closure; + status = _cairo_polygon_close (polygon); + if (status) + return status; - return _cairo_traps_tessellate_polygon (filler->traps, - &filler->polygon, - filler->gstate->fill_rule); + return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_path_fill_to_traps (cairo_path_t *path, cairo_gstate_t *gstate, cairo_traps_t *traps) { - static const cairo_path_callbacks_t filler_callbacks = { - _cairo_filler_add_edge, - _cairo_filler_add_spline, - _cairo_filler_done_sub_path, - _cairo_filler_done_path - }; - - cairo_status_t status; + cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_filler_t filler; _cairo_filler_init (&filler, gstate, traps); status = _cairo_path_interpret (path, CAIRO_DIRECTION_FORWARD, - &filler_callbacks, &filler); - if (status) { - _cairo_filler_fini (&filler); - return status; - } + _cairo_filler_move_to, + _cairo_filler_line_to, + _cairo_filler_curve_to, + _cairo_filler_close_path, + &filler); + if (status) + goto BAIL; + status = _cairo_polygon_close (&filler.polygon); + if (status) + goto BAIL; + + status = _cairo_traps_tessellate_polygon (filler.traps, + &filler.polygon, + filler.gstate->fill_rule); + if (status) + goto BAIL; + +BAIL: _cairo_filler_fini (&filler); - return CAIRO_STATUS_SUCCESS; + return status; } diff --git a/src/cairo_path_stroke.c b/src/cairo_path_stroke.c index 35e7b1b7..c2412579 100644 --- a/src/cairo_path_stroke.c +++ b/src/cairo_path_stroke.c @@ -31,11 +31,17 @@ typedef struct cairo_stroker { cairo_gstate_t *gstate; cairo_traps_t *traps; - int have_prev; - int have_first; - int is_first; - cairo_stroke_face_t prev; - cairo_stroke_face_t first; + int has_current_point; + cairo_point_t current_point; + cairo_point_t first_point; + + int has_current_face; + cairo_stroke_face_t current_face; + + int has_first_face; + cairo_stroke_face_t first_face; + + int dashed; int dash_index; int dash_on; double dash_remain; @@ -49,21 +55,22 @@ static void _cairo_stroker_fini (cairo_stroker_t *stroker); static cairo_status_t -_cairo_stroker_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2); +_cairo_stroker_move_to (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_stroker_add_edge_dashed (void *closure, cairo_point_t *p1, cairo_point_t *p2); +_cairo_stroker_line_to (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_stroker_add_spline (void *closure, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d); +_cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point); static cairo_status_t -_cairo_stroker_done_sub_path (void *closure, cairo_sub_path_done_t done); +_cairo_stroker_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d); static cairo_status_t -_cairo_stroker_done_path (void *closure); +_cairo_stroker_close_path (void *closure); static void _translate_point (cairo_point_t *point, cairo_point_t *offset); @@ -89,6 +96,7 @@ _cairo_stroker_start_dash (cairo_stroker_t *stroker) if (++i == gstate->num_dashes) i = 0; } + stroker->dashed = 1; stroker->dash_index = i; stroker->dash_on = on; stroker->dash_remain = gstate->dash[i] - offset; @@ -113,11 +121,15 @@ _cairo_stroker_init (cairo_stroker_t *stroker, cairo_gstate_t *gstate, cairo_tra { stroker->gstate = gstate; stroker->traps = traps; - stroker->have_prev = 0; - stroker->have_first = 0; - stroker->is_first = 1; + + stroker->has_current_point = 0; + stroker->has_current_face = 0; + stroker->has_first_face = 0; + if (gstate->dash) _cairo_stroker_start_dash (stroker); + else + stroker->dashed = 0; } static void @@ -286,10 +298,11 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st outer.x = _cairo_fixed_from_double (mx); outer.y = _cairo_fixed_from_double (my); _cairo_polygon_init (&polygon); - _cairo_polygon_add_edge (&polygon, &in->point, inpt); - _cairo_polygon_add_edge (&polygon, inpt, &outer); - _cairo_polygon_add_edge (&polygon, &outer, outpt); - _cairo_polygon_add_edge (&polygon, outpt, &in->point); + _cairo_polygon_move_to (&polygon, &in->point); + _cairo_polygon_line_to (&polygon, inpt); + _cairo_polygon_line_to (&polygon, &outer); + _cairo_polygon_line_to (&polygon, outpt); + _cairo_polygon_close (&polygon); status = _cairo_traps_tessellate_polygon (stroker->traps, &polygon, CAIRO_FILL_RULE_WINDING); @@ -351,8 +364,6 @@ _cairo_stroker_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f) cairo_point_t occw, ocw; cairo_polygon_t polygon; - _cairo_polygon_init (&polygon); - dx = f->usr_vector.x; dy = f->usr_vector.y; dx *= gstate->line_width / 2.0; @@ -365,10 +376,12 @@ _cairo_stroker_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f) ocw.x = f->cw.x + fvector.dx; ocw.y = f->cw.y + fvector.dy; - _cairo_polygon_add_edge (&polygon, &f->cw, &ocw); - _cairo_polygon_add_edge (&polygon, &ocw, &occw); - _cairo_polygon_add_edge (&polygon, &occw, &f->ccw); - _cairo_polygon_add_edge (&polygon, &f->ccw, &f->cw); + _cairo_polygon_init (&polygon); + _cairo_polygon_move_to (&polygon, &f->cw); + _cairo_polygon_line_to (&polygon, &ocw); + _cairo_polygon_line_to (&polygon, &occw); + _cairo_polygon_line_to (&polygon, &f->ccw); + _cairo_polygon_close (&polygon); status = _cairo_traps_tessellate_polygon (stroker->traps, &polygon, CAIRO_FILL_RULE_WINDING); _cairo_polygon_fini (&polygon); @@ -454,8 +467,9 @@ static cairo_status_t _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, cairo_point_t *p1, cairo_point_t *p2, cairo_stroke_face_t *start, cairo_stroke_face_t *end) { + cairo_status_t status; cairo_gstate_t *gstate = stroker->gstate; - cairo_point_t quad[4]; + cairo_polygon_t polygon; cairo_slope_t slope; if (p1->x == p2->x && p1->y == p2->y) { @@ -473,20 +487,58 @@ _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, cairo_point_t *p1, cairo_ fields from start. */ _compute_face (p2, &slope, gstate, end); - quad[0] = start->cw; - quad[1] = start->ccw; - quad[2] = end->ccw; - quad[3] = end->cw; + /* XXX: I should really check the return value of the + move_to/line_to functions here to catch out of memory + conditions. But since that would be ugly, I'd prefer to add a + status flag to the polygon object that I could check only once + at then end of this sequence, (like we do with cairo_t + already). */ + _cairo_polygon_init (&polygon); + _cairo_polygon_move_to (&polygon, &start->cw); + _cairo_polygon_line_to (&polygon, &start->ccw); + _cairo_polygon_line_to (&polygon, &end->ccw); + _cairo_polygon_line_to (&polygon, &end->cw); + _cairo_polygon_close (&polygon); + + /* XXX: We can't use tessellate_rectangle as the matrix may have + skewed this into a non-rectangular shape. Perhaps it would be + worth checking the matrix for skew so that the common case + could use the faster tessellate_rectangle rather than + tessellate_polygon? */ + status = _cairo_traps_tessellate_polygon (stroker->traps, + &polygon, CAIRO_FILL_RULE_WINDING); + + _cairo_polygon_fini (&polygon); - return _cairo_traps_tessellate_rectangle (stroker->traps, quad); + return status; } static cairo_status_t -_cairo_stroker_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2) +_cairo_stroker_move_to (void *closure, cairo_point_t *point) +{ + cairo_stroker_t *stroker = closure; + + stroker->first_point = *point; + stroker->current_point = *point; + stroker->has_current_point = 1; + + stroker->has_first_face = 0; + stroker->has_current_face = 0; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_stroker_line_to (void *closure, cairo_point_t *point) { cairo_status_t status; cairo_stroker_t *stroker = closure; cairo_stroke_face_t start, end; + cairo_point_t *p1 = &stroker->current_point; + cairo_point_t *p2 = point; + + if (!stroker->has_current_point) + return _cairo_stroker_move_to (stroker, point); if (p1->x == p2->x && p1->y == p2->y) { /* XXX: Need to rethink how this case should be handled, (both @@ -500,19 +552,20 @@ _cairo_stroker_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2) if (status) return status; - if (stroker->have_prev) { - status = _cairo_stroker_join (stroker, &stroker->prev, &start); + if (stroker->has_current_face) { + status = _cairo_stroker_join (stroker, &stroker->current_face, &start); if (status) return status; } else { - stroker->have_prev = 1; - if (stroker->is_first) { - stroker->have_first = 1; - stroker->first = start; + if (!stroker->has_first_face) { + stroker->first_face = start; + stroker->has_first_face = 1; } } - stroker->prev = end; - stroker->is_first = 0; + stroker->current_face = end; + stroker->has_current_face = 1; + + stroker->current_point = *point; return CAIRO_STATUS_SUCCESS; } @@ -521,7 +574,7 @@ _cairo_stroker_add_edge (void *closure, cairo_point_t *p1, cairo_point_t *p2) * Dashed lines. Cap each dash end, join around turns when on */ static cairo_status_t -_cairo_stroker_add_edge_dashed (void *closure, cairo_point_t *p1, cairo_point_t *p2) +_cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_stroker_t *stroker = closure; @@ -532,6 +585,11 @@ _cairo_stroker_add_edge_dashed (void *closure, cairo_point_t *p1, cairo_point_t cairo_point_t fd1, fd2; int first = 1; cairo_stroke_face_t sub_start, sub_end; + cairo_point_t *p1 = &stroker->current_point; + cairo_point_t *p2 = point; + + if (!stroker->has_current_point) + return _cairo_stroker_move_to (stroker, point); dx = _cairo_fixed_to_double (p2->x - p1->x); dy = _cairo_fixed_to_double (p2->y - p1->y); @@ -569,18 +627,18 @@ _cairo_stroker_add_edge_dashed (void *closure, cairo_point_t *p1, cairo_point_t return status; } else { /* - * First in this segment, join to any prev, else + * First in this segment, join to any current_face, else * if at start of sub-path, mark position, else * cap */ - if (stroker->have_prev) { - status = _cairo_stroker_join (stroker, &stroker->prev, &sub_start); + if (stroker->has_current_face) { + status = _cairo_stroker_join (stroker, &stroker->current_face, &sub_start); if (status) return status; } else { - if (stroker->is_first) { - stroker->have_first = 1; - stroker->first = sub_start; + if (!stroker->has_first_face) { + stroker->first_face = sub_start; + stroker->has_first_face = 1; } else { status = _cairo_stroker_cap (stroker, &sub_start); if (status) @@ -600,8 +658,8 @@ _cairo_stroker_add_edge_dashed (void *closure, cairo_point_t *p1, cairo_point_t * Mark previous line face and fix up next time * through */ - stroker->prev = sub_end; - stroker->have_prev = 1; + stroker->current_face = sub_end; + stroker->has_current_face = 1; } } else { /* @@ -609,27 +667,30 @@ _cairo_stroker_add_edge_dashed (void *closure, cairo_point_t *p1, cairo_point_t * and cap if necessary */ if (first) { - if (stroker->have_prev) { - status = _cairo_stroker_cap (stroker, &stroker->prev); + if (stroker->has_current_face) { + status = _cairo_stroker_cap (stroker, &stroker->current_face); if (status) return status; } } if (!remain) - stroker->have_prev = 0; + stroker->has_current_face = 0; } _cairo_stroker_step_dash (stroker, tmp); fd1 = fd2; first = 0; } - stroker->is_first = 0; + + stroker->current_point = *point; + return status; } static cairo_status_t -_cairo_stroker_add_spline (void *closure, - cairo_point_t *a, cairo_point_t *b, - cairo_point_t *c, cairo_point_t *d) +_cairo_stroker_curve_to (void *closure, + cairo_point_t *b, + cairo_point_t *c, + cairo_point_t *d) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_stroker_t *stroker = closure; @@ -638,6 +699,7 @@ _cairo_stroker_add_spline (void *closure, cairo_pen_t pen; cairo_stroke_face_t start, end; cairo_point_t extra_points[4]; + cairo_point_t *a = &stroker->current_point; status = _cairo_spline_init (&spline, a, b, c, d); if (status == CAIRO_INT_STATUS_DEGENERATE) @@ -650,19 +712,18 @@ _cairo_stroker_add_spline (void *closure, _compute_face (a, &spline.initial_slope, gstate, &start); _compute_face (d, &spline.final_slope, gstate, &end); - if (stroker->have_prev) { - status = _cairo_stroker_join (stroker, &stroker->prev, &start); + if (stroker->has_current_face) { + status = _cairo_stroker_join (stroker, &stroker->current_face, &start); if (status) return status; } else { - stroker->have_prev = 1; - if (stroker->is_first) { - stroker->have_first = 1; - stroker->first = start; + if (!stroker->has_first_face) { + stroker->first_face = start; + stroker->has_first_face = 1; } } - stroker->prev = end; - stroker->is_first = 0; + stroker->current_face = end; + stroker->has_current_face = 1; extra_points[0] = start.cw; extra_points[0].x -= start.point.x; @@ -690,91 +751,89 @@ _cairo_stroker_add_spline (void *closure, CLEANUP_SPLINE: _cairo_spline_fini (&spline); + stroker->current_point = *d; + return status; } static cairo_status_t -_cairo_stroker_done_sub_path (void *closure, cairo_sub_path_done_t done) +_cairo_stroker_close_path (void *closure) { cairo_status_t status; cairo_stroker_t *stroker = closure; - switch (done) { - case CAIRO_SUB_PATH_DONE_JOIN: - if (stroker->have_first && stroker->have_prev) { - status = _cairo_stroker_join (stroker, &stroker->prev, &stroker->first); - if (status) - return status; - break; - } - /* fall through... */ - case CAIRO_SUB_PATH_DONE_CAP: - if (stroker->have_first) { - cairo_point_t t; - /* The initial cap needs an outward facing vector. Reverse everything */ - stroker->first.usr_vector.x = -stroker->first.usr_vector.x; - stroker->first.usr_vector.y = -stroker->first.usr_vector.y; - stroker->first.dev_vector.dx = -stroker->first.dev_vector.dx; - stroker->first.dev_vector.dy = -stroker->first.dev_vector.dy; - t = stroker->first.cw; - stroker->first.cw = stroker->first.ccw; - stroker->first.ccw = t; - status = _cairo_stroker_cap (stroker, &stroker->first); - if (status) - return status; - } - if (stroker->have_prev) { - status = _cairo_stroker_cap (stroker, &stroker->prev); - if (status) - return status; - } - break; + if (stroker->has_current_point) { + if (stroker->dashed) + status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point); + else + status = _cairo_stroker_line_to (stroker, &stroker->first_point); + if (status) + return status; } - stroker->have_prev = 0; - stroker->have_first = 0; - stroker->is_first = 1; + if (stroker->has_first_face && stroker->has_current_face) { + status = _cairo_stroker_join (stroker, &stroker->current_face, &stroker->first_face); + if (status) + return status; + } - return CAIRO_STATUS_SUCCESS; -} + stroker->has_first_face = 0; + stroker->has_current_face = 0; + stroker->has_current_point = 0; -static cairo_status_t -_cairo_stroker_done_path (void *closure) -{ return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_path_stroke_to_traps (cairo_path_t *path, cairo_gstate_t *gstate, cairo_traps_t *traps) { - static const cairo_path_callbacks_t stroker_solid_cb = { - _cairo_stroker_add_edge, - _cairo_stroker_add_spline, - _cairo_stroker_done_sub_path, - _cairo_stroker_done_path - }; - static const cairo_path_callbacks_t stroker_dashed_cb = { - _cairo_stroker_add_edge_dashed, - _cairo_stroker_add_spline, - _cairo_stroker_done_sub_path, - _cairo_stroker_done_path - }; - const cairo_path_callbacks_t *callbacks = gstate->dash ? &stroker_dashed_cb : &stroker_solid_cb; - - cairo_status_t status; + cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_stroker_t stroker; _cairo_stroker_init (&stroker, gstate, traps); - status = _cairo_path_interpret (path, - CAIRO_DIRECTION_FORWARD, - callbacks, &stroker); - if (status) { - _cairo_stroker_fini (&stroker); - return status; + if (gstate->dash) + status = _cairo_path_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_stroker_move_to, + _cairo_stroker_line_to_dashed, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); + else + status = _cairo_path_interpret (path, + CAIRO_DIRECTION_FORWARD, + _cairo_stroker_move_to, + _cairo_stroker_line_to, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); + if (status) + goto BAIL; + + if (stroker.has_first_face) { + cairo_point_t t; + /* The initial cap needs an outward facing vector. Reverse everything */ + stroker.first_face.usr_vector.x = -stroker.first_face.usr_vector.x; + stroker.first_face.usr_vector.y = -stroker.first_face.usr_vector.y; + stroker.first_face.dev_vector.dx = -stroker.first_face.dev_vector.dx; + stroker.first_face.dev_vector.dy = -stroker.first_face.dev_vector.dy; + t = stroker.first_face.cw; + stroker.first_face.cw = stroker.first_face.ccw; + stroker.first_face.ccw = t; + status = _cairo_stroker_cap (&stroker, &stroker.first_face); + if (status) + goto BAIL; } + if (stroker.has_current_face) { + status = _cairo_stroker_cap (&stroker, &stroker.current_face); + if (status) + goto BAIL; + } + +BAIL: _cairo_stroker_fini (&stroker); - return CAIRO_STATUS_SUCCESS; + return status; } diff --git a/src/cairo_pattern.c b/src/cairo_pattern.c new file mode 100644 index 00000000..dcdb1566 --- /dev/null +++ b/src/cairo_pattern.c @@ -0,0 +1,716 @@ +/* + * Copyright © 2002 University of Southern California + * + * 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 the + * University of Southern California not be used in advertising or + * publicity pertaining to distribution of the software without + * specific, written prior permission. The University of Southern + * California makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF + * SOUTHERN CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <c99drn@cs.umu.se> + */ + +#include "cairoint.h" + +void +_cairo_pattern_init (cairo_pattern_t *pattern) +{ + pattern->ref_count = 1; + + pattern->extend = CAIRO_EXTEND_DEFAULT; + pattern->filter = CAIRO_FILTER_DEFAULT; + + _cairo_color_init (&pattern->color); + + _cairo_matrix_init (&pattern->matrix); + + pattern->stops = NULL; + pattern->n_stops = 0; + + pattern->type = CAIRO_PATTERN_SOLID; + + pattern->source = NULL; + pattern->source_offset.x = 0.0; + pattern->source_offset.y = 0.0; +} + +cairo_status_t +_cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other) +{ + *pattern = *other; + + pattern->ref_count = 1; + + 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); + } + + if (pattern->source) + cairo_surface_reference (other->source); + + if (pattern->type == CAIRO_PATTERN_SURFACE) + cairo_surface_reference (other->u.surface.surface); + + return CAIRO_STATUS_SUCCESS; +} + +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); + } + + if (pattern->source) + cairo_surface_destroy (pattern->source); +} + +void +_cairo_pattern_init_solid (cairo_pattern_t *pattern, + double red, double green, double blue) +{ + _cairo_pattern_init (pattern); + + pattern->type = CAIRO_PATTERN_SOLID; + _cairo_color_set_rgb (&pattern->color, red, green, blue); +} + +cairo_pattern_t * +_cairo_pattern_create_solid (double red, double green, double blue) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_pattern_t)); + if (pattern == NULL) + return NULL; + + _cairo_pattern_init_solid (pattern, red, green, blue); + + return pattern; +} + +cairo_pattern_t * +cairo_pattern_create_for_surface (cairo_surface_t *surface) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_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); + + return pattern; +} + +cairo_pattern_t * +cairo_pattern_create_linear (double x0, double y0, double x1, double y1) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_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; + + return pattern; +} + +cairo_pattern_t * +cairo_pattern_create_radial (double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1) +{ + cairo_pattern_t *pattern; + + pattern = malloc (sizeof (cairo_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.dx = radius0; + pattern->u.radial.radius0.dy = radius0; + pattern->u.radial.center1.x = cx1; + pattern->u.radial.center1.y = cy1; + pattern->u.radial.radius1.dx = radius1; + pattern->u.radial.radius1.dy = radius1; + + return pattern; +} + +void +cairo_pattern_reference (cairo_pattern_t *pattern) +{ + if (pattern == NULL) + return; + + pattern->ref_count++; +} + +void +cairo_pattern_destroy (cairo_pattern_t *pattern) +{ + if (pattern == NULL) + return; + + pattern->ref_count--; + if (pattern->ref_count) + return; + + _cairo_pattern_fini (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) +{ + cairo_color_stop_t *stop; + + _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); + if (pattern->stops == NULL) { + pattern->n_stops = 0; + + return CAIRO_STATUS_NO_MEMORY; + } + + stop = &pattern->stops[pattern->n_stops - 1]; + + stop->offset = offset; + stop->id = pattern->n_stops; + _cairo_color_init (&stop->color); + _cairo_color_set_rgb (&stop->color, red, green, blue); + _cairo_color_set_alpha (&stop->color, alpha); + stop->color_char[0] = stop->color.red_short / 256; + stop->color_char[1] = stop->color.green_short / 256; + stop->color_char[2] = stop->color.blue_short / 256; + stop->color_char[3] = stop->color.alpha_short / 256; + + /* sort stops in ascending order */ + qsort (pattern->stops, pattern->n_stops, sizeof (cairo_color_stop_t), + _cairo_pattern_stop_compare); + + return CAIRO_STATUS_SUCCESS; +} + +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; +} + +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; +} + +cairo_status_t +cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter) +{ + pattern->filter = filter; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_filter_t +cairo_pattern_get_filter (cairo_pattern_t *pattern) +{ + return pattern->filter; +} + +cairo_status_t +cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) +{ + pattern->extend = extend; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_extend_t +cairo_pattern_get_extend (cairo_pattern_t *pattern) +{ + return pattern->extend; +} + +cairo_status_t +_cairo_pattern_get_rgb (cairo_pattern_t *pattern, + double *red, double *green, double *blue) +{ + _cairo_color_get_rgb (&pattern->color, red, green, blue); + + return CAIRO_STATUS_SUCCESS; +} + +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++) { + cairo_color_stop_t *stop = &pattern->stops[i]; + + _cairo_color_set_alpha (&stop->color, stop->color.alpha * alpha); + + stop->color_char[0] = stop->color.red_short / 256; + stop->color_char[1] = stop->color.green_short / 256; + stop->color_char[2] = stop->color.blue_short / 256; + stop->color_char[3] = stop->color.alpha_short / 256; + } +} + +void +_cairo_pattern_add_source_offset (cairo_pattern_t *pattern, + double x, double y) +{ + pattern->source_offset.x += x; + pattern->source_offset.y += y; +} + +void +_cairo_pattern_transform (cairo_pattern_t *pattern, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse) +{ + cairo_matrix_t matrix; + + switch (pattern->type) { + case CAIRO_PATTERN_SURFACE: + /* hmm, maybe we should instead multiply with the inverse of the + pattern matrix here? */ + cairo_matrix_multiply (&pattern->matrix, ctm_inverse, + &pattern->matrix); + break; + case CAIRO_PATTERN_LINEAR: + cairo_matrix_multiply (&matrix, &pattern->matrix, ctm); + cairo_matrix_transform_point (&matrix, + &pattern->u.linear.point0.x, + &pattern->u.linear.point0.y); + cairo_matrix_transform_point (&matrix, + &pattern->u.linear.point1.x, + &pattern->u.linear.point1.y); + break; + case CAIRO_PATTERN_RADIAL: + cairo_matrix_multiply (&matrix, &pattern->matrix, ctm); + cairo_matrix_transform_point (&matrix, + &pattern->u.radial.center0.x, + &pattern->u.radial.center0.y); + cairo_matrix_transform_distance (&matrix, + &pattern->u.radial.radius0.dx, + &pattern->u.radial.radius0.dy); + cairo_matrix_transform_point (&matrix, + &pattern->u.radial.center1.x, + &pattern->u.radial.center1.y); + cairo_matrix_transform_distance (&matrix, + &pattern->u.radial.radius1.dx, + &pattern->u.radial.radius1.dy); + break; + case CAIRO_PATTERN_SOLID: + break; + } +} + +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); +} + +typedef void (*cairo_shader_function_t) (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color); + +#define INTERPOLATE_COLOR_NEAREST(c1, c2, factor) \ + ((unsigned char) ((factor < 0.5)? c1: c2)) + +static void +_cairo_pattern_shader_nearest (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color) +{ + result_color[0] = INTERPOLATE_COLOR_NEAREST (color0[0], color1[0], factor); + result_color[1] = INTERPOLATE_COLOR_NEAREST (color0[1], color1[1], factor); + result_color[2] = INTERPOLATE_COLOR_NEAREST (color0[2], color1[2], factor); + result_color[3] = INTERPOLATE_COLOR_NEAREST (color0[3], color1[3], factor); +} + +#undef INTERPOLATE_COLOR_NEAREST + +#define INTERPOLATE_COLOR_LINEAR(c1, c2, factor) \ + ((unsigned char) ((c2 * factor) + (c1 * (1.0 - factor)))) + +static void +_cairo_pattern_shader_linear (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color) +{ + result_color[0] = INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor); + result_color[1] = INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor); + result_color[2] = INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor); + result_color[3] = INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor); +} + +static void +_cairo_pattern_shader_gaussian (unsigned char *color0, + unsigned char *color1, + double factor, + unsigned char *result_color) +{ + factor = (exp (factor * factor) - 1.0) / (M_E - 1.0); + + result_color[0] = INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor); + result_color[1] = INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor); + result_color[2] = INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor); + result_color[3] = INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor); +} + +#undef INTERPOLATE_COLOR_LINEAR + +void +_cairo_pattern_calc_color_at_pixel (cairo_pattern_t *pattern, + double factor, + int *pixel) +{ + int p, colorstop; + double factorscale; + unsigned char result_color[4]; + cairo_shader_function_t shader_function; + + switch (pattern->filter) { + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + shader_function = _cairo_pattern_shader_nearest; + break; + case CAIRO_FILTER_GAUSSIAN: + shader_function = _cairo_pattern_shader_gaussian; + break; + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + shader_function = _cairo_pattern_shader_linear; + break; + } + + if (factor > 1.0 || factor < 0.0) { + switch (pattern->extend) { + case CAIRO_EXTEND_REPEAT: + factor -= floor (factor); + break; + case CAIRO_EXTEND_REFLECT: + if (factor >= 0.0) { + if (((int) factor) % 2) + factor = 1.0 - (factor - floor (factor)); + else + factor -= floor (factor); + } else { + if (((int) factor) % 2) + factor -= floor (factor); + else + factor = 1.0 - (factor - floor (factor)); + } + break; + case CAIRO_EXTEND_NONE: + break; + } + } + + if (factor < pattern->stops[0].offset) + factor = pattern->stops[0].offset; + + if (factor > pattern->stops[pattern->n_stops - 1].offset) + factor = pattern->stops[pattern->n_stops - 1].offset; + + for (colorstop = 0; colorstop < pattern->n_stops - 1; colorstop++) { + if (factor <= pattern->stops[colorstop + 1].offset) { + factorscale = fabs (pattern->stops[colorstop].offset - + pattern->stops[colorstop + 1].offset); + + /* abrubt change, difference between two offsets == 0.0 */ + if (factorscale == 0) + break; + + factor -= pattern->stops[colorstop].offset; + + /* take offset as new 0 of coordinate system */ + factor /= factorscale; + + shader_function (pattern->stops[colorstop].color_char, + pattern->stops[colorstop + 1].color_char, + factor, result_color); + + p = ((result_color[3] << 24) | + (result_color[0] << 16) | + (result_color[1] << 8) | (result_color[2] << 0)); + *pixel = p; + break; + } + } +} + +static void +_cairo_image_data_set_linear (cairo_pattern_t *pattern, + double offset_x, + double offset_y, + char *data, + int width, + int height) +{ + int x, y; + cairo_point_double_t point0, point1, angle; + double a, length, start, end; + double factor; + + point0.x = pattern->u.linear.point0.x - offset_x; + point0.y = pattern->u.linear.point0.y - offset_y; + point1.x = pattern->u.linear.point1.x - offset_x; + point1.y = pattern->u.linear.point1.y - offset_y; + + length = sqrt ((point1.x - point0.x) * (point1.x - point0.x) + + (point1.y - point0.y) * (point1.y - point0.y)); + length = (length) ? 1.0 / length : INT_MAX; + + a = -atan2 (point1.y - point0.y, point1.x - point0.x); + angle.x = cos (a); + angle.y = -sin (a); + + start = angle.x * point0.x; + start += angle.y * point0.y; + + end = angle.x * point1.x; + end += angle.y * point1.y; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + + factor = angle.x * (double) x; + factor += angle.y * (double) y; + + factor = factor - start; + factor *= length; + + _cairo_pattern_calc_color_at_pixel (pattern, factor, (int *) + &data[y * width * 4 + x * 4]); + } + } +} + +/* TODO: Inner circle is currently ignored. */ +static void +_cairo_image_data_set_radial (cairo_pattern_t *pattern, + double offset_x, + double offset_y, + char *data, + int width, + int height) +{ + int x, y; + cairo_point_double_t center1, pos; + cairo_distance_double_t length; + double factor; + double min_length; + + center1.x = pattern->u.radial.center1.x - offset_x; + center1.y = pattern->u.radial.center1.y - offset_y; + + min_length = + fabs ((pattern->u.radial.radius1.dx < pattern->u.radial.radius1.dy) ? + pattern->u.radial.radius1.dx : pattern->u.radial.radius1.dy); + + /* ugly */ + if (min_length == 0.0) + min_length = 0.000001; + + length.dx = min_length / pattern->u.radial.radius1.dx; + length.dy = min_length / pattern->u.radial.radius1.dy; + + min_length = 1.0 / min_length; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + pos.x = x - center1.x; + pos.y = y - center1.y; + + pos.x *= length.dx; + pos.y *= length.dy; + + factor = sqrt (pos.x * pos.x + pos.y * pos.y) * min_length; + + _cairo_pattern_calc_color_at_pixel (pattern, factor, (int *) + &data[y * width * 4 + x * 4]); + } + } +} + +cairo_image_surface_t * +_cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box) +{ + cairo_surface_t *surface; + + switch (pattern->type) { + case CAIRO_PATTERN_LINEAR: + case CAIRO_PATTERN_RADIAL: { + char *data; + int width = ceil (_cairo_fixed_to_double (box->p2.x) - + _cairo_fixed_to_double (box->p1.x)); + int height = ceil (_cairo_fixed_to_double (box->p2.y) - + _cairo_fixed_to_double (box->p1.y)); + + data = malloc (width * height * 4); + if (!data) + return NULL; + + _cairo_pattern_add_source_offset (pattern, + _cairo_fixed_to_double (box->p1.x), + _cairo_fixed_to_double (box->p1.y)); + + if (pattern->type == CAIRO_PATTERN_RADIAL) + _cairo_image_data_set_radial (pattern, + pattern->source_offset.x, + pattern->source_offset.y, + data, width, height); + else + _cairo_image_data_set_linear (pattern, + pattern->source_offset.x, + pattern->source_offset.y, + data, width, height); + + surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_ARGB32, + width, height, + width * 4); + + if (surface) + _cairo_image_surface_assume_ownership_of_data ( + (cairo_image_surface_t *) surface); + } + 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); + } + break; + case CAIRO_PATTERN_SURFACE: { + cairo_image_surface_t *image; + + image = _cairo_surface_get_image (pattern->u.surface.surface); + if (image) + surface = &image->base; + else + surface = NULL; + + } + break; + } + + return (cairo_image_surface_t *) surface; +} diff --git a/src/cairo_pen.c b/src/cairo_pen.c index dd054372..0bb5debd 100644 --- a/src/cairo_pen.c +++ b/src/cairo_pen.c @@ -288,7 +288,7 @@ _cairo_pen_stroke_spline_half (cairo_pen_t *pen, while (i != stop) { hull_point.x = point[i].x + pen->vertices[active].point.x; hull_point.y = point[i].y + pen->vertices[active].point.y; - status = _cairo_polygon_add_point (polygon, &hull_point); + status = _cairo_polygon_line_to (polygon, &hull_point); if (status) return status; diff --git a/src/cairo_png_surface.c b/src/cairo_png_surface.c new file mode 100644 index 00000000..ed7bb039 --- /dev/null +++ b/src/cairo_png_surface.c @@ -0,0 +1,333 @@ +#include <png.h> + +#include "cairoint.h" + +static const cairo_surface_backend_t cairo_png_surface_backend; + +void +cairo_set_target_png (cairo_t *cr, + FILE *file, + cairo_format_t format, + int width, + int height) +{ + cairo_surface_t *surface; + + surface = cairo_png_surface_create (file, format, + width, height); + + 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_png_surface { + cairo_surface_t base; + + /* PNG-specific fields */ + FILE *file; + + png_structp png_w; + png_infop png_i; + + cairo_image_surface_t *image; +} cairo_png_surface_t; + + +static void +_cairo_png_surface_erase (cairo_png_surface_t *surface); + +cairo_surface_t * +cairo_png_surface_create (FILE *file, + cairo_format_t format, + int width, + int height) +{ + cairo_png_surface_t *surface; + time_t now = time (NULL); + png_time png_time; + + if (format == CAIRO_FORMAT_A8 || + format == CAIRO_FORMAT_A1 || + file == NULL) + return NULL; + + surface = malloc (sizeof (cairo_png_surface_t)); + if (surface == NULL) + goto failure; + + _cairo_surface_init (&surface->base, &cairo_png_surface_backend); + surface->png_w = NULL; + surface->png_i = NULL; + + surface->image = (cairo_image_surface_t *) + cairo_image_surface_create (format, width, height); + if (surface->image == NULL) + goto failure; + + _cairo_png_surface_erase (surface); + + surface->file = file; + + surface->png_w = png_create_write_struct (PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + if (surface->png_w == NULL) + goto failure; + surface->png_i = png_create_info_struct (surface->png_w); + if (surface->png_i == NULL) + goto failure; + + if (setjmp (png_jmpbuf (surface->png_w))) + goto failure; + + png_init_io (surface->png_w, surface->file); + + switch (format) { + case CAIRO_FORMAT_ARGB32: + png_set_IHDR (surface->png_w, surface->png_i, + width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + break; + case CAIRO_FORMAT_RGB24: + png_set_IHDR (surface->png_w, surface->png_i, + width, height, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + break; + } + + png_convert_from_time_t (&png_time, now); + png_set_tIME (surface->png_w, surface->png_i, &png_time); + + png_write_info (surface->png_w, surface->png_i); + + switch (format) { + case CAIRO_FORMAT_ARGB32: + png_set_bgr (surface->png_w); + break; + case CAIRO_FORMAT_RGB24: + png_set_filler (surface->png_w, 0, PNG_FILLER_AFTER); + png_set_bgr (surface->png_w); + break; + } + + return &surface->base; + + + failure: + if (surface) { + if (surface->image) + cairo_surface_destroy (&surface->image->base); + if (surface->png_i) + png_destroy_write_struct (&surface->png_w, &surface->png_i); + else if (surface->png_w) + png_destroy_write_struct (&surface->png_w, NULL); + free (surface); + } + return NULL; +} + + +static cairo_surface_t * +_cairo_png_surface_create_similar (void *abstract_src, + cairo_format_t format, + int width, + int height) +{ + return NULL; +} + +static void +_cairo_png_surface_destroy (void *abstract_surface) +{ + cairo_png_surface_t *surface = abstract_surface; + int i; + png_byte *row; + + if (setjmp (png_jmpbuf (surface->png_w))) + goto failure; + + row = surface->image->data; + for (i=0; i < surface->image->height; i++) { + png_write_row (surface->png_w, row); + row += surface->image->stride; + } + + png_write_end (surface->png_w, surface->png_i); + + failure: + png_destroy_write_struct (&surface->png_w, &surface->png_i); + + cairo_surface_destroy (&surface->image->base); + + free (surface); +} + +static void +_cairo_png_surface_erase (cairo_png_surface_t *surface) +{ + cairo_color_t transparent; + + _cairo_color_init (&transparent); + _cairo_color_set_rgb (&transparent, 0., 0., 0.); + _cairo_color_set_alpha (&transparent, 0.); + _cairo_surface_fill_rectangle (&surface->image->base, + CAIRO_OPERATOR_SRC, + &transparent, + 0, 0, + surface->image->width, + surface->image->height); +} + +static double +_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) +{ + cairo_png_surface_t *surface = abstract_surface; + + cairo_surface_reference (&surface->image->base); + + return surface->image; +} + +static cairo_status_t +_cairo_png_surface_set_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + 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_t *surface = abstract_surface; + + return _cairo_image_surface_set_matrix (surface->image, matrix); +} + +static cairo_status_t +_cairo_png_surface_set_filter (void *abstract_surface, + cairo_filter_t filter) +{ + 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_t *surface = abstract_surface; + + return _cairo_image_surface_set_repeat (surface->image, repeat); +} + +static cairo_int_status_t +_cairo_png_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) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_png_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t operator, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_png_surface_composite_trapezoids (cairo_operator_t operator, + cairo_surface_t *generic_src, + void *abstract_dst, + int x_src, + int y_src, + cairo_trapezoid_t *traps, + int num_traps) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_png_surface_copy_page (void *abstract_surface) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_png_surface_show_page (void *abstract_surface) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_png_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + cairo_png_surface_t *surface = 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_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 +}; diff --git a/src/cairo_polygon.c b/src/cairo_polygon.c index 97f074a3..fbda7321 100644 --- a/src/cairo_polygon.c +++ b/src/cairo_polygon.c @@ -35,9 +35,6 @@ static cairo_status_t _cairo_polygon_grow_by (cairo_polygon_t *polygon, int additional); -static void -_cairo_polygon_set_last_point (cairo_polygon_t *polygon, cairo_point_t *point); - void _cairo_polygon_init (cairo_polygon_t *polygon) { @@ -46,10 +43,7 @@ _cairo_polygon_init (cairo_polygon_t *polygon) polygon->edges_size = 0; polygon->edges = NULL; - polygon->first_point_defined = 0; - polygon->last_point_defined = 0; - - polygon->closed = 0; + polygon->has_current_point = 0; } void @@ -62,10 +56,7 @@ _cairo_polygon_fini (cairo_polygon_t *polygon) polygon->num_edges = 0; } - polygon->first_point_defined = 0; - polygon->last_point_defined = 0; - - polygon->closed = 0; + polygon->has_current_point = 0; } static cairo_status_t @@ -92,25 +83,12 @@ _cairo_polygon_grow_by (cairo_polygon_t *polygon, int additional) return CAIRO_STATUS_SUCCESS; } -static void -_cairo_polygon_set_last_point (cairo_polygon_t *polygon, cairo_point_t *point) -{ - polygon->last_point = *point; - polygon->last_point_defined = 1; -} - cairo_status_t _cairo_polygon_add_edge (cairo_polygon_t *polygon, cairo_point_t *p1, cairo_point_t *p2) { cairo_status_t status; cairo_edge_t *edge; - if (! polygon->first_point_defined) { - polygon->first_point = *p1; - polygon->first_point_defined = 1; - polygon->closed = 0; - } - /* drop horizontal edges */ if (p1->y == p2->y) { goto DONE; @@ -137,20 +115,31 @@ _cairo_polygon_add_edge (cairo_polygon_t *polygon, cairo_point_t *p1, cairo_poin polygon->num_edges++; DONE: - _cairo_polygon_set_last_point (polygon, p2); + _cairo_polygon_move_to (polygon, p2); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_polygon_move_to (cairo_polygon_t *polygon, cairo_point_t *point) +{ + if (! polygon->has_current_point) + polygon->first_point = *point; + polygon->current_point = *point; + polygon->has_current_point = 1; return CAIRO_STATUS_SUCCESS; } cairo_status_t -_cairo_polygon_add_point (cairo_polygon_t *polygon, cairo_point_t *point) +_cairo_polygon_line_to (cairo_polygon_t *polygon, cairo_point_t *point) { cairo_status_t status = CAIRO_STATUS_SUCCESS; - if (polygon->last_point_defined) { - status = _cairo_polygon_add_edge (polygon, &polygon->last_point, point); + if (polygon->has_current_point) { + status = _cairo_polygon_add_edge (polygon, &polygon->current_point, point); } else { - _cairo_polygon_set_last_point (polygon, point); + _cairo_polygon_move_to (polygon, point); } return status; @@ -161,13 +150,14 @@ _cairo_polygon_close (cairo_polygon_t *polygon) { cairo_status_t status; - if (polygon->closed == 0 && polygon->last_point_defined) { - status = _cairo_polygon_add_edge (polygon, &polygon->last_point, &polygon->first_point); + if (polygon->has_current_point) { + status = _cairo_polygon_add_edge (polygon, + &polygon->current_point, + &polygon->first_point); if (status) return status; - polygon->closed = 1; - polygon->first_point_defined = 0; + polygon->has_current_point = 0; } return CAIRO_STATUS_SUCCESS; diff --git a/src/cairo_ps_surface.c b/src/cairo_ps_surface.c index c66b7098..98d34e44 100644 --- a/src/cairo_ps_surface.c +++ b/src/cairo_ps_surface.c @@ -286,19 +286,19 @@ _cairo_ps_surface_copy_page (void *abstract_surface) int i, x, y; cairo_surface_t *white_surface; - char *bgr, *compressed; - long bgr_size, compressed_size; + char *rgb, *compressed; + long rgb_size, compressed_size; cairo_color_t white; - bgr_size = 3 * width * height; - bgr = malloc (bgr_size); - if (bgr == NULL) { + rgb_size = 3 * width * height; + rgb = malloc (rgb_size); + if (rgb == NULL) { status = CAIRO_STATUS_NO_MEMORY; goto BAIL0; } - compressed_size = (int) (1.0 + 1.1 * bgr_size); + compressed_size = (int) (1.0 + 1.1 * rgb_size); compressed = malloc (compressed_size); if (compressed == NULL) { status = CAIRO_STATUS_NO_MEMORY; @@ -330,16 +330,15 @@ _cairo_ps_surface_copy_page (void *abstract_surface) i = 0; for (y = 0; y < height; y++) { - char *line = surface->image->data + y * surface->image->stride; - for (x = 0; x < width; x++) { - unsigned char *pixel = (unsigned char *) line + x * 4; - bgr[i++] = *(pixel+2); - bgr[i++] = *(pixel+1); - bgr[i++] = *(pixel); + pixman_bits_t *pixel = (pixman_bits_t *) (surface->image->data + y * surface->image->stride); + for (x = 0; x < width; x++, pixel++) { + rgb[i++] = (*pixel & 0x00ff0000) >> 16; + rgb[i++] = (*pixel & 0x0000ff00) >> 8; + rgb[i++] = (*pixel & 0x000000ff) >> 0; } } - compress (compressed, &compressed_size, bgr, bgr_size); + compress (compressed, &compressed_size, rgb, rgb_size); /* Page header */ fprintf (file, "%%%%Page: %d\n", ++surface->pages); @@ -375,7 +374,7 @@ _cairo_ps_surface_copy_page (void *abstract_surface) BAIL2: free (compressed); BAIL1: - free (bgr); + free (rgb); BAIL0: return status; } @@ -395,6 +394,23 @@ _cairo_ps_surface_show_page (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_ps_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + cairo_ps_surface_t *surface = 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, @@ -408,5 +424,7 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = { _cairo_ps_surface_fill_rectangles, _cairo_ps_surface_composite_trapezoids, _cairo_ps_surface_copy_page, - _cairo_ps_surface_show_page + _cairo_ps_surface_show_page, + _cairo_ps_surface_set_clip_region, + _cairo_ps_surface_create_pattern }; diff --git a/src/cairo_surface.c b/src/cairo_surface.c index ff4f39c1..91ab8aba 100644 --- a/src/cairo_surface.c +++ b/src/cairo_surface.c @@ -38,6 +38,7 @@ _cairo_surface_init (cairo_surface_t *surface, surface->ref_count = 1; _cairo_matrix_init (&surface->matrix); + surface->filter = CAIRO_FILTER_NEAREST; surface->repeat = 0; } @@ -165,9 +166,16 @@ cairo_surface_set_filter (cairo_surface_t *surface, cairo_filter_t filter) if (surface == NULL) return CAIRO_STATUS_NULL_POINTER; + surface->filter = filter; return surface->backend->set_filter (surface, filter); } +cairo_filter_t +cairo_surface_get_filter (cairo_surface_t *surface) +{ + return surface->filter; +} + /* XXX: NYI cairo_status_t cairo_surface_clip_rectangle (cairo_surface_t *surface, @@ -364,3 +372,116 @@ _cairo_surface_show_page (cairo_surface_t *surface) return CAIRO_STATUS_SUCCESS; } +cairo_status_t +_cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region) +{ + 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) { + int width = ceil (_cairo_fixed_to_double (box->p2.x) - + _cairo_fixed_to_double (box->p1.x)); + int height = ceil (_cairo_fixed_to_double (box->p2.y) - + _cairo_fixed_to_double (box->p1.y)); + 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_add_source_offset (pattern, + _cairo_fixed_to_double (box->p1.x), + _cairo_fixed_to_double (box->p1.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 63df3ea4..9b44d38e 100644 --- a/src/cairo_traps.c +++ b/src/cairo_traps.c @@ -58,7 +58,7 @@ _compute_inverse_slope (cairo_line_t *l); static double _compute_x_intercept (cairo_line_t *l, double inverse_slope); -static cairo_fixed_t +static int _line_segs_intersect_ceil (cairo_line_t *left, cairo_line_t *right, cairo_fixed_t *y_ret); void @@ -490,7 +490,7 @@ _line_segs_intersect_ceil (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_ */ cairo_status_t _cairo_traps_tessellate_polygon (cairo_traps_t *traps, - cairo_polygon_t *poly, + cairo_polygon_t *poly, cairo_fill_rule_t fill_rule) { cairo_status_t status; @@ -608,3 +608,40 @@ _cairo_traps_contain (cairo_traps_t *traps, double x, double y) 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; +} + +void +_cairo_traps_extents (cairo_traps_t *traps, cairo_box_t *extents) +{ + int i; + + extents->p1.x = extents->p1.y = SHRT_MAX << 16; + extents->p2.x = extents->p2.y = SHRT_MIN << 16; + + for (i = 0; i < traps->num_traps; i++) + _cairo_trap_extents (&traps->traps[i], extents); +} diff --git a/src/cairo_xcb_surface.c b/src/cairo_xcb_surface.c new file mode 100644 index 00000000..ea1f72e1 --- /dev/null +++ b/src/cairo_xcb_surface.c @@ -0,0 +1,799 @@ +/* + * Copyright © 2002 University of Southern California + * + * 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 the + * University of Southern California not be used in advertising or + * publicity pertaining to distribution of the software without + * specific, written prior permission. The University of Southern + * California makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF + * SOUTHERN CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Carl D. Worth <cworth@isi.edu> + */ + +#include "cairoint.h" + +cairo_surface_t * +cairo_xcb_surface_create (XCBConnection *dpy, + XCBDRAWABLE drawable, + XCBVISUALTYPE *visual, + cairo_format_t format); + +#define AllPlanes ((unsigned long)~0L) + +static XCBRenderPICTFORMAT +format_from_visual(XCBConnection *c, XCBVISUALID visual) +{ + static const XCBRenderPICTFORMAT nil = { 0 }; + XCBRenderQueryPictFormatsRep *r; + XCBRenderPICTSCREENIter si; + XCBRenderPICTDEPTHIter di; + XCBRenderPICTVISUALIter vi; + + r = XCBRenderQueryPictFormatsReply(c, XCBRenderQueryPictFormats(c), 0); + if(!r) + return nil; + + for(si = XCBRenderQueryPictFormatsScreens(r); si.rem; XCBRenderPICTSCREENNext(&si)) + for(di = XCBRenderPICTSCREENDepths(si.data); di.rem; XCBRenderPICTDEPTHNext(&di)) + for(vi = XCBRenderPICTDEPTHVisuals(di.data); vi.rem; XCBRenderPICTVISUALNext(&vi)) + if(vi.data->visual.id == visual.id) + { + XCBRenderPICTFORMAT ret = vi.data->format; + free(r); + return ret; + } + + return nil; +} + +static XCBRenderPICTFORMAT +format_from_cairo(XCBConnection *c, cairo_format_t fmt) +{ + XCBRenderPICTFORMAT ret = { 0 }; + struct tmpl_t { + XCBRenderDIRECTFORMAT direct; + CARD8 depth; + }; + static const struct tmpl_t templates[] = { + /* CAIRO_FORMAT_ARGB32 */ + { + { + 16, 0xff, + 8, 0xff, + 0, 0xff, + 24, 0xff + }, + 32 + }, + /* CAIRO_FORMAT_RGB24 */ + { + { + 16, 0xff, + 8, 0xff, + 0, 0xff, + 0, 0x00 + }, + 24 + }, + /* CAIRO_FORMAT_A8 */ + { + { + 0, 0x00, + 0, 0x00, + 0, 0x00, + 0, 0xff + }, + 8 + }, + /* CAIRO_FORMAT_A1 */ + { + { + 0, 0x00, + 0, 0x00, + 0, 0x00, + 0, 0x01 + }, + 1 + }, + }; + const struct tmpl_t *tmpl; + XCBRenderQueryPictFormatsRep *r; + XCBRenderPICTFORMINFOIter fi; + + if(fmt < 0 || fmt >= (sizeof(templates) / sizeof(*templates))) + return ret; + tmpl = templates + fmt; + + r = XCBRenderQueryPictFormatsReply(c, XCBRenderQueryPictFormats(c), 0); + if(!r) + return ret; + + for(fi = XCBRenderQueryPictFormatsFormats(r); fi.rem; XCBRenderPICTFORMINFONext(&fi)) + { + const XCBRenderDIRECTFORMAT *t, *f; + if(fi.data->type != XCBRenderPictTypeDirect) + continue; + if(fi.data->depth != tmpl->depth) + continue; + t = &tmpl->direct; + f = &fi.data->direct; + if(t->red_mask && (t->red_mask != f->red_mask || t->red_shift != f->red_shift)) + continue; + if(t->green_mask && (t->green_mask != f->green_mask || t->green_shift != f->green_shift)) + continue; + if(t->blue_mask && (t->blue_mask != f->blue_mask || t->blue_shift != f->blue_shift)) + continue; + if(t->alpha_mask && (t->alpha_mask != f->alpha_mask || t->alpha_shift != f->alpha_shift)) + continue; + + ret = fi.data->id; + } + + free(r); + return ret; +} + +void +cairo_set_target_xcb (cairo_t *cr, + XCBConnection *dpy, + XCBDRAWABLE drawable, + XCBVISUALTYPE *visual, + cairo_format_t format) +{ + cairo_surface_t *surface; + + if (cr->status && cr->status != CAIRO_STATUS_NO_TARGET_SURFACE) + return; + + surface = cairo_xcb_surface_create (dpy, drawable, visual, format); + 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_xcb_surface { + cairo_surface_t base; + + XCBConnection *dpy; + XCBGCONTEXT gc; + XCBDRAWABLE drawable; + int owns_pixmap; + XCBVISUALTYPE *visual; + cairo_format_t format; + + int render_major; + int render_minor; + + int width; + int height; + + XCBRenderPICTURE picture; +} cairo_xcb_surface_t; + +#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ + (((surface)->render_major > major) || \ + (((surface)->render_major == major) && ((surface)->render_minor >= minor))) + +#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_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) + +#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) +#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) + +#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) +#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) +#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) +#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) + +#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) +#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) + +static int +_CAIRO_FORMAT_DEPTH (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_A1: + return 1; + case CAIRO_FORMAT_A8: + return 8; + case CAIRO_FORMAT_RGB24: + return 24; + case CAIRO_FORMAT_ARGB32: + default: + return 32; + } +} + +static cairo_surface_t * +_cairo_xcb_surface_create_similar (void *abstract_src, + cairo_format_t format, + int width, + int height) +{ + cairo_xcb_surface_t *src = abstract_src; + XCBConnection *dpy = src->dpy; + XCBDRAWABLE d; + cairo_xcb_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; + } + + d.pixmap = XCBPIXMAPNew (dpy); + XCBCreatePixmap (dpy, _CAIRO_FORMAT_DEPTH (format), + d.pixmap, src->drawable, + width, height); + + surface = (cairo_xcb_surface_t *) + cairo_xcb_surface_create (dpy, d, NULL, format); + surface->owns_pixmap = 1; + + surface->width = width; + surface->height = height; + + return &surface->base; +} + +static void +_cairo_xcb_surface_destroy (void *abstract_surface) +{ + cairo_xcb_surface_t *surface = abstract_surface; + if (surface->picture.xid) + XCBRenderFreePicture (surface->dpy, surface->picture); + + if (surface->owns_pixmap) + XCBFreePixmap (surface->dpy, surface->drawable.pixmap); + + if (surface->gc.xid) + XCBFreeGC (surface->dpy, surface->gc); + + surface->dpy = 0; + + free (surface); +} + +static double +_cairo_xcb_surface_pixels_per_inch (void *abstract_surface) +{ + /* XXX: We should really get this value from somewhere like Xft.dpy */ + return 96.0; +} + +static int +bits_per_pixel(XCBConnection *c, int depth) +{ + XCBFORMAT *fmt = XCBConnSetupSuccessRepPixmapFormats(XCBGetSetup(c)); + XCBFORMAT *fmtend = fmt + XCBConnSetupSuccessRepPixmapFormatsLength(XCBGetSetup(c)); + + for(; fmt != fmtend; ++fmt) + if(fmt->depth == depth) + return fmt->bits_per_pixel; + + if(depth <= 4) + return 4; + if(depth <= 8) + return 8; + if(depth <= 16) + return 16; + return 32; +} + +static int +bytes_per_line(XCBConnection *c, int width, int bpp) +{ + int bitmap_pad = XCBGetSetup(c)->bitmap_format_scanline_pad; + return (bpp * width + bitmap_pad - 1) & -bitmap_pad; +} + +static cairo_image_surface_t * +_cairo_xcb_surface_get_image (void *abstract_surface) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + XCBGetGeometryRep *geomrep; + XCBGetImageRep *imagerep; + int bpp; + + geomrep = XCBGetGeometryReply(surface->dpy, XCBGetGeometry(surface->dpy, surface->drawable), 0); + if(!geomrep) + return 0; + + surface->width = geomrep->width; + surface->height = geomrep->height; + free(geomrep); + + imagerep = XCBGetImageReply(surface->dpy, + XCBGetImage(surface->dpy, ZPixmap, + surface->drawable, + 0, 0, + surface->width, surface->height, + AllPlanes), 0); + if(!imagerep) + return 0; + + bpp = bits_per_pixel(surface->dpy, imagerep->depth); + + if (surface->visual) { + cairo_format_masks_t masks; + + /* XXX: Add support here for pictures with external alpha? */ + + masks.bpp = bpp; + masks.alpha_mask = 0; + masks.red_mask = surface->visual->red_mask; + masks.green_mask = surface->visual->green_mask; + masks.blue_mask = surface->visual->blue_mask; + + image = _cairo_image_surface_create_with_masks (XCBGetImageData(imagerep), + &masks, + surface->width, + surface->height, + 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, + bytes_per_line(surface->dpy, surface->width, bpp)); + } + + /* Let the surface take ownership of the data */ + /* XXX: Can probably come up with a cleaner API here. */ + _cairo_image_surface_assume_ownership_of_data (image); + /* FIXME: imagerep can't be freed correctly, I think. must copy. :-( */ + + _cairo_image_surface_set_repeat (image, surface->base.repeat); + _cairo_image_surface_set_matrix (image, &(surface->base.matrix)); + + return image; +} + +static void +_cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface) +{ + if (surface->gc.xid) + return; + + surface->gc = XCBGCONTEXTNew(surface->dpy); + XCBCreateGC (surface->dpy, surface->gc, surface->drawable, 0, 0); +} + +static cairo_status_t +_cairo_xcb_surface_set_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_xcb_surface_t *surface = abstract_surface; + int bpp, data_len; + + _cairo_xcb_surface_ensure_gc (surface); + bpp = bits_per_pixel(surface->dpy, image->depth); + data_len = bytes_per_line(surface->dpy, surface->width, bpp) * surface->height; + XCBPutImage(surface->dpy, ZPixmap, surface->drawable, surface->gc, + image->width, + image->height, + /* dst_x */ 0, /* dst_y */ 0, + /* left_pad */ 0, image->depth, + data_len, image->data); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_set_matrix (void *abstract_surface, cairo_matrix_t *matrix) +{ + cairo_xcb_surface_t *surface = abstract_surface; + XCBRenderTRANSFORM xtransform; + + if (!surface->picture.xid) + return CAIRO_STATUS_SUCCESS; + + xtransform.matrix11 = _cairo_fixed_from_double (matrix->m[0][0]); + xtransform.matrix12 = _cairo_fixed_from_double (matrix->m[1][0]); + xtransform.matrix13 = _cairo_fixed_from_double (matrix->m[2][0]); + + xtransform.matrix21 = _cairo_fixed_from_double (matrix->m[0][1]); + xtransform.matrix22 = _cairo_fixed_from_double (matrix->m[1][1]); + xtransform.matrix23 = _cairo_fixed_from_double (matrix->m[2][1]); + + xtransform.matrix31 = 0; + xtransform.matrix32 = 0; + xtransform.matrix33 = _cairo_fixed_from_double (1); + + 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 */ + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_set_filter (void *abstract_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))) + return CAIRO_STATUS_SUCCESS; + + switch (filter) { + case CAIRO_FILTER_FAST: + render_filter = "fast"; + break; + case CAIRO_FILTER_GOOD: + render_filter = "good"; + break; + case CAIRO_FILTER_BEST: + render_filter = "best"; + break; + case CAIRO_FILTER_NEAREST: + render_filter = "nearest"; + break; + case CAIRO_FILTER_BILINEAR: + render_filter = "bilinear"; + break; + default: + render_filter = "best"; + break; + } + + XCBRenderSetPictureFilter(surface->dpy, surface->picture, + strlen(render_filter), 0, render_filter, NULL); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xcb_surface_set_repeat (void *abstract_surface, int repeat) +{ + cairo_xcb_surface_t *surface = abstract_surface; + + CARD32 mask = XCBRenderCPRepeat; + CARD32 pa[] = { repeat }; + + if (!surface->picture.xid) + return CAIRO_STATUS_SUCCESS; + + XCBRenderChangePicture (surface->dpy, surface->picture, mask, pa); + + 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) +{ + 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, + 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_xcb_surface_set_matrix (clone, &(src_image->base.matrix)); + + cairo_surface_destroy (&src_image->base); + + return clone; +} + +static int +_render_operator (cairo_operator_t operator) +{ + switch (operator) { + case CAIRO_OPERATOR_CLEAR: + return XCBRenderPictOpClear; + case CAIRO_OPERATOR_SRC: + return XCBRenderPictOpSrc; + case CAIRO_OPERATOR_DST: + return XCBRenderPictOpDst; + case CAIRO_OPERATOR_OVER: + return XCBRenderPictOpOver; + case CAIRO_OPERATOR_OVER_REVERSE: + return XCBRenderPictOpOverReverse; + case CAIRO_OPERATOR_IN: + return XCBRenderPictOpIn; + case CAIRO_OPERATOR_IN_REVERSE: + return XCBRenderPictOpInReverse; + case CAIRO_OPERATOR_OUT: + return XCBRenderPictOpOut; + case CAIRO_OPERATOR_OUT_REVERSE: + return XCBRenderPictOpOutReverse; + case CAIRO_OPERATOR_ATOP: + return XCBRenderPictOpAtop; + case CAIRO_OPERATOR_ATOP_REVERSE: + return XCBRenderPictOpAtopReverse; + case CAIRO_OPERATOR_XOR: + return XCBRenderPictOpXor; + case CAIRO_OPERATOR_ADD: + return XCBRenderPictOpAdd; + case CAIRO_OPERATOR_SATURATE: + return XCBRenderPictOpSaturate; + default: + return XCBRenderPictOpOver; + } +} + +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_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 }; + + + 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; + } + + 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); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xcb_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t operator, + const cairo_color_t *color, + cairo_rectangle_t *rects, + int num_rects) +{ + cairo_xcb_surface_t *surface = abstract_surface; + XCBRenderCOLOR render_color; + + if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE (surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + render_color.red = color->red_short; + render_color.green = color->green_short; + render_color.blue = color->blue_short; + render_color.alpha = color->alpha_short; + + /* XXX: This XCBRECTANGLE cast is evil... it needs to go away somehow. */ + XCBRenderFillRectangles (surface->dpy, + _render_operator (operator), + surface->picture, + render_color, num_rects, (XCBRECTANGLE *) rects); + + return CAIRO_STATUS_SUCCESS; +} + +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_xcb_surface_t *dst = abstract_dst; + cairo_xcb_surface_t *src = (cairo_xcb_surface_t *) generic_src; + cairo_xcb_surface_t *src_clone = NULL; + + 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; + } + + /* 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); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xcb_surface_copy_page (void *abstract_surface) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_xcb_surface_show_page (void *abstract_surface) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_xcb_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + /* FIXME */ + 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 struct cairo_surface_backend 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_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 +}; + +static void +query_render_version (XCBConnection *c, cairo_xcb_surface_t *surface) +{ + XCBRenderQueryVersionRep *r; + + surface->render_major = -1; + surface->render_minor = -1; + + if (!XCBRenderInit(c)) + return; + + r = XCBRenderQueryVersionReply(c, XCBRenderQueryVersion(c, 0, 6), 0); + if (!r) + return; + + surface->render_major = r->major_version; + surface->render_minor = r->minor_version; + free(r); +} + +cairo_surface_t * +cairo_xcb_surface_create (XCBConnection *dpy, + XCBDRAWABLE drawable, + XCBVISUALTYPE *visual, + cairo_format_t format) +{ + cairo_xcb_surface_t *surface; + + surface = malloc (sizeof (cairo_xcb_surface_t)); + if (surface == NULL) + return NULL; + + _cairo_surface_init (&surface->base, &cairo_xcb_surface_backend); + + surface->dpy = dpy; + surface->gc.xid = 0; + surface->drawable = drawable; + surface->owns_pixmap = 0; + surface->visual = visual; + surface->format = format; + + query_render_version(dpy, surface); + + if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) + { + XCBRenderPICTFORMAT fmt; + if(visual) + fmt = format_from_visual (dpy, visual->visual_id); + else + fmt = format_from_cairo (dpy, format); + surface->picture = XCBRenderPICTURENew(dpy); + XCBRenderCreatePicture (dpy, surface->picture, drawable, + fmt, 0, NULL); + } + else + surface->picture.xid = 0; + + return (cairo_surface_t *) surface; +} diff --git a/src/cairo_xlib_surface.c b/src/cairo_xlib_surface.c index 49abdb56..19dfde50 100644 --- a/src/cairo_xlib_surface.c +++ b/src/cairo_xlib_surface.c @@ -90,6 +90,7 @@ typedef struct cairo_xlib_surface { #define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) #define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) +#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) static int _CAIRO_FORMAT_DEPTH (cairo_format_t format) @@ -257,7 +258,7 @@ _cairo_xlib_surface_set_image (void *abstract_surface, ximage = XCreateImage (surface->dpy, DefaultVisual(surface->dpy, DefaultScreen(surface->dpy)), - image->depth == 32 ? 24 : image->depth, + image->depth, ZPixmap, 0, image->data, @@ -320,26 +321,29 @@ _cairo_xlib_surface_set_filter (void *abstract_surface, cairo_filter_t filter) cairo_xlib_surface_t *surface = abstract_surface; char *render_filter; - if (!surface->picture) + if (!(surface->picture + && CAIRO_SURFACE_RENDER_HAS_FILTERS(surface))) return CAIRO_STATUS_SUCCESS; - - /* XXX: The Render specification has capitalized versions of these - strings. However, the current implementation is - case-sensitive and expects lowercase versions. - */ + switch (filter) { case CAIRO_FILTER_FAST: - render_filter = "fast"; + render_filter = FilterFast; + break; case CAIRO_FILTER_GOOD: - render_filter = "good"; + render_filter = FilterGood; + break; case CAIRO_FILTER_BEST: - render_filter = "best"; + render_filter = FilterBest; + break; case CAIRO_FILTER_NEAREST: - render_filter = "nearest"; + render_filter = FilterNearest; + break; case CAIRO_FILTER_BILINEAR: - render_filter = "bilinear"; + render_filter = FilterBilinear; + break; default: - render_filter = "best"; + render_filter = FilterBest; + break; } XRenderSetPictureFilter (surface->dpy, surface->picture, @@ -384,6 +388,8 @@ _cairo_xlib_surface_clone_similar (cairo_surface_t *src, 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)); @@ -567,6 +573,60 @@ _cairo_xlib_surface_show_page (void *abstract_surface) return CAIRO_INT_STATUS_UNSUPPORTED; } +static cairo_int_status_t +_cairo_xlib_surface_set_clip_region (void *abstract_surface, + pixman_region16_t *region) +{ + Region xregion; + XRectangle xr; + pixman_box16_t *box; + cairo_xlib_surface_t *surf; + int n, m; + + surf = (cairo_xlib_surface_t *) abstract_surface; + + if (region == NULL) { + /* NULL region == reset the clip */ + xregion = XCreateRegion(); + xr.x = 0; + xr.y = 0; + xr.width = surf->width; + xr.height = surf->height; + XUnionRectWithRegion (&xr, xregion, xregion); + } else { + n = pixman_region_num_rects (region); + /* XXX: Are we sure these are the semantics we want for an + * empty, (not null) region? */ + if (n == 0) + return CAIRO_STATUS_SUCCESS; + + box = pixman_region_rects (region); + xregion = XCreateRegion(); + + m = n; + for (; n > 0; --n, ++box) { + xr.x = (short) box->x1; + xr.y = (short) box->y1; + xr.width = (unsigned short) (box->x2 - box->x1); + xr.height = (unsigned short) (box->y2 - box->y1); + XUnionRectWithRegion (&xr, xregion, xregion); + } + } + + XRenderSetPictureClipRegion (surf->dpy, surf->picture, xregion); + XDestroyRegion(xregion); + + 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 const struct cairo_surface_backend cairo_xlib_surface_backend = { _cairo_xlib_surface_create_similar, _cairo_xlib_surface_destroy, @@ -580,7 +640,9 @@ static const struct cairo_surface_backend cairo_xlib_surface_backend = { _cairo_xlib_surface_fill_rectangles, _cairo_xlib_surface_composite_trapezoids, _cairo_xlib_surface_copy_page, - _cairo_xlib_surface_show_page + _cairo_xlib_surface_show_page, + _cairo_xlib_surface_set_clip_region, + _cairo_xlib_surface_create_pattern }; cairo_surface_t * diff --git a/src/cairoint.h b/src/cairoint.h index 036bff21..5b4ccb56 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -43,7 +43,33 @@ #include "cairo.h" -#include <slim_internal.h> +#if __GNUC__ >= 3 && defined(__ELF__) +# define slim_hidden_proto(name) slim_hidden_proto1(name, INT_##name) +# define slim_hidden_def(name) slim_hidden_def1(name, INT_##name) +# define slim_hidden_proto1(name, internal) \ + extern __typeof (name) name \ + __asm__ (slim_hidden_asmname (internal)) \ + __internal_linkage; +# define slim_hidden_def1(name, internal) \ + extern __typeof (name) EXT_##name __asm__(slim_hidden_asmname(name)) \ + __attribute__((__alias__(slim_hidden_asmname(internal)))) +# define slim_hidden_ulp slim_hidden_ulp1(__USER_LABEL_PREFIX__) +# define slim_hidden_ulp1(x) slim_hidden_ulp2(x) +# define slim_hidden_ulp2(x) #x +# define slim_hidden_asmname(name) slim_hidden_asmname1(name) +# define slim_hidden_asmname1(name) slim_hidden_ulp #name +#else +# define slim_hidden_proto(name) +# define slim_hidden_def(name) +#endif + +/* slim_internal.h */ +#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && defined(__ELF__) +#define __internal_linkage __attribute__((__visibility__("hidden"))) +#else +#define __internal_linkage +#endif + /* These macros allow us to deprecate a function by providing an alias for the old function name to the new function name. With this @@ -102,17 +128,22 @@ typedef struct cairo_slope { cairo_fixed_t dx; cairo_fixed_t dy; -} cairo_slope_t; +} cairo_slope_t, cairo_distance_t; typedef struct cairo_point_double { double x; double y; } cairo_point_double_t; +typedef struct cairo_distance_double { + double dx; + double dy; +} cairo_distance_double_t; + typedef struct cairo_line { cairo_point_t p1; cairo_point_t p2; -} cairo_line_t; +} cairo_line_t, cairo_box_t; typedef struct cairo_trapezoid { cairo_fixed_t top, bottom; @@ -144,18 +175,6 @@ typedef enum cairo_direction { CAIRO_DIRECTION_REVERSE } cairo_direction_t; -typedef enum cairo_sub_path_done { - CAIRO_SUB_PATH_DONE_CAP, - CAIRO_SUB_PATH_DONE_JOIN -} cairo_sub_path_done_t; - -typedef struct cairo_path_callbacks { - cairo_status_t (*add_edge) (void *closure, cairo_point_t *p1, cairo_point_t *p2); - cairo_status_t (*add_spline) (void *closure, cairo_point_t *a, cairo_point_t *b, cairo_point_t *c, cairo_point_t *d); - cairo_status_t (*done_sub_path) (void *closure, cairo_sub_path_done_t done); - cairo_status_t (*done_path) (void *closure); -} cairo_path_callbacks_t; - #define CAIRO_PATH_BUF_SZ 64 typedef struct cairo_path_op_buf { @@ -178,6 +197,10 @@ typedef struct cairo_path { cairo_path_arg_buf_t *arg_head; cairo_path_arg_buf_t *arg_tail; + + cairo_point_t last_move_point; + cairo_point_t current_point; + int has_current_point; } cairo_path_t; typedef struct cairo_edge { @@ -193,9 +216,8 @@ typedef struct cairo_polygon { cairo_edge_t *edges; cairo_point_t first_point; - int first_point_defined; - cairo_point_t last_point; - int last_point_defined; + cairo_point_t current_point; + int has_current_point; int closed; } cairo_polygon_t; @@ -229,53 +251,51 @@ typedef struct cairo_pen { typedef struct cairo_color cairo_color_t; typedef struct cairo_font_backend { + cairo_font_t *(*create) (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight); + + cairo_font_t *(*copy) (void *font); - cairo_status_t (*font_extents) (cairo_font_t *font, - cairo_font_extents_t *extents); + void (*destroy) (void *font); - cairo_status_t (*text_extents) (cairo_font_t *font, - const unsigned char *utf8, - cairo_text_extents_t *extents); - - cairo_status_t (*glyph_extents) (cairo_font_t *font, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_text_extents_t *extents); + cairo_status_t (*font_extents) (void *font, + cairo_font_extents_t *extents); + + cairo_status_t (*text_extents) (void *font, + const unsigned char *utf8, + cairo_text_extents_t *extents); - cairo_status_t (*show_text) (cairo_font_t *font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - double x, - double y, - const unsigned char *utf8); - - cairo_status_t (*show_glyphs) (cairo_font_t *font, - cairo_operator_t operator, - cairo_surface_t *source, - cairo_surface_t *surface, - double x, - double y, - const cairo_glyph_t *glyphs, - int num_glyphs); + cairo_status_t (*glyph_extents) (void *font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); - cairo_status_t (*text_path) (cairo_font_t *font, - cairo_path_t *path, - const unsigned char *utf8); - - cairo_status_t (*glyph_path) (cairo_font_t *font, - cairo_path_t *path, - cairo_glyph_t *glyphs, - int num_glyphs); - - cairo_font_t *(*create) (const char *family, - cairo_font_slant_t slant, - cairo_font_weight_t weight); - - cairo_font_t *(*copy) (cairo_font_t *other); - - void (*destroy) (cairo_font_t *font); + cairo_status_t (*show_text) (void *font, + cairo_operator_t operator, + cairo_surface_t *source, + cairo_surface_t *surface, + double x, + double y, + const unsigned char *utf8); + + cairo_status_t (*show_glyphs) (void *font, + cairo_operator_t operator, + cairo_surface_t *source, + cairo_surface_t *surface, + const cairo_glyph_t *glyphs, + int num_glyphs); + cairo_status_t (*text_path) (void *font, + double x, + double y, + const unsigned char *utf8, + cairo_path_t *path); + + cairo_status_t (*glyph_path) (void *font, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_t *path); } cairo_font_backend_t; /* concrete font backends */ @@ -357,6 +377,14 @@ typedef struct cairo_surface_backend { cairo_int_status_t (*show_page) (void *surface); + + 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); } cairo_surface_backend_t; struct cairo_matrix { @@ -377,6 +405,7 @@ struct cairo_surface { unsigned int ref_count; cairo_matrix_t matrix; + cairo_filter_t filter; int repeat; }; @@ -392,7 +421,7 @@ struct cairo_image_surface { int stride; int depth; - IcImage *ic_image; + pixman_image_t *pixman_image; }; /* XXX: Right now, the cairo_color structure puts unpremultiplied @@ -413,6 +442,59 @@ struct cairo_color { unsigned short alpha_short; }; +#define CAIRO_EXTEND_DEFAULT CAIRO_EXTEND_NONE +#define CAIRO_FILTER_DEFAULT CAIRO_FILTER_NEAREST + +typedef enum { + CAIRO_PATTERN_SOLID, + CAIRO_PATTERN_SURFACE, + CAIRO_PATTERN_LINEAR, + CAIRO_PATTERN_RADIAL +} cairo_pattern_type_t; + +typedef struct cairo_color_stop { + double offset; + int id; + cairo_color_t color; + unsigned char color_char[4]; +} cairo_color_stop_t; + +struct cairo_pattern { + unsigned int ref_count; + + 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; + cairo_distance_double_t radius0; + cairo_distance_double_t radius1; + } radial; + } u; +}; + typedef struct cairo_traps { cairo_trapezoid_t *traps; int num_traps; @@ -447,6 +529,7 @@ typedef struct cairo_clip_rec { int y; int width; int height; + pixman_region16_t *region; cairo_surface_t *surface; } cairo_clip_rec_t; @@ -471,25 +554,18 @@ typedef struct cairo_gstate { cairo_surface_t *surface; - cairo_surface_t *source; - cairo_point_double_t source_offset; - int source_is_solid; + cairo_pattern_t *pattern; + cairo_point_double_t pattern_offset; + double alpha; cairo_clip_rec_t clip; - double alpha; - cairo_color_t color; - double pixels_per_inch; cairo_matrix_t ctm; cairo_matrix_t ctm_inverse; cairo_path_t path; - cairo_point_double_t last_move_point; - cairo_point_double_t current_point; - int has_current_point; - cairo_pen_t pen_regular; struct cairo_gstate *next; @@ -509,6 +585,10 @@ typedef struct cairo_stroke_face { cairo_point_double_t usr_vector; } cairo_stroke_face_t; +/* cairo.c */ +extern void __internal_linkage +_cairo_restrict_value (double *value, double min, double max); + /* cairo_fixed.c */ extern cairo_fixed_t __internal_linkage _cairo_fixed_from_int (int i); @@ -516,6 +596,9 @@ _cairo_fixed_from_int (int i); extern cairo_fixed_t _cairo_fixed_from_double (double d); +cairo_fixed_t +_cairo_fixed_from_26_6 (uint32_t i); + extern double _cairo_fixed_to_double (cairo_fixed_t f); @@ -554,7 +637,10 @@ extern cairo_surface_t * __internal_linkage _cairo_gstate_current_target_surface (cairo_gstate_t *gstate); extern cairo_status_t __internal_linkage -_cairo_gstate_set_pattern (cairo_gstate_t *gstate, cairo_surface_t *pattern); +_cairo_gstate_set_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern); + +extern cairo_pattern_t *__internal_linkage +_cairo_gstate_current_pattern (cairo_gstate_t *gstate); extern cairo_status_t __internal_linkage _cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t operator); @@ -705,6 +791,14 @@ extern cairo_status_t __internal_linkage _cairo_gstate_current_point (cairo_gstate_t *gstate, double *x, double *y); extern cairo_status_t __internal_linkage +_cairo_gstate_interpret_path (cairo_gstate_t *gstate, + cairo_move_to_func_t *move_to, + cairo_line_to_func_t *line_to, + cairo_curve_to_func_t *curve_to, + cairo_close_path_func_t *close_path, + void *closure); + +extern cairo_status_t __internal_linkage _cairo_gstate_stroke (cairo_gstate_t *gstate); extern cairo_status_t __internal_linkage @@ -717,6 +811,16 @@ extern cairo_status_t __internal_linkage _cairo_gstate_show_page (cairo_gstate_t *gstate); extern cairo_status_t __internal_linkage +_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, + double *x1, double *y1, + double *x2, double *y2); + +extern cairo_status_t __internal_linkage +_cairo_gstate_fill_extents (cairo_gstate_t *gstate, + double *x1, double *y1, + double *x2, double *y2); + +extern cairo_status_t __internal_linkage _cairo_gstate_in_stroke (cairo_gstate_t *gstate, double x, double y, @@ -729,6 +833,9 @@ _cairo_gstate_in_fill (cairo_gstate_t *gstate, int *inside_ret); extern cairo_status_t __internal_linkage +_cairo_gstate_init_clip (cairo_gstate_t *gstate); + +extern cairo_status_t __internal_linkage _cairo_gstate_clip (cairo_gstate_t *gstate); extern cairo_status_t __internal_linkage @@ -739,7 +846,7 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, extern cairo_status_t __internal_linkage _cairo_gstate_select_font (cairo_gstate_t *gstate, - char *family, + const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight); @@ -813,7 +920,7 @@ _cairo_color_set_alpha (cairo_color_t *color, double alpha); /* cairo_font.c */ extern cairo_font_t * __internal_linkage -_cairo_font_create (char *family, +_cairo_font_create (const char *family, cairo_font_slant_t slant, cairo_font_weight_t weight); @@ -860,23 +967,22 @@ _cairo_font_show_glyphs (cairo_font_t *font, cairo_operator_t operator, cairo_surface_t *source, cairo_surface_t *surface, - double x, - double y, cairo_glyph_t *glyphs, int num_glyphs); extern cairo_int_status_t __internal_linkage _cairo_font_text_path (cairo_font_t *font, - cairo_path_t *path, - const unsigned char *utf8); + double x, + double y, + const unsigned char *utf8, + cairo_path_t *path); extern cairo_int_status_t __internal_linkage _cairo_font_glyph_path (cairo_font_t *font, - cairo_path_t *path, cairo_glyph_t *glyphs, - int num_glyphs); - + int num_glyphs, + cairo_path_t *path); /* cairo_hull.c */ extern cairo_status_t @@ -893,25 +999,56 @@ extern void __internal_linkage _cairo_path_fini (cairo_path_t *path); extern cairo_status_t __internal_linkage -_cairo_path_move_to (cairo_path_t *path, double x, double y); +_cairo_path_move_to (cairo_path_t *path, cairo_point_t *point); + +extern cairo_status_t __internal_linkage +_cairo_path_rel_move_to (cairo_path_t *path, cairo_slope_t *slope); extern cairo_status_t __internal_linkage -_cairo_path_line_to (cairo_path_t *path, double x, double y); +_cairo_path_line_to (cairo_path_t *path, cairo_point_t *point); + +extern cairo_status_t __internal_linkage +_cairo_path_rel_line_to (cairo_path_t *path, cairo_slope_t *slope); extern cairo_status_t __internal_linkage _cairo_path_curve_to (cairo_path_t *path, - double x1, double y1, - double x2, double y2, - double x3, double y3); + cairo_point_t *p0, + cairo_point_t *p1, + cairo_point_t *p2); + +extern cairo_status_t __internal_linkage +_cairo_path_rel_curve_to (cairo_path_t *path, + cairo_slope_t *s0, + cairo_slope_t *s1, + cairo_slope_t *s2); extern cairo_status_t __internal_linkage _cairo_path_close_path (cairo_path_t *path); extern cairo_status_t __internal_linkage -_cairo_path_interpret (cairo_path_t *path, - cairo_direction_t dir, - const cairo_path_callbacks_t *cb, - void *closure); +_cairo_path_current_point (cairo_path_t *path, cairo_point_t *point); + +typedef cairo_status_t (cairo_path_move_to_func_t) (void *closure, + cairo_point_t *point); + +typedef cairo_status_t (cairo_path_line_to_func_t) (void *closure, + cairo_point_t *point); + +typedef cairo_status_t (cairo_path_curve_to_func_t) (void *closure, + cairo_point_t *p0, + cairo_point_t *p1, + cairo_point_t *p2); + +typedef cairo_status_t (cairo_path_close_path_func_t) (void *closure); + +extern cairo_status_t __internal_linkage +_cairo_path_interpret (cairo_path_t *path, + cairo_direction_t dir, + cairo_path_move_to_func_t *move_to, + cairo_path_line_to_func_t *line_to, + cairo_path_curve_to_func_t *curve_to, + cairo_path_close_path_func_t *close_path, + void *closure); extern cairo_status_t __internal_linkage _cairo_path_bounds (cairo_path_t *path, double *x1, double *y1, double *x2, double *y2); @@ -991,6 +1128,14 @@ extern cairo_status_t __internal_linkage _cairo_surface_set_image (cairo_surface_t *surface, cairo_image_surface_t *image); +extern cairo_status_t __internal_linkage +_cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region); + +extern cairo_status_t __internal_linkage +_cairo_surface_create_pattern (cairo_surface_t *surface, + cairo_pattern_t *pattern, + cairo_box_t *extents); + /* cairo_image_surface.c */ extern cairo_image_surface_t * __internal_linkage @@ -1007,7 +1152,7 @@ extern cairo_status_t __internal_linkage _cairo_image_surface_set_matrix (cairo_image_surface_t *surface, cairo_matrix_t *matrix); -cairo_status_t +extern cairo_status_t __internal_linkage _cairo_image_surface_set_filter (cairo_image_surface_t *surface, cairo_filter_t filter); @@ -1015,6 +1160,10 @@ extern cairo_status_t __internal_linkage _cairo_image_surface_set_repeat (cairo_image_surface_t *surface, int repeat); +extern cairo_int_status_t __internal_linkage +_cairo_image_surface_set_clip_region (cairo_image_surface_t *surface, + pixman_region16_t *region); + /* cairo_pen.c */ extern cairo_status_t __internal_linkage _cairo_pen_init (cairo_pen_t *pen, double radius, cairo_gstate_t *gstate); @@ -1065,7 +1214,10 @@ extern cairo_status_t __internal_linkage _cairo_polygon_add_edge (cairo_polygon_t *polygon, cairo_point_t *p1, cairo_point_t *p2); extern cairo_status_t __internal_linkage -_cairo_polygon_add_point (cairo_polygon_t *polygon, cairo_point_t *point); +_cairo_polygon_move_to (cairo_polygon_t *polygon, cairo_point_t *point); + +extern cairo_status_t __internal_linkage +_cairo_polygon_line_to (cairo_polygon_t *polygon, cairo_point_t *point); extern cairo_status_t __internal_linkage _cairo_polygon_close (cairo_polygon_t *polygon); @@ -1114,6 +1266,9 @@ _cairo_matrix_compute_determinant (cairo_matrix_t *matrix, double *det); extern cairo_status_t __internal_linkage _cairo_matrix_compute_eigen_values (cairo_matrix_t *matrix, double *lambda1, double *lambda2); +extern cairo_status_t __internal_linkage +_cairo_matrix_compute_scale_factors (cairo_matrix_t *matrix, double *sx, double *sy); + /* cairo_traps.c */ extern void __internal_linkage _cairo_traps_init (cairo_traps_t *traps); @@ -1135,6 +1290,9 @@ _cairo_traps_tessellate_polygon (cairo_traps_t *traps, extern int __internal_linkage _cairo_traps_contain (cairo_traps_t *traps, double x, double y); +extern void __internal_linkage +_cairo_traps_extents (cairo_traps_t *traps, cairo_box_t *extents); + /* cairo_slope.c */ extern void __internal_linkage _cairo_slope_init (cairo_slope_t *slope, cairo_point_t *a, cairo_point_t *b); @@ -1148,6 +1306,50 @@ _cairo_slope_clockwise (cairo_slope_t *a, cairo_slope_t *b); extern int __internal_linkage _cairo_slope_counter_clockwise (cairo_slope_t *a, cairo_slope_t *b); +/* cairo_pattern.c */ +extern void __internal_linkage +_cairo_pattern_init (cairo_pattern_t *pattern); + +extern cairo_status_t __internal_linkage +_cairo_pattern_init_copy (cairo_pattern_t *pattern, cairo_pattern_t *other); + +extern void __internal_linkage +_cairo_pattern_fini (cairo_pattern_t *pattern); + +extern void __internal_linkage +_cairo_pattern_init_solid (cairo_pattern_t *pattern, + double red, double green, double blue); + +extern cairo_pattern_t *__internal_linkage +_cairo_pattern_create_solid (double red, double green, double blue); + +extern cairo_status_t __internal_linkage +_cairo_pattern_get_rgb (cairo_pattern_t *pattern, + double *red, double *green, double *blue); + +extern void __internal_linkage +_cairo_pattern_set_alpha (cairo_pattern_t *pattern, double alpha); + +extern void __internal_linkage +_cairo_pattern_add_source_offset (cairo_pattern_t *pattern, + double x, double y); + +extern void __internal_linkage +_cairo_pattern_transform (cairo_pattern_t *pattern, + cairo_matrix_t *matrix, + cairo_matrix_t *matrix_inverse); + +extern void __internal_linkage +_cairo_pattern_prepare_surface (cairo_pattern_t *pattern); + +extern void __internal_linkage +_cairo_pattern_calc_color_at_pixel (cairo_pattern_t *pattern, + double factor, + int *pixel); + +extern cairo_image_surface_t *__internal_linkage +_cairo_pattern_get_image (cairo_pattern_t *pattern, cairo_box_t *box); + /* Avoid unnecessary PLT entries. */ slim_hidden_proto(cairo_close_path) |