diff options
author | Owen Taylor <otaylor@redhat.com> | 2005-05-02 11:01:18 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@redhat.com> | 2005-05-02 11:01:18 +0000 |
commit | 79b2a79f2d9ba8dbef44d04656fa84d83836ac6c (patch) | |
tree | 4e905226d89f28bfe18bcef83c365e73ff5dcd8d | |
parent | 0c40f66c04bffa1847d37cf9c64e48db9e76f51f (diff) |
src/cairo.[ch] src/cairo-gstate.c: Add cairo_mask() and cairo_mask_surface().
test/maks.c tests/Makefile.am tests/mask-ref.png: Add a comprehensive tests for cairo_mask().
Updated
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | doc/public/cairo-sections.txt | 2 | ||||
-rw-r--r-- | doc/public/tmpl/cairo.sgml | 20 | ||||
-rw-r--r-- | src/cairo-gstate.c | 259 | ||||
-rw-r--r-- | src/cairo.c | 62 | ||||
-rw-r--r-- | src/cairo.h | 10 | ||||
-rw-r--r-- | src/cairoint.h | 4 | ||||
-rw-r--r-- | test/Makefile.am | 3 | ||||
-rw-r--r-- | test/mask-ref.png | bin | 0 -> 58954 bytes | |||
-rw-r--r-- | test/mask.c | 241 |
10 files changed, 544 insertions, 67 deletions
@@ -1,3 +1,13 @@ +2005-05-02 Owen Taylor <otaylor@redhat.com> + + * src/cairo.[ch] src/cairo-gstate.c: Add cairo_mask() + and cairo_mask_surface(). + + * test/maks.c tests/Makefile.am tests/mask-ref.png: Add a + comprehensive tests for cairo_mask(). + + * docs/public/cairo-sections.txt: Updated + 2005-05-02 Kristian Høgsberg <krh@redhat.com> * src/cairo-gstate.c (_cairo_gstate_glyph_path): Also call diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt index 9e329b9c..14a490b2 100644 --- a/doc/public/cairo-sections.txt +++ b/doc/public/cairo-sections.txt @@ -218,6 +218,8 @@ cairo_rel_curve_to cairo_rectangle cairo_close_path cairo_paint +cairo_mask +cairo_mask_surface cairo_stroke cairo_stroke_preserve cairo_fill diff --git a/doc/public/tmpl/cairo.sgml b/doc/public/tmpl/cairo.sgml index ceac45c0..342c1eda 100644 --- a/doc/public/tmpl/cairo.sgml +++ b/doc/public/tmpl/cairo.sgml @@ -523,6 +523,26 @@ Drawing contexts. @cr: +<!-- ##### FUNCTION cairo_mask ##### --> +<para> + +</para> + +@cr: +@pattern: + + +<!-- ##### FUNCTION cairo_mask_surface ##### --> +<para> + +</para> + +@cr: +@surface: +@surface_x: +@surface_y: + + <!-- ##### FUNCTION cairo_stroke ##### --> <para> diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 1cc01816..f4bfb5d8 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -745,6 +745,192 @@ _cairo_gstate_paint (cairo_gstate_t *gstate) return CAIRO_STATUS_SUCCESS; } +/* Combines @gstate->clip_surface using the IN operator with + * the given intermediate surface, which corresponds to the + * rectangle of the destination space given by @extents. + */ +static cairo_status_t +_cairo_gstate_combine_clip_surface (cairo_gstate_t *gstate, + cairo_surface_t *intermediate, + cairo_rectangle_t *extents) +{ + cairo_pattern_union_t pattern; + cairo_status_t status; + + _cairo_pattern_init_for_surface (&pattern.surface, + gstate->clip.surface); + + status = _cairo_surface_composite (CAIRO_OPERATOR_IN, + &pattern.base, + NULL, + intermediate, + extents->x - gstate->clip.rect.x, + extents->y - gstate->clip.rect.y, + 0, 0, + 0, 0, + extents->width, extents->height); + + _cairo_pattern_fini (&pattern.base); + + return status; +} + +/* Creates a region from a cairo_rectangle_t */ +static cairo_status_t +_region_new_from_rect (cairo_rectangle_t *rect, + pixman_region16_t **region) +{ + *region = pixman_region_create (); + if (pixman_region_union_rect (*region, *region, + rect->x, rect->y, + rect->width, rect->height) != PIXMAN_REGION_STATUS_SUCCESS) { + pixman_region_destroy (*region); + return CAIRO_STATUS_NO_MEMORY; + } + + return CAIRO_STATUS_SUCCESS; +} + +/* Gets the bounding box of a region as a cairo_rectangle_t */ +static void +_region_rect_extents (pixman_region16_t *region, + cairo_rectangle_t *rect) +{ + pixman_box16_t *region_extents = pixman_region_extents (region); + + rect->x = region_extents->x1; + rect->y = region_extents->y1; + rect->width = region_extents->x2 - region_extents->x1; + rect->height = region_extents->y2 - region_extents->y1; +} + +/* Intersects @region with the clipping bounds (both region + * and surface) of @gstate + */ +static cairo_status_t +_cairo_gstate_intersect_clip (cairo_gstate_t *gstate, + pixman_region16_t *region) +{ + if (gstate->clip.region) + pixman_region_intersect (region, gstate->clip.region, region); + + if (gstate->clip.surface) { + pixman_region16_t *clip_rect; + cairo_status_t status; + + status = _region_new_from_rect (&gstate->clip.rect, &clip_rect); + if (!CAIRO_OK (status)) + return status; + + if (pixman_region_intersect (region, + clip_rect, + region) != PIXMAN_REGION_STATUS_SUCCESS) + status = CAIRO_STATUS_NO_MEMORY; + + pixman_region_destroy (clip_rect); + + if (!CAIRO_OK (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_get_mask_extents (cairo_gstate_t *gstate, + cairo_pattern_t *mask, + cairo_rectangle_t *extents) +{ + cairo_rectangle_t clip_rect; + pixman_region16_t *clip_region; + cairo_status_t status; + + status = _cairo_surface_get_clip_extents (gstate->surface, &clip_rect); + if (!CAIRO_OK (status)) + return status; + + status = _region_new_from_rect (&clip_rect, &clip_region); + if (!CAIRO_OK (status)) + return status; + + status = _cairo_gstate_intersect_clip (gstate, clip_region); + if (!CAIRO_OK (status)) + return status; + + _region_rect_extents (clip_region, extents); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_mask (cairo_gstate_t *gstate, + cairo_pattern_t *mask) +{ + cairo_rectangle_t extents; + cairo_surface_pattern_t intermediate_pattern; + cairo_pattern_t *effective_mask; + cairo_status_t status; + int mask_x, mask_y; + + _get_mask_extents (gstate, mask, &extents); + + if (gstate->clip.surface) { + /* When there is clip surface, we'll need to create a + * temporary surface that combines the clip and mask + */ + cairo_surface_t *intermediate; + + intermediate = cairo_surface_create_similar (gstate->clip.surface, + CAIRO_FORMAT_A8, + extents.width, + extents.height); + if (intermediate == NULL) + return CAIRO_STATUS_NO_MEMORY; + + status = _cairo_surface_composite (CAIRO_OPERATOR_SRC, + mask, NULL, intermediate, + extents.x, extents.y, + 0, 0, + 0, 0, + extents.width, extents.height); + if (!CAIRO_OK (status)) { + cairo_surface_destroy (intermediate); + return status; + } + + status = _cairo_gstate_combine_clip_surface (gstate, intermediate, &extents); + if (!CAIRO_OK (status)) { + cairo_surface_destroy (intermediate); + return status; + } + + _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate); + cairo_surface_destroy (intermediate); + + effective_mask = &intermediate_pattern.base; + mask_x = extents.x; + mask_y = extents.y; + + } else { + effective_mask = mask; + mask_x = mask_y = 0; + } + + status = _cairo_surface_composite (gstate->operator, + gstate->source, + effective_mask, + gstate->surface, + extents.x, extents.y, + extents.x - mask_x, extents.y - mask_y, + extents.x, extents.y, + extents.width, extents.height); + + if (gstate->clip.surface) + _cairo_pattern_fini (&intermediate_pattern.base); + + return status; +} + cairo_status_t _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path) { @@ -855,35 +1041,6 @@ _cairo_rectangle_empty (cairo_rectangle_t *rect) return rect->width == 0 || rect->height == 0; } -/* Creates a region from a cairo_rectangle_t */ -static cairo_status_t -_region_new_from_rect (cairo_rectangle_t *rect, - pixman_region16_t **region) -{ - *region = pixman_region_create (); - if (pixman_region_union_rect (*region, *region, - rect->x, rect->y, - rect->width, rect->height) != PIXMAN_REGION_STATUS_SUCCESS) { - pixman_region_destroy (*region); - return CAIRO_STATUS_NO_MEMORY; - } - - return CAIRO_STATUS_SUCCESS; -} - -/* Gets the bounding box of a region as a cairo_rectangle_t */ -static void -_region_rect_extents (pixman_region16_t *region, - cairo_rectangle_t *rect) -{ - pixman_box16_t *region_extents = pixman_region_extents (region); - - rect->x = region_extents->x1; - rect->y = region_extents->y1; - rect->width = region_extents->x2 - region_extents->x1; - rect->height = region_extents->y2 - region_extents->y1; -} - /* Given a region representing a set of trapezoids that will be * drawn, clip the region according to the gstate and compute * the overall extents. @@ -893,30 +1050,11 @@ _clip_and_compute_extents_region (cairo_gstate_t *gstate, pixman_region16_t *trap_region, cairo_rectangle_t *extents) { - if (gstate->clip.region) - pixman_region_intersect (trap_region, - gstate->clip.region, - trap_region); - - if (gstate->clip.surface) { - pixman_region16_t *clip_rect; - cairo_status_t status; + cairo_status_t status; - status = _region_new_from_rect (&gstate->clip.rect, - &clip_rect); - if (!CAIRO_OK (status)) - return status; - - if (pixman_region_intersect (trap_region, - clip_rect, - trap_region) != PIXMAN_REGION_STATUS_SUCCESS) - status = CAIRO_STATUS_NO_MEMORY; - - pixman_region_destroy (clip_rect); - - if (!CAIRO_OK (status)) - return status; - } + status = _cairo_gstate_intersect_clip (gstate, trap_region); + if (!CAIRO_OK (status)) + return status; _region_rect_extents (trap_region, extents); @@ -1058,8 +1196,8 @@ _composite_traps_intermediate_surface (cairo_gstate_t *gstate, cairo_traps_t *traps, cairo_rectangle_t *extents) { - cairo_surface_t *intermediate; cairo_pattern_union_t pattern; + cairo_surface_t *intermediate; cairo_surface_pattern_t intermediate_pattern; cairo_status_t status; @@ -1089,20 +1227,7 @@ _composite_traps_intermediate_surface (cairo_gstate_t *gstate, if (!CAIRO_OK (status)) goto out; - _cairo_pattern_init_for_surface (&pattern.surface, - gstate->clip.surface); - - status = _cairo_surface_composite (CAIRO_OPERATOR_IN, - &pattern.base, - NULL, - intermediate, - extents->x - gstate->clip.rect.x, - extents->y - gstate->clip.rect.y, - 0, 0, - 0, 0, - extents->width, extents->height); - _cairo_pattern_fini (&pattern.base); - + status = _cairo_gstate_combine_clip_surface (gstate, intermediate, extents); if (!CAIRO_OK (status)) goto out; diff --git a/src/cairo.c b/src/cairo.c index 316827f7..4f17fbea 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -1495,6 +1495,68 @@ cairo_paint (cairo_t *cr) } /** + * cairo_mask: + * @cr: a cairo context + * @pattern: a #cairo_pattern_t + * + * A drawing operator that paints the current source + * using the alpha channel of @pattern as a mask. (Opaque + * areas of @mask are painted with the source, transparent + * areas are not painted.) + */ +void +cairo_mask (cairo_t *cr, + cairo_pattern_t *pattern) +{ + CAIRO_CHECK_SANITY (cr); + if (cr->status) + return; + + cr->status = _cairo_gstate_mask (cr->gstate, pattern); + + CAIRO_CHECK_SANITY (cr); +} + +/** + * cairo_mask_surface: + * @cr: a cairo context + * @surface: a #cairo_surface_t + * @surface_x: X coordinate at which to place the origin of @surface + * @surface_y: Y coordinate at which to place the origin of @surface + * + * A drawing operator that paints the current source + * using the alpha channel of @surface as a mask. (Opaque + * areas of @surface are painted with the source, transparent + * areas are not painted.) + */ +void +cairo_mask_surface (cairo_t *cr, + cairo_surface_t *surface, + double surface_x, + double surface_y) +{ + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + + CAIRO_CHECK_SANITY (cr); + if (cr->status) + return; + + pattern = cairo_pattern_create_for_surface (surface); + if (!pattern) { + cr->status = CAIRO_STATUS_NO_MEMORY; + return; + } + + cairo_matrix_init_translate (&matrix, - surface_x, - surface_y); + cairo_pattern_set_matrix (pattern, &matrix); + + cairo_mask (cr, pattern); + + cairo_pattern_destroy (pattern); +} + +/** * cairo_stroke: * @cr: a cairo context * diff --git a/src/cairo.h b/src/cairo.h index 58b58d82..c0a122fd 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -463,6 +463,16 @@ void cairo_paint (cairo_t *cr); void +cairo_mask (cairo_t *cr, + cairo_pattern_t *pattern); + +void +cairo_mask_surface (cairo_t *cr, + cairo_surface_t *surface, + double surface_x, + double surface_y); + +void cairo_stroke (cairo_t *cr); void diff --git a/src/cairoint.h b/src/cairoint.h index 44618cc7..1a6ff337 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -1052,6 +1052,10 @@ cairo_private cairo_status_t _cairo_gstate_paint (cairo_gstate_t *gstate); cairo_private cairo_status_t +_cairo_gstate_mask (cairo_gstate_t *gstate, + cairo_pattern_t *mask); + +cairo_private cairo_status_t _cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path); cairo_private cairo_status_t diff --git a/test/Makefile.am b/test/Makefile.am index f7f12382..ef625e66 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -10,6 +10,7 @@ gradient-alpha \ leaky-polygon \ line-width \ linear-gradient \ +mask \ move-to-show-surface \ paint \ path-data \ @@ -37,6 +38,7 @@ gradient-alpha-ref.png \ leaky-polygon-ref.png \ line-width-ref.png \ linear-gradient-ref.png \ +mask.png \ move-to-show-surface-ref.png \ coverage-ref.png \ clip-twice-ref.png \ @@ -105,6 +107,7 @@ gradient_alpha_LDADD = $(LDADDS) leaky_polygon_LDADD = $(LDADDS) line_width_LDADD = $(LDADDS) linear_gradient_LDADD = $(LDADDS) +mask_LDADD = $(LDADDS) move_to_show_surface_LDADD = $(LDADDS) paint_LDADD = $(LDADDS) path_data_LDADD = $(LDADDS) diff --git a/test/mask-ref.png b/test/mask-ref.png Binary files differnew file mode 100644 index 00000000..518e3d42 --- /dev/null +++ b/test/mask-ref.png diff --git a/test/mask.c b/test/mask.c new file mode 100644 index 00000000..20547b02 --- /dev/null +++ b/test/mask.c @@ -0,0 +1,241 @@ +/* + * Copyright © 2005 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Red Hat, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Red Hat, Inc. makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Authors: Owen Taylor <otaylor@redhat.com> + * Kristian Høgsberg <krh@redhat.com> + */ + +#include <math.h> +#include "cairo-test.h" +#include <stdio.h> + +#define WIDTH 64 +#define HEIGHT 64 +#define PAD 10 + +const char png_filename[] = "romedalen.png"; + +static void +set_solid_pattern (cairo_t *cr, int x, int y) +{ + cairo_set_source_rgb (cr, 0, 0, 0.6); +} + +static void +set_translucent_pattern (cairo_t *cr, int x, int y) +{ + cairo_set_source_rgba (cr, 0, 0, 0.6, 0.5); +} + +static void +set_gradient_pattern (cairo_t *cr, int x, int y) +{ + cairo_pattern_t *pattern; + + pattern = + cairo_pattern_create_linear (x, y, x + WIDTH, y + HEIGHT); + cairo_pattern_add_color_stop (pattern, 0, 1, 1, 1, 1); + cairo_pattern_add_color_stop (pattern, 1, 0, 0, 0.4, 1); + cairo_set_pattern (cr, pattern); +} + +static void +set_image_pattern (cairo_t *cr, int x, int y) +{ + cairo_pattern_t *pattern; + + pattern = cairo_test_create_png_pattern (cr, png_filename); + cairo_set_pattern (cr, pattern); +} + +static void +mask_polygon (cairo_t *cr, int x, int y) +{ + cairo_surface_t *mask_surface; + cairo_t *cr2; + + mask_surface = cairo_surface_create_similar (cairo_get_target_surface (cr), + CAIRO_FORMAT_A8, + WIDTH, HEIGHT); + cr2 = cairo_create (); + cairo_set_target_surface (cr2, mask_surface); + + cairo_save (cr2); + cairo_set_source_rgba (cr2, 0, 0, 0, 0); /* transparent */ + cairo_set_operator (cr2, CAIRO_OPERATOR_SRC); + cairo_paint (cr2); + cairo_restore (cr2); + + cairo_set_source_rgb (cr2, 1, 1, 1); /* white */ + + cairo_new_path (cr2); + cairo_move_to (cr2, 0, 0); + cairo_line_to (cr2, 0, HEIGHT); + cairo_line_to (cr2, WIDTH / 2, 3 * HEIGHT / 4); + cairo_line_to (cr2, WIDTH, HEIGHT); + cairo_line_to (cr2, WIDTH, 0); + cairo_line_to (cr2, WIDTH / 2, HEIGHT / 4); + cairo_close_path (cr2); + cairo_fill (cr2); + + cairo_destroy (cr2); + + cairo_mask_surface (cr, mask_surface, x, y); + + cairo_surface_destroy (mask_surface); +} + +static void +mask_gradient (cairo_t *cr, int x, int y) +{ + cairo_pattern_t *pattern; + + pattern = cairo_pattern_create_linear (x, y, + x + WIDTH, y + HEIGHT); + + cairo_pattern_add_color_stop_rgba (pattern, + 0, + 1, 1, 1, 1); + cairo_pattern_add_color_stop_rgba (pattern, + 1, + 1, 1, 1, 0); + + cairo_mask (cr, pattern); + + cairo_pattern_destroy (pattern); +} + +static void +clip_none (cairo_t *cr, int x, int y) +{ +} + +static void +clip_rects (cairo_t *cr, int x, int y) +{ + int height = HEIGHT / 3; + + cairo_new_path (cr); + cairo_rectangle (cr, x, y, WIDTH, height); + cairo_rectangle (cr, x, y + 2 * height, WIDTH, height); + cairo_clip (cr); +} + +static void +clip_circle (cairo_t *cr, int x, int y) +{ + cairo_new_path (cr); + cairo_arc (cr, x + WIDTH / 2, y + HEIGHT / 2, + WIDTH / 2, 0, 2 * M_PI); + cairo_clip (cr); + cairo_new_path (cr); +} + +static void (*pattern_funcs[])(cairo_t *cr, int x, int y) = { + set_solid_pattern, + set_translucent_pattern, + set_gradient_pattern, + set_image_pattern, +}; + +static void (*mask_funcs[])(cairo_t *cr, int x, int y) = { + mask_gradient, + mask_polygon, +}; + +static void (*clip_funcs[])(cairo_t *cr, int x, int y) = { + clip_none, + clip_rects, + clip_circle, +}; + +#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) +#define IMAGE_WIDTH (ARRAY_SIZE (pattern_funcs) * (WIDTH + PAD) + PAD) +#define IMAGE_HEIGHT (ARRAY_SIZE (mask_funcs) * ARRAY_SIZE (clip_funcs) * (HEIGHT + PAD) + PAD) + +static cairo_test_t test = { + "mask", + "Tests of cairo_mask", + IMAGE_WIDTH, IMAGE_HEIGHT +}; + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + cairo_surface_t *tmp_surface; + cairo_pattern_t *tmp_pattern; + int i, j, k; + cairo_t *cr2; + + /* Some of our drawing is unbounded, so we draw each test to + * a temporary surface and copy over. + */ + tmp_surface = cairo_surface_create_similar (cairo_get_target_surface (cr), + CAIRO_FORMAT_ARGB32, + IMAGE_WIDTH, IMAGE_HEIGHT); + cr2 = cairo_create (); + cairo_set_target_surface (cr2, tmp_surface); + + tmp_pattern = cairo_pattern_create_for_surface (tmp_surface); + cairo_set_source (cr, tmp_pattern); + + for (k = 0; k < ARRAY_SIZE (clip_funcs); k++) { + for (j = 0; j < ARRAY_SIZE (mask_funcs); j++) { + for (i = 0; i < ARRAY_SIZE (pattern_funcs); i++) { + int x = i * (WIDTH + PAD) + PAD; + int y = (ARRAY_SIZE (mask_funcs) * k + j) * (HEIGHT + PAD) + PAD; + + /* Clear area we are going to be drawing onto */ + cairo_save (cr2); + cairo_set_source_rgba (cr2, 0, 0, 0, 0); /* transparent */ + cairo_set_operator (cr2, CAIRO_OPERATOR_SRC); + cairo_rectangle (cr2, x, y, WIDTH, HEIGHT); + cairo_fill (cr2); + cairo_restore (cr2); + + /* draw */ + cairo_save (cr2); + + clip_funcs[k] (cr2, x, y); + pattern_funcs[i] (cr2, x, y); + mask_funcs[j] (cr2, x, y); + + cairo_restore (cr2); + + /* Copy back to the main pixmap */ + cairo_rectangle (cr, x, y, WIDTH, HEIGHT); + cairo_fill (cr); + } + } + } + + cairo_destroy (cr2); + cairo_surface_destroy (tmp_surface); + + return CAIRO_TEST_SUCCESS; +} + +int +main (void) +{ + return cairo_test (&test, draw); +} |