Home | History | Annotate | Download | only in graphics
      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