diff options
Diffstat (limited to 'src/cairo-gstate.c')
-rw-r--r-- | src/cairo-gstate.c | 442 |
1 files changed, 338 insertions, 104 deletions
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 0ff24c15d..6d89964e8 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -78,8 +78,6 @@ _cairo_gstate_init (cairo_gstate_t *gstate) 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; gstate->pixels_per_inch = CAIRO_GSTATE_PIXELS_PER_INCH_DEFAULT; @@ -400,8 +398,6 @@ _cairo_gstate_set_rgb_color (cairo_gstate_t *gstate, double red, double green, d cairo_pattern_destroy (gstate->pattern); gstate->pattern = _cairo_pattern_create_solid (red, green, blue); - gstate->pattern_offset.x = 0.0; - gstate->pattern_offset.y = 0.0; return CAIRO_STATUS_SUCCESS; } @@ -883,25 +879,26 @@ _cairo_gstate_arc_dir (cairo_gstate_t *gstate, /* Recurse if drawing arc larger than pi */ if (angle_max - angle_min > M_PI) { + double angle_mid = angle_min + (angle_max - angle_min) / 2.0; /* XXX: Something tells me this block could be condensed. */ if (dir == CAIRO_DIRECTION_FORWARD) { status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle_min, angle_min + M_PI, dir); + angle_min, angle_mid, dir); if (status) return status; status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle_min + M_PI, angle_max, dir); + angle_mid, angle_max, dir); if (status) return status; } else { status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle_min + M_PI, angle_max, dir); + angle_mid, angle_max, dir); if (status) return status; status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, - angle_min, angle_min + M_PI, dir); + angle_min, angle_mid, dir); if (status) return status; } @@ -1268,18 +1265,22 @@ _cairo_gstate_create_pattern (cairo_gstate_t *gstate, if (pattern->n_stops < 2) { pattern->type = CAIRO_PATTERN_SOLID; - if (pattern->n_stops) - pattern->color = pattern->stops->color; + if (pattern->n_stops) { + cairo_color_stop_t *stop = pattern->stops; + + _cairo_color_set_rgb (&pattern->color, + (double) stop->color_char[0] / 0xff, + (double) stop->color_char[1] / 0xff, + (double) stop->color_char[2] / 0xff); + _cairo_color_set_alpha (&pattern->color, + (double) stop->color_char[3] / 0xff); + } } } _cairo_pattern_set_alpha (pattern, gstate->alpha); _cairo_pattern_transform (pattern, &gstate->ctm_inverse); - _cairo_pattern_set_source_offset (pattern, - gstate->pattern_offset.x, - gstate->pattern_offset.y); - status = _cairo_surface_create_pattern (gstate->surface, pattern, extents); if (status) { _cairo_pattern_fini (pattern); @@ -1362,6 +1363,7 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, cairo_status_t status; cairo_pattern_t pattern; cairo_box_t extents; + int x_src, y_src; if (traps->num_traps == 0) return CAIRO_STATUS_SUCCESS; @@ -1404,6 +1406,14 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, t->right.p2.y -= yoff; } + if (traps->traps[0].left.p1.y < traps->traps[0].left.p2.y) { + x_src = _cairo_fixed_to_double (traps->traps[0].left.p1.x); + y_src = _cairo_fixed_to_double (traps->traps[0].left.p1.y); + } else { + x_src = _cairo_fixed_to_double (traps->traps[0].left.p2.x); + y_src = _cairo_fixed_to_double (traps->traps[0].left.p2.y); + } + _cairo_pattern_init_solid (&pattern, 1.0, 1.0, 1.0); _cairo_pattern_set_alpha (&pattern, 1.0); @@ -1414,7 +1424,8 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD, pattern.source, intermediate, - 0, 0, + x_src, + y_src, traps->traps, traps->num_traps); if (status) @@ -1443,12 +1454,15 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, if (status) goto BAIL2; + if (dst == gstate->clip.surface) + xoff = yoff = 0; + status = _cairo_surface_composite (operator, pattern.source, intermediate, dst, 0, 0, 0, 0, - gstate->clip.x, - gstate->clip.y, + xoff >> 16, + yoff >> 16, gstate->clip.width, gstate->clip.height); @@ -1462,14 +1476,12 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, return status; } else { - int xoff, yoff; - if (traps->traps[0].left.p1.y < traps->traps[0].left.p2.y) { - xoff = _cairo_fixed_to_double (traps->traps[0].left.p1.x); - yoff = _cairo_fixed_to_double (traps->traps[0].left.p1.y); + x_src = _cairo_fixed_to_double (traps->traps[0].left.p1.x); + y_src = _cairo_fixed_to_double (traps->traps[0].left.p1.y); } else { - xoff = _cairo_fixed_to_double (traps->traps[0].left.p2.x); - yoff = _cairo_fixed_to_double (traps->traps[0].left.p2.y); + x_src = _cairo_fixed_to_double (traps->traps[0].left.p2.x); + y_src = _cairo_fixed_to_double (traps->traps[0].left.p2.y); } _cairo_pattern_init_copy (&pattern, src); @@ -1481,8 +1493,8 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, status = _cairo_surface_composite_trapezoids (gstate->operator, pattern.source, dst, - xoff - pattern.source_offset.x, - yoff - pattern.source_offset.y, + x_src - pattern.source_offset.x, + y_src - pattern.source_offset.y, traps->traps, traps->num_traps); @@ -1640,8 +1652,9 @@ _cairo_gstate_init_clip (cairo_gstate_t *gstate) gstate->clip.region = NULL; /* reset the surface's clip to the whole surface */ - _cairo_surface_set_clip_region (gstate->surface, - gstate->clip.region); + if (gstate->surface) + _cairo_surface_set_clip_region (gstate->surface, + gstate->clip.region); return CAIRO_STATUS_SUCCESS; } @@ -1651,9 +1664,6 @@ 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; @@ -1666,25 +1676,22 @@ extract_transformed_rectangle(cairo_matrix_t *mat, && 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); + && _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; - -#undef CAIRO_FIXED_IS_INTEGER -#undef CAIRO_FIXED_INTEGER_PART } cairo_status_t @@ -1751,20 +1758,20 @@ _cairo_gstate_clip (cairo_gstate_t *gstate) _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) + cairo_box_t extents; + + _cairo_traps_extents (&traps, &extents); + gstate->clip.x = extents.p1.x >> 16; + gstate->clip.y = extents.p1.y >> 16; + gstate->clip.width = ((extents.p2.x + 65535) >> 16) - gstate->clip.x; + gstate->clip.height = ((extents.p2.y + 65535) >> 16) - gstate->clip.y; + 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; } @@ -1790,14 +1797,78 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, int width, int height) { + + /* We are dealing with 5 coordinate spaces in this function. this makes + * it ugly. + * + * - "Image" space is the space of the surface we're reading pixels from. + * it is the surface argument to this function. The surface has a + * matrix attached to it which maps "user" space (see below) into + * image space. + * + * - "Device" space is the space of the surface we're ultimately writing + * pixels to. It is the current surface of the gstate argument to + * this function. + * + * - "User" space is an arbitrary space defined by the user, defined + * implicitly by the gstate's CTM. The CTM maps from user space to + * device space. The CTM inverse (which is also kept at all times) + * maps from device space to user space. + * + * - "Clip" space is the space of the surface being used to clip pixels + * during compositing. Space-wise, it is a bounding box (offset+size) + * within device space. This surface is usually smaller than the device + * surface (and possibly the image surface too) and logically occupies + * a bounding box around the "clip path", situated somewhere in device + * space. The clip path is already painted on the clip surface. + * + * - "Pattern" space is another arbitrary space defined in the pattern + * element of gstate. As pixels are read from image space, they are + * combined with pixels being read from pattern space and pixels + * already existing in device space. User coordinates are converted + * to pattern space, similarly, using a matrix attached to the pattern. + * (in fact, there is a 6th space in here, which is the space of the + * surface acting as a source for the pattern) + * + * To composite these spaces, we temporarily change the image surface + * so that it can be read and written in device coordinates; in a sense + * this makes it "spatially compatible" with the clip and device spaces. + * + * + * There is also some confusion about the interaction between a clip and + * a pattern; it is assumed that in this "show surface" operation a pattern + * is to be used as an auxiliary alpha mask. this might be wrong, but it's + * what we're doing now. + * + * so, to follow the operations below, remember that in the compositing + * model, each operation is always of the form ((src IN mask) OP dst). + * that's the basic operation. + * + * so the compositing we are trying to do here, in general, involves 2 + * steps, going via a temporary surface: + * + * - combining clip and pattern pixels together into a mask channel. + * this will be ((pattern IN clip) SRC temporary). it ignores the + * pixels already in the temporary, overwriting it with the + * pattern, clipped to the clip mask. + * + * - combining temporary and "image" pixels with "device" pixels, + * with a user-provided porter/duff operator. this will be + * ((image IN temporary) OP device). + * + * if there is no clip, the degenerate case is just the second step + * with pattern standing in for temporary. + * + */ + cairo_status_t status; 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_pattern_t pattern; - cairo_box_t extents; - + cairo_box_t pattern_extents; + cairo_surface_get_matrix (surface, &user_to_image); cairo_matrix_multiply (&device_to_image, &gstate->ctm_inverse, &user_to_image); cairo_surface_set_matrix (surface, &device_to_image); @@ -1816,31 +1887,84 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate, _cairo_pattern_init (&pattern); if ((gstate->pattern->type != CAIRO_PATTERN_SOLID) || - (gstate->alpha != 1.0)) { + (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); + pattern_extents.p1.x = _cairo_fixed_from_double (device_x); + pattern_extents.p1.y = _cairo_fixed_from_double (device_y); + pattern_extents.p2.x = _cairo_fixed_from_double (device_x + device_width); + pattern_extents.p2.y = _cairo_fixed_from_double (device_y + device_height); + status = _cairo_gstate_create_pattern (gstate, &pattern, &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, pattern.source, gstate->surface, - device_x, device_y, - 0, 0, - device_x, device_y, - device_width, - device_height); + if (gstate->clip.surface) + { + cairo_surface_t *intermediate; + cairo_color_t empty_color; - _cairo_pattern_fini (&pattern); + _cairo_color_init (&empty_color); + _cairo_color_set_alpha (&empty_color, .0); + intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, + CAIRO_FORMAT_A8, + gstate->clip.width, + gstate->clip.height, + &empty_color); + + /* it is not completely clear what the "right" way to combine the + pattern and mask surface is. I will use the the clip as a source + and the pattern as a mask in building up my temporary, because + this is not *totally* bogus and accomodates the case where + pattern's source image is NULL reasonably well. feel free to + correct this if you see a reason. */ + status = _cairo_surface_composite (CAIRO_OPERATOR_SRC, + gstate->clip.surface, + pattern.source, + intermediate, + 0, 0, + 0, 0, + 0, 0, + gstate->clip.width, + gstate->clip.height); + + if (status) + goto BAIL; + + status = _cairo_surface_composite (gstate->operator, + surface, + intermediate, + gstate->surface, + gstate->clip.x, gstate->clip.y, + 0, 0, + gstate->clip.x, gstate->clip.y, + gstate->clip.width, + gstate->clip.height); + + BAIL: + cairo_surface_destroy (intermediate); + } + else + { + + /* 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, + pattern.source, + gstate->surface, + device_x, device_y, + 0, 0, + device_x, device_y, + device_width, + device_height); + + } + + _cairo_pattern_fini (&pattern); + /* restore the matrix originally in the surface */ cairo_surface_set_matrix (surface, &user_to_image); @@ -1983,8 +2107,7 @@ _cairo_gstate_show_text (cairo_gstate_t *gstate, double x, y; cairo_matrix_t saved_font_matrix; cairo_pattern_t pattern; - cairo_text_extents_t text_extents; - cairo_box_t extents; + cairo_box_t bbox; status = _cairo_path_current_point (&gstate->path, &point); if (status == CAIRO_STATUS_NO_CURRENT_POINT) { @@ -2001,21 +2124,76 @@ _cairo_gstate_show_text (cairo_gstate_t *gstate, _cairo_pattern_init_copy (&pattern, gstate->pattern); - status = _cairo_gstate_text_extents (gstate, utf8, &text_extents); + status = _cairo_font_text_bbox (gstate->font, gstate->surface, + x, y, utf8, &bbox); 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); + status = _cairo_gstate_create_pattern (gstate, &pattern, &bbox); if (status) return status; - status = _cairo_font_show_text (gstate->font, - gstate->operator, pattern.source, - gstate->surface, x, y, utf8); + if (gstate->clip.surface) + { + cairo_surface_t *intermediate; + cairo_color_t empty_color; + + _cairo_color_init (&empty_color); + _cairo_color_set_alpha (&empty_color, .0); + intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, + CAIRO_FORMAT_A8, + gstate->clip.width, + gstate->clip.height, + &empty_color); + + status = _cairo_font_show_text (gstate->font, + CAIRO_OPERATOR_ADD, pattern.source, + intermediate, + gstate->clip.x - pattern.source_offset.x, + gstate->clip.y - pattern.source_offset.y, + x - gstate->clip.x, + y - gstate->clip.y, utf8); + + if (status) + goto BAIL; + + status = _cairo_surface_composite (CAIRO_OPERATOR_IN, + gstate->clip.surface, + NULL, + intermediate, + 0, 0, + 0, 0, + 0, 0, + gstate->clip.width, + gstate->clip.height); + + if (status) + goto BAIL; + + status = _cairo_surface_composite (gstate->operator, + pattern.source, + intermediate, + gstate->surface, + 0, 0, + 0, 0, + gstate->clip.x, + gstate->clip.y, + gstate->clip.width, + gstate->clip.height); + + BAIL: + cairo_surface_destroy (intermediate); + + } + else + { + status = _cairo_font_show_text (gstate->font, + gstate->operator, pattern.source, + gstate->surface, + -pattern.source_offset.x, + -pattern.source_offset.y, + x, y, utf8); + } cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); @@ -2034,8 +2212,7 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, int i; cairo_glyph_t *transformed_glyphs = NULL; cairo_pattern_t pattern; - cairo_text_extents_t text_extents; - cairo_box_t extents; + cairo_box_t bbox; transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); if (transformed_glyphs == NULL) @@ -2053,25 +2230,82 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate, 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); + status = _cairo_font_glyph_bbox (gstate->font, gstate->surface, + transformed_glyphs, num_glyphs, &bbox); 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); + status = _cairo_gstate_create_pattern (gstate, &pattern, &bbox); if (status) return status; + + if (gstate->clip.surface) + { + cairo_surface_t *intermediate; + cairo_color_t empty_color; + + _cairo_color_init (&empty_color); + _cairo_color_set_alpha (&empty_color, .0); + intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, + CAIRO_FORMAT_A8, + gstate->clip.width, + gstate->clip.height, + &empty_color); + + /* move the glyphs again, from dev space to clip space */ + for (i = 0; i < num_glyphs; ++i) + { + transformed_glyphs[i].x -= gstate->clip.x; + transformed_glyphs[i].y -= gstate->clip.y; + } + + status = _cairo_font_show_glyphs (gstate->font, + CAIRO_OPERATOR_ADD, + pattern.source, intermediate, + gstate->clip.x - pattern.source_offset.x, + gstate->clip.y - pattern.source_offset.y, + transformed_glyphs, num_glyphs); - status = _cairo_font_show_glyphs (gstate->font, - gstate->operator, pattern.source, - gstate->surface, - transformed_glyphs, num_glyphs); + if (status) + goto BAIL; + + status = _cairo_surface_composite (CAIRO_OPERATOR_IN, + gstate->clip.surface, + NULL, + intermediate, + 0, 0, + 0, 0, + 0, 0, + gstate->clip.width, + gstate->clip.height); + + if (status) + goto BAIL; + + status = _cairo_surface_composite (gstate->operator, + pattern.source, + intermediate, + gstate->surface, + 0, 0, + 0, 0, + gstate->clip.x, + gstate->clip.y, + gstate->clip.width, + gstate->clip.height); + + BAIL: + cairo_surface_destroy (intermediate); + + } + else + { + status = _cairo_font_show_glyphs (gstate->font, + gstate->operator, pattern.source, + gstate->surface, + -pattern.source_offset.x, + -pattern.source_offset.y, + transformed_glyphs, num_glyphs); + } cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); |