diff options
Diffstat (limited to 'src/cairo-surface-shadow.c')
-rwxr-xr-x | src/cairo-surface-shadow.c | 2286 |
1 files changed, 2286 insertions, 0 deletions
diff --git a/src/cairo-surface-shadow.c b/src/cairo-surface-shadow.c new file mode 100755 index 000000000..5224bee40 --- /dev/null +++ b/src/cairo-surface-shadow.c @@ -0,0 +1,2286 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * Copyright © 2007 Adrian Johnson + * Copyright © 2009 Chris Wilson + * Copyright © 2013 Samsung Research America, Silicon Valley + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Henry Song <henry.song@samsung.com + */ + +#include "cairoint.h" +#include "cairo-surface-private.h" +#include "cairo-clip-inline.h" +#include "cairo-error-private.h" +#include "cairo-pattern-private.h" +#include "cairo-surface-shadow-private.h" +#include "cairo-surface-scale-translate-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-list-inline.h" +#include "cairo-device-private.h" +#include "cairo-image-surface-private.h" + +#define MAX_SHADOW_SIZE 1024 + +typedef struct _cairo_shadow_cache_list { + cairo_list_t *caches; + unsigned long *size; + cairo_bool_t locked; +} cairo_shadow_cache_list_t; + +static unsigned long +_cairo_stroke_style_hash (unsigned long hash, + const cairo_stroke_style_t *style) +{ + hash = _cairo_hash_bytes (hash, style, sizeof (cairo_stroke_style_t)); + if (style->num_dashes) + hash = _cairo_hash_bytes (hash, style->dash, sizeof (double) * style->num_dashes); + return hash; +} + +static unsigned long +_cairo_matrix_hash (unsigned long hash, const cairo_matrix_t *matrix) +{ + return _cairo_hash_bytes (hash, matrix, sizeof (cairo_matrix_t)); +} + +static unsigned long +_cairo_path_fixed_rel_hash (unsigned long hash, const cairo_path_fixed_t *path) +{ + const cairo_path_buf_t *buf; + unsigned int count; + cairo_path_fixed_t path_copy; + cairo_status_t status; + unsigned int i; + cairo_fixed_t dx, dy; + + status = _cairo_path_fixed_init_copy (&path_copy, path); + if (unlikely (status)) + return hash; + + dx = path_copy.buf.points[0].x; + dy = path_copy.buf.points[0].y; + + cairo_path_foreach_buf_start (buf, &path_copy) { + for (i = 0; i < buf->num_points; i++) { + buf->points[i].x -= dx; + buf->points[i].y -= dy; + } + } cairo_path_foreach_buf_end (buf, &path_copy); + + count = 0; + cairo_path_foreach_buf_start (buf, &path_copy) { + hash = _cairo_hash_bytes (hash, buf->op, + buf->num_ops * sizeof (buf->op[0])); + count += buf->num_ops; + } cairo_path_foreach_buf_end (buf, &path_copy); + hash = _cairo_hash_bytes (hash, &count, sizeof (count)); + + count = 0; + cairo_path_foreach_buf_start (buf, &path_copy) { + hash = _cairo_hash_bytes (hash, buf->points, + buf->num_points * sizeof (buf->points[0])); + count += buf->num_points; + } cairo_path_foreach_buf_end (buf, &path_copy); + hash = _cairo_hash_bytes (hash, &count, sizeof (count)); + + _cairo_path_fixed_fini (&path_copy); + + return hash; +} + +static unsigned long +_cairo_shadow_hash (unsigned long hash, const cairo_shadow_t *shadow) +{ + return _cairo_hash_bytes (hash, shadow, sizeof (cairo_shadow_t) - sizeof (cairo_bool_t)); +} + +static unsigned long +_cairo_shadow_hash_for_paint (const cairo_pattern_t *source, + const cairo_shadow_t *shadow) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + cairo_bool_t use_color = shadow->type == CAIRO_SHADOW_INSET; + + hash = _cairo_pattern_hash_with_hash (hash, source, use_color); + return _cairo_shadow_hash (hash, shadow); +} + +static unsigned long +_cairo_shadow_hash_for_mask (const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_shadow_t *shadow) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + cairo_bool_t use_color = shadow->type == CAIRO_SHADOW_INSET; + + hash = _cairo_pattern_hash_with_hash (hash, source, use_color); + hash = _cairo_pattern_hash_with_hash (hash, mask, use_color); + return _cairo_shadow_hash (hash, shadow); +} + +static unsigned long +_cairo_shadow_hash_for_fill (const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + const cairo_shadow_t *shadow) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + /* FIXME: for OVER operator, we don't need to hash the source + * color, for other operators, we might */ + cairo_bool_t use_color = shadow->type == CAIRO_SHADOW_INSET; + use_color = FALSE; + + hash = _cairo_pattern_hash_with_hash (hash, source, use_color); + hash = _cairo_path_fixed_rel_hash (hash, path); + hash = _cairo_hash_bytes (hash, &fill_rule, sizeof (cairo_fill_rule_t)); + return _cairo_shadow_hash (hash, shadow); +} + +static unsigned long +_cairo_shadow_hash_for_stroke (const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t*stroke_style, + const cairo_matrix_t *ctm, + const cairo_shadow_t *shadow) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + + /* FIXME: for OVER operator, we don't need to hash the source + * color, for other operators, we might */ + cairo_bool_t use_color = shadow->type == CAIRO_SHADOW_INSET; + use_color = FALSE; + + hash = _cairo_pattern_hash_with_hash (hash, source, use_color); + hash = _cairo_path_fixed_rel_hash (hash, path); + hash = _cairo_stroke_style_hash (hash, stroke_style); + hash = _cairo_matrix_hash (hash, ctm); + return _cairo_shadow_hash (hash, shadow); +} + +static void +_cairo_shadow_cache_init (cairo_shadow_cache_t *shadow_cache, + cairo_surface_t *cache_surface, + unsigned long size, + unsigned long hash, + int x_blur, + int y_blur, + double scale) +{ + cairo_list_init (&shadow_cache->link); + shadow_cache->surface = cairo_surface_reference (cache_surface); + shadow_cache->size = size; + shadow_cache->hash = hash; + shadow_cache->x_blur = x_blur; + shadow_cache->y_blur = y_blur; + shadow_cache->scale = scale; +} + +static void +_cairo_shadow_cache_destroy (cairo_shadow_cache_t *shadow_cache) +{ + cairo_list_del (&shadow_cache->link); + cairo_surface_destroy (shadow_cache->surface); + free (shadow_cache); +} + +static void +_cairo_shadow_cache_list_shrink_to_accomodate (cairo_shadow_cache_list_t *shadow_caches, + unsigned long additional) +{ + cairo_shadow_cache_t *shadow_cache; + + while (*(shadow_caches->size) + additional > MAX_SHADOW_CACHE_SIZE) { + shadow_cache = cairo_list_last_entry (shadow_caches->caches, + cairo_shadow_cache_t, + link); + *(shadow_caches->size) -= shadow_cache->size; + _cairo_shadow_cache_destroy (shadow_cache); + } +} + +static cairo_shadow_cache_t * +_cairo_shadow_cache_list_find (cairo_shadow_cache_list_t *shadow_caches, + unsigned long hash) +{ + cairo_shadow_cache_t *shadow_cache; + + cairo_list_foreach_entry (shadow_cache, + cairo_shadow_cache_t, + shadow_caches->caches, link) + if (shadow_cache->hash == hash) { + return shadow_cache; + } + + return NULL; +} + +static double +_calculate_shadow_extents_scale (cairo_rectangle_int_t *extents, + int shadow_width, int shadow_height) +{ + double x_scale = (double)extents->width / (double)shadow_width; + double y_scale = (double)extents->height / (double)shadow_height; + + return MIN (1.0, MIN (x_scale, y_scale)); +} + +static void +_cairo_shadow_cache_list_init (cairo_shadow_cache_list_t *shadow_cache_list, + cairo_surface_t *target) +{ + cairo_status_t status; + cairo_device_t *device = NULL; + + if(target != NULL) + device = target->device; + + if (device != NULL) { + shadow_cache_list->caches = &device->shadow_caches; + shadow_cache_list->size = &device->shadow_caches_size; + shadow_cache_list->locked = FALSE; + } + else if (target != NULL && + target->backend && + target->backend->has_shadow_cache && + target->backend->has_shadow_cache (target)) { + status = target->backend->shadow_cache_acquire (target); + shadow_cache_list->locked = TRUE; + + if (status == CAIRO_STATUS_SUCCESS) { + shadow_cache_list->caches = target->backend->get_shadow_cache (target); + if (shadow_cache_list->caches) { + shadow_cache_list->size = target->backend->get_shadow_cache_size (target); + } + } + } +} + +static cairo_surface_t* +_cairo_ensure_shadow_surface (cairo_surface_t *target, + cairo_rectangle_int_t *shadow_surface_extents, + int x_blur, int y_blur, + int shadow_width, int shadow_height) +{ + int width_out, height_out; + cairo_content_t content; + cairo_surface_t *shadow_surface; + cairo_bool_t has_blur = ! (x_blur == 0 && y_blur == 0); + + if (target->backend->get_shadow_surface) + shadow_surface = target->backend->get_shadow_surface (target, + has_blur, + shadow_width, + shadow_height, + &width_out, + &height_out); + else { + if (has_blur) { + width_out = MIN (shadow_width, MAX_SHADOW_SIZE) * 0.5; + height_out = MIN (shadow_width, MAX_SHADOW_SIZE) * 0.5; + } + else { + width_out = MIN (shadow_width, MAX_SHADOW_SIZE); + height_out = MIN (shadow_width, MAX_SHADOW_SIZE); + } + + content = cairo_surface_get_content (target); + if (content == CAIRO_CONTENT_COLOR) + content = CAIRO_CONTENT_COLOR_ALPHA; + shadow_surface = cairo_surface_create_similar (target, + content, + width_out, + height_out); + _cairo_surface_release_device_reference (shadow_surface); + } + + shadow_surface_extents->x = 0; + shadow_surface_extents->y = 0; + shadow_surface_extents->width = width_out; + shadow_surface_extents->height = height_out; + + return shadow_surface; +} + +/* A collection of routines to draw shadow*/ + +cairo_status_t +_cairo_surface_shadow_paint (cairo_surface_t *target, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip, + const cairo_shadow_t *shadow) +{ + cairo_status_t status; + cairo_pattern_union_t shadow_source; + cairo_rectangle_t shadow_extents; + cairo_pattern_t *shadow_pattern = NULL; + cairo_pattern_t *color_pattern = NULL; + cairo_surface_t *shadow_surface = NULL; + cairo_rectangle_int_t shadow_surface_extents; + + int shadow_width, shadow_height; + int x_blur, y_blur; + cairo_shadow_t shadow_copy = *shadow; + + cairo_matrix_t m; + double scale; + double x_offset = shadow->x_offset; + double y_offset = shadow->y_offset; + cairo_content_t content; + + unsigned long hash = 0; + cairo_shadow_cache_t *shadow_cache = NULL; + cairo_device_t *device = target->device; + unsigned long size; + cairo_surface_t *cache_surface = NULL; + cairo_bool_t bounded; + cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only; + cairo_shadow_type_t shadow_type = source->shadow.type; + cairo_bool_t has_blur = ! (source->shadow.x_blur == 0.0 && + source->shadow.y_blur == 0.0); + + cairo_shadow_cache_list_t shadow_cache_list; + + if (shadow->type != CAIRO_SHADOW_DROP) + return CAIRO_STATUS_SUCCESS; + + if (shadow->color.alpha == 0.0) + return CAIRO_STATUS_SUCCESS; + + if (shadow->x_blur <= 0.0 && shadow->y_blur <= 0.0 && + shadow->x_offset == 0.0 && shadow->y_offset == 0.0) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; + + _cairo_shadow_cache_list_init (&shadow_cache_list, target); + if (shadow_cache_list.caches != NULL) { + hash = _cairo_shadow_hash_for_paint (source, shadow); + shadow_cache = _cairo_shadow_cache_list_find (&shadow_cache_list, hash); + } + + if (shadow_cache != NULL) { + /* paint the shadow surface to target */ + x_blur = shadow_cache->x_blur; + y_blur = shadow_cache->y_blur; + + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + 1.0); + + status = _cairo_surface_paint_get_offset_extents (target, + x_offset, + y_offset, + source, + clip, + &shadow_source.base, + &shadow_extents, + &bounded); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 || shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + cairo_matrix_init_scale (&m, shadow_cache->scale, shadow_cache->scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + shadow_pattern = cairo_pattern_create_for_surface (shadow_cache->surface); + cairo_pattern_set_matrix (shadow_pattern, &m); + + status = _cairo_surface_mask (target, op, color_pattern, + shadow_pattern, clip); + cairo_list_move (&shadow_cache->link, shadow_cache_list.caches); + goto FINISH; + } + + ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE; + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE; + + x_blur = ceil (shadow_copy.x_blur); + y_blur = ceil (shadow_copy.y_blur); + + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + shadow_copy.color.alpha); + + status = _cairo_surface_paint_get_offset_extents (target, + x_offset, y_offset, + source, + clip, + &shadow_source.base, + &shadow_extents, + &bounded); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 && shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + shadow_width = ceil (shadow_extents.width + x_blur * 2); + shadow_height = ceil (shadow_extents.height + y_blur * 2); + + shadow_surface = _cairo_ensure_shadow_surface (target, + &shadow_surface_extents, + x_blur, y_blur, + shadow_width, shadow_height); + if (! shadow_surface || unlikely (shadow_surface->status)) + goto FINISH; + + if ((device || shadow_cache_list.locked) && + shadow->enable_cache && bounded && has_blur) { + content = cairo_surface_get_content (target); + if (content == CAIRO_CONTENT_COLOR) + content = CAIRO_CONTENT_COLOR_ALPHA; + + cache_surface = cairo_surface_create_similar (target, content, + shadow_surface_extents.width, + shadow_surface_extents.height); + if (unlikely (cache_surface->status)) + goto FINISH; + + if (device) + _cairo_surface_release_device_reference (cache_surface); + } + + scale = _calculate_shadow_extents_scale (&shadow_surface_extents, + shadow_width, + shadow_height); + cairo_matrix_init_scale (&m, scale, scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + /* paint with offset and scale */ + status = _cairo_surface_scale_translate_paint (shadow_surface, + TRUE, + &m, + CAIRO_OPERATOR_OVER, + &shadow_source.base, + NULL); + + if (unlikely (status)) + goto FINISH; + + shadow_pattern = cairo_pattern_create_for_surface (shadow_surface); + cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN); + cairo_pattern_set_sigma (shadow_pattern, + shadow_copy.x_blur * scale * 0.5, + shadow_copy.y_blur * scale * 0.5); + + status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, 1024); + if (unlikely (status)) + goto FINISH; + + if ((shadow_cache_list.locked ||device) && + shadow->enable_cache && bounded && has_blur) { + status = _cairo_surface_mask (cache_surface, CAIRO_OPERATOR_OVER, + color_pattern, shadow_pattern, NULL); + if (unlikely (status)) + goto FINISH; + + cairo_pattern_destroy (shadow_pattern); + + size = shadow_surface_extents.width * shadow_surface_extents.height; + _cairo_shadow_cache_list_shrink_to_accomodate (&shadow_cache_list, + size); + + shadow_cache = malloc (sizeof (cairo_shadow_cache_t)); + _cairo_shadow_cache_init (shadow_cache, + cache_surface, + size, + hash, + x_blur, + y_blur, + scale); + + cairo_list_add (&shadow_cache->link, shadow_cache_list.caches); + *shadow_cache_list.size += size; + + shadow_pattern = cairo_pattern_create_for_surface (cache_surface); + cairo_pattern_set_matrix (shadow_pattern, &m); + + cairo_pattern_destroy (color_pattern); + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + 1.0); + } + else + cairo_pattern_set_matrix (shadow_pattern, &m); + + status = _cairo_surface_mask (target, op, + color_pattern, shadow_pattern, clip); + +FINISH: + cairo_pattern_destroy (color_pattern); + + if (shadow_pattern) + cairo_pattern_destroy (shadow_pattern); + + cairo_surface_destroy (shadow_surface); + cairo_surface_destroy (cache_surface); + + if (shadow_cache_list.locked) + target->backend->shadow_cache_release (target); + + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only; + ((cairo_pattern_t *)source)->shadow.type = shadow_type; + return status; +} + +cairo_status_t +_cairo_surface_shadow_mask (cairo_surface_t *target, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip, + const cairo_shadow_t *shadow) +{ + cairo_status_t status; + cairo_pattern_union_t shadow_source; + cairo_pattern_union_t shadow_mask; + cairo_rectangle_t shadow_extents; + cairo_pattern_t *shadow_pattern = NULL; + cairo_pattern_t *color_pattern = NULL; + cairo_surface_t *shadow_surface = NULL; + cairo_rectangle_int_t shadow_surface_extents; + cairo_content_t content; + + int shadow_width, shadow_height; + int x_blur, y_blur; + cairo_shadow_t shadow_copy = *shadow; + + cairo_matrix_t m; + double scale; + double x_offset = shadow->x_offset; + double y_offset = shadow->y_offset; + + unsigned long hash = 0; + cairo_shadow_cache_t *shadow_cache = NULL; + cairo_device_t *device = target->device; + unsigned long size; + cairo_surface_t *cache_surface = NULL; + cairo_bool_t bounded; + cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only; + cairo_shadow_type_t shadow_type = source->shadow.type; + cairo_bool_t has_blur = ! (source->shadow.x_blur == 0.0 && + source->shadow.y_blur == 0.0); + + cairo_shadow_cache_list_t shadow_cache_list; + + if (shadow->type != CAIRO_SHADOW_DROP) + return CAIRO_STATUS_SUCCESS; + + if (shadow->color.alpha == 0.0) + return CAIRO_STATUS_SUCCESS; + + if (shadow->x_blur <= 0.0 && shadow->y_blur <= 0.0 && + shadow->x_offset == 0.0 && shadow->y_offset == 0.0) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; + + if (shadow->x_blur == 0.0 && shadow->y_blur == 0.0) { + status = _cairo_surface_mask_get_offset_extents (target, + x_offset, + y_offset, + source, + mask, + clip, + &shadow_source.base, + &shadow_mask.base, + &shadow_extents, + &bounded); + if (unlikely (status)) { + return status; + } + + cairo_matrix_init_identity (&m); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + /* stroke to target with offset */ + shadow_source.base.shadow.type = CAIRO_SHADOW_NONE; + shadow_source.base.shadow.draw_shadow_only = FALSE; + status = _cairo_surface_scale_translate_mask (target, + FALSE, + &m, + op, + &shadow_source.base, + &shadow_mask.base, + clip); + + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only; + ((cairo_pattern_t *)source)->shadow.type = shadow_type; + return status; + } + + _cairo_shadow_cache_list_init (&shadow_cache_list, target); + if (shadow_cache_list.caches != NULL) { + hash = _cairo_shadow_hash_for_mask (source, mask, shadow); + shadow_cache = _cairo_shadow_cache_list_find (&shadow_cache_list, hash); + } + + if (shadow_cache != NULL) { + /* paint the shadow surface to target */ + x_blur = shadow_cache->x_blur; + y_blur = shadow_cache->y_blur; + + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + 1.0); + + status = _cairo_surface_mask_get_offset_extents (target, + x_offset, + y_offset, + source, + mask, + clip, + &shadow_source.base, + &shadow_mask.base, + &shadow_extents, + &bounded); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 || shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + cairo_matrix_init_scale (&m, shadow_cache->scale, shadow_cache->scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + shadow_pattern = cairo_pattern_create_for_surface (shadow_cache->surface); + cairo_pattern_set_matrix (shadow_pattern, &m); + + status = _cairo_surface_mask (target, op, color_pattern, + shadow_pattern, clip); + cairo_list_move (&shadow_cache->link, shadow_cache_list.caches); + goto FINISH; + } + + ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE; + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE; + + x_blur = ceil (shadow_copy.x_blur); + y_blur = ceil (shadow_copy.y_blur); + + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + shadow_copy.color.alpha); + + status = _cairo_surface_mask_get_offset_extents (target, + x_offset, y_offset, + source, + mask, + clip, + &shadow_source.base, + &shadow_mask.base, + &shadow_extents, + &bounded); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 && shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + shadow_width = ceil (shadow_extents.width + x_blur * 2); + shadow_height = ceil (shadow_extents.height + y_blur * 2); + + shadow_surface = _cairo_ensure_shadow_surface (target, + &shadow_surface_extents, + x_blur, y_blur, + shadow_width, shadow_height); + if (! shadow_surface || unlikely (shadow_surface->status)) + goto FINISH; + + if ((shadow_cache_list.locked || device) && + shadow->enable_cache && bounded && has_blur) { + content = cairo_surface_get_content (target); + if (content == CAIRO_CONTENT_COLOR) + content = CAIRO_CONTENT_COLOR_ALPHA; + + cache_surface = cairo_surface_create_similar (target, content, + shadow_surface_extents.width, + shadow_surface_extents.height); + if (unlikely (cache_surface->status)) + goto FINISH; + + if (device) + _cairo_surface_release_device_reference (cache_surface); + } + + scale = _calculate_shadow_extents_scale (&shadow_surface_extents, + shadow_width, + shadow_height); + cairo_matrix_init_scale (&m, scale, scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + /* paint with offset and scale */ + status = _cairo_surface_scale_translate_mask (shadow_surface, + TRUE, + &m, + CAIRO_OPERATOR_OVER, + &shadow_source.base, + &shadow_mask.base, + NULL); + if (unlikely (status)) + goto FINISH; + + shadow_pattern = cairo_pattern_create_for_surface (shadow_surface); + cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN); + cairo_pattern_set_sigma (shadow_pattern, + shadow_copy.x_blur * scale * 0.5, + shadow_copy.y_blur * scale * 0.5); + + status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, 1024); + if (unlikely (status)) + goto FINISH; + + if ((shadow_cache_list.locked || device) && + shadow->enable_cache && bounded && has_blur) { + status = _cairo_surface_mask (cache_surface, CAIRO_OPERATOR_OVER, + color_pattern, shadow_pattern, NULL); + if (unlikely (status)) + goto FINISH; + + cairo_pattern_destroy (shadow_pattern); + + size = shadow_surface_extents.width * shadow_surface_extents.height; + _cairo_shadow_cache_list_shrink_to_accomodate (&shadow_cache_list, + size); + + shadow_cache = malloc (sizeof (cairo_shadow_cache_t)); + _cairo_shadow_cache_init (shadow_cache, + cache_surface, + size, + hash, + x_blur, + y_blur, + scale); + + cairo_list_add (&shadow_cache->link, shadow_cache_list.caches); + *shadow_cache_list.size += size; + + shadow_pattern = cairo_pattern_create_for_surface (cache_surface); + cairo_pattern_set_matrix (shadow_pattern, &m); + + cairo_pattern_destroy (color_pattern); + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + 1.0); + } + else + cairo_pattern_set_matrix (shadow_pattern, &m); + + status = _cairo_surface_mask (target, op, + color_pattern, shadow_pattern, clip); + +FINISH: + cairo_pattern_destroy (color_pattern); + + if (shadow_pattern) + cairo_pattern_destroy (shadow_pattern); + + cairo_surface_destroy (shadow_surface); + cairo_surface_destroy (cache_surface); + + if (shadow_cache_list.locked) + target->backend->shadow_cache_release (target); + + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only; + ((cairo_pattern_t *)source)->shadow.type = shadow_type; + return status; +} + +static cairo_status_t +_cairo_surface_inset_shadow_stroke (cairo_surface_t *target, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t*stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip, + const cairo_shadow_t *shadow) +{ + cairo_status_t status; + cairo_pattern_union_t shadow_source; + cairo_path_fixed_t shadow_path; + cairo_rectangle_t shadow_extents; + cairo_pattern_t *shadow_pattern = NULL; + cairo_pattern_t *color_pattern = NULL; + cairo_surface_t *shadow_surface = NULL; + cairo_rectangle_int_t extents; + cairo_rectangle_int_t shadow_surface_extents; + cairo_matrix_t shadow_ctm, shadow_ctm_inverse; + cairo_content_t content; + + int shadow_width, shadow_height; + int x_blur, y_blur; + cairo_shadow_t shadow_copy = *shadow; + cairo_color_t bg_color; + + cairo_matrix_t m; + double scale; + double x_offset = shadow->x_offset; + double y_offset = shadow->y_offset; + unsigned long hash = 0; + cairo_shadow_cache_t *shadow_cache = NULL; + cairo_device_t *device = target->device; + unsigned long size; + cairo_surface_t *cache_surface = NULL; + cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only; + cairo_shadow_type_t shadow_type = source->shadow.type; + cairo_bool_t has_blur = ! (source->shadow.x_blur == 0.0 && + source->shadow.y_blur == 0.0); + double line_width = stroke_style->line_width; + + cairo_shadow_cache_list_t shadow_cache_list; + + if (shadow->color.alpha == 0.0) + return CAIRO_STATUS_SUCCESS; + + _cairo_shadow_cache_list_init (&shadow_cache_list, target); + if (shadow_cache_list.caches != NULL) { + hash = _cairo_shadow_hash_for_stroke (source, path, stroke_style, ctm, shadow); + shadow_cache = _cairo_shadow_cache_list_find (&shadow_cache_list, hash); + } + + if (shadow_cache != NULL) { + /* paint the shadow surface to target */ + x_blur = shadow_cache->x_blur; + y_blur = shadow_cache->y_blur; + + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + 1.0); + + status = _cairo_surface_stroke_get_offset_extents (target, + TRUE, + x_offset, + y_offset, + source, + path, + stroke_style, + ctm, ctm_inverse, + tolerance, clip, + &shadow_source.base, + &shadow_path, + &shadow_ctm, + &shadow_ctm_inverse, + &shadow_extents); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 || shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + cairo_matrix_init_scale (&m, shadow_cache->scale, shadow_cache->scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + shadow_pattern = cairo_pattern_create_for_surface (shadow_cache->surface); + cairo_pattern_set_matrix (shadow_pattern, &m); + + status = _cairo_surface_stroke (target, op, shadow_pattern, + path, stroke_style, + ctm, ctm_inverse, tolerance, + antialias, clip); + cairo_list_move (&shadow_cache->link, shadow_cache_list.caches); + goto FINISH; + } + + ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE; + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE; + + x_blur = ceil (shadow_copy.x_blur); + y_blur = ceil (shadow_copy.y_blur); + + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + shadow_copy.color.alpha); + + status = _cairo_surface_stroke_get_offset_extents (target, + TRUE, + x_offset, y_offset, + source, + path, + stroke_style, + ctm, ctm_inverse, + tolerance, clip, + &shadow_source.base, + &shadow_path, + &shadow_ctm, + &shadow_ctm_inverse, + &shadow_extents); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 || shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + shadow_width = ceil (shadow_extents.width + x_blur * 2); + shadow_height = ceil (shadow_extents.height + y_blur * 2); + + shadow_surface = _cairo_ensure_shadow_surface (target, + &shadow_surface_extents, + x_blur, y_blur, + shadow_width, shadow_height); + if (! shadow_surface || unlikely (shadow_surface->status)) + goto FINISH; + + _cairo_surface_get_extents (shadow_surface, &extents); + + if ((shadow_cache_list.locked || device) && + shadow->enable_cache && has_blur) { + content = cairo_surface_get_content (target); + if (content == CAIRO_CONTENT_COLOR) + content = CAIRO_CONTENT_COLOR_ALPHA; + + cache_surface = cairo_surface_create_similar (target, content, + shadow_surface_extents.width, + shadow_surface_extents.height); + if (unlikely (cache_surface->status)) + goto FINISH; + + if (device) + _cairo_surface_release_device_reference (cache_surface); + } + + scale = _calculate_shadow_extents_scale (&shadow_surface_extents, + shadow_width, + shadow_height); + if (line_width * scale <= 1.0) + ((cairo_stroke_style_t *)stroke_style)->line_width = line_width / scale; + cairo_matrix_init_scale (&m, scale, scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + _cairo_color_init_rgba (&bg_color, + shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + shadow_copy.color.alpha); + + /* paint with offset and scale */ + status = _cairo_surface_scale_translate_stroke (shadow_surface, + &bg_color, + &m, + CAIRO_OPERATOR_CLEAR, + &shadow_source.base, + &shadow_path, + stroke_style, + &shadow_ctm, + &shadow_ctm_inverse, + tolerance, + antialias, + NULL); + + if (unlikely (status)) + goto FINISH; + + shadow_pattern = cairo_pattern_create_for_surface (shadow_surface); + cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN); + cairo_pattern_set_sigma (shadow_pattern, + shadow_copy.x_blur * scale * 0.5, + shadow_copy.y_blur * scale * 0.5); + + status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, + line_width * scale); + if (unlikely (status)) + goto FINISH; + + /* blur to mask surface */ + cairo_matrix_init_scale (&m, scale, scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + if ((shadow_cache_list.locked || device) && + shadow->enable_cache && has_blur) { + status = _cairo_surface_paint (cache_surface, CAIRO_OPERATOR_OVER, + shadow_pattern, NULL); + if (unlikely (status)) + goto FINISH; + + cairo_pattern_destroy (shadow_pattern); + + size = shadow_surface_extents.width * shadow_surface_extents.height; + _cairo_shadow_cache_list_shrink_to_accomodate (&shadow_cache_list, + size); + + shadow_cache = malloc (sizeof (cairo_shadow_cache_t)); + _cairo_shadow_cache_init (shadow_cache, + cache_surface, + size, + hash, + x_blur, + y_blur, + scale); + + cairo_list_add (&shadow_cache->link, shadow_cache_list.caches); + *shadow_cache_list.size += size; + + shadow_pattern = cairo_pattern_create_for_surface (cache_surface); + cairo_pattern_set_matrix (shadow_pattern, &m); + + status = _cairo_surface_stroke (target, op, shadow_pattern, + path, stroke_style, ctm, + ctm_inverse, tolerance, + antialias, clip); + + } + else { + cairo_pattern_set_matrix (shadow_pattern, &m); + status = _cairo_surface_stroke (target, op, shadow_pattern, + path, stroke_style, + ctm, ctm_inverse, + tolerance, antialias, clip); + } + +FINISH: + _cairo_path_fixed_fini (&shadow_path); + cairo_pattern_destroy (color_pattern); + + if (shadow_pattern) + cairo_pattern_destroy (shadow_pattern); + + cairo_surface_destroy (shadow_surface); + cairo_surface_destroy (cache_surface); + + if (shadow_cache_list.locked) + target->backend->shadow_cache_release (target); + + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only; + ((cairo_pattern_t *)source)->shadow.type = shadow_type; + ((cairo_stroke_style_t *)stroke_style)->line_width = line_width; + return status; +} + +cairo_status_t +_cairo_surface_shadow_stroke (cairo_surface_t *target, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t*stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip, + const cairo_shadow_t *shadow) +{ + cairo_status_t status; + cairo_pattern_union_t shadow_source; + cairo_path_fixed_t shadow_path; + cairo_rectangle_t shadow_extents; + cairo_pattern_t *shadow_pattern = NULL; + cairo_pattern_t *color_pattern = NULL; + cairo_surface_t *shadow_surface = NULL; + cairo_rectangle_int_t shadow_surface_extents; + cairo_matrix_t shadow_ctm, shadow_ctm_inverse; + cairo_content_t content; + cairo_color_t bg_color; + + int shadow_width, shadow_height; + int x_blur, y_blur; + cairo_shadow_t shadow_copy = *shadow; + + cairo_matrix_t m; + double scale; + double x_offset = shadow->x_offset; + double y_offset = shadow->y_offset; + unsigned long hash = 0; + cairo_shadow_cache_t *shadow_cache = NULL; + cairo_device_t *device = target->device; + unsigned long size; + cairo_surface_t *cache_surface = NULL; + cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only; + cairo_shadow_type_t shadow_type = source->shadow.type; + cairo_bool_t has_blur = ! (source->shadow.x_blur == 0.0 && + source->shadow.y_blur == 0.0); + double line_width = stroke_style->line_width; + + cairo_shadow_cache_list_t shadow_cache_list; + + if (shadow->type == CAIRO_SHADOW_NONE) + return CAIRO_STATUS_SUCCESS; + + if (shadow->color.alpha == 0.0) + return CAIRO_STATUS_SUCCESS; + + if (shadow->x_blur <= 0.0 && shadow->y_blur <= 0.0 && + shadow->x_offset == 0.0 && shadow->y_offset == 0.0) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; + + if (shadow->type == CAIRO_SHADOW_INSET) + return _cairo_surface_inset_shadow_stroke (target, op, source, + path, stroke_style, + ctm, ctm_inverse, + tolerance, antialias, + clip, shadow); + + _cairo_shadow_cache_list_init (&shadow_cache_list, target); + if (shadow_cache_list.caches != NULL) { + hash = _cairo_shadow_hash_for_stroke (source, path, stroke_style, ctm, shadow); + shadow_cache = _cairo_shadow_cache_list_find (&shadow_cache_list, hash); + } + + if (shadow_cache != NULL) { + /* paint the shadow surface to target */ + x_blur = shadow_cache->x_blur; + y_blur = shadow_cache->y_blur; + + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + 1.0); + + status = _cairo_surface_stroke_get_offset_extents (target, + FALSE, + x_offset, + y_offset, + source, + path, + stroke_style, + ctm, ctm_inverse, + tolerance, clip, + &shadow_source.base, + &shadow_path, + &shadow_ctm, + &shadow_ctm_inverse, + &shadow_extents); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 || shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + cairo_matrix_init_scale (&m, shadow_cache->scale, shadow_cache->scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + shadow_pattern = cairo_pattern_create_for_surface (shadow_cache->surface); + cairo_pattern_set_matrix (shadow_pattern, &m); + + status = _cairo_surface_mask (target, op, color_pattern, + shadow_pattern, clip); + cairo_list_move (&shadow_cache->link, shadow_cache_list.caches); + goto FINISH; + } + + ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE; + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE; + + x_blur = ceil (shadow_copy.x_blur); + y_blur = ceil (shadow_copy.y_blur); + + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + shadow_copy.color.alpha); + + status = _cairo_surface_stroke_get_offset_extents (target, + FALSE, + x_offset, y_offset, + source, + path, + stroke_style, + ctm, ctm_inverse, + tolerance, clip, + &shadow_source.base, + &shadow_path, + &shadow_ctm, + &shadow_ctm_inverse, + &shadow_extents); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 || shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + shadow_width = ceil (shadow_extents.width + x_blur * 2); + shadow_height = ceil (shadow_extents.height + y_blur * 2); + + shadow_surface = _cairo_ensure_shadow_surface (target, + &shadow_surface_extents, + x_blur, y_blur, + shadow_width, shadow_height); + if (! shadow_surface || unlikely (shadow_surface->status)) + goto FINISH; + + if ((shadow_cache_list.locked || device) && + shadow->enable_cache && has_blur) { + content = cairo_surface_get_content (target); + if (content == CAIRO_CONTENT_COLOR) + content = CAIRO_CONTENT_COLOR_ALPHA; + + cache_surface = cairo_surface_create_similar (target, content, + shadow_surface_extents.width, + shadow_surface_extents.height); + if (unlikely (cache_surface->status)) + goto FINISH; + + if (device) + _cairo_surface_release_device_reference (cache_surface); + } + + scale = _calculate_shadow_extents_scale (&shadow_surface_extents, + shadow_width, + shadow_height); + + if (line_width * scale <= 1.0) + ((cairo_stroke_style_t *)stroke_style)->line_width = line_width / scale; + + cairo_matrix_init_scale (&m, scale, scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + /* paint with offset and scale */ + _cairo_color_init_rgba (&bg_color, 0, 0, 0, 0); + status = _cairo_surface_scale_translate_stroke (shadow_surface, + &bg_color, + &m, + CAIRO_OPERATOR_OVER, + &shadow_source.base, + &shadow_path, + stroke_style, + &shadow_ctm, + &shadow_ctm_inverse, + tolerance, + antialias, + NULL); + + if (unlikely (status)) + goto FINISH; + + shadow_pattern = cairo_pattern_create_for_surface (shadow_surface); + cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN); + cairo_pattern_set_sigma (shadow_pattern, + shadow_copy.x_blur * scale * 0.5, + shadow_copy.y_blur * scale * 0.5); + + status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, + line_width * scale); + if (unlikely (status)) + goto FINISH; + + if ((shadow_cache_list.locked || device) && + shadow->enable_cache && has_blur) { + status = _cairo_surface_mask (cache_surface, CAIRO_OPERATOR_OVER, + color_pattern, shadow_pattern, NULL); + if (unlikely (status)) + goto FINISH; + + cairo_pattern_destroy (shadow_pattern); + + size = shadow_surface_extents.width * shadow_surface_extents.height; + _cairo_shadow_cache_list_shrink_to_accomodate (&shadow_cache_list, + size); + + shadow_cache = malloc (sizeof (cairo_shadow_cache_t)); + _cairo_shadow_cache_init (shadow_cache, + cache_surface, + size, + hash, + x_blur, + y_blur, + scale); + + cairo_list_add (&shadow_cache->link, shadow_cache_list.caches); + *shadow_cache_list.size += size; + + shadow_pattern = cairo_pattern_create_for_surface (cache_surface); + cairo_pattern_set_matrix (shadow_pattern, &m); + + cairo_pattern_destroy (color_pattern); + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + 1.0); + } + else + cairo_pattern_set_matrix (shadow_pattern, &m); + + status = _cairo_surface_mask (target, op, + color_pattern, shadow_pattern, clip); + +FINISH: + _cairo_path_fixed_fini (&shadow_path); + cairo_pattern_destroy (color_pattern); + + if (shadow_pattern) + cairo_pattern_destroy (shadow_pattern); + + cairo_surface_destroy (shadow_surface); + cairo_surface_destroy (cache_surface); + + if (shadow_cache_list.locked) + target->backend->shadow_cache_release (target); + + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only; + ((cairo_pattern_t *)source)->shadow.type = shadow_type; + ((cairo_stroke_style_t *)stroke_style)->line_width = line_width; + return status; +} + +static cairo_status_t +_cairo_surface_inset_shadow_fill (cairo_surface_t *target, + cairo_operator_t op, + const cairo_pattern_t*source, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip, + const cairo_shadow_t *shadow) +{ + cairo_status_t status; + cairo_pattern_union_t shadow_source; + cairo_path_fixed_t shadow_path; + cairo_rectangle_t shadow_extents; + cairo_pattern_t *shadow_pattern = NULL; + cairo_pattern_t *color_pattern = NULL; + cairo_surface_t *shadow_surface = NULL; + cairo_surface_t *cache_surface = NULL; + cairo_rectangle_int_t shadow_surface_extents; + cairo_rectangle_int_t extents; + cairo_content_t content; + + int shadow_width, shadow_height; + int x_blur, y_blur; + cairo_shadow_t shadow_copy = *shadow; + + cairo_matrix_t m; + double scale; + double x_offset = shadow->x_offset; + double y_offset = shadow->y_offset; + unsigned long hash = 0; + cairo_shadow_cache_t *shadow_cache = NULL; + cairo_device_t *device = target->device; + unsigned long size; + cairo_color_t bg_color; + cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only; + cairo_shadow_type_t shadow_type = source->shadow.type; + cairo_bool_t has_blur = ! (source->shadow.x_blur == 0.0 && + source->shadow.y_blur == 0.0); + + cairo_shadow_cache_list_t shadow_cache_list; + + if (shadow->color.alpha == 0.0) + return CAIRO_STATUS_SUCCESS; + + _cairo_shadow_cache_list_init (&shadow_cache_list, target); + if (shadow_cache_list.caches != NULL) { + hash = _cairo_shadow_hash_for_fill (source, path, fill_rule, shadow); + shadow_cache = _cairo_shadow_cache_list_find (&shadow_cache_list, hash); + } + + if (shadow_cache != NULL) { + /* paint the shadow surface to target */ + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + 1.0); + x_blur = shadow_cache->x_blur; + y_blur = shadow_cache->y_blur; + + status = _cairo_surface_fill_get_offset_extents (target, + TRUE, + x_offset, + y_offset, + source, + path, + fill_rule, + clip, + &shadow_source.base, + &shadow_path, + &shadow_extents); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 || shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + cairo_matrix_init_scale (&m, shadow_cache->scale, shadow_cache->scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + shadow_pattern = cairo_pattern_create_for_surface (shadow_cache->surface); + cairo_pattern_set_matrix (shadow_pattern, &m); + + if (! shadow->path_is_fill_with_spread) + status = _cairo_surface_fill (target, op, shadow_pattern, + path, fill_rule, tolerance, + antialias, clip); + else + status = _cairo_surface_paint (target, op, shadow_pattern, + clip); + + cairo_list_move (&shadow_cache->link, shadow_cache_list.caches); + goto FINISH; + } + + ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE; + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE; + + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + shadow_copy.color.alpha); + + x_blur = ceil (shadow_copy.x_blur); + y_blur = ceil (shadow_copy.y_blur); + + status = _cairo_surface_fill_get_offset_extents (target, + TRUE, + x_offset, y_offset, + source, + path, + fill_rule, + clip, + &shadow_source.base, + &shadow_path, + &shadow_extents); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 && shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + shadow_width = ceil (shadow_extents.width + x_blur * 2); + shadow_height = ceil (shadow_extents.height + y_blur * 2); + + shadow_surface = _cairo_ensure_shadow_surface (target, + &shadow_surface_extents, + x_blur, y_blur, + shadow_width, shadow_height); + if (! shadow_surface || unlikely (shadow_surface->status)) + goto FINISH; + + _cairo_surface_get_extents (shadow_surface, &extents); + + if ((shadow_cache_list.locked || device) && + shadow->enable_cache && has_blur) { + content = cairo_surface_get_content (target); + if (content == CAIRO_CONTENT_COLOR) + content = CAIRO_CONTENT_COLOR_ALPHA; + + cache_surface = cairo_surface_create_similar (target, content, + shadow_surface_extents.width, + shadow_surface_extents.height); + if (unlikely (cache_surface->status)) + goto FINISH; + + if (device) + _cairo_surface_release_device_reference (cache_surface); + } + + scale = _calculate_shadow_extents_scale (&shadow_surface_extents, + shadow_width, + shadow_height); + cairo_matrix_init_scale (&m, scale, scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + _cairo_color_init_rgba (&bg_color, + shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + shadow_copy.color.alpha); + /* paint with offset and scale */ + status = _cairo_surface_scale_translate_fill (shadow_surface, + &bg_color, + &m, + CAIRO_OPERATOR_CLEAR, + &shadow_source.base, + &shadow_path, + fill_rule, + tolerance, + antialias, + NULL); + + if (unlikely (status)) + goto FINISH; + shadow_pattern = cairo_pattern_create_for_surface (shadow_surface); + cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN); + cairo_pattern_set_sigma (shadow_pattern, + shadow_copy.x_blur * scale * 0.5, + shadow_copy.y_blur * scale * 0.5); + + status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, 1024); + if (unlikely (status)) + goto FINISH; + + /* blur to cache surface */ + cairo_matrix_init_scale (&m, scale, scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + if ((shadow_cache_list.locked || device) && + shadow->enable_cache && has_blur) { + status = _cairo_surface_paint (cache_surface, CAIRO_OPERATOR_OVER, + shadow_pattern, NULL); + + if (unlikely (status)) + goto FINISH; + + cairo_pattern_destroy (shadow_pattern); + + size = shadow_surface_extents.width * shadow_surface_extents.height; + _cairo_shadow_cache_list_shrink_to_accomodate (&shadow_cache_list, + size); + + shadow_cache = malloc (sizeof (cairo_shadow_cache_t)); + _cairo_shadow_cache_init (shadow_cache, + cache_surface, + size, + hash, + x_blur, + y_blur, + scale); + + cairo_list_add (&shadow_cache->link, shadow_cache_list.caches); + *shadow_cache_list.size += size; + + shadow_pattern = cairo_pattern_create_for_surface (cache_surface); + + cairo_pattern_set_matrix (shadow_pattern, &m); + if (! shadow_copy.path_is_fill_with_spread) + status = _cairo_surface_fill (target, op, shadow_pattern, + path, fill_rule, tolerance, + antialias, clip); + else + status = _cairo_surface_paint (target, op, shadow_pattern, + clip); + } + else { + cairo_pattern_set_matrix (shadow_pattern, &m); + if (! shadow_copy.path_is_fill_with_spread) + status = _cairo_surface_fill (target, op, shadow_pattern, + path, fill_rule, tolerance, + antialias, clip); + else + status = _cairo_surface_paint (target, op, shadow_pattern, + clip); + } +FINISH: + _cairo_path_fixed_fini (&shadow_path); + cairo_pattern_destroy (color_pattern); + + if (shadow_pattern) + cairo_pattern_destroy (shadow_pattern); + + if (cache_surface) + cairo_surface_destroy (cache_surface); + + cairo_surface_destroy (shadow_surface); + + if (shadow_cache_list.locked) + target->backend->shadow_cache_release (target); + + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only; + ((cairo_pattern_t *)source)->shadow.type = shadow_type; + return status; +} + +cairo_status_t +_cairo_surface_shadow_fill (cairo_surface_t *target, + cairo_operator_t op, + const cairo_pattern_t*source, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip, + const cairo_shadow_t *shadow) +{ + cairo_status_t status; + cairo_pattern_union_t shadow_source; + cairo_path_fixed_t shadow_path; + cairo_rectangle_t shadow_extents; + cairo_pattern_t *shadow_pattern = NULL; + cairo_pattern_t *color_pattern = NULL; + cairo_surface_t *shadow_surface = NULL; + cairo_rectangle_int_t shadow_surface_extents; + cairo_content_t content; + + int shadow_width, shadow_height; + int x_blur, y_blur; + cairo_shadow_t shadow_copy = *shadow; + cairo_color_t bg_color; + + cairo_matrix_t m; + double scale; + double x_offset = shadow->x_offset; + double y_offset = shadow->y_offset; + unsigned long hash = 0; + cairo_shadow_cache_t *shadow_cache = NULL; + cairo_device_t *device = target->device; + unsigned long size; + cairo_surface_t *cache_surface = NULL; + cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only; + cairo_shadow_type_t shadow_type = source->shadow.type; + cairo_bool_t has_blur = ! (source->shadow.x_blur == 0.0 && + source->shadow.y_blur == 0.0); + + cairo_shadow_cache_list_t shadow_cache_list; + + if (shadow->type == CAIRO_SHADOW_NONE) + return CAIRO_STATUS_SUCCESS; + + if (shadow->color.alpha == 0.0) + return CAIRO_STATUS_SUCCESS; + + if (shadow->x_blur <= 0.0 && shadow->y_blur <= 0.0 && + shadow->x_offset == 0.0 && shadow->y_offset == 0.0) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; + + if (shadow->type == CAIRO_SHADOW_INSET) + return _cairo_surface_inset_shadow_fill (target, op, source, + path, fill_rule, + tolerance, antialias, + clip, shadow); + + if (shadow->x_blur == 0.0 && shadow->y_blur == 0.0) { + status = _cairo_surface_fill_get_offset_extents (target, + FALSE, + x_offset, + y_offset, + source, + path, + fill_rule, + clip, + &shadow_source.base, + &shadow_path, + &shadow_extents); + if (unlikely (status)) { + _cairo_path_fixed_fini (&shadow_path); + return status; + } + + cairo_matrix_init_identity (&m); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + /* stroke to target with offset */ + shadow_source.base.shadow.type = CAIRO_SHADOW_NONE; + shadow_source.base.shadow.draw_shadow_only = FALSE; + status = _cairo_surface_scale_translate_fill (target, + NULL, + &m, + op, + &shadow_source.base, + &shadow_path, + fill_rule, + tolerance, + antialias, + clip); + + _cairo_path_fixed_fini (&shadow_path); + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only; + ((cairo_pattern_t *)source)->shadow.type = shadow_type; + return status; + } + + _cairo_shadow_cache_list_init (&shadow_cache_list, target); + if (shadow_cache_list.caches != NULL) { + hash = _cairo_shadow_hash_for_fill (source, path, fill_rule, shadow); + shadow_cache = _cairo_shadow_cache_list_find (&shadow_cache_list, hash); + } + + if (shadow_cache != NULL) { + /* paint the shadow surface to target */ + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + 1.0); + x_blur = shadow_cache->x_blur; + y_blur = shadow_cache->y_blur; + + status = _cairo_surface_fill_get_offset_extents (target, + FALSE, + x_offset, + y_offset, + source, + path, + fill_rule, + clip, + &shadow_source.base, + &shadow_path, + &shadow_extents); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 || shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + cairo_matrix_init_scale (&m, shadow_cache->scale, shadow_cache->scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + shadow_pattern = cairo_pattern_create_for_surface (shadow_cache->surface); + cairo_pattern_set_matrix (shadow_pattern, &m); + + status = _cairo_surface_mask (target, op, color_pattern, + shadow_pattern, clip); + cairo_list_move (&shadow_cache->link, shadow_cache_list.caches); + goto FINISH; + } + + ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE; + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE; + + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + shadow_copy.color.alpha); + + x_blur = ceil (shadow_copy.x_blur); + y_blur = ceil (shadow_copy.y_blur); + + status = _cairo_surface_fill_get_offset_extents (target, + FALSE, + x_offset, y_offset, + source, + path, + fill_rule, + clip, + &shadow_source.base, + &shadow_path, + &shadow_extents); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 && shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + shadow_width = ceil (shadow_extents.width + x_blur * 2); + shadow_height = ceil (shadow_extents.height + y_blur * 2); + + shadow_surface = _cairo_ensure_shadow_surface (target, + &shadow_surface_extents, + x_blur, y_blur, + shadow_width, shadow_height); + if (! shadow_surface || unlikely (shadow_surface->status)) + goto FINISH; + + if ((shadow_cache_list.locked || device) && + shadow->enable_cache && has_blur) { + content = cairo_surface_get_content (target); + if (content == CAIRO_CONTENT_COLOR) + content = CAIRO_CONTENT_COLOR_ALPHA; + + cache_surface = cairo_surface_create_similar (target, content, + shadow_surface_extents.width, + shadow_surface_extents.height); + if (unlikely (cache_surface->status)) + goto FINISH; + + if (device) + _cairo_surface_release_device_reference (cache_surface); + } + + scale = _calculate_shadow_extents_scale (&shadow_surface_extents, + shadow_width, + shadow_height); + cairo_matrix_init_scale (&m, scale, scale); + cairo_matrix_translate (&m, -x_offset, -y_offset); + + /* paint with offset and scale */ + _cairo_color_init_rgba (&bg_color, 0, 0, 0, 0); + status = _cairo_surface_scale_translate_fill (shadow_surface, + &bg_color, + &m, + CAIRO_OPERATOR_OVER, + &shadow_source.base, + &shadow_path, + fill_rule, + tolerance, + antialias, + NULL); + + if (unlikely (status)) + goto FINISH; + + shadow_pattern = cairo_pattern_create_for_surface (shadow_surface); + cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN); + cairo_pattern_set_sigma (shadow_pattern, + shadow_copy.x_blur * scale * 0.5, + shadow_copy.y_blur * scale * 0.5); + + status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, 1024); + if (unlikely (status)) + goto FINISH; + + if ((shadow_cache_list.locked || device) && + shadow->enable_cache && has_blur) { + status = _cairo_surface_mask (cache_surface, CAIRO_OPERATOR_OVER, + color_pattern, shadow_pattern, NULL); + if (unlikely (status)) + goto FINISH; + + cairo_pattern_destroy (shadow_pattern); + + size = shadow_surface_extents.width * shadow_surface_extents.height; + _cairo_shadow_cache_list_shrink_to_accomodate (&shadow_cache_list, + size); + + shadow_cache = malloc (sizeof (cairo_shadow_cache_t)); + _cairo_shadow_cache_init (shadow_cache, + cache_surface, + size, + hash, + x_blur, + y_blur, + scale); + + cairo_list_add (&shadow_cache->link, shadow_cache_list.caches); + *shadow_cache_list.size += size; + + shadow_pattern = cairo_pattern_create_for_surface (cache_surface); + cairo_pattern_set_matrix (shadow_pattern, &m); + + cairo_pattern_destroy (color_pattern); + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + 1.0); + } + else + cairo_pattern_set_matrix (shadow_pattern, &m); + + status = _cairo_surface_mask (target, op, + color_pattern, shadow_pattern, clip); + +FINISH: + _cairo_path_fixed_fini (&shadow_path); + cairo_pattern_destroy (color_pattern); + + if (shadow_pattern) + cairo_pattern_destroy (shadow_pattern); + + cairo_surface_destroy (cache_surface); + + cairo_surface_destroy (shadow_surface); + + if (shadow_cache_list.locked) + target->backend->shadow_cache_release (target); + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only; + ((cairo_pattern_t *)source)->shadow.type = shadow_type; + + return status; +} + +static cairo_status_t +_cairo_surface_inset_shadow_glyphs (cairo_surface_t *target, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_clip_t *clip, + const cairo_shadow_t *shadow) +{ + cairo_status_t status; + cairo_pattern_union_t shadow_source; + cairo_rectangle_t shadow_extents; + cairo_pattern_t *shadow_pattern = NULL; + cairo_pattern_t *color_pattern = NULL; + cairo_surface_t *shadow_surface = NULL; + cairo_surface_t *mask_surface = NULL; + cairo_rectangle_int_t shadow_surface_extents; + cairo_glyph_t *shadow_glyphs; + cairo_content_t content; + cairo_color_t bg_color; + + int shadow_width, shadow_height; + int x_blur, y_blur; + cairo_shadow_t shadow_copy = *shadow; + + cairo_matrix_t m; + double x_offset = shadow->x_offset; + double y_offset = shadow->y_offset; + cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only; + cairo_shadow_type_t shadow_type = source->shadow.type; + + if (shadow->color.alpha == 0.0) + return CAIRO_STATUS_SUCCESS; + + ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE; + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE; + + x_blur = ceil (shadow_copy.x_blur); + y_blur = ceil (shadow_copy.y_blur); + + shadow_glyphs = (cairo_glyph_t *)_cairo_malloc_ab (num_glyphs, + sizeof (cairo_glyph_t)); + if (shadow_glyphs == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto FINISH; + } + + status = _cairo_surface_glyphs_get_offset_extents (target, + TRUE, + 0, 0, + source, + scaled_font, + glyphs, + num_glyphs, + clip, + &shadow_source.base, + shadow_glyphs, + &shadow_extents); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 && shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + shadow_width = ceil (shadow_extents.width + x_blur * 2 + fabs (shadow->x_offset)); + shadow_height = ceil (shadow_extents.height + y_blur * 2 + fabs (shadow->y_offset)); + + if (target->backend->get_glyph_shadow_surface) { + shadow_surface = target->backend->get_glyph_shadow_surface (target, + shadow_width, + shadow_height, + FALSE); + } + else { + content = cairo_surface_get_content (target); + if (content == CAIRO_CONTENT_COLOR) + content = CAIRO_CONTENT_COLOR_ALPHA; + shadow_surface = cairo_surface_create_similar (target, + content, + shadow_width, + shadow_height); + _cairo_surface_release_device_reference (shadow_surface); + } + if (! shadow_surface || unlikely (shadow_surface->status)) + goto FINISH; + + if(! _cairo_surface_get_extents (shadow_surface, &shadow_surface_extents)) + goto FINISH; + + if (target->backend->get_glyph_shadow_mask_surface) { + mask_surface = target->backend->get_glyph_shadow_mask_surface (shadow_surface, + shadow_surface_extents.width, + shadow_surface_extents.height, + 0); + } + else { + mask_surface = cairo_surface_create_similar (shadow_surface, + CAIRO_CONTENT_COLOR_ALPHA, + shadow_surface_extents.width, + shadow_surface_extents.height); + _cairo_surface_release_device_reference (mask_surface); + } + if (! mask_surface || unlikely (mask_surface->status)) + goto FINISH; + + cairo_matrix_init_translate (&m, -x_offset, -y_offset); + + /* paint with offset and scale */ + _cairo_color_init_rgba (&bg_color, 0, 0, 0, 0); + color_pattern = cairo_pattern_create_rgba (1, 1, 1, 1); + + status = _cairo_surface_translate_glyphs (mask_surface, + &bg_color, + &m, + CAIRO_OPERATOR_OVER, + color_pattern, + scaled_font, + shadow_glyphs, + num_glyphs, + NULL); + + if (unlikely (status)) + goto FINISH; + + /* with fast path, we paint shadow color and source directly to + * shadow_surface, and then blur to target */ + cairo_pattern_destroy (color_pattern); + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + shadow_copy.color.alpha); + + status = _cairo_surface_paint (shadow_surface, + CAIRO_OPERATOR_SOURCE, + color_pattern, NULL); + if (unlikely (status)) + goto FINISH; + + shadow_pattern = cairo_pattern_create_for_surface (mask_surface); + cairo_pattern_destroy (color_pattern); + color_pattern = cairo_pattern_create_rgba (0, 0, 0, 0); + + status = _cairo_surface_mask (shadow_surface, CAIRO_OPERATOR_SOURCE, + color_pattern, shadow_pattern, + NULL); + if (unlikely (status)) + goto FINISH; + + cairo_pattern_destroy (shadow_pattern); + shadow_pattern = cairo_pattern_create_for_surface (shadow_surface); + cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN); + cairo_pattern_set_sigma (shadow_pattern, + shadow_copy.x_blur * 0.5, + shadow_copy.y_blur * 0.5); + status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, 1024); + if (unlikely (status)) + goto FINISH; + + cairo_pattern_destroy (color_pattern); + color_pattern = cairo_pattern_create_for_surface (mask_surface); + cairo_pattern_set_matrix (color_pattern, &m); + + cairo_matrix_translate (&m, -shadow->x_offset, + -shadow->y_offset); + cairo_pattern_set_matrix (shadow_pattern, &m); + + status = _cairo_surface_mask (target, op, shadow_pattern, + color_pattern, clip); +FINISH: + cairo_pattern_destroy (color_pattern); + + if (shadow_pattern) + cairo_pattern_destroy (shadow_pattern); + + free (shadow_glyphs); + + cairo_surface_destroy (shadow_surface); + cairo_surface_destroy (mask_surface); + + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only; + ((cairo_pattern_t *)source)->shadow.type = shadow_type; + return status; +} + +cairo_status_t +_cairo_surface_shadow_glyphs (cairo_surface_t *target, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_clip_t *clip, + const cairo_shadow_t *shadow) +{ + cairo_status_t status; + cairo_pattern_union_t shadow_source; + cairo_rectangle_t shadow_extents; + cairo_pattern_t *shadow_pattern = NULL; + cairo_pattern_t *color_pattern; + cairo_surface_t *shadow_surface = NULL; + cairo_rectangle_int_t shadow_surface_extents; + + cairo_glyph_t *shadow_glyphs; + cairo_content_t content; + cairo_color_t bg_color; + int shadow_width, shadow_height; + int x_blur, y_blur; + cairo_shadow_t shadow_copy = *shadow; + + cairo_matrix_t m; + double x_offset = shadow->x_offset; + double y_offset = shadow->y_offset; + cairo_bool_t draw_shadow_only = source->shadow.draw_shadow_only; + cairo_shadow_type_t shadow_type = source->shadow.type; + + if (shadow->type == CAIRO_SHADOW_NONE) + return CAIRO_STATUS_SUCCESS; + + if (shadow->color.alpha == 0.0) + return CAIRO_STATUS_SUCCESS; + + if (shadow->x_blur <= 0.0 && shadow->y_blur <= 0.0 && + shadow->x_offset == 0.0 && shadow->y_offset == 0.0) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; + + if (shadow->type == CAIRO_SHADOW_INSET) + return _cairo_surface_inset_shadow_glyphs (target, op, source, + scaled_font, glyphs, + num_glyphs, clip, + shadow); + shadow_glyphs = (cairo_glyph_t *)_cairo_malloc_ab (num_glyphs, + sizeof (cairo_glyph_t)); + if (shadow_glyphs == NULL) + return CAIRO_STATUS_NO_MEMORY; + + ((cairo_pattern_t *)source)->shadow.type = CAIRO_SHADOW_NONE; + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = FALSE; + + x_blur = ceil (shadow_copy.x_blur); + y_blur = ceil (shadow_copy.y_blur); + + color_pattern = cairo_pattern_create_rgba (shadow_copy.color.red, + shadow_copy.color.green, + shadow_copy.color.blue, + shadow_copy.color.alpha); + + status = _cairo_surface_glyphs_get_offset_extents (target, + FALSE, + x_offset, y_offset, + source, + scaled_font, + glyphs, + num_glyphs, + clip, + &shadow_source.base, + shadow_glyphs, + &shadow_extents); + if (unlikely (status)) + goto FINISH; + + if (shadow_extents.width == 0 && shadow_extents.height == 0) + goto FINISH; + + x_offset = shadow_extents.x - x_blur; + y_offset = shadow_extents.y - y_blur; + + shadow_width = ceil (shadow_extents.width + x_blur * 2); + shadow_height = ceil (shadow_extents.height + y_blur * 2); + + if (target->backend->get_glyph_shadow_surface) + shadow_surface = target->backend->get_glyph_shadow_surface (target, + shadow_width, + shadow_height, + FALSE); + else { + content = cairo_surface_get_content (target); + if (content == CAIRO_CONTENT_COLOR) + content = CAIRO_CONTENT_COLOR_ALPHA; + shadow_surface = cairo_surface_create_similar (target, + content, + shadow_width, + shadow_height); + _cairo_surface_release_device_reference (shadow_surface); + } + if (! shadow_surface || unlikely (shadow_surface->status)) + goto FINISH; + + if(! _cairo_surface_get_extents (shadow_surface, &shadow_surface_extents)) + goto FINISH; + + cairo_matrix_init_translate (&m, -x_offset, -y_offset); + + /* paint with offset and scale */ + _cairo_color_init_rgba (&bg_color, 0, 0, 0, 0); + status = _cairo_surface_translate_glyphs (shadow_surface, + &bg_color, + &m, + CAIRO_OPERATOR_OVER, + &shadow_source.base, + scaled_font, + shadow_glyphs, + num_glyphs, + NULL); + + if (unlikely (status)) + goto FINISH; + + shadow_pattern = cairo_pattern_create_for_surface (shadow_surface); + cairo_pattern_set_filter (shadow_pattern, CAIRO_FILTER_GAUSSIAN); + cairo_pattern_set_sigma (shadow_pattern, + shadow_copy.x_blur * 0.5, + shadow_copy.y_blur * 0.5); + + status = _cairo_pattern_create_gaussian_matrix (shadow_pattern, 1024); + if (unlikely (status)) + goto FINISH; + + cairo_pattern_set_matrix (shadow_pattern, &m); + + status = _cairo_surface_mask (target, op, color_pattern, + shadow_pattern, NULL); + +FINISH: + cairo_pattern_destroy (color_pattern); + + if (shadow_pattern) + cairo_pattern_destroy (shadow_pattern); + + free (shadow_glyphs); + + cairo_surface_destroy (shadow_surface); + + ((cairo_pattern_t *)source)->shadow.draw_shadow_only = draw_shadow_only; + ((cairo_pattern_t *)source)->shadow.type = shadow_type; + return status; +} |