/* 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 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); if (shadow_surface->device) _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); if (shadow_surface->device) _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); if (mask_surface->device) _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); if (shadow_surface->device) _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; }