1/*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Sencha, Inc. All rights reserved.
4 * Copyright (C) 2010 Igalia S.L. All rights reserved.
5 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
6 * Copyright (C) 2013 Digia Plc. and/or its subsidiary(-ies).
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "config.h"
31#include "ShadowBlur.h"
32
33#include "AffineTransform.h"
34#include "FloatQuad.h"
35#include "GraphicsContext.h"
36#include "ImageBuffer.h"
37#include "Timer.h"
38#include <wtf/MathExtras.h>
39#include <wtf/Noncopyable.h>
40
41using namespace std;
42
43namespace WebCore {
44
45enum {
46    leftLobe = 0,
47    rightLobe = 1
48};
49
50static inline int roundUpToMultipleOf32(int d)
51{
52    return (1 + (d >> 5)) << 5;
53}
54
55// ShadowBlur needs a scratch image as the buffer for the blur filter.
56// Instead of creating and destroying the buffer for every operation,
57// we create a buffer which will be automatically purged via a timer.
58class ScratchBuffer {
59    WTF_MAKE_FAST_ALLOCATED;
60public:
61    ScratchBuffer()
62        : m_purgeTimer(this, &ScratchBuffer::timerFired)
63        , m_lastWasInset(false)
64#if !ASSERT_DISABLED
65        , m_bufferInUse(false)
66#endif
67    {
68    }
69
70    ImageBuffer* getScratchBuffer(const IntSize& size)
71    {
72        ASSERT(!m_bufferInUse);
73#if !ASSERT_DISABLED
74        m_bufferInUse = true;
75#endif
76        // We do not need to recreate the buffer if the current buffer is large enough.
77        if (m_imageBuffer && m_imageBuffer->logicalSize().width() >= size.width() && m_imageBuffer->logicalSize().height() >= size.height())
78            return m_imageBuffer.get();
79
80        // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests.
81        IntSize roundedSize(roundUpToMultipleOf32(size.width()), roundUpToMultipleOf32(size.height()));
82
83        clearScratchBuffer();
84        m_imageBuffer = ImageBuffer::create(roundedSize, 1);
85        return m_imageBuffer.get();
86    }
87
88    bool setCachedShadowValues(const FloatSize& radius, const Color& color, ColorSpace colorSpace, const FloatRect& shadowRect, const RoundedRect::Radii& radii, const FloatSize& layerSize)
89    {
90        if (!m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && m_lastShadowRect == shadowRect &&  m_lastRadii == radii && m_lastLayerSize == layerSize)
91            return false;
92
93        m_lastWasInset = false;
94        m_lastRadius = radius;
95        m_lastColor = color;
96        m_lastColorSpace = colorSpace;
97        m_lastShadowRect = shadowRect;
98        m_lastRadii = radii;
99        m_lastLayerSize = layerSize;
100
101        return true;
102    }
103
104    bool setCachedInsetShadowValues(const FloatSize& radius, const Color& color, ColorSpace colorSpace, const FloatRect& bounds, const FloatRect& shadowRect, const RoundedRect::Radii& radii)
105    {
106        if (m_lastWasInset && m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && m_lastInsetBounds == bounds && shadowRect == m_lastShadowRect && radii == m_lastRadii)
107            return false;
108
109        m_lastWasInset = true;
110        m_lastInsetBounds = bounds;
111        m_lastRadius = radius;
112        m_lastColor = color;
113        m_lastColorSpace = colorSpace;
114        m_lastShadowRect = shadowRect;
115        m_lastRadii = radii;
116
117        return true;
118    }
119
120    void scheduleScratchBufferPurge()
121    {
122#if !ASSERT_DISABLED
123        m_bufferInUse = false;
124#endif
125        if (m_purgeTimer.isActive())
126            m_purgeTimer.stop();
127
128        const double scratchBufferPurgeInterval = 2;
129        m_purgeTimer.startOneShot(scratchBufferPurgeInterval);
130    }
131
132    static ScratchBuffer& shared();
133
134private:
135    void timerFired(Timer<ScratchBuffer>*)
136    {
137        clearScratchBuffer();
138    }
139
140    void clearScratchBuffer()
141    {
142        m_imageBuffer = nullptr;
143        m_lastRadius = FloatSize();
144    }
145
146    OwnPtr<ImageBuffer> m_imageBuffer;
147    Timer<ScratchBuffer> m_purgeTimer;
148
149    FloatRect m_lastInsetBounds;
150    FloatRect m_lastShadowRect;
151    RoundedRect::Radii m_lastRadii;
152    Color m_lastColor;
153    ColorSpace m_lastColorSpace;
154    FloatSize m_lastRadius;
155    bool m_lastWasInset;
156    FloatSize m_lastLayerSize;
157
158#if !ASSERT_DISABLED
159    bool m_bufferInUse;
160#endif
161};
162
163ScratchBuffer& ScratchBuffer::shared()
164{
165    DEFINE_STATIC_LOCAL(ScratchBuffer, scratchBuffer, ());
166    return scratchBuffer;
167}
168
169static const int templateSideLength = 1;
170
171#if USE(CG)
172static float radiusToLegacyRadius(float radius)
173{
174    return radius > 8 ? 8 + 4 * sqrt((radius - 8) / 2) : radius;
175}
176#endif
177
178ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace)
179    : m_color(color)
180    , m_colorSpace(colorSpace)
181    , m_blurRadius(radius)
182    , m_offset(offset)
183    , m_layerImage(0)
184    , m_shadowsIgnoreTransforms(false)
185{
186    updateShadowBlurValues();
187}
188
189ShadowBlur::ShadowBlur(const GraphicsContextState& state)
190    : m_color(state.shadowColor)
191    , m_colorSpace(state.shadowColorSpace)
192    , m_blurRadius(state.shadowBlur, state.shadowBlur)
193    , m_offset(state.shadowOffset)
194    , m_layerImage(0)
195    , m_shadowsIgnoreTransforms(state.shadowsIgnoreTransforms)
196{
197#if USE(CG)
198    if (state.shadowsUseLegacyRadius) {
199        float shadowBlur = radiusToLegacyRadius(state.shadowBlur);
200        m_blurRadius = FloatSize(shadowBlur, shadowBlur);
201    }
202#endif
203    updateShadowBlurValues();
204}
205
206ShadowBlur::ShadowBlur()
207    : m_type(NoShadow)
208    , m_blurRadius(0, 0)
209    , m_shadowsIgnoreTransforms(false)
210{
211}
212
213void ShadowBlur::setShadowValues(const FloatSize& radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace, bool ignoreTransforms)
214{
215    m_blurRadius = radius;
216    m_offset = offset;
217    m_color = color;
218    m_colorSpace = colorSpace;
219    m_shadowsIgnoreTransforms = ignoreTransforms;
220
221    updateShadowBlurValues();
222}
223
224void ShadowBlur::updateShadowBlurValues()
225{
226    // Limit blur radius to 128 to avoid lots of very expensive blurring.
227    m_blurRadius = m_blurRadius.shrunkTo(FloatSize(128, 128));
228
229    // The type of shadow is decided by the blur radius, shadow offset, and shadow color.
230    if (!m_color.isValid() || !m_color.alpha()) {
231        // Can't paint the shadow with invalid or invisible color.
232        m_type = NoShadow;
233    } else if (m_blurRadius.width() > 0 || m_blurRadius.height() > 0) {
234        // Shadow is always blurred, even the offset is zero.
235        m_type = BlurShadow;
236    } else if (!m_offset.width() && !m_offset.height()) {
237        // Without blur and zero offset means the shadow is fully hidden.
238        m_type = NoShadow;
239    } else
240        m_type = SolidShadow;
241}
242
243// Instead of integer division, we use 17.15 for fixed-point division.
244static const int blurSumShift = 15;
245
246// Takes a two dimensional array with three rows and two columns for the lobes.
247static void calculateLobes(int lobes[][2], float blurRadius, bool shadowsIgnoreTransforms)
248{
249    int diameter;
250    if (shadowsIgnoreTransforms)
251        diameter = max(2, static_cast<int>(floorf((2 / 3.f) * blurRadius))); // Canvas shadow. FIXME: we should adjust the blur radius higher up.
252    else {
253        // http://dev.w3.org/csswg/css3-background/#box-shadow
254        // Approximate a Gaussian blur with a standard deviation equal to half the blur radius,
255        // which http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement tell us how to do.
256        // However, shadows rendered according to that spec will extend a little further than m_blurRadius,
257        // so we apply a fudge factor to bring the radius down slightly.
258        float stdDev = blurRadius / 2;
259        const float gaussianKernelFactor = 3 / 4.f * sqrtf(2 * piFloat);
260        const float fudgeFactor = 0.88f;
261        diameter = max(2, static_cast<int>(floorf(stdDev * gaussianKernelFactor * fudgeFactor + 0.5f)));
262    }
263
264    if (diameter & 1) {
265        // if d is odd, use three box-blurs of size 'd', centered on the output pixel.
266        int lobeSize = (diameter - 1) / 2;
267        lobes[0][leftLobe] = lobeSize;
268        lobes[0][rightLobe] = lobeSize;
269        lobes[1][leftLobe] = lobeSize;
270        lobes[1][rightLobe] = lobeSize;
271        lobes[2][leftLobe] = lobeSize;
272        lobes[2][rightLobe] = lobeSize;
273    } else {
274        // if d is even, two box-blurs of size 'd' (the first one centered on the pixel boundary
275        // between the output pixel and the one to the left, the second one centered on the pixel
276        // boundary between the output pixel and the one to the right) and one box blur of size 'd+1' centered on the output pixel
277        int lobeSize = diameter / 2;
278        lobes[0][leftLobe] = lobeSize;
279        lobes[0][rightLobe] = lobeSize - 1;
280        lobes[1][leftLobe] = lobeSize - 1;
281        lobes[1][rightLobe] = lobeSize;
282        lobes[2][leftLobe] = lobeSize;
283        lobes[2][rightLobe] = lobeSize;
284    }
285}
286
287void ShadowBlur::clear()
288{
289    m_type = NoShadow;
290    m_color = Color();
291    m_blurRadius = FloatSize();
292    m_offset = FloatSize();
293}
294
295void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, int rowStride)
296{
297    const int channels[4] = { 3, 0, 1, 3 };
298
299    int lobes[3][2]; // indexed by pass, and left/right lobe
300    calculateLobes(lobes, m_blurRadius.width(), m_shadowsIgnoreTransforms);
301
302    // First pass is horizontal.
303    int stride = 4;
304    int delta = rowStride;
305    int final = size.height();
306    int dim = size.width();
307
308    // Two stages: horizontal and vertical
309    for (int pass = 0; pass < 2; ++pass) {
310        unsigned char* pixels = imageData;
311
312        if (!pass && !m_blurRadius.width())
313            final = 0; // Do no work if horizonal blur is zero.
314
315        for (int j = 0; j < final; ++j, pixels += delta) {
316            // For each step, we blur the alpha in a channel and store the result
317            // in another channel for the subsequent step.
318            // We use sliding window algorithm to accumulate the alpha values.
319            // This is much more efficient than computing the sum of each pixels
320            // covered by the box kernel size for each x.
321            for (int step = 0; step < 3; ++step) {
322                int side1 = lobes[step][leftLobe];
323                int side2 = lobes[step][rightLobe];
324                int pixelCount = side1 + 1 + side2;
325                int invCount = ((1 << blurSumShift) + pixelCount - 1) / pixelCount;
326                int ofs = 1 + side2;
327                int alpha1 = pixels[channels[step]];
328                int alpha2 = pixels[(dim - 1) * stride + channels[step]];
329
330                unsigned char* ptr = pixels + channels[step + 1];
331                unsigned char* prev = pixels + stride + channels[step];
332                unsigned char* next = pixels + ofs * stride + channels[step];
333
334                int i;
335                int sum = side1 * alpha1 + alpha1;
336                int limit = (dim < side2 + 1) ? dim : side2 + 1;
337
338                for (i = 1; i < limit; ++i, prev += stride)
339                    sum += *prev;
340
341                if (limit <= side2)
342                    sum += (side2 - limit + 1) * alpha2;
343
344                limit = (side1 < dim) ? side1 : dim;
345                for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) {
346                    *ptr = (sum * invCount) >> blurSumShift;
347                    sum += ((ofs < dim) ? *next : alpha2) - alpha1;
348                }
349
350                prev = pixels + channels[step];
351                for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) {
352                    *ptr = (sum * invCount) >> blurSumShift;
353                    sum += (*next) - (*prev);
354                }
355
356                for (; i < dim; ptr += stride, prev += stride, ++i) {
357                    *ptr = (sum * invCount) >> blurSumShift;
358                    sum += alpha2 - (*prev);
359                }
360            }
361        }
362
363        // Last pass is vertical.
364        stride = rowStride;
365        delta = 4;
366        final = size.width();
367        dim = size.height();
368
369        if (!m_blurRadius.height())
370            break;
371
372        if (m_blurRadius.width() != m_blurRadius.height())
373            calculateLobes(lobes, m_blurRadius.height(), m_shadowsIgnoreTransforms);
374    }
375}
376
377void ShadowBlur::adjustBlurRadius(GraphicsContext* context)
378{
379    if (!m_shadowsIgnoreTransforms)
380        return;
381
382    AffineTransform transform = context->getCTM();
383    m_blurRadius.scale(1 / static_cast<float>(transform.xScale()), 1 / static_cast<float>(transform.yScale()));
384}
385
386IntSize ShadowBlur::blurredEdgeSize() const
387{
388    IntSize edgeSize = expandedIntSize(m_blurRadius);
389
390    // To avoid slowing down blurLayerImage() for radius == 1, we give it two empty pixels on each side.
391    if (edgeSize.width() == 1)
392        edgeSize.setWidth(2);
393
394    if (edgeSize.height() == 1)
395        edgeSize.setHeight(2);
396
397    return edgeSize;
398}
399
400IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const FloatRect& shadowedRect, const IntRect& clipRect)
401{
402    IntSize edgeSize = blurredEdgeSize();
403
404    // Calculate the destination of the blurred and/or transformed layer.
405    FloatRect layerRect;
406    IntSize inflation;
407
408    const AffineTransform transform = context->getCTM();
409    if (m_shadowsIgnoreTransforms && !transform.isIdentity()) {
410        FloatQuad transformedPolygon = transform.mapQuad(FloatQuad(shadowedRect));
411        transformedPolygon.move(m_offset);
412        layerRect = transform.inverse().mapQuad(transformedPolygon).boundingBox();
413    } else {
414        layerRect = shadowedRect;
415        layerRect.move(m_offset);
416    }
417
418    // We expand the area by the blur radius to give extra space for the blur transition.
419    if (m_type == BlurShadow) {
420        layerRect.inflateX(edgeSize.width());
421        layerRect.inflateY(edgeSize.height());
422        inflation = edgeSize;
423    }
424
425    FloatRect unclippedLayerRect = layerRect;
426
427    if (!clipRect.contains(enclosingIntRect(layerRect))) {
428        // If we are totally outside the clip region, we aren't painting at all.
429        if (intersection(layerRect, clipRect).isEmpty())
430            return IntRect();
431
432        IntRect inflatedClip = clipRect;
433        // Pixels at the edges can be affected by pixels outside the buffer,
434        // so intersect with the clip inflated by the blur.
435        if (m_type == BlurShadow) {
436            inflatedClip.inflateX(edgeSize.width());
437            inflatedClip.inflateY(edgeSize.height());
438        } else {
439            // Enlarge the clipping area 1 pixel so that the fill does not
440            // bleed (due to antialiasing) even if the unaligned clip rect occurred
441            inflatedClip.inflateX(1);
442            inflatedClip.inflateY(1);
443        }
444
445        layerRect.intersect(inflatedClip);
446    }
447
448    IntSize frameSize = inflation;
449    frameSize.scale(2);
450    m_sourceRect = FloatRect(0, 0, shadowedRect.width() + frameSize.width(), shadowedRect.height() + frameSize.height());
451    m_layerOrigin = FloatPoint(layerRect.x(), layerRect.y());
452    m_layerSize = layerRect.size();
453
454    const FloatPoint unclippedLayerOrigin = FloatPoint(unclippedLayerRect.x(), unclippedLayerRect.y());
455    const FloatSize clippedOut = unclippedLayerOrigin - m_layerOrigin;
456
457    // Set the origin as the top left corner of the scratch image, or, in case there's a clipped
458    // out region, set the origin accordingly to the full bounding rect's top-left corner.
459    float translationX = -shadowedRect.x() + inflation.width() - fabsf(clippedOut.width());
460    float translationY = -shadowedRect.y() + inflation.height() - fabsf(clippedOut.height());
461    m_layerContextTranslation = FloatSize(translationX, translationY);
462
463    return enclosingIntRect(layerRect);
464}
465
466void ShadowBlur::drawShadowBuffer(GraphicsContext* graphicsContext)
467{
468    if (!m_layerImage)
469        return;
470
471    GraphicsContextStateSaver stateSaver(*graphicsContext);
472
473    IntSize bufferSize = m_layerImage->internalSize();
474    if (bufferSize != m_layerSize) {
475        // The rect passed to clipToImageBuffer() has to be the size of the entire buffer,
476        // but we may not have cleared it all, so clip to the filled part first.
477        graphicsContext->clip(FloatRect(m_layerOrigin, m_layerSize));
478    }
479    graphicsContext->clipToImageBuffer(m_layerImage, FloatRect(m_layerOrigin, bufferSize));
480    graphicsContext->setFillColor(m_color, m_colorSpace);
481
482    graphicsContext->clearShadow();
483    graphicsContext->fillRect(FloatRect(m_layerOrigin, m_sourceRect.size()));
484}
485
486static void computeSliceSizesFromRadii(const IntSize& twiceRadius, const RoundedRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice)
487{
488    leftSlice = twiceRadius.width() + max(radii.topLeft().width(), radii.bottomLeft().width());
489    rightSlice = twiceRadius.width() + max(radii.topRight().width(), radii.bottomRight().width());
490
491    topSlice = twiceRadius.height() + max(radii.topLeft().height(), radii.topRight().height());
492    bottomSlice = twiceRadius.height() + max(radii.bottomLeft().height(), radii.bottomRight().height());
493}
494
495IntSize ShadowBlur::templateSize(const IntSize& radiusPadding, const RoundedRect::Radii& radii) const
496{
497    const int templateSideLength = 1;
498
499    int leftSlice;
500    int rightSlice;
501    int topSlice;
502    int bottomSlice;
503
504    IntSize blurExpansion = radiusPadding;
505    blurExpansion.scale(2);
506
507    computeSliceSizesFromRadii(blurExpansion, radii, leftSlice, rightSlice, topSlice, bottomSlice);
508
509    return IntSize(templateSideLength + leftSlice + rightSlice,
510                   templateSideLength + topSlice + bottomSlice);
511}
512
513void ShadowBlur::drawRectShadow(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii)
514{
515    IntRect layerRect = calculateLayerBoundingRect(graphicsContext, shadowedRect, graphicsContext->clipBounds());
516    if (layerRect.isEmpty())
517        return;
518
519    adjustBlurRadius(graphicsContext);
520
521    // drawRectShadowWithTiling does not work with rotations.
522    // https://bugs.webkit.org/show_bug.cgi?id=45042
523    if (!graphicsContext->getCTM().preservesAxisAlignment() || m_type != BlurShadow) {
524        drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect);
525        return;
526    }
527
528    IntSize edgeSize = blurredEdgeSize();
529    IntSize templateSize = this->templateSize(edgeSize, radii);
530
531    if (templateSize.width() > shadowedRect.width() || templateSize.height() > shadowedRect.height()
532        || (templateSize.width() * templateSize.height() > m_sourceRect.width() * m_sourceRect.height())) {
533        drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect);
534        return;
535    }
536
537    drawRectShadowWithTiling(graphicsContext, shadowedRect, radii, templateSize, edgeSize);
538}
539
540void ShadowBlur::drawInsetShadow(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedRect::Radii& holeRadii)
541{
542    IntRect layerRect = calculateLayerBoundingRect(graphicsContext, rect, graphicsContext->clipBounds());
543    if (layerRect.isEmpty())
544        return;
545
546    adjustBlurRadius(graphicsContext);
547
548    // drawInsetShadowWithTiling does not work with rotations.
549    // https://bugs.webkit.org/show_bug.cgi?id=45042
550    if (!graphicsContext->getCTM().preservesAxisAlignment() || m_type != BlurShadow) {
551        drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect);
552        return;
553    }
554
555    IntSize edgeSize = blurredEdgeSize();
556    IntSize templateSize = this->templateSize(edgeSize, holeRadii);
557
558    if (templateSize.width() > holeRect.width() || templateSize.height() > holeRect.height()
559        || (templateSize.width() * templateSize.height() > holeRect.width() * holeRect.height())) {
560        drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect);
561        return;
562    }
563
564    drawInsetShadowWithTiling(graphicsContext, rect, holeRect, holeRadii, templateSize, edgeSize);
565}
566
567void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii, const IntRect& layerRect)
568{
569    m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
570    if (!m_layerImage)
571        return;
572
573    FloatRect bufferRelativeShadowedRect = shadowedRect;
574    bufferRelativeShadowedRect.move(m_layerContextTranslation);
575
576    // Only redraw in the scratch buffer if its cached contents don't match our needs
577    bool redrawNeeded = ScratchBuffer::shared().setCachedShadowValues(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeShadowedRect, radii, m_layerSize);
578    if (redrawNeeded) {
579        GraphicsContext* shadowContext = m_layerImage->context();
580        GraphicsContextStateSaver stateSaver(*shadowContext);
581
582        // Add a pixel to avoid later edge aliasing when rotated.
583        shadowContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
584        shadowContext->translate(m_layerContextTranslation);
585        shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
586        if (radii.isZero())
587            shadowContext->fillRect(shadowedRect);
588        else {
589            Path path;
590            path.addRoundedRect(shadowedRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
591            shadowContext->fillPath(path);
592        }
593
594        blurShadowBuffer(expandedIntSize(m_layerSize));
595    }
596
597    drawShadowBuffer(graphicsContext);
598    m_layerImage = 0;
599    ScratchBuffer::shared().scheduleScratchBufferPurge();
600}
601
602void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedRect::Radii& holeRadii, const IntRect& layerRect)
603{
604    m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
605    if (!m_layerImage)
606        return;
607
608    FloatRect bufferRelativeRect = rect;
609    bufferRelativeRect.move(m_layerContextTranslation);
610
611    FloatRect bufferRelativeHoleRect = holeRect;
612    bufferRelativeHoleRect.move(m_layerContextTranslation);
613
614    // Only redraw in the scratch buffer if its cached contents don't match our needs
615    bool redrawNeeded = ScratchBuffer::shared().setCachedInsetShadowValues(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeRect, bufferRelativeHoleRect, holeRadii);
616    if (redrawNeeded) {
617        GraphicsContext* shadowContext = m_layerImage->context();
618        GraphicsContextStateSaver stateSaver(*shadowContext);
619
620        // Add a pixel to avoid later edge aliasing when rotated.
621        shadowContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
622        shadowContext->translate(m_layerContextTranslation);
623
624        Path path;
625        path.addRect(rect);
626        if (holeRadii.isZero())
627            path.addRect(holeRect);
628        else
629            path.addRoundedRect(holeRect, holeRadii.topLeft(), holeRadii.topRight(), holeRadii.bottomLeft(), holeRadii.bottomRight());
630
631        shadowContext->setFillRule(RULE_EVENODD);
632        shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
633        shadowContext->fillPath(path);
634
635        blurShadowBuffer(expandedIntSize(m_layerSize));
636    }
637
638    drawShadowBuffer(graphicsContext);
639    m_layerImage = 0;
640    ScratchBuffer::shared().scheduleScratchBufferPurge();
641}
642
643/*
644  These functions use tiling to improve the performance of the shadow
645  drawing of rounded rectangles. The code basically does the following
646  steps:
647
648     1. Calculate the size of the shadow template, a rectangle that
649     contains all the necessary tiles to draw the complete shadow.
650
651     2. If that size is smaller than the real rectangle render the new
652     template rectangle and its shadow in a new surface, in other case
653     render the shadow of the real rectangle in the destination
654     surface.
655
656     3. Calculate the sizes and positions of the tiles and their
657     destinations and use drawPattern to render the final shadow. The
658     code divides the rendering in 8 tiles:
659
660        1 | 2 | 3
661       -----------
662        4 |   | 5
663       -----------
664        6 | 7 | 8
665
666     The corners are directly copied from the template rectangle to the
667     real one and the side tiles are 1 pixel width, we use them as
668     tiles to cover the destination side. The corner tiles are bigger
669     than just the side of the rounded corner, we need to increase it
670     because the modifications caused by the corner over the blur
671     effect. We fill the central or outer part with solid color to complete
672     the shadow.
673 */
674
675void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedRect::Radii& radii, const IntSize& templateSize, const IntSize& edgeSize)
676{
677    m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
678    if (!m_layerImage)
679        return;
680
681    // Draw the rectangle with hole.
682    FloatRect templateBounds(0, 0, templateSize.width(), templateSize.height());
683    FloatRect templateHole = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height());
684
685    // Only redraw in the scratch buffer if its cached contents don't match our needs
686    bool redrawNeeded = ScratchBuffer::shared().setCachedInsetShadowValues(m_blurRadius, m_color, m_colorSpace, templateBounds, templateHole, radii);
687    if (redrawNeeded) {
688        // Draw shadow into a new ImageBuffer.
689        GraphicsContext* shadowContext = m_layerImage->context();
690        GraphicsContextStateSaver shadowStateSaver(*shadowContext);
691        shadowContext->clearRect(templateBounds);
692        shadowContext->setFillRule(RULE_EVENODD);
693        shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
694
695        Path path;
696        path.addRect(templateBounds);
697        if (radii.isZero())
698            path.addRect(templateHole);
699        else
700            path.addRoundedRect(templateHole, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
701
702        shadowContext->fillPath(path);
703
704        blurAndColorShadowBuffer(templateSize);
705    }
706    FloatSize offset = m_offset;
707    if (shadowsIgnoreTransforms()) {
708        AffineTransform transform = graphicsContext->getCTM();
709        offset.scale(1 / transform.xScale(), 1 / transform.yScale());
710    }
711
712    FloatRect boundingRect = rect;
713    boundingRect.move(offset);
714
715    FloatRect destHoleRect = holeRect;
716    destHoleRect.move(offset);
717    FloatRect destHoleBounds = destHoleRect;
718    destHoleBounds.inflateX(edgeSize.width());
719    destHoleBounds.inflateY(edgeSize.height());
720
721    // Fill the external part of the shadow (which may be visible because of offset).
722    Path exteriorPath;
723    exteriorPath.addRect(boundingRect);
724    exteriorPath.addRect(destHoleBounds);
725
726    {
727        GraphicsContextStateSaver fillStateSaver(*graphicsContext);
728        graphicsContext->setFillRule(RULE_EVENODD);
729        graphicsContext->setFillColor(m_color, m_colorSpace);
730        graphicsContext->clearShadow();
731        graphicsContext->fillPath(exteriorPath);
732    }
733
734    drawLayerPieces(graphicsContext, destHoleBounds, radii, edgeSize, templateSize, InnerShadow);
735
736    m_layerImage = 0;
737    ScratchBuffer::shared().scheduleScratchBufferPurge();
738}
739
740void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedRect::Radii& radii, const IntSize& templateSize, const IntSize& edgeSize)
741{
742    m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
743    if (!m_layerImage)
744        return;
745
746    FloatRect templateShadow = FloatRect(edgeSize.width(), edgeSize.height(), templateSize.width() - 2 * edgeSize.width(), templateSize.height() - 2 * edgeSize.height());
747
748    // Only redraw in the scratch buffer if its cached contents don't match our needs
749    bool redrawNeeded = ScratchBuffer::shared().setCachedShadowValues(m_blurRadius, m_color, m_colorSpace, templateShadow, radii, m_layerSize);
750    if (redrawNeeded) {
751        // Draw shadow into the ImageBuffer.
752        GraphicsContext* shadowContext = m_layerImage->context();
753        GraphicsContextStateSaver shadowStateSaver(*shadowContext);
754
755        shadowContext->clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
756        shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
757
758        if (radii.isZero())
759            shadowContext->fillRect(templateShadow);
760        else {
761            Path path;
762            path.addRoundedRect(templateShadow, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
763            shadowContext->fillPath(path);
764        }
765
766        blurAndColorShadowBuffer(templateSize);
767    }
768    FloatSize offset = m_offset;
769    if (shadowsIgnoreTransforms()) {
770        AffineTransform transform = graphicsContext->getCTM();
771        offset.scale(1 / transform.xScale(), 1 / transform.yScale());
772    }
773
774    FloatRect shadowBounds = shadowedRect;
775    shadowBounds.move(offset);
776    shadowBounds.inflateX(edgeSize.width());
777    shadowBounds.inflateY(edgeSize.height());
778
779    drawLayerPieces(graphicsContext, shadowBounds, radii, edgeSize, templateSize, OuterShadow);
780
781    m_layerImage = 0;
782    ScratchBuffer::shared().scheduleScratchBufferPurge();
783}
784
785void ShadowBlur::drawLayerPieces(GraphicsContext* graphicsContext, const FloatRect& shadowBounds, const RoundedRect::Radii& radii, const IntSize& bufferPadding, const IntSize& templateSize, ShadowDirection direction)
786{
787    const IntSize twiceRadius = IntSize(bufferPadding.width() * 2, bufferPadding.height() * 2);
788
789    int leftSlice;
790    int rightSlice;
791    int topSlice;
792    int bottomSlice;
793    computeSliceSizesFromRadii(twiceRadius, radii, leftSlice, rightSlice, topSlice, bottomSlice);
794
795    int centerWidth = shadowBounds.width() - leftSlice - rightSlice;
796    int centerHeight = shadowBounds.height() - topSlice - bottomSlice;
797
798    if (direction == OuterShadow) {
799        FloatRect shadowInterior(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
800        if (!shadowInterior.isEmpty()) {
801            GraphicsContextStateSaver stateSaver(*graphicsContext);
802            graphicsContext->setFillColor(m_color, m_colorSpace);
803            graphicsContext->clearShadow();
804            graphicsContext->fillRect(shadowInterior);
805        }
806    }
807
808    GraphicsContextStateSaver stateSaver(*graphicsContext);
809    graphicsContext->setFillColor(m_color, m_colorSpace);
810    graphicsContext->clearShadow();
811
812    // Note that drawing the ImageBuffer is faster than creating a Image and drawing that,
813    // because ImageBuffer::draw() knows that it doesn't have to copy the image bits.
814    FloatRect centerRect(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
815    centerRect = graphicsContext->roundToDevicePixels(centerRect);
816
817    // Top side.
818    FloatRect tileRect = FloatRect(leftSlice, 0, templateSideLength, topSlice);
819    FloatRect destRect = FloatRect(centerRect.x(), centerRect.y() - topSlice, centerRect.width(), topSlice);
820    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
821
822    // Draw the bottom side.
823    tileRect.setY(templateSize.height() - bottomSlice);
824    tileRect.setHeight(bottomSlice);
825    destRect.setY(centerRect.maxY());
826    destRect.setHeight(bottomSlice);
827    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
828
829    // Left side.
830    tileRect = FloatRect(0, topSlice, leftSlice, templateSideLength);
831    destRect = FloatRect(centerRect.x() - leftSlice, centerRect.y(), leftSlice, centerRect.height());
832    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
833
834    // Right side.
835    tileRect.setX(templateSize.width() - rightSlice);
836    tileRect.setWidth(rightSlice);
837    destRect.setX(centerRect.maxX());
838    destRect.setWidth(rightSlice);
839    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
840
841    // Top left corner.
842    tileRect = FloatRect(0, 0, leftSlice, topSlice);
843    destRect = FloatRect(centerRect.x() - leftSlice, centerRect.y() - topSlice, leftSlice, topSlice);
844    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
845
846    // Top right corner.
847    tileRect = FloatRect(templateSize.width() - rightSlice, 0, rightSlice, topSlice);
848    destRect = FloatRect(centerRect.maxX(), centerRect.y() - topSlice, rightSlice, topSlice);
849    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
850
851    // Bottom right corner.
852    tileRect = FloatRect(templateSize.width() - rightSlice, templateSize.height() - bottomSlice, rightSlice, bottomSlice);
853    destRect = FloatRect(centerRect.maxX(), centerRect.maxY(), rightSlice, bottomSlice);
854    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
855
856    // Bottom left corner.
857    tileRect = FloatRect(0, templateSize.height() - bottomSlice, leftSlice, bottomSlice);
858    destRect = FloatRect(centerRect.x() - leftSlice, centerRect.maxY(), leftSlice, bottomSlice);
859    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
860}
861
862
863void ShadowBlur::blurShadowBuffer(const IntSize& templateSize)
864{
865    if (m_type != BlurShadow)
866        return;
867
868    IntRect blurRect(IntPoint(), templateSize);
869    RefPtr<Uint8ClampedArray> layerData = m_layerImage->getUnmultipliedImageData(blurRect);
870    blurLayerImage(layerData->data(), blurRect.size(), blurRect.width() * 4);
871    m_layerImage->putByteArray(Unmultiplied, layerData.get(), blurRect.size(), blurRect, IntPoint());
872}
873
874void ShadowBlur::blurAndColorShadowBuffer(const IntSize& templateSize)
875{
876    blurShadowBuffer(templateSize);
877
878    // Mask the image with the shadow color.
879    GraphicsContext* shadowContext = m_layerImage->context();
880    GraphicsContextStateSaver stateSaver(*shadowContext);
881    shadowContext->setCompositeOperation(CompositeSourceIn);
882    shadowContext->setFillColor(m_color, m_colorSpace);
883    shadowContext->fillRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
884}
885
886GraphicsContext* ShadowBlur::beginShadowLayer(GraphicsContext *context, const FloatRect& layerArea)
887{
888    adjustBlurRadius(context);
889
890    IntRect layerRect = calculateLayerBoundingRect(context, layerArea, context->clipBounds());
891
892    if (layerRect.isEmpty())
893        return 0;
894
895    // We reset the scratch buffer values here, because the buffer will no longer contain
896    // data from any previous rectangle or inset shadows drawn via the tiling path.
897    ScratchBuffer::shared().setCachedShadowValues(FloatSize(), Color::black, ColorSpaceDeviceRGB, IntRect(), RoundedRect::Radii(), m_layerSize);
898    m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
899
900    GraphicsContext* shadowContext = m_layerImage->context();
901    shadowContext->save();
902
903    // Add a pixel to avoid later edge aliasing when rotated.
904    shadowContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
905
906    shadowContext->translate(m_layerContextTranslation);
907    return shadowContext;
908}
909
910void ShadowBlur::endShadowLayer(GraphicsContext* context)
911{
912    m_layerImage->context()->restore();
913
914    blurAndColorShadowBuffer(expandedIntSize(m_layerSize));
915    GraphicsContextStateSaver stateSave(*context);
916
917    context->clearShadow();
918    context->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, roundedIntPoint(m_layerOrigin), IntRect(0, 0, m_layerSize.width(), m_layerSize.height()), context->compositeOperation());
919
920    m_layerImage = 0;
921    ScratchBuffer::shared().scheduleScratchBufferPurge();
922}
923
924} // namespace WebCore
925