153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)/*
253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * Copyright (C) 2011 Apple Inc. All rights reserved.
353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * Copyright (C) 2010 Sencha, Inc. All rights reserved.
453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * Copyright (C) 2010 Igalia S.L. All rights reserved.
553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * Copyright (C) Research In Motion Limited 2011. All rights reserved.
653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) *
753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * Redistribution and use in source and binary forms, with or without
853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * modification, are permitted provided that the following conditions
953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * are met:
1053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * 1. Redistributions of source code must retain the above copyright
1153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) *    notice, this list of conditions and the following disclaimer.
1253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * 2. Redistributions in binary form must reproduce the above copyright
1353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) *    notice, this list of conditions and the following disclaimer in the
1453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) *    documentation and/or other materials provided with the distribution.
1553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) *
1653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
1753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
2053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
2353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
2453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2602772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) */
2853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
2953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "config.h"
3053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/platform/graphics/ShadowBlur.h"
3153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch#include "wtf/MathExtras.h"
3353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
3453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)using namespace std;
3553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
3653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)namespace WebCore {
3753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
3853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)enum {
3953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    leftLobe = 0,
4053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    rightLobe = 1
4153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)};
4253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
435267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)ShadowBlur::ShadowBlur(const FloatSize& radius, const FloatSize& offset, const Color& color)
4453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    : m_color(color)
4553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    , m_blurRadius(radius)
4653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    , m_offset(offset)
4753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    , m_shadowsIgnoreTransforms(false)
4853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles){
4953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    updateShadowBlurValues();
5053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)}
5153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
5253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)void ShadowBlur::updateShadowBlurValues()
5353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles){
5453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    // Limit blur radius to 128 to avoid lots of very expensive blurring.
5553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    m_blurRadius = m_blurRadius.shrunkTo(FloatSize(128, 128));
5653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
5753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    // The type of shadow is decided by the blur radius, shadow offset, and shadow color.
5883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    if (!m_color.alpha()) {
5953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // Can't paint the shadow with invalid or invisible color.
6053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        m_type = NoShadow;
6153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    } else if (m_blurRadius.width() > 0 || m_blurRadius.height() > 0) {
6253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // Shadow is always blurred, even the offset is zero.
6353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        m_type = BlurShadow;
6453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    } else if (!m_offset.width() && !m_offset.height()) {
6553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // Without blur and zero offset means the shadow is fully hidden.
6653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        m_type = NoShadow;
6753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    } else
6853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        m_type = SolidShadow;
6953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)}
7053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
7153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)// Instead of integer division, we use 17.15 for fixed-point division.
7253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)static const int blurSumShift = 15;
7353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
7453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)// Takes a two dimensional array with three rows and two columns for the lobes.
7553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)static void calculateLobes(int lobes[][2], float blurRadius, bool shadowsIgnoreTransforms)
7653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles){
7753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    int diameter;
7853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    if (shadowsIgnoreTransforms)
7953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        diameter = max(2, static_cast<int>(floorf((2 / 3.f) * blurRadius))); // Canvas shadow. FIXME: we should adjust the blur radius higher up.
8053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    else {
8153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // http://dev.w3.org/csswg/css3-background/#box-shadow
8253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // Approximate a Gaussian blur with a standard deviation equal to half the blur radius,
8353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // which http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement tell us how to do.
8453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // However, shadows rendered according to that spec will extend a little further than m_blurRadius,
8553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // so we apply a fudge factor to bring the radius down slightly.
8653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        float stdDev = blurRadius / 2;
8753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        const float gaussianKernelFactor = 3 / 4.f * sqrtf(2 * piFloat);
8853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        const float fudgeFactor = 0.88f;
8953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        diameter = max(2, static_cast<int>(floorf(stdDev * gaussianKernelFactor * fudgeFactor + 0.5f)));
9053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    }
9153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
9253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    if (diameter & 1) {
9353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // if d is odd, use three box-blurs of size 'd', centered on the output pixel.
9453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        int lobeSize = (diameter - 1) / 2;
9553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lobes[0][leftLobe] = lobeSize;
9653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lobes[0][rightLobe] = lobeSize;
9753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lobes[1][leftLobe] = lobeSize;
9853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lobes[1][rightLobe] = lobeSize;
9953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lobes[2][leftLobe] = lobeSize;
10053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lobes[2][rightLobe] = lobeSize;
10153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    } else {
10253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // if d is even, two box-blurs of size 'd' (the first one centered on the pixel boundary
10353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // between the output pixel and the one to the left, the second one centered on the pixel
10453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // boundary between the output pixel and the one to the right) and one box blur of size 'd+1' centered on the output pixel
10553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        int lobeSize = diameter / 2;
10653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lobes[0][leftLobe] = lobeSize;
10753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lobes[0][rightLobe] = lobeSize - 1;
10853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lobes[1][leftLobe] = lobeSize - 1;
10953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lobes[1][rightLobe] = lobeSize;
11053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lobes[2][leftLobe] = lobeSize;
11153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lobes[2][rightLobe] = lobeSize;
11253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    }
11353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)}
11453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
11553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, int rowStride)
11653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles){
11753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    const int channels[4] = { 3, 0, 1, 3 };
11853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
11953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    int lobes[3][2]; // indexed by pass, and left/right lobe
12053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    calculateLobes(lobes, m_blurRadius.width(), m_shadowsIgnoreTransforms);
12153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
12253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    // First pass is horizontal.
12353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    int stride = 4;
12453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    int delta = rowStride;
12553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    int final = size.height();
12653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    int dim = size.width();
12753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
12853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    // Two stages: horizontal and vertical
12953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    for (int pass = 0; pass < 2; ++pass) {
13053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        unsigned char* pixels = imageData;
13102772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch
13253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        if (!pass && !m_blurRadius.width())
13353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            final = 0; // Do no work if horizonal blur is zero.
13453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
13553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        for (int j = 0; j < final; ++j, pixels += delta) {
13653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            // For each step, we blur the alpha in a channel and store the result
13753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            // in another channel for the subsequent step.
13853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            // We use sliding window algorithm to accumulate the alpha values.
13953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            // This is much more efficient than computing the sum of each pixels
14053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            // covered by the box kernel size for each x.
14153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            for (int step = 0; step < 3; ++step) {
14253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                int side1 = lobes[step][leftLobe];
14353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                int side2 = lobes[step][rightLobe];
14453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                int pixelCount = side1 + 1 + side2;
14553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                int invCount = ((1 << blurSumShift) + pixelCount - 1) / pixelCount;
14653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                int ofs = 1 + side2;
14753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                int alpha1 = pixels[channels[step]];
14853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                int alpha2 = pixels[(dim - 1) * stride + channels[step]];
14953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
15053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                unsigned char* ptr = pixels + channels[step + 1];
15153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                unsigned char* prev = pixels + stride + channels[step];
15253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                unsigned char* next = pixels + ofs * stride + channels[step];
15353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
15453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                int i;
15553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                int sum = side1 * alpha1 + alpha1;
15653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                int limit = (dim < side2 + 1) ? dim : side2 + 1;
15753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
15853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                for (i = 1; i < limit; ++i, prev += stride)
15953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    sum += *prev;
16053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
16153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                if (limit <= side2)
16253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    sum += (side2 - limit + 1) * alpha2;
16353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
16453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                limit = (side1 < dim) ? side1 : dim;
16553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) {
16653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    *ptr = (sum * invCount) >> blurSumShift;
16753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    sum += ((ofs < dim) ? *next : alpha2) - alpha1;
16853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                }
16902772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch
17053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                prev = pixels + channels[step];
17153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) {
17253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    *ptr = (sum * invCount) >> blurSumShift;
17353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    sum += (*next) - (*prev);
17453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                }
17502772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch
17653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                for (; i < dim; ptr += stride, prev += stride, ++i) {
17753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    *ptr = (sum * invCount) >> blurSumShift;
17853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    sum += alpha2 - (*prev);
17953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                }
18053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            }
18153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        }
18253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
18353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        // Last pass is vertical.
18453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        stride = rowStride;
18553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        delta = 4;
18653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        final = size.width();
18753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        dim = size.height();
18853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
18953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        if (!m_blurRadius.height())
19053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            break;
19153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
19253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        if (m_blurRadius.width() != m_blurRadius.height())
19353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            calculateLobes(lobes, m_blurRadius.height(), m_shadowsIgnoreTransforms);
19453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    }
19553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)}
19653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
19753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)} // namespace WebCore
198