diff options
Diffstat (limited to 'src/cairo-gstate.c')
-rw-r--r-- | src/cairo-gstate.c | 1010 |
1 files changed, 661 insertions, 349 deletions
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; } |