1bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen/*
2bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * Copyright (C) 2010 Sencha, Inc.
3bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * Copyright (C) 2010 Igalia S.L.
4bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen *
5bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * All rights reserved.
6bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen *
7bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * Redistribution and use in source and binary forms, with or without
8bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * modification, are permitted provided that the following conditions
9bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * are met:
10bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * 1. Redistributions of source code must retain the above copyright
11bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen *    notice, this list of conditions and the following disclaimer.
12bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * 2. Redistributions in binary form must reproduce the above copyright
13bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen *    notice, this list of conditions and the following disclaimer in the
14bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen *    documentation and/or other materials provided with the distribution.
15bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen *
16bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen */
28bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
29bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen#include "config.h"
30bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen#include "ContextShadow.h"
31bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
32a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch#include "AffineTransform.h"
33bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen#include "CairoUtilities.h"
34cad810f21b803229eb11403f9209855525a25d57Steve Block#include "GraphicsContext.h"
35a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch#include "OwnPtrCairo.h"
36a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch#include "Path.h"
372bde8e466a4451c7319e3a072d118917957d6554Steve Block#include "PlatformContextCairo.h"
38bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen#include "Timer.h"
39bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen#include <cairo.h>
40bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
41a94275402997c11dd2e778633dacf4b7e630a35dBen Murdochusing WTF::max;
42a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
43bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsennamespace WebCore {
44bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
452daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdochstatic RefPtr<cairo_surface_t> gScratchBuffer;
46bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsenstatic void purgeScratchBuffer()
47bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen{
482daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    gScratchBuffer.clear();
49bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen}
50bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
51bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen// ContextShadow needs a scratch image as the buffer for the blur filter.
52bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen// Instead of creating and destroying the buffer for every operation,
53bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen// we create a buffer which will be automatically purged via a timer.
54bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsenclass PurgeScratchBufferTimer : public TimerBase {
55bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsenprivate:
56bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    virtual void fired() { purgeScratchBuffer(); }
57bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen};
58bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsenstatic PurgeScratchBufferTimer purgeScratchBufferTimer;
59bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsenstatic void scheduleScratchBufferPurge()
60bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen{
61bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    if (purgeScratchBufferTimer.isActive())
62bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen        purgeScratchBufferTimer.stop();
63bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    purgeScratchBufferTimer.startOneShot(2);
64bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen}
65bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
66bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsenstatic cairo_surface_t* getScratchBuffer(const IntSize& size)
67bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen{
68bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    int width = size.width();
69bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    int height = size.height();
702daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    int scratchWidth = gScratchBuffer.get() ? cairo_image_surface_get_width(gScratchBuffer.get()) : 0;
712daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    int scratchHeight = gScratchBuffer.get() ? cairo_image_surface_get_height(gScratchBuffer.get()) : 0;
72bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
73bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    // We do not need to recreate the buffer if the current buffer is large enough.
742daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    if (gScratchBuffer.get() && scratchWidth >= width && scratchHeight >= height)
752daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        return gScratchBuffer.get();
76bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
77bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    purgeScratchBuffer();
78bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
79bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests.
80bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    width = (1 + (width >> 5)) << 5;
81bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    height = (1 + (height >> 5)) << 5;
822daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    gScratchBuffer = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height));
832daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    return gScratchBuffer.get();
84bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen}
85bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
86cad810f21b803229eb11403f9209855525a25d57Steve BlockPlatformContext ContextShadow::beginShadowLayer(GraphicsContext* context, const FloatRect& layerArea)
87bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen{
88f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    adjustBlurDistance(context);
89f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch
90bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    double x1, x2, y1, y2;
912bde8e466a4451c7319e3a072d118917957d6554Steve Block    cairo_clip_extents(context->platformContext()->cr(), &x1, &y1, &x2, &y2);
92f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    IntRect layerRect = calculateLayerBoundingRect(context, layerArea, IntRect(x1, y1, x2 - x1, y2 - y1));
93bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
94bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    // Don't paint if we are totally outside the clip region.
95f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if (layerRect.isEmpty())
96bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen        return 0;
97bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
98f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    m_layerImage = getScratchBuffer(layerRect.size());
99bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    m_layerContext = cairo_create(m_layerImage);
100bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
101bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    // Always clear the surface first.
102bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    cairo_set_operator(m_layerContext, CAIRO_OPERATOR_CLEAR);
103bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    cairo_paint(m_layerContext);
104bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    cairo_set_operator(m_layerContext, CAIRO_OPERATOR_OVER);
105bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
106f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    cairo_translate(m_layerContext, m_layerContextTranslation.x(), m_layerContextTranslation.y());
107bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    return m_layerContext;
108bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen}
109bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
110cad810f21b803229eb11403f9209855525a25d57Steve Blockvoid ContextShadow::endShadowLayer(GraphicsContext* context)
111bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen{
112bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    cairo_destroy(m_layerContext);
113bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    m_layerContext = 0;
114bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
115a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    if (m_type == BlurShadow) {
116a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        cairo_surface_flush(m_layerImage);
117bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen        blurLayerImage(cairo_image_surface_get_data(m_layerImage),
118bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen                       IntSize(cairo_image_surface_get_width(m_layerImage), cairo_image_surface_get_height(m_layerImage)),
119bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen                       cairo_image_surface_get_stride(m_layerImage));
120a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        cairo_surface_mark_dirty(m_layerImage);
121a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    }
122bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
1232bde8e466a4451c7319e3a072d118917957d6554Steve Block    cairo_t* cr = context->platformContext()->cr();
124bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    cairo_save(cr);
125bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    setSourceRGBAFromColor(cr, m_color);
126f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    cairo_mask_surface(cr, m_layerImage, m_layerOrigin.x(), m_layerOrigin.y());
127bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    cairo_restore(cr);
128bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
129bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    // Schedule a purge of the scratch buffer. We do not need to destroy the surface.
130bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    scheduleScratchBufferPurge();
131bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen}
132bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen
133cad810f21b803229eb11403f9209855525a25d57Steve Blockvoid ContextShadow::drawRectShadowWithoutTiling(GraphicsContext* context, const IntRect& shadowRect, const IntSize& topLeftRadius, const IntSize& topRightRadius, const IntSize& bottomLeftRadius, const IntSize& bottomRightRadius, float alpha)
134a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch{
135a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    beginShadowLayer(context, shadowRect);
136a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
137a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    if (!m_layerContext)
138a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        return;
139a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
140a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    Path path;
141a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    path.addRoundedRect(shadowRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
142a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
143a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    appendWebCorePathToCairoContext(m_layerContext, path);
144a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_set_source_rgba(m_layerContext, 0, 0, 0, alpha);
145a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_fill(m_layerContext);
146a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
147a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    endShadowLayer(context);
148a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch}
149a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
150a94275402997c11dd2e778633dacf4b7e630a35dBen Murdochstatic inline FloatPoint getPhase(const FloatRect& dest, const FloatRect& tile)
151a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch{
152a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    FloatPoint phase = dest.location();
153a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    phase.move(-tile.x(), -tile.y());
154a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
155a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    return phase;
156a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch}
157a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
158a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch/*
159a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch  This function uses tiling to improve the performance of the shadow
160a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch  drawing of rounded rectangles. The code basically does the following
161a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch  steps:
162a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
163a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     1. Calculate the size of the shadow template, a rectangle that
164a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     contains all the necessary tiles to draw the complete shadow.
165a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
166a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     2. If that size is smaller than the real rectangle render the new
167a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     template rectangle and its shadow in a new surface, in other case
168a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     render the shadow of the real rectangle in the destination
169a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     surface.
170a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
171a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     3. Calculate the sizes and positions of the tiles and their
172a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     destinations and use drawPattern to render the final shadow. The
173a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     code divides the rendering in 8 tiles:
174a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
175a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        1 | 2 | 3
176a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch       -----------
177a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        4 |   | 5
178a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch       -----------
179a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        6 | 7 | 8
180a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
181a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     The corners are directly copied from the template rectangle to the
182a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     real one and the side tiles are 1 pixel width, we use them as
183a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
184a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     tiles to cover the destination side. The corner tiles are bigger
185a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     than just the side of the rounded corner, we need to increase it
186a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     because the modifications caused by the corner over the blur
187a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     effect. We fill the central part with solid color to complete the
188a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch     shadow.
189a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch */
190a94275402997c11dd2e778633dacf4b7e630a35dBen Murdochvoid ContextShadow::drawRectShadow(GraphicsContext* context, const IntRect& rect, const IntSize& topLeftRadius, const IntSize& topRightRadius, const IntSize& bottomLeftRadius, const IntSize& bottomRightRadius)
191a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch{
192a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
193a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    float radiusTwice = m_blurDistance * 2;
194cad810f21b803229eb11403f9209855525a25d57Steve Block
195cad810f21b803229eb11403f9209855525a25d57Steve Block    // Find the space the corners need inside the rect for its shadows.
196cad810f21b803229eb11403f9209855525a25d57Steve Block    int internalShadowWidth = radiusTwice + max(topLeftRadius.width(), bottomLeftRadius.width()) +
197cad810f21b803229eb11403f9209855525a25d57Steve Block        max(topRightRadius.width(), bottomRightRadius.width());
198cad810f21b803229eb11403f9209855525a25d57Steve Block    int internalShadowHeight = radiusTwice + max(topLeftRadius.height(), topRightRadius.height()) +
199cad810f21b803229eb11403f9209855525a25d57Steve Block        max(bottomLeftRadius.height(), bottomRightRadius.height());
200cad810f21b803229eb11403f9209855525a25d57Steve Block
2012bde8e466a4451c7319e3a072d118917957d6554Steve Block    cairo_t* cr = context->platformContext()->cr();
202cad810f21b803229eb11403f9209855525a25d57Steve Block
203cad810f21b803229eb11403f9209855525a25d57Steve Block    // drawShadowedRect still does not work with rotations.
204cad810f21b803229eb11403f9209855525a25d57Steve Block    // https://bugs.webkit.org/show_bug.cgi?id=45042
205cad810f21b803229eb11403f9209855525a25d57Steve Block    if ((!context->getCTM().isIdentityOrTranslationOrFlipped()) || (internalShadowWidth > rect.width())
206cad810f21b803229eb11403f9209855525a25d57Steve Block        || (internalShadowHeight > rect.height()) || (m_type != BlurShadow)) {
207cad810f21b803229eb11403f9209855525a25d57Steve Block        drawRectShadowWithoutTiling(context, rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, context->getAlpha());
208a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        return;
209a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    }
210a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
211a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Calculate size of the template shadow buffer.
212a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    IntSize shadowBufferSize = IntSize(rect.width() + radiusTwice, rect.height() + radiusTwice);
213a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
214a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Determine dimensions of shadow rect.
215a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    FloatRect shadowRect = FloatRect(rect.location(), shadowBufferSize);
216a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    shadowRect.move(- m_blurDistance, - m_blurDistance);
217a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
218a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Size of the tiling side.
219a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    int sideTileWidth = 1;
220a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
221a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // The length of a side of the buffer is the enough space for four blur radii,
222a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // the radii of the corners, and then 1 pixel to draw the side tiles.
223cad810f21b803229eb11403f9209855525a25d57Steve Block    IntSize shadowTemplateSize = IntSize(sideTileWidth + radiusTwice + internalShadowWidth,
224cad810f21b803229eb11403f9209855525a25d57Steve Block                                         sideTileWidth + radiusTwice + internalShadowHeight);
225a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
226a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Reduce the size of what we have to draw with the clip area.
227a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    double x1, x2, y1, y2;
228a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
229cad810f21b803229eb11403f9209855525a25d57Steve Block    calculateLayerBoundingRect(context, shadowRect, IntRect(x1, y1, x2 - x1, y2 - y1));
230a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
231f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch    if ((shadowTemplateSize.width() * shadowTemplateSize.height() > m_sourceRect.width() * m_sourceRect.height())) {
232cad810f21b803229eb11403f9209855525a25d57Steve Block        drawRectShadowWithoutTiling(context, rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, context->getAlpha());
233a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        return;
234a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    }
235a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
236a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    shadowRect.move(m_offset.width(), m_offset.height());
237a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
238a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    m_layerImage = getScratchBuffer(shadowTemplateSize);
239a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
240a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Draw shadow into a new ImageBuffer.
241a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    m_layerContext = cairo_create(m_layerImage);
242a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
243a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Clear the surface first.
244a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_set_operator(m_layerContext, CAIRO_OPERATOR_CLEAR);
245a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_paint(m_layerContext);
246a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_set_operator(m_layerContext, CAIRO_OPERATOR_OVER);
247a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
248a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Draw the rectangle.
249a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    IntRect templateRect = IntRect(m_blurDistance, m_blurDistance, shadowTemplateSize.width() - radiusTwice, shadowTemplateSize.height() - radiusTwice);
250a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    Path path;
251a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    path.addRoundedRect(templateRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
252a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    appendWebCorePathToCairoContext(m_layerContext, path);
253a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
254a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_set_source_rgba(m_layerContext, 0, 0, 0, context->getAlpha());
255a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_fill(m_layerContext);
256a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
257a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Blur the image.
258a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_surface_flush(m_layerImage);
259a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    blurLayerImage(cairo_image_surface_get_data(m_layerImage), shadowTemplateSize, cairo_image_surface_get_stride(m_layerImage));
260a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_surface_mark_dirty(m_layerImage);
261a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
262a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Mask the image with the shadow color.
263a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_set_operator(m_layerContext, CAIRO_OPERATOR_IN);
264a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    setSourceRGBAFromColor(m_layerContext, m_color);
265a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_paint(m_layerContext);
266a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
267a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    cairo_destroy(m_layerContext);
268a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    m_layerContext = 0;
269a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
270a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Fill the internal part of the shadow.
271a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    shadowRect.inflate(-radiusTwice);
272a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    if (!shadowRect.isEmpty()) {
273a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        cairo_save(cr);
274a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        path.clear();
275a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        path.addRoundedRect(shadowRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
276a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        appendWebCorePathToCairoContext(cr, path);
277a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        setSourceRGBAFromColor(cr, m_color);
278a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        cairo_fill(cr);
279a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        cairo_restore(cr);
280a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    }
281a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    shadowRect.inflate(radiusTwice);
282a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
283a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Draw top side.
284a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    FloatRect tileRect = FloatRect(radiusTwice + topLeftRadius.width(), 0, sideTileWidth, radiusTwice);
285a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    FloatRect destRect = tileRect;
286a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect.move(shadowRect.x(), shadowRect.y());
287a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect.setWidth(shadowRect.width() - topLeftRadius.width() - topRightRadius.width() - m_blurDistance * 4);
288a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    FloatPoint phase = getPhase(destRect, tileRect);
289a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    AffineTransform patternTransform;
290a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    patternTransform.makeIdentity();
291a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);
292a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
293a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Draw the bottom side.
294a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    tileRect = FloatRect(radiusTwice + bottomLeftRadius.width(), shadowTemplateSize.height() - radiusTwice, sideTileWidth, radiusTwice);
295a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect = tileRect;
296a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect.move(shadowRect.x(), shadowRect.y() + radiusTwice + rect.height() - shadowTemplateSize.height());
297a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect.setWidth(shadowRect.width() - bottomLeftRadius.width() - bottomRightRadius.width() - m_blurDistance * 4);
298a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    phase = getPhase(destRect, tileRect);
299a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);
300a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
301a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Draw the right side.
302a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    tileRect = FloatRect(shadowTemplateSize.width() - radiusTwice, radiusTwice + topRightRadius.height(), radiusTwice, sideTileWidth);
303a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect = tileRect;
304a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect.move(shadowRect.x() + radiusTwice + rect.width() - shadowTemplateSize.width(), shadowRect.y());
305a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect.setHeight(shadowRect.height() - topRightRadius.height() - bottomRightRadius.height() - m_blurDistance * 4);
306a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    phase = getPhase(destRect, tileRect);
307a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);
308a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
309a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Draw the left side.
310a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    tileRect = FloatRect(0, radiusTwice + topLeftRadius.height(), radiusTwice, sideTileWidth);
311a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect = tileRect;
312a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect.move(shadowRect.x(), shadowRect.y());
313a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect.setHeight(shadowRect.height() - topLeftRadius.height() - bottomLeftRadius.height() - m_blurDistance * 4);
314a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    phase = FloatPoint(destRect.x() - tileRect.x(), destRect.y() - tileRect.y());
315a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);
316a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
317a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Draw the top left corner.
318a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    tileRect = FloatRect(0, 0, radiusTwice + topLeftRadius.width(), radiusTwice + topLeftRadius.height());
319a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect = tileRect;
320a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect.move(shadowRect.x(), shadowRect.y());
321a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    phase = getPhase(destRect, tileRect);
322a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);
323a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
324a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Draw the top right corner.
325a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    tileRect = FloatRect(shadowTemplateSize.width() - radiusTwice - topRightRadius.width(), 0, radiusTwice + topRightRadius.width(),
326a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch                         radiusTwice + topRightRadius.height());
327a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect = tileRect;
328a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect.move(shadowRect.x() + rect.width() - shadowTemplateSize.width() + radiusTwice, shadowRect.y());
329a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    phase = getPhase(destRect, tileRect);
330a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);
331a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
332a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Draw the bottom right corner.
333a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    tileRect = FloatRect(shadowTemplateSize.width() - radiusTwice - bottomRightRadius.width(),
334a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch                         shadowTemplateSize.height() - radiusTwice - bottomRightRadius.height(),
335a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch                         radiusTwice + bottomRightRadius.width(), radiusTwice + bottomRightRadius.height());
336a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect = tileRect;
337a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect.move(shadowRect.x() + rect.width() - shadowTemplateSize.width() + radiusTwice,
338a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch                  shadowRect.y() + rect.height() - shadowTemplateSize.height() + radiusTwice);
339a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    phase = getPhase(destRect, tileRect);
340a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);
341a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
342a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Draw the bottom left corner.
343a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    tileRect = FloatRect(0, shadowTemplateSize.height() - radiusTwice - bottomLeftRadius.height(),
344a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch                         radiusTwice + bottomLeftRadius.width(), radiusTwice + bottomLeftRadius.height());
345a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect = tileRect;
346a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    destRect.move(shadowRect.x(), shadowRect.y() + rect.height() - shadowTemplateSize.height() + radiusTwice);
347a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    phase = getPhase(destRect, tileRect);
348a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect);
349a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
350a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    // Schedule a purge of the scratch buffer.
351a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch    scheduleScratchBufferPurge();
352a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch}
353a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch
354bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen}
355