From 0dcc70b2b339ae404e571327e6898731b582aa7a Mon Sep 17 00:00:00 2001 From: Andy Wood Date: Sat, 24 Jan 2015 09:09:23 +0000 Subject: [PATCH] Create a new image surface to render blurred shadows to, this means that vector formats like PDF will now render blurs. --- src/CanvasRenderingContext2d.cc | 70 +++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index c43f64d..81c5415 100755 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -349,22 +349,68 @@ Context2d::shadow(void (fn)(cairo_t *cr)) { cairo_path_t *path = cairo_copy_path_flat(_context); cairo_save(_context); - // Offset - cairo_translate( - _context - , state->shadowOffsetX - , state->shadowOffsetY); - - // Apply shadow cairo_push_group(_context); - cairo_new_path(_context); - cairo_append_path(_context, path); - setSourceRGBA(state->shadow); - fn(_context); // No need to invoke blur if shadowBlur is 0 if (state->shadowBlur) { - blur(cairo_get_group_target(_context), state->shadowBlur); + // find out extent of path + double x1, y1, x2, y2; + if (fn == cairo_fill || fn == cairo_fill_preserve) { + cairo_fill_extents(_context, &x1, &y1, &x2, &y2); + } else { + cairo_stroke_extents(_context, &x1, &y1, &x2, &y2); + } + + // create new image surface that size + padding for blurring + int pad = state->shadowBlur * 2; + int resolution = 4; // approx 300px/72pt + cairo_surface_t *surface = cairo_get_group_target(_context); + cairo_surface_t *shadow_surface = cairo_surface_create_similar_image(surface, + CAIRO_FORMAT_ARGB32, + (x2-x1 + 2*pad)*resolution, + (y2-y1 + 2*pad)*resolution); + cairo_t *shadow_context = cairo_create(shadow_surface); + + // transform path to the right place + cairo_matrix_t matrix; + cairo_get_matrix(_context, &matrix); + cairo_scale(shadow_context, resolution, resolution); + cairo_set_matrix(shadow_context, &matrix); + cairo_translate(shadow_context, pad-x1, pad-y1); + + // draw the path and blur + cairo_new_path(shadow_context); + cairo_append_path(shadow_context, path); + cairo_set_source_rgba( + shadow_context + , state->shadow.r + , state->shadow.g + , state->shadow.b + , state->shadow.a * state->globalAlpha); + fn(shadow_context); + blur(shadow_surface, state->shadowBlur); // *resolution? + + // paint to original context + cairo_set_source_surface(_context, shadow_surface, + x1 - pad + state->shadowOffsetX, + y1 - pad + state->shadowOffsetY); + cairo_scale(shadow_context, 1/resolution, 1/resolution); + cairo_paint(_context); + cairo_destroy(shadow_context); + cairo_surface_destroy(shadow_surface); + } else { + // Offset + cairo_translate( + _context + , state->shadowOffsetX + , state->shadowOffsetY); + + // Apply shadow + cairo_new_path(_context); + cairo_append_path(_context, path); + setSourceRGBA(state->shadow); + + fn(_context); } // Paint the shadow