12fc2651226baac27029e38c9d6ef883fa32084dbSteve Block/*
22fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * Copyright (C) 2011 Apple Inc.
32fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * Copyright (C) 2010 Sencha, Inc.
42fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * Copyright (C) 2010 Igalia S.L.
52fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * All rights reserved.
62fc2651226baac27029e38c9d6ef883fa32084dbSteve Block *
72fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * Redistribution and use in source and binary forms, with or without
82fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * modification, are permitted provided that the following conditions
92fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * are met:
102fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * 1. Redistributions of source code must retain the above copyright
112fc2651226baac27029e38c9d6ef883fa32084dbSteve Block *    notice, this list of conditions and the following disclaimer.
122fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * 2. Redistributions in binary form must reproduce the above copyright
132fc2651226baac27029e38c9d6ef883fa32084dbSteve Block *    notice, this list of conditions and the following disclaimer in the
142fc2651226baac27029e38c9d6ef883fa32084dbSteve Block *    documentation and/or other materials provided with the distribution.
152fc2651226baac27029e38c9d6ef883fa32084dbSteve Block *
162fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
172fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
182fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
192fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
202fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
212fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
222fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
232fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
242fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
252fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
262fc2651226baac27029e38c9d6ef883fa32084dbSteve Block * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272fc2651226baac27029e38c9d6ef883fa32084dbSteve Block */
282fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
292fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#include "config.h"
302fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#include "ShadowBlur.h"
312fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
322fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#include "AffineTransform.h"
332fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#include "FloatQuad.h"
342fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#include "GraphicsContext.h"
352fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#include "ImageBuffer.h"
362fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#include "Timer.h"
372fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#include <wtf/MathExtras.h>
382fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#include <wtf/Noncopyable.h>
392fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#include <wtf/UnusedParam.h>
402fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
412fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockusing namespace std;
422fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
432fc2651226baac27029e38c9d6ef883fa32084dbSteve Blocknamespace WebCore {
442fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
452fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockstatic inline int roundUpToMultipleOf32(int d)
462fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
472fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    return (1 + (d >> 5)) << 5;
482fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
492fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
502fc2651226baac27029e38c9d6ef883fa32084dbSteve Block// ShadowBlur needs a scratch image as the buffer for the blur filter.
512fc2651226baac27029e38c9d6ef883fa32084dbSteve Block// Instead of creating and destroying the buffer for every operation,
522fc2651226baac27029e38c9d6ef883fa32084dbSteve Block// we create a buffer which will be automatically purged via a timer.
532fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockclass ScratchBuffer {
542fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockpublic:
552fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    ScratchBuffer()
562fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        : m_purgeTimer(this, &ScratchBuffer::timerFired)
572daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        , m_lastRadius(0)
582daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        , m_lastWasInset(false)
592fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#if !ASSERT_DISABLED
602fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        , m_bufferInUse(false)
612fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#endif
622fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    {
632fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
642fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
652fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    ImageBuffer* getScratchBuffer(const IntSize& size)
662fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    {
672fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        ASSERT(!m_bufferInUse);
682fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#if !ASSERT_DISABLED
692fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        m_bufferInUse = true;
702fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#endif
712fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // We do not need to recreate the buffer if the current buffer is large enough.
722fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        if (m_imageBuffer && m_imageBuffer->width() >= size.width() && m_imageBuffer->height() >= size.height())
732fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            return m_imageBuffer.get();
742fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
752fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests.
762fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        IntSize roundedSize(roundUpToMultipleOf32(size.width()), roundUpToMultipleOf32(size.height()));
772fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
782fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        m_imageBuffer = ImageBuffer::create(roundedSize);
792fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return m_imageBuffer.get();
802fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
812fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
822daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    void setLastShadowValues(float radius, const Color& color, ColorSpace colorSpace, const FloatRect& shadowRect, const RoundedIntRect::Radii& radii)
832daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    {
842daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastWasInset = false;
852daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastRadius = radius;
862daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastColor = color;
872daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastColorSpace = colorSpace;
882daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastShadowRect = shadowRect;
892daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastRadii = radii;
902daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    }
912daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
922daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    void setLastInsetShadowValues(float radius, const Color& color, ColorSpace colorSpace, const FloatRect& bounds, const FloatRect& shadowRect, const RoundedIntRect::Radii& radii)
932daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    {
942daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastWasInset = true;
952daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastInsetBounds = bounds;
962daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastRadius = radius;
972daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastColor = color;
982daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastColorSpace = colorSpace;
992daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastShadowRect = shadowRect;
1002daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastRadii = radii;
1012daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    }
1022daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
1032daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    bool matchesLastShadow(float radius, const Color& color, ColorSpace colorSpace, const FloatRect& shadowRect, const RoundedIntRect::Radii& radii) const
1042daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    {
1052daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        if (m_lastWasInset)
1062daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            return false;
1072daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        return m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && shadowRect == m_lastShadowRect && radii == m_lastRadii;
1082daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    }
1092daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
1102daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    bool matchesLastInsetShadow(float radius, const Color& color, ColorSpace colorSpace, const FloatRect& bounds, const FloatRect& shadowRect, const RoundedIntRect::Radii& radii) const
1112daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    {
1122daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        if (!m_lastWasInset)
1132daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            return false;
1142daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        return m_lastRadius == radius && m_lastColor == color && m_lastColorSpace == colorSpace && m_lastInsetBounds == bounds && shadowRect == m_lastShadowRect && radii == m_lastRadii;
1152daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    }
1162daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
1172fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    void scheduleScratchBufferPurge()
1182fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    {
1192fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#if !ASSERT_DISABLED
1202fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        m_bufferInUse = false;
1212fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#endif
1222fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        if (m_purgeTimer.isActive())
1232fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            m_purgeTimer.stop();
1242fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1252fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        const double scratchBufferPurgeInterval = 2;
1262fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        m_purgeTimer.startOneShot(scratchBufferPurgeInterval);
1272fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
1282fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1292fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    static ScratchBuffer& shared();
1302fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1312fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockprivate:
1322fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    void timerFired(Timer<ScratchBuffer>*)
1332fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    {
1342fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        clearScratchBuffer();
1352fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
1362fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1372fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    void clearScratchBuffer()
1382fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    {
1392fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        m_imageBuffer = 0;
1402daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        m_lastRadius = 0;
1412fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
1422fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1432fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    OwnPtr<ImageBuffer> m_imageBuffer;
1442fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    Timer<ScratchBuffer> m_purgeTimer;
1452daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
1462daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    FloatRect m_lastInsetBounds;
1472daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    FloatRect m_lastShadowRect;
1482daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    RoundedIntRect::Radii m_lastRadii;
1492daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    Color m_lastColor;
1502daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    ColorSpace m_lastColorSpace;
1512daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    float m_lastRadius;
1522daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    bool m_lastWasInset;
1532daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
1542fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#if !ASSERT_DISABLED
1552fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    bool m_bufferInUse;
1562fc2651226baac27029e38c9d6ef883fa32084dbSteve Block#endif
1572fc2651226baac27029e38c9d6ef883fa32084dbSteve Block};
1582fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1592fc2651226baac27029e38c9d6ef883fa32084dbSteve BlockScratchBuffer& ScratchBuffer::shared()
1602fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
1612fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    DEFINE_STATIC_LOCAL(ScratchBuffer, scratchBuffer, ());
1622fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    return scratchBuffer;
1632fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
1642fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1652fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockstatic const int templateSideLength = 1;
1662fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1672fc2651226baac27029e38c9d6ef883fa32084dbSteve BlockShadowBlur::ShadowBlur(float radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace)
1682fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    : m_color(color)
1692fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    , m_colorSpace(colorSpace)
1702fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    , m_blurRadius(radius)
1712fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    , m_offset(offset)
1722fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    , m_layerImage(0)
1732fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    , m_shadowsIgnoreTransforms(false)
1742fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
1752fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Limit blur radius to 128 to avoid lots of very expensive blurring.
1762fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    m_blurRadius = min<float>(m_blurRadius, 128);
1772fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1782fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // The type of shadow is decided by the blur radius, shadow offset, and shadow color.
1792fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (!m_color.isValid() || !color.alpha()) {
1802fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // Can't paint the shadow with invalid or invisible color.
1812fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        m_type = NoShadow;
1822fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    } else if (m_blurRadius > 0) {
1832fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // Shadow is always blurred, even the offset is zero.
1842fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        m_type = BlurShadow;
1852fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    } else if (!m_offset.width() && !m_offset.height()) {
1862fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // Without blur and zero offset means the shadow is fully hidden.
1872fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        m_type = NoShadow;
1882fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    } else
1892fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        m_type = SolidShadow;
1902fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
1912fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1922fc2651226baac27029e38c9d6ef883fa32084dbSteve Block// Instead of integer division, we use 17.15 for fixed-point division.
1932fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockstatic const int blurSumShift = 15;
1942fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1952fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockvoid ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, int rowStride)
1962fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
1972daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    const int channels[4] = { 3, 0, 1, 3 };
1982fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
1992fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int diameter;
2002fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (m_shadowsIgnoreTransforms)
2012fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        diameter = max(2, static_cast<int>(floorf((2 / 3.f) * m_blurRadius))); // Canvas shadow. FIXME: we should adjust the blur radius higher up.
2022fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    else {
2032fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // http://dev.w3.org/csswg/css3-background/#box-shadow
2042fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // Approximate a Gaussian blur with a standard deviation equal to half the blur radius,
2052fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // which http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement tell us how to do.
2062fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // However, shadows rendered according to that spec will extend a little further than m_blurRadius,
2072fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // so we apply a fudge factor to bring the radius down slightly.
2082fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        float stdDev = m_blurRadius / 2;
20981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        const float gaussianKernelFactor = 3 / 4.f * sqrtf(2 * piFloat);
2102fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        const float fudgeFactor = 0.88f;
2112fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        diameter = max(2, static_cast<int>(floorf(stdDev * gaussianKernelFactor * fudgeFactor + 0.5f)));
2122fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
2132fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2142fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    enum {
2152fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        leftLobe = 0,
2162fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        rightLobe = 1
2172fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    };
2182fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2192fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int lobes[3][2]; // indexed by pass, and left/right lobe
2202fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2212fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (diameter & 1) {
2222fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // if d is odd, use three box-blurs of size 'd', centered on the output pixel.
2232fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        int lobeSize = (diameter - 1) / 2;
2242fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        lobes[0][leftLobe] = lobeSize;
2252fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        lobes[0][rightLobe] = lobeSize;
2262fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        lobes[1][leftLobe] = lobeSize;
2272fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        lobes[1][rightLobe] = lobeSize;
2282fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        lobes[2][leftLobe] = lobeSize;
2292fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        lobes[2][rightLobe] = lobeSize;
2302fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    } else {
2312fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // if d is even, two box-blurs of size 'd' (the first one centered on the pixel boundary
2322fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // between the output pixel and the one to the left, the second one centered on the pixel
2332fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // boundary between the output pixel and the one to the right) and one box blur of size 'd+1' centered on the output pixel
2342fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        int lobeSize = diameter / 2;
2352fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        lobes[0][leftLobe] = lobeSize;
2362fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        lobes[0][rightLobe] = lobeSize - 1;
2372fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        lobes[1][leftLobe] = lobeSize - 1;
2382fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        lobes[1][rightLobe] = lobeSize;
2392fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        lobes[2][leftLobe] = lobeSize;
2402fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        lobes[2][rightLobe] = lobeSize;
2412fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
2422fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2432fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // First pass is horizontal.
2442fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int stride = 4;
2452fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int delta = rowStride;
2462fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int final = size.height();
2472fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int dim = size.width();
2482fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2492fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Two stages: horizontal and vertical
2502fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    for (int pass = 0; pass < 2; ++pass) {
2512fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        unsigned char* pixels = imageData;
2522fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2532fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        for (int j = 0; j < final; ++j, pixels += delta) {
2542fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            // For each step, we blur the alpha in a channel and store the result
2552fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            // in another channel for the subsequent step.
2562fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            // We use sliding window algorithm to accumulate the alpha values.
2572fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            // This is much more efficient than computing the sum of each pixels
2582fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            // covered by the box kernel size for each x.
2592fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            for (int step = 0; step < 3; ++step) {
2602fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                int side1 = lobes[step][leftLobe];
2612fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                int side2 = lobes[step][rightLobe];
2622fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                int pixelCount = side1 + 1 + side2;
2632fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                int invCount = ((1 << blurSumShift) + pixelCount - 1) / pixelCount;
2642fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                int ofs = 1 + side2;
2652fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                int alpha1 = pixels[channels[step]];
2662fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                int alpha2 = pixels[(dim - 1) * stride + channels[step]];
2672fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2682fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                unsigned char* ptr = pixels + channels[step + 1];
2692fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                unsigned char* prev = pixels + stride + channels[step];
2702fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                unsigned char* next = pixels + ofs * stride + channels[step];
2712fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2722fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                int i;
2732fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                int sum = side1 * alpha1 + alpha1;
2742fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                int limit = (dim < side2 + 1) ? dim : side2 + 1;
2752fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2762fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                for (i = 1; i < limit; ++i, prev += stride)
2772fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                    sum += *prev;
2782fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2792fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                if (limit <= side2)
2802fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                    sum += (side2 - limit + 1) * alpha2;
2812fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2822fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                limit = (side1 < dim) ? side1 : dim;
2832fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) {
2842fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                    *ptr = (sum * invCount) >> blurSumShift;
2852fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                    sum += ((ofs < dim) ? *next : alpha2) - alpha1;
2862fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                }
2872fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2882fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                prev = pixels + channels[step];
2892fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) {
2902fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                    *ptr = (sum * invCount) >> blurSumShift;
2912fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                    sum += (*next) - (*prev);
2922fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                }
2932fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
2942fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                for (; i < dim; ptr += stride, prev += stride, ++i) {
2952fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                    *ptr = (sum * invCount) >> blurSumShift;
2962fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                    sum += alpha2 - (*prev);
2972fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                }
2982fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            }
2992fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        }
3002fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3012fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // Last pass is vertical.
3022fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        stride = rowStride;
3032fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        delta = 4;
3042fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        final = size.width();
3052fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        dim = size.height();
3062fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
3072fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
3082fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3092fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockvoid ShadowBlur::adjustBlurRadius(GraphicsContext* context)
3102fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
3112fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (!m_shadowsIgnoreTransforms)
3122fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return;
3132fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3142fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const AffineTransform transform = context->getCTM();
3152fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3162fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Adjust blur if we're scaling, since the radius must not be affected by transformations.
3172fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // FIXME: use AffineTransform::isIdentityOrTranslationOrFlipped()?
3182fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (transform.isIdentity())
3192fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return;
3202fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3212fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Calculate transformed unit vectors.
3222fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const FloatQuad unitQuad(FloatPoint(0, 0), FloatPoint(1, 0),
3232fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                             FloatPoint(0, 1), FloatPoint(1, 1));
3242fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const FloatQuad transformedUnitQuad = transform.mapQuad(unitQuad);
3252fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3262fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Calculate X axis scale factor.
3272fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const FloatSize xUnitChange = transformedUnitQuad.p2() - transformedUnitQuad.p1();
3282fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const float xAxisScale = sqrtf(xUnitChange.width() * xUnitChange.width()
3292fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                                   + xUnitChange.height() * xUnitChange.height());
3302fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3312fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Calculate Y axis scale factor.
3322fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const FloatSize yUnitChange = transformedUnitQuad.p3() - transformedUnitQuad.p1();
3332fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const float yAxisScale = sqrtf(yUnitChange.width() * yUnitChange.width()
3342fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                                   + yUnitChange.height() * yUnitChange.height());
3352fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3362fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // blurLayerImage() does not support per-axis blurring, so calculate a balanced scaling.
3372fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // FIXME: does AffineTransform.xScale()/yScale() help?
3382fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const float scale = sqrtf(xAxisScale * yAxisScale);
3392fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    m_blurRadius = roundf(m_blurRadius / scale);
3402fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
3412fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3422fc2651226baac27029e38c9d6ef883fa32084dbSteve BlockIntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const FloatRect& shadowedRect, const IntRect& clipRect)
3432fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
3442fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const float roundedRadius = ceilf(m_blurRadius);
3452fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3462fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Calculate the destination of the blurred and/or transformed layer.
3472fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    FloatRect layerRect;
3482fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    float inflation = 0;
3492fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3502fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const AffineTransform transform = context->getCTM();
3512fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (m_shadowsIgnoreTransforms && !transform.isIdentity()) {
3522fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        FloatQuad transformedPolygon = transform.mapQuad(FloatQuad(shadowedRect));
3532fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        transformedPolygon.move(m_offset);
3542fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        layerRect = transform.inverse().mapQuad(transformedPolygon).boundingBox();
3552fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    } else {
3562fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        layerRect = shadowedRect;
3572fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        layerRect.move(m_offset);
3582fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
3592fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3602fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // We expand the area by the blur radius to give extra space for the blur transition.
3612fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (m_type == BlurShadow) {
3622fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        layerRect.inflate(roundedRadius);
3632fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        inflation = roundedRadius;
3642fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
3652fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3662fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    FloatRect unclippedLayerRect = layerRect;
3672fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3682fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (!clipRect.contains(enclosingIntRect(layerRect))) {
3692fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // If we are totally outside the clip region, we aren't painting at all.
3702fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        if (intersection(layerRect, clipRect).isEmpty())
3712fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            return IntRect();
3722fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3732fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        IntRect inflatedClip = clipRect;
3742fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // Pixels at the edges can be affected by pixels outside the buffer,
3752fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // so intersect with the clip inflated by the blur.
3762fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        if (m_type == BlurShadow)
3772fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            inflatedClip.inflate(roundedRadius);
3782fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3792fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        layerRect.intersect(inflatedClip);
3802fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
3812fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3822fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const float frameSize = inflation * 2;
3832fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    m_sourceRect = FloatRect(0, 0, shadowedRect.width() + frameSize, shadowedRect.height() + frameSize);
3842fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    m_layerOrigin = FloatPoint(layerRect.x(), layerRect.y());
3852fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    m_layerSize = layerRect.size();
3862fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3872fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const FloatPoint unclippedLayerOrigin = FloatPoint(unclippedLayerRect.x(), unclippedLayerRect.y());
3882fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const FloatSize clippedOut = unclippedLayerOrigin - m_layerOrigin;
3892fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3902fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Set the origin as the top left corner of the scratch image, or, in case there's a clipped
3912fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // out region, set the origin accordingly to the full bounding rect's top-left corner.
3922fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    float translationX = -shadowedRect.x() + inflation - fabsf(clippedOut.width());
3932fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    float translationY = -shadowedRect.y() + inflation - fabsf(clippedOut.height());
3942fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    m_layerContextTranslation = FloatSize(translationX, translationY);
3952fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3962fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    return enclosingIntRect(layerRect);
3972fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
3982fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
3992daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdochvoid ShadowBlur::drawShadowBuffer(GraphicsContext* graphicsContext)
4002fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
4012fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (!m_layerImage)
4022fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return;
4032fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4042fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->save();
4052fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4062fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    IntSize bufferSize = m_layerImage->size();
4072fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (bufferSize != m_layerSize) {
4082fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // The rect passed to clipToImageBuffer() has to be the size of the entire buffer,
4092fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        // but we may not have cleared it all, so clip to the filled part first.
4102fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        graphicsContext->clip(FloatRect(m_layerOrigin, m_layerSize));
4112fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
4122fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->clipToImageBuffer(m_layerImage, FloatRect(m_layerOrigin, bufferSize));
4132fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->setFillColor(m_color, m_colorSpace);
4142fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4152fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->clearShadow();
4162fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->fillRect(FloatRect(m_layerOrigin, m_sourceRect.size()));
4172fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4182fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->restore();
4192fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
4202fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4212fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockstatic void computeSliceSizesFromRadii(int twiceRadius, const RoundedIntRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice)
4222fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
4232fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    leftSlice = twiceRadius + max(radii.topLeft().width(), radii.bottomLeft().width());
4242fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    rightSlice = twiceRadius + max(radii.topRight().width(), radii.bottomRight().width());
4252fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4262fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    topSlice = twiceRadius + max(radii.topLeft().height(), radii.topRight().height());
4272fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    bottomSlice = twiceRadius + max(radii.bottomLeft().height(), radii.bottomRight().height());
4282fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
4292fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4302fc2651226baac27029e38c9d6ef883fa32084dbSteve BlockIntSize ShadowBlur::templateSize(const RoundedIntRect::Radii& radii) const
4312fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
4322fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const int templateSideLength = 1;
4332fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4342fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int leftSlice;
4352fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int rightSlice;
4362fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int topSlice;
4372fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int bottomSlice;
4382fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    computeSliceSizesFromRadii(2 * ceilf(m_blurRadius), radii, leftSlice, rightSlice, topSlice, bottomSlice);
4392fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4402fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    return IntSize(templateSideLength + leftSlice + rightSlice,
4412fc2651226baac27029e38c9d6ef883fa32084dbSteve Block                   templateSideLength + topSlice + bottomSlice);
4422fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
4432fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4442fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockvoid ShadowBlur::drawRectShadow(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii)
4452fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
4462fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    IntRect layerRect = calculateLayerBoundingRect(graphicsContext, shadowedRect, graphicsContext->clipBounds());
4472fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (layerRect.isEmpty())
4482fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return;
4492fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4502daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    adjustBlurRadius(graphicsContext);
4512daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
4522fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // drawRectShadowWithTiling does not work with rotations.
4532fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // https://bugs.webkit.org/show_bug.cgi?id=45042
4542fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (!graphicsContext->getCTM().isIdentityOrTranslationOrFlipped() || m_type != BlurShadow) {
4552fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect);
4562fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return;
4572fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
4582fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4592fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    IntSize templateSize = this->templateSize(radii);
4602fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4612fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (templateSize.width() > shadowedRect.width() || templateSize.height() > shadowedRect.height()
4622fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        || (templateSize.width() * templateSize.height() > m_sourceRect.width() * m_sourceRect.height())) {
4632fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect);
4642fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return;
4652fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
4662fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4672fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    drawRectShadowWithTiling(graphicsContext, shadowedRect, radii, templateSize);
4682fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
4692fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4702fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockvoid ShadowBlur::drawInsetShadow(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedIntRect::Radii& holeRadii)
4712fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
4722fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    IntRect layerRect = calculateLayerBoundingRect(graphicsContext, rect, graphicsContext->clipBounds());
4732fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (layerRect.isEmpty())
4742fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return;
4752fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4762daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    adjustBlurRadius(graphicsContext);
4772daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
4782fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // drawInsetShadowWithTiling does not work with rotations.
4792fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // https://bugs.webkit.org/show_bug.cgi?id=45042
4802fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (!graphicsContext->getCTM().isIdentityOrTranslationOrFlipped() || m_type != BlurShadow) {
4812fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect);
4822fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return;
4832fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
4842fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4852fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    IntSize templateSize = this->templateSize(holeRadii);
4862fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4872fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (templateSize.width() > holeRect.width() || templateSize.height() > holeRect.height()
4882fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        || (templateSize.width() * templateSize.height() > holeRect.width() * holeRect.height())) {
4892fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect);
4902fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return;
4912fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
4922fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4932fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    drawInsetShadowWithTiling(graphicsContext, rect, holeRect, holeRadii, templateSize);
4942fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
4952fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
4962fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockvoid ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, const IntRect& layerRect)
4972fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
4982daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
4992daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    if (!m_layerImage)
5002fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return;
5012fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5022daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    FloatRect bufferRelativeShadowedRect = shadowedRect;
5032daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    bufferRelativeShadowedRect.move(m_layerContextTranslation);
5042daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    if (!ScratchBuffer::shared().matchesLastShadow(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeShadowedRect, radii)) {
5052daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        GraphicsContext* shadowContext = m_layerImage->context();
5062daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->save();
5072daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
5082daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        // Add a pixel to avoid later edge aliasing when rotated.
5092daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
5102daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->translate(m_layerContextTranslation);
5112daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
5122daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        if (radii.isZero())
5132daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            shadowContext->fillRect(shadowedRect);
5142daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        else {
5152daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            Path path;
5162daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            path.addRoundedRect(shadowedRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
5172daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            shadowContext->fillPath(path);
5182daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        }
5192daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
5202daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        blurShadowBuffer(expandedIntSize(m_layerSize));
5212fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5222daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->restore();
5232daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
5242daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        ScratchBuffer::shared().setLastShadowValues(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeShadowedRect, radii);
5252daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    }
5262fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5272daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    drawShadowBuffer(graphicsContext);
5282daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    m_layerImage = 0;
5292daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    ScratchBuffer::shared().scheduleScratchBufferPurge();
5302fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
5312fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5322fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockvoid ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedIntRect::Radii& holeRadii, const IntRect& layerRect)
5332fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
5342daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size());
5352daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    if (!m_layerImage)
5362fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        return;
5372fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5382daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    FloatRect bufferRelativeRect = rect;
5392daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    bufferRelativeRect.move(m_layerContextTranslation);
5402daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
5412daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    FloatRect bufferRelativeHoleRect = holeRect;
5422daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    bufferRelativeHoleRect.move(m_layerContextTranslation);
5432fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5442daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    if (!ScratchBuffer::shared().matchesLastInsetShadow(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeRect, bufferRelativeHoleRect, holeRadii)) {
5452daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        GraphicsContext* shadowContext = m_layerImage->context();
5462daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->save();
5472fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5482daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        // Add a pixel to avoid later edge aliasing when rotated.
5492daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1));
5502daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->translate(m_layerContextTranslation);
5512daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
5522daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        Path path;
5532daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        path.addRect(rect);
5542daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        if (holeRadii.isZero())
5552daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            path.addRect(holeRect);
5562daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        else
5572daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            path.addRoundedRect(holeRect, holeRadii.topLeft(), holeRadii.topRight(), holeRadii.bottomLeft(), holeRadii.bottomRight());
5582daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
5592daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->setFillRule(RULE_EVENODD);
5602daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
5612daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->fillPath(path);
5622daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
5632daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        blurShadowBuffer(expandedIntSize(m_layerSize));
5642daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
5652daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->restore();
5662daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
5672daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        ScratchBuffer::shared().setLastInsetShadowValues(m_blurRadius, Color::black, ColorSpaceDeviceRGB, bufferRelativeRect, bufferRelativeHoleRect, holeRadii);
5682daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    }
5692daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
5702daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    drawShadowBuffer(graphicsContext);
5712daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    m_layerImage = 0;
5722daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    ScratchBuffer::shared().scheduleScratchBufferPurge();
5732fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
5742fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5752fc2651226baac27029e38c9d6ef883fa32084dbSteve Block/*
5762fc2651226baac27029e38c9d6ef883fa32084dbSteve Block  These functions use tiling to improve the performance of the shadow
5772fc2651226baac27029e38c9d6ef883fa32084dbSteve Block  drawing of rounded rectangles. The code basically does the following
5782fc2651226baac27029e38c9d6ef883fa32084dbSteve Block  steps:
5792fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5802fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     1. Calculate the size of the shadow template, a rectangle that
5812fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     contains all the necessary tiles to draw the complete shadow.
5822fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5832fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     2. If that size is smaller than the real rectangle render the new
5842fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     template rectangle and its shadow in a new surface, in other case
5852fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     render the shadow of the real rectangle in the destination
5862fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     surface.
5872fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5882fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     3. Calculate the sizes and positions of the tiles and their
5892fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     destinations and use drawPattern to render the final shadow. The
5902fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     code divides the rendering in 8 tiles:
5912fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5922fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        1 | 2 | 3
5932fc2651226baac27029e38c9d6ef883fa32084dbSteve Block       -----------
5942fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        4 |   | 5
5952fc2651226baac27029e38c9d6ef883fa32084dbSteve Block       -----------
5962fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        6 | 7 | 8
5972fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
5982fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     The corners are directly copied from the template rectangle to the
5992fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     real one and the side tiles are 1 pixel width, we use them as
6002fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     tiles to cover the destination side. The corner tiles are bigger
6012fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     than just the side of the rounded corner, we need to increase it
6022fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     because the modifications caused by the corner over the blur
6032fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     effect. We fill the central or outer part with solid color to complete
6042fc2651226baac27029e38c9d6ef883fa32084dbSteve Block     the shadow.
6052fc2651226baac27029e38c9d6ef883fa32084dbSteve Block */
6062fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6072fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockvoid ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedIntRect::Radii& radii, const IntSize& templateSize)
6082fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
6092fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->save();
6102fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->clearShadow();
6112fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6122fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const float roundedRadius = ceilf(m_blurRadius);
6132fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const float twiceRadius = roundedRadius * 2;
6142fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6152fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
6162daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    if (!m_layerImage)
6172daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        return;
6182fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6192fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Draw the rectangle with hole.
6202fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    FloatRect templateBounds(0, 0, templateSize.width(), templateSize.height());
6212fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    FloatRect templateHole = FloatRect(roundedRadius, roundedRadius, templateSize.width() - twiceRadius, templateSize.height() - twiceRadius);
6222fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6232daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    if (!ScratchBuffer::shared().matchesLastInsetShadow(m_blurRadius, m_color, m_colorSpace, templateBounds, templateHole, radii)) {
6242daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        // Draw shadow into a new ImageBuffer.
6252daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        GraphicsContext* shadowContext = m_layerImage->context();
6262daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->save();
6272daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->clearRect(templateBounds);
6282daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->setFillRule(RULE_EVENODD);
6292daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
6302daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
6312daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        Path path;
6322daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        path.addRect(templateBounds);
6332daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        if (radii.isZero())
6342daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            path.addRect(templateHole);
6352daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        else
6362daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            path.addRoundedRect(templateHole, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
6372daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
6382daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->fillPath(path);
6392daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
6402daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        blurAndColorShadowBuffer(templateSize);
6412daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->restore();
6422daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
6432daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        ScratchBuffer::shared().setLastInsetShadowValues(m_blurRadius, m_color, m_colorSpace, templateBounds, templateHole, radii);
6442daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    }
6452fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6462fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    FloatRect boundingRect = rect;
6472fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    boundingRect.move(m_offset);
6482fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6492fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    FloatRect destHoleRect = holeRect;
6502fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    destHoleRect.move(m_offset);
6512fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    FloatRect destHoleBounds = destHoleRect;
6522fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    destHoleBounds.inflate(roundedRadius);
6532fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6542fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Fill the external part of the shadow (which may be visible because of offset).
6552fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    Path exteriorPath;
6562fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    exteriorPath.addRect(boundingRect);
6572fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    exteriorPath.addRect(destHoleBounds);
6582fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6592fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->save();
6602fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->setFillRule(RULE_EVENODD);
6612fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->setFillColor(m_color, m_colorSpace);
6622fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->fillPath(exteriorPath);
6632fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->restore();
6642fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6652fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    drawLayerPieces(graphicsContext, destHoleBounds, radii, roundedRadius, templateSize, InnerShadow);
6662fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6672fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->restore();
6682fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6692fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    m_layerImage = 0;
6702fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    ScratchBuffer::shared().scheduleScratchBufferPurge();
6712fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
6722fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6732fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockvoid ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, const IntSize& templateSize)
6742fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
6752fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->save();
6762fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->clearShadow();
6772fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6782fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const float roundedRadius = ceilf(m_blurRadius);
6792fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const float twiceRadius = roundedRadius * 2;
6802fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6812fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize);
6822daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    if (!m_layerImage)
6832daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        return;
6842fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6852fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    FloatRect templateShadow = FloatRect(roundedRadius, roundedRadius, templateSize.width() - twiceRadius, templateSize.height() - twiceRadius);
6862fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
6872daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    if (!ScratchBuffer::shared().matchesLastShadow(m_blurRadius, m_color, m_colorSpace, templateShadow, radii)) {
6882daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        // Draw shadow into the ImageBuffer.
6892daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        GraphicsContext* shadowContext = m_layerImage->context();
6902daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->save();
6912daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
6922daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
6932daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
6942daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        if (radii.isZero())
6952daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            shadowContext->fillRect(templateShadow);
6962daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        else {
6972daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            Path path;
6982daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            path.addRoundedRect(templateShadow, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
6992daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            shadowContext->fillPath(path);
7002daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        }
7012daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
7022daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        blurAndColorShadowBuffer(templateSize);
7032daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        shadowContext->restore();
7042daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
7052daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        ScratchBuffer::shared().setLastShadowValues(m_blurRadius, m_color, m_colorSpace, templateShadow, radii);
7062daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    }
7072fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7082fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    FloatRect shadowBounds = shadowedRect;
7092fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    shadowBounds.move(m_offset.width(), m_offset.height());
7102fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    shadowBounds.inflate(roundedRadius);
7112fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7122fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    drawLayerPieces(graphicsContext, shadowBounds, radii, roundedRadius, templateSize, OuterShadow);
7132fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7142fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->restore();
7152fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7162fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    m_layerImage = 0;
7172fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    ScratchBuffer::shared().scheduleScratchBufferPurge();
7182fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
7192fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7202fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockvoid ShadowBlur::drawLayerPieces(GraphicsContext* graphicsContext, const FloatRect& shadowBounds, const RoundedIntRect::Radii& radii, float roundedRadius, const IntSize& templateSize, ShadowDirection direction)
7212fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
7222fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    const float twiceRadius = roundedRadius * 2;
7232fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7242fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int leftSlice;
7252fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int rightSlice;
7262fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int topSlice;
7272fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int bottomSlice;
7282fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    computeSliceSizesFromRadii(twiceRadius, radii, leftSlice, rightSlice, topSlice, bottomSlice);
7292fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7302fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int centerWidth = shadowBounds.width() - leftSlice - rightSlice;
7312fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    int centerHeight = shadowBounds.height() - topSlice - bottomSlice;
7322fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7332fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    if (direction == OuterShadow) {
7342fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        FloatRect shadowInterior(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight);
7352fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        if (!shadowInterior.isEmpty()) {
7362fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            graphicsContext->save();
7372fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7382fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            graphicsContext->setFillColor(m_color, m_colorSpace);
7392fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            graphicsContext->fillRect(shadowInterior);
7402fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7412fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            graphicsContext->restore();
7422fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        }
7432fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    }
7442fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7452fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Note that drawing the ImageBuffer is faster than creating a Image and drawing that,
7462fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // because ImageBuffer::draw() knows that it doesn't have to copy the image bits.
7472fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7482fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Top side.
7492fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    FloatRect tileRect = FloatRect(leftSlice, 0, templateSideLength, topSlice);
7502fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    FloatRect destRect = FloatRect(shadowBounds.x() + leftSlice, shadowBounds.y(), centerWidth, topSlice);
7512fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
7522fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7532fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Draw the bottom side.
7542fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    tileRect.setY(templateSize.height() - bottomSlice);
7552fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    tileRect.setHeight(bottomSlice);
7562fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    destRect.setY(shadowBounds.maxY() - bottomSlice);
7572fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    destRect.setHeight(bottomSlice);
7582fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
7592fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7602fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Left side.
7612fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    tileRect = FloatRect(0, topSlice, leftSlice, templateSideLength);
7622fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    destRect = FloatRect(shadowBounds.x(), shadowBounds.y() + topSlice, leftSlice, centerHeight);
7632fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
7642fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7652fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Right side.
7662fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    tileRect.setX(templateSize.width() - rightSlice);
7672fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    tileRect.setWidth(rightSlice);
7682fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    destRect.setX(shadowBounds.maxX() - rightSlice);
7692fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    destRect.setWidth(rightSlice);
7702fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
7712fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7722fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Top left corner.
7732fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    tileRect = FloatRect(0, 0, leftSlice, topSlice);
7742fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    destRect = FloatRect(shadowBounds.x(), shadowBounds.y(), leftSlice, topSlice);
7752fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
7762fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7772fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Top right corner.
7782fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    tileRect = FloatRect(templateSize.width() - rightSlice, 0, rightSlice, topSlice);
7792fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    destRect = FloatRect(shadowBounds.maxX() - rightSlice, shadowBounds.y(), rightSlice, topSlice);
7802fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
7812fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7822fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Bottom right corner.
7832fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    tileRect = FloatRect(templateSize.width() - rightSlice, templateSize.height() - bottomSlice, rightSlice, bottomSlice);
7842fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    destRect = FloatRect(shadowBounds.maxX() - rightSlice, shadowBounds.maxY() - bottomSlice, rightSlice, bottomSlice);
7852fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
7862fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7872fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Bottom left corner.
7882fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    tileRect = FloatRect(0, templateSize.height() - bottomSlice, leftSlice, bottomSlice);
7892fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    destRect = FloatRect(shadowBounds.x(), shadowBounds.maxY() - bottomSlice, leftSlice, bottomSlice);
7902fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
7912fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
7922fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7932fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
7942daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdochvoid ShadowBlur::blurShadowBuffer(const IntSize& templateSize)
7952daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch{
7962daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    if (m_type != BlurShadow)
7972daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        return;
7982daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
7992daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    IntRect blurRect(IntPoint(), templateSize);
8002daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    RefPtr<ByteArray> layerData = m_layerImage->getUnmultipliedImageData(blurRect);
8012daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    blurLayerImage(layerData->data(), blurRect.size(), blurRect.width() * 4);
8022daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    m_layerImage->putUnmultipliedImageData(layerData.get(), blurRect.size(), blurRect, IntPoint());
8032daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch}
8042daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
8052fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockvoid ShadowBlur::blurAndColorShadowBuffer(const IntSize& templateSize)
8062fc2651226baac27029e38c9d6ef883fa32084dbSteve Block{
8072daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    blurShadowBuffer(templateSize);
8082fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
8092fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    // Mask the image with the shadow color.
8102fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    GraphicsContext* shadowContext = m_layerImage->context();
8112fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    shadowContext->setCompositeOperation(CompositeSourceIn);
8122fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    shadowContext->setFillColor(m_color, m_colorSpace);
8132fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    shadowContext->fillRect(FloatRect(0, 0, templateSize.width(), templateSize.height()));
8142fc2651226baac27029e38c9d6ef883fa32084dbSteve Block}
8152fc2651226baac27029e38c9d6ef883fa32084dbSteve Block
8162fc2651226baac27029e38c9d6ef883fa32084dbSteve Block} // namespace WebCore
817