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