1/*
2 * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
4 * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
5 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
6 * Copyright (C) 2010 Igalia, S.L.
7 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
8 * Copyright (C) 2013 Google Inc. All rights reserved.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB.  If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26#include "config.h"
27
28#include "platform/graphics/filters/FEGaussianBlur.h"
29
30#include "platform/graphics/GraphicsContext.h"
31#include "platform/graphics/cpu/arm/filters/FEGaussianBlurNEON.h"
32#include "platform/graphics/filters/ParallelJobs.h"
33#include "platform/graphics/filters/SkiaImageFilterBuilder.h"
34#include "platform/text/TextStream.h"
35#include "wtf/MathExtras.h"
36#include "wtf/Uint8ClampedArray.h"
37
38#include "SkBlurImageFilter.h"
39
40using namespace std;
41
42static inline float gaussianKernelFactor()
43{
44    return 3 / 4.f * sqrtf(twoPiFloat);
45}
46
47static const int gMaxKernelSize = 1000;
48
49namespace WebCore {
50
51FEGaussianBlur::FEGaussianBlur(Filter* filter, float x, float y)
52    : FilterEffect(filter)
53    , m_stdX(x)
54    , m_stdY(y)
55{
56}
57
58PassRefPtr<FEGaussianBlur> FEGaussianBlur::create(Filter* filter, float x, float y)
59{
60    return adoptRef(new FEGaussianBlur(filter, x, y));
61}
62
63float FEGaussianBlur::stdDeviationX() const
64{
65    return m_stdX;
66}
67
68void FEGaussianBlur::setStdDeviationX(float x)
69{
70    m_stdX = x;
71}
72
73float FEGaussianBlur::stdDeviationY() const
74{
75    return m_stdY;
76}
77
78void FEGaussianBlur::setStdDeviationY(float y)
79{
80    m_stdY = y;
81}
82
83IntSize FEGaussianBlur::calculateUnscaledKernelSize(const FloatPoint& std)
84{
85    ASSERT(std.x() >= 0 && std.y() >= 0);
86
87    IntSize kernelSize;
88    // Limit the kernel size to 1000. A bigger radius won't make a big difference for the result image but
89    // inflates the absolute paint rect to much. This is compatible with Firefox' behavior.
90    if (std.x()) {
91        int size = max<unsigned>(2, static_cast<unsigned>(floorf(std.x() * gaussianKernelFactor() + 0.5f)));
92        kernelSize.setWidth(min(size, gMaxKernelSize));
93    }
94
95    if (std.y()) {
96        int size = max<unsigned>(2, static_cast<unsigned>(floorf(std.y() * gaussianKernelFactor() + 0.5f)));
97        kernelSize.setHeight(min(size, gMaxKernelSize));
98    }
99
100    return kernelSize;
101}
102
103IntSize FEGaussianBlur::calculateKernelSize(Filter* filter, const FloatPoint& std)
104{
105    FloatPoint stdError(filter->applyHorizontalScale(std.x()), filter->applyVerticalScale(std.y()));
106
107    return calculateUnscaledKernelSize(stdError);
108}
109
110FloatRect FEGaussianBlur::mapRect(const FloatRect& rect, bool)
111{
112    FloatRect result = rect;
113    IntSize kernelSize = calculateKernelSize(filter(), FloatPoint(m_stdX, m_stdY));
114
115    // We take the half kernel size and multiply it with three, because we run box blur three times.
116    result.inflateX(3 * kernelSize.width() * 0.5f);
117    result.inflateY(3 * kernelSize.height() * 0.5f);
118    return result;
119}
120
121FloatRect FEGaussianBlur::determineAbsolutePaintRect(const FloatRect& originalRequestedRect)
122{
123    FloatRect requestedRect = originalRequestedRect;
124    if (clipsToBounds())
125        requestedRect.intersect(maxEffectRect());
126
127    FilterEffect* input = inputEffect(0);
128    FloatRect inputRect = input->determineAbsolutePaintRect(mapRect(requestedRect, false));
129    FloatRect outputRect = mapRect(inputRect, true);
130    outputRect.intersect(requestedRect);
131    addAbsolutePaintRect(outputRect);
132
133    // Blur needs space for both input and output pixels in the paint area.
134    // Input is also clipped to subregion.
135    if (clipsToBounds())
136        inputRect.intersect(maxEffectRect());
137    addAbsolutePaintRect(inputRect);
138    return outputRect;
139}
140
141void FEGaussianBlur::applySoftware()
142{
143    ImageBuffer* resultImage = createImageBufferResult();
144    if (!resultImage)
145        return;
146
147    FilterEffect* in = inputEffect(0);
148
149    IntRect drawingRegion = drawingRegionOfInputImage(in->absolutePaintRect());
150
151    setIsAlphaImage(in->isAlphaImage());
152
153    float stdX = filter()->applyHorizontalScale(m_stdX);
154    float stdY = filter()->applyVerticalScale(m_stdY);
155
156    RefPtr<Image> image = in->asImageBuffer()->copyImage(DontCopyBackingStore);
157
158    SkPaint paint;
159    GraphicsContext* dstContext = resultImage->context();
160    paint.setImageFilter(SkBlurImageFilter::Create(stdX, stdY))->unref();
161
162    SkRect bounds = SkRect::MakeWH(absolutePaintRect().width(), absolutePaintRect().height());
163    dstContext->saveLayer(&bounds, &paint);
164    paint.setColor(0xFFFFFFFF);
165    dstContext->drawImage(image.get(), drawingRegion.location(), CompositeCopy);
166    dstContext->restoreLayer();
167}
168
169PassRefPtr<SkImageFilter> FEGaussianBlur::createImageFilter(SkiaImageFilterBuilder* builder)
170{
171    RefPtr<SkImageFilter> input(builder->build(inputEffect(0), operatingColorSpace()));
172    float stdX = filter()->applyHorizontalScale(m_stdX);
173    float stdY = filter()->applyVerticalScale(m_stdY);
174    SkImageFilter::CropRect rect = getCropRect(builder->cropOffset());
175    return adoptRef(SkBlurImageFilter::Create(SkFloatToScalar(stdX), SkFloatToScalar(stdY), input.get(), &rect));
176}
177
178TextStream& FEGaussianBlur::externalRepresentation(TextStream& ts, int indent) const
179{
180    writeIndent(ts, indent);
181    ts << "[feGaussianBlur";
182    FilterEffect::externalRepresentation(ts);
183    ts << " stdDeviation=\"" << m_stdX << ", " << m_stdY << "\"]\n";
184    inputEffect(0)->externalRepresentation(ts, indent + 1);
185    return ts;
186}
187
188} // namespace WebCore
189