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