| /* |
| * Copyright (C) 2011 Apple Inc. All rights reserved. |
| * Copyright (C) 2010 Sencha, Inc. All rights reserved. |
| * Copyright (C) 2010 Igalia S.L. All rights reserved. |
| * Copyright (C) Research In Motion Limited 2011. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "core/platform/graphics/ShadowBlur.h" |
| |
| #include "wtf/MathExtras.h" |
| #include <algorithm> |
| |
| using namespace std; |
| |
| namespace WebCore { |
| |
| enum { |
| leftLobe = 0, |
| rightLobe = 1 |
| }; |
| |
| ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color& color) |
| : m_color(color) |
| , m_blurRadius(radius) |
| , m_offset(offset) |
| , m_shadowsIgnoreTransforms(false) |
| { |
| updateShadowBlurValues(); |
| } |
| |
| void ShadowBlur::updateShadowBlurValues() |
| { |
| // Limit blur radius to 128 to avoid lots of very expensive blurring. |
| m_blurRadius = m_blurRadius.shrunkTo(FloatSize(128, 128)); |
| |
| // The type of shadow is decided by the blur radius, shadow offset, and shadow color. |
| if (!m_color.isValid() || !m_color.alpha()) { |
| // Can't paint the shadow with invalid or invisible color. |
| m_type = NoShadow; |
| } else if (m_blurRadius.width() > 0 || m_blurRadius.height() > 0) { |
| // Shadow is always blurred, even the offset is zero. |
| m_type = BlurShadow; |
| } else if (!m_offset.width() && !m_offset.height()) { |
| // Without blur and zero offset means the shadow is fully hidden. |
| m_type = NoShadow; |
| } else |
| m_type = SolidShadow; |
| } |
| |
| // Instead of integer division, we use 17.15 for fixed-point division. |
| static const int blurSumShift = 15; |
| |
| // Takes a two dimensional array with three rows and two columns for the lobes. |
| static void calculateLobes(int lobes[][2], float blurRadius, bool shadowsIgnoreTransforms) |
| { |
| int diameter; |
| if (shadowsIgnoreTransforms) |
| diameter = max(2, static_cast<int>(floorf((2 / 3.f) * blurRadius))); // Canvas shadow. FIXME: we should adjust the blur radius higher up. |
| else { |
| // http://dev.w3.org/csswg/css3-background/#box-shadow |
| // Approximate a Gaussian blur with a standard deviation equal to half the blur radius, |
| // which http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement tell us how to do. |
| // However, shadows rendered according to that spec will extend a little further than m_blurRadius, |
| // so we apply a fudge factor to bring the radius down slightly. |
| float stdDev = blurRadius / 2; |
| const float gaussianKernelFactor = 3 / 4.f * sqrtf(2 * piFloat); |
| const float fudgeFactor = 0.88f; |
| diameter = max(2, static_cast<int>(floorf(stdDev * gaussianKernelFactor * fudgeFactor + 0.5f))); |
| } |
| |
| if (diameter & 1) { |
| // if d is odd, use three box-blurs of size 'd', centered on the output pixel. |
| int lobeSize = (diameter - 1) / 2; |
| lobes[0][leftLobe] = lobeSize; |
| lobes[0][rightLobe] = lobeSize; |
| lobes[1][leftLobe] = lobeSize; |
| lobes[1][rightLobe] = lobeSize; |
| lobes[2][leftLobe] = lobeSize; |
| lobes[2][rightLobe] = lobeSize; |
| } else { |
| // if d is even, two box-blurs of size 'd' (the first one centered on the pixel boundary |
| // between the output pixel and the one to the left, the second one centered on the pixel |
| // boundary between the output pixel and the one to the right) and one box blur of size 'd+1' centered on the output pixel |
| int lobeSize = diameter / 2; |
| lobes[0][leftLobe] = lobeSize; |
| lobes[0][rightLobe] = lobeSize - 1; |
| lobes[1][leftLobe] = lobeSize - 1; |
| lobes[1][rightLobe] = lobeSize; |
| lobes[2][leftLobe] = lobeSize; |
| lobes[2][rightLobe] = lobeSize; |
| } |
| } |
| |
| void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, int rowStride) |
| { |
| const int channels[4] = { 3, 0, 1, 3 }; |
| |
| int lobes[3][2]; // indexed by pass, and left/right lobe |
| calculateLobes(lobes, m_blurRadius.width(), m_shadowsIgnoreTransforms); |
| |
| // First pass is horizontal. |
| int stride = 4; |
| int delta = rowStride; |
| int final = size.height(); |
| int dim = size.width(); |
| |
| // Two stages: horizontal and vertical |
| for (int pass = 0; pass < 2; ++pass) { |
| unsigned char* pixels = imageData; |
| |
| if (!pass && !m_blurRadius.width()) |
| final = 0; // Do no work if horizonal blur is zero. |
| |
| for (int j = 0; j < final; ++j, pixels += delta) { |
| // For each step, we blur the alpha in a channel and store the result |
| // in another channel for the subsequent step. |
| // We use sliding window algorithm to accumulate the alpha values. |
| // This is much more efficient than computing the sum of each pixels |
| // covered by the box kernel size for each x. |
| for (int step = 0; step < 3; ++step) { |
| int side1 = lobes[step][leftLobe]; |
| int side2 = lobes[step][rightLobe]; |
| int pixelCount = side1 + 1 + side2; |
| int invCount = ((1 << blurSumShift) + pixelCount - 1) / pixelCount; |
| int ofs = 1 + side2; |
| int alpha1 = pixels[channels[step]]; |
| int alpha2 = pixels[(dim - 1) * stride + channels[step]]; |
| |
| unsigned char* ptr = pixels + channels[step + 1]; |
| unsigned char* prev = pixels + stride + channels[step]; |
| unsigned char* next = pixels + ofs * stride + channels[step]; |
| |
| int i; |
| int sum = side1 * alpha1 + alpha1; |
| int limit = (dim < side2 + 1) ? dim : side2 + 1; |
| |
| for (i = 1; i < limit; ++i, prev += stride) |
| sum += *prev; |
| |
| if (limit <= side2) |
| sum += (side2 - limit + 1) * alpha2; |
| |
| limit = (side1 < dim) ? side1 : dim; |
| for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) { |
| *ptr = (sum * invCount) >> blurSumShift; |
| sum += ((ofs < dim) ? *next : alpha2) - alpha1; |
| } |
| |
| prev = pixels + channels[step]; |
| for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) { |
| *ptr = (sum * invCount) >> blurSumShift; |
| sum += (*next) - (*prev); |
| } |
| |
| for (; i < dim; ptr += stride, prev += stride, ++i) { |
| *ptr = (sum * invCount) >> blurSumShift; |
| sum += alpha2 - (*prev); |
| } |
| } |
| } |
| |
| // Last pass is vertical. |
| stride = rowStride; |
| delta = 4; |
| final = size.width(); |
| dim = size.height(); |
| |
| if (!m_blurRadius.height()) |
| break; |
| |
| if (m_blurRadius.width() != m_blurRadius.height()) |
| calculateLobes(lobes, m_blurRadius.height(), m_shadowsIgnoreTransforms); |
| } |
| } |
| |
| } // namespace WebCore |