1 /* 2 * Copyright (C) 2011 Apple Inc. All rights reserved. 3 * Copyright (C) 2010 Sencha, Inc. All rights reserved. 4 * Copyright (C) 2010 Igalia S.L. All rights reserved. 5 * Copyright (C) Research In Motion Limited 2011. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "config.h" 30 #include "core/platform/graphics/ShadowBlur.h" 31 32 #include "wtf/MathExtras.h" 33 34 using namespace std; 35 36 namespace WebCore { 37 38 enum { 39 leftLobe = 0, 40 rightLobe = 1 41 }; 42 43 ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color& color) 44 : m_color(color) 45 , m_blurRadius(radius) 46 , m_offset(offset) 47 , m_shadowsIgnoreTransforms(false) 48 { 49 updateShadowBlurValues(); 50 } 51 52 void ShadowBlur::updateShadowBlurValues() 53 { 54 // Limit blur radius to 128 to avoid lots of very expensive blurring. 55 m_blurRadius = m_blurRadius.shrunkTo(FloatSize(128, 128)); 56 57 // The type of shadow is decided by the blur radius, shadow offset, and shadow color. 58 if (!m_color.alpha()) { 59 // Can't paint the shadow with invalid or invisible color. 60 m_type = NoShadow; 61 } else if (m_blurRadius.width() > 0 || m_blurRadius.height() > 0) { 62 // Shadow is always blurred, even the offset is zero. 63 m_type = BlurShadow; 64 } else if (!m_offset.width() && !m_offset.height()) { 65 // Without blur and zero offset means the shadow is fully hidden. 66 m_type = NoShadow; 67 } else 68 m_type = SolidShadow; 69 } 70 71 // Instead of integer division, we use 17.15 for fixed-point division. 72 static const int blurSumShift = 15; 73 74 // Takes a two dimensional array with three rows and two columns for the lobes. 75 static void calculateLobes(int lobes[][2], float blurRadius, bool shadowsIgnoreTransforms) 76 { 77 int diameter; 78 if (shadowsIgnoreTransforms) 79 diameter = max(2, static_cast<int>(floorf((2 / 3.f) * blurRadius))); // Canvas shadow. FIXME: we should adjust the blur radius higher up. 80 else { 81 // http://dev.w3.org/csswg/css3-background/#box-shadow 82 // Approximate a Gaussian blur with a standard deviation equal to half the blur radius, 83 // which http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement tell us how to do. 84 // However, shadows rendered according to that spec will extend a little further than m_blurRadius, 85 // so we apply a fudge factor to bring the radius down slightly. 86 float stdDev = blurRadius / 2; 87 const float gaussianKernelFactor = 3 / 4.f * sqrtf(2 * piFloat); 88 const float fudgeFactor = 0.88f; 89 diameter = max(2, static_cast<int>(floorf(stdDev * gaussianKernelFactor * fudgeFactor + 0.5f))); 90 } 91 92 if (diameter & 1) { 93 // if d is odd, use three box-blurs of size 'd', centered on the output pixel. 94 int lobeSize = (diameter - 1) / 2; 95 lobes[0][leftLobe] = lobeSize; 96 lobes[0][rightLobe] = lobeSize; 97 lobes[1][leftLobe] = lobeSize; 98 lobes[1][rightLobe] = lobeSize; 99 lobes[2][leftLobe] = lobeSize; 100 lobes[2][rightLobe] = lobeSize; 101 } else { 102 // if d is even, two box-blurs of size 'd' (the first one centered on the pixel boundary 103 // between the output pixel and the one to the left, the second one centered on the pixel 104 // boundary between the output pixel and the one to the right) and one box blur of size 'd+1' centered on the output pixel 105 int lobeSize = diameter / 2; 106 lobes[0][leftLobe] = lobeSize; 107 lobes[0][rightLobe] = lobeSize - 1; 108 lobes[1][leftLobe] = lobeSize - 1; 109 lobes[1][rightLobe] = lobeSize; 110 lobes[2][leftLobe] = lobeSize; 111 lobes[2][rightLobe] = lobeSize; 112 } 113 } 114 115 void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, int rowStride) 116 { 117 const int channels[4] = { 3, 0, 1, 3 }; 118 119 int lobes[3][2]; // indexed by pass, and left/right lobe 120 calculateLobes(lobes, m_blurRadius.width(), m_shadowsIgnoreTransforms); 121 122 // First pass is horizontal. 123 int stride = 4; 124 int delta = rowStride; 125 int final = size.height(); 126 int dim = size.width(); 127 128 // Two stages: horizontal and vertical 129 for (int pass = 0; pass < 2; ++pass) { 130 unsigned char* pixels = imageData; 131 132 if (!pass && !m_blurRadius.width()) 133 final = 0; // Do no work if horizonal blur is zero. 134 135 for (int j = 0; j < final; ++j, pixels += delta) { 136 // For each step, we blur the alpha in a channel and store the result 137 // in another channel for the subsequent step. 138 // We use sliding window algorithm to accumulate the alpha values. 139 // This is much more efficient than computing the sum of each pixels 140 // covered by the box kernel size for each x. 141 for (int step = 0; step < 3; ++step) { 142 int side1 = lobes[step][leftLobe]; 143 int side2 = lobes[step][rightLobe]; 144 int pixelCount = side1 + 1 + side2; 145 int invCount = ((1 << blurSumShift) + pixelCount - 1) / pixelCount; 146 int ofs = 1 + side2; 147 int alpha1 = pixels[channels[step]]; 148 int alpha2 = pixels[(dim - 1) * stride + channels[step]]; 149 150 unsigned char* ptr = pixels + channels[step + 1]; 151 unsigned char* prev = pixels + stride + channels[step]; 152 unsigned char* next = pixels + ofs * stride + channels[step]; 153 154 int i; 155 int sum = side1 * alpha1 + alpha1; 156 int limit = (dim < side2 + 1) ? dim : side2 + 1; 157 158 for (i = 1; i < limit; ++i, prev += stride) 159 sum += *prev; 160 161 if (limit <= side2) 162 sum += (side2 - limit + 1) * alpha2; 163 164 limit = (side1 < dim) ? side1 : dim; 165 for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) { 166 *ptr = (sum * invCount) >> blurSumShift; 167 sum += ((ofs < dim) ? *next : alpha2) - alpha1; 168 } 169 170 prev = pixels + channels[step]; 171 for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) { 172 *ptr = (sum * invCount) >> blurSumShift; 173 sum += (*next) - (*prev); 174 } 175 176 for (; i < dim; ptr += stride, prev += stride, ++i) { 177 *ptr = (sum * invCount) >> blurSumShift; 178 sum += alpha2 - (*prev); 179 } 180 } 181 } 182 183 // Last pass is vertical. 184 stride = rowStride; 185 delta = 4; 186 final = size.width(); 187 dim = size.height(); 188 189 if (!m_blurRadius.height()) 190 break; 191 192 if (m_blurRadius.width() != m_blurRadius.height()) 193 calculateLobes(lobes, m_blurRadius.height(), m_shadowsIgnoreTransforms); 194 } 195 } 196 197 } // namespace WebCore 198