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) Research In Motion Limited 2010. All rights reserved.
7 * Copyright (C) 2013 Google Inc. All rights reserved.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB.  If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25#include "config.h"
26#include "platform/graphics/filters/FEDisplacementMap.h"
27
28#include "SkBitmapSource.h"
29#include "SkDisplacementMapEffect.h"
30#include "platform/graphics/GraphicsContext.h"
31#include "platform/graphics/filters/SkiaImageFilterBuilder.h"
32#include "platform/graphics/skia/NativeImageSkia.h"
33#include "platform/text/TextStream.h"
34#include "wtf/Uint8ClampedArray.h"
35
36namespace blink {
37
38FEDisplacementMap::FEDisplacementMap(Filter* filter, ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float scale)
39    : FilterEffect(filter)
40    , m_xChannelSelector(xChannelSelector)
41    , m_yChannelSelector(yChannelSelector)
42    , m_scale(scale)
43{
44}
45
46PassRefPtr<FEDisplacementMap> FEDisplacementMap::create(Filter* filter, ChannelSelectorType xChannelSelector,
47    ChannelSelectorType yChannelSelector, float scale)
48{
49    return adoptRef(new FEDisplacementMap(filter, xChannelSelector, yChannelSelector, scale));
50}
51
52FloatRect FEDisplacementMap::mapPaintRect(const FloatRect& rect, bool)
53{
54    FloatRect result = rect;
55    result.inflateX(filter()->applyHorizontalScale(m_scale / 2));
56    result.inflateY(filter()->applyVerticalScale(m_scale / 2));
57    return result;
58}
59
60ChannelSelectorType FEDisplacementMap::xChannelSelector() const
61{
62    return m_xChannelSelector;
63}
64
65bool FEDisplacementMap::setXChannelSelector(const ChannelSelectorType xChannelSelector)
66{
67    if (m_xChannelSelector == xChannelSelector)
68        return false;
69    m_xChannelSelector = xChannelSelector;
70    return true;
71}
72
73ChannelSelectorType FEDisplacementMap::yChannelSelector() const
74{
75    return m_yChannelSelector;
76}
77
78bool FEDisplacementMap::setYChannelSelector(const ChannelSelectorType yChannelSelector)
79{
80    if (m_yChannelSelector == yChannelSelector)
81        return false;
82    m_yChannelSelector = yChannelSelector;
83    return true;
84}
85
86float FEDisplacementMap::scale() const
87{
88    return m_scale;
89}
90
91bool FEDisplacementMap::setScale(float scale)
92{
93    if (m_scale == scale)
94        return false;
95    m_scale = scale;
96    return true;
97}
98
99void FEDisplacementMap::setResultColorSpace(ColorSpace)
100{
101    // Spec: The 'color-interpolation-filters' property only applies to the 'in2' source image
102    // and does not apply to the 'in' source image. The 'in' source image must remain in its
103    // current color space.
104    // The result is in that smae color space because it is a displacement of the 'in' image.
105    FilterEffect::setResultColorSpace(inputEffect(0)->resultColorSpace());
106}
107
108void FEDisplacementMap::transformResultColorSpace(FilterEffect* in, const int index)
109{
110    // Do not transform the first primitive input, as per the spec.
111    if (index)
112        in->transformResultColorSpace(operatingColorSpace());
113}
114
115void FEDisplacementMap::applySoftware()
116{
117    FilterEffect* in = inputEffect(0);
118    FilterEffect* in2 = inputEffect(1);
119
120    ASSERT(m_xChannelSelector != CHANNEL_UNKNOWN);
121    ASSERT(m_yChannelSelector != CHANNEL_UNKNOWN);
122
123    Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult();
124    if (!dstPixelArray)
125        return;
126
127    IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
128    RefPtr<Uint8ClampedArray> srcPixelArrayA = in->asPremultipliedImage(effectADrawingRect);
129
130    IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect());
131    RefPtr<Uint8ClampedArray> srcPixelArrayB = in2->asUnmultipliedImage(effectBDrawingRect);
132
133    ASSERT(srcPixelArrayA->length() == srcPixelArrayB->length());
134
135    Filter* filter = this->filter();
136    IntSize paintSize = absolutePaintRect().size();
137    float scaleX = filter->applyHorizontalScale(m_scale);
138    float scaleY = filter->applyVerticalScale(m_scale);
139    float scaleForColorX = scaleX / 255.0;
140    float scaleForColorY = scaleY / 255.0;
141    float scaledOffsetX = 0.5 - scaleX * 0.5;
142    float scaledOffsetY = 0.5 - scaleY * 0.5;
143    int stride = paintSize.width() * 4;
144    for (int y = 0; y < paintSize.height(); ++y) {
145        int line = y * stride;
146        for (int x = 0; x < paintSize.width(); ++x) {
147            int dstIndex = line + x * 4;
148            int srcX = x + static_cast<int>(scaleForColorX * srcPixelArrayB->item(dstIndex + m_xChannelSelector - 1) + scaledOffsetX);
149            int srcY = y + static_cast<int>(scaleForColorY * srcPixelArrayB->item(dstIndex + m_yChannelSelector - 1) + scaledOffsetY);
150            for (unsigned channel = 0; channel < 4; ++channel) {
151                if (srcX < 0 || srcX >= paintSize.width() || srcY < 0 || srcY >= paintSize.height()) {
152                    dstPixelArray->set(dstIndex + channel, static_cast<unsigned char>(0));
153                } else {
154                    unsigned char pixelValue = srcPixelArrayA->item(srcY * stride + srcX * 4 + channel);
155                    dstPixelArray->set(dstIndex + channel, pixelValue);
156                }
157            }
158        }
159    }
160}
161
162static SkDisplacementMapEffect::ChannelSelectorType toSkiaMode(ChannelSelectorType type)
163{
164    switch (type) {
165    case CHANNEL_R:
166        return SkDisplacementMapEffect::kR_ChannelSelectorType;
167    case CHANNEL_G:
168        return SkDisplacementMapEffect::kG_ChannelSelectorType;
169    case CHANNEL_B:
170        return SkDisplacementMapEffect::kB_ChannelSelectorType;
171    case CHANNEL_A:
172        return SkDisplacementMapEffect::kA_ChannelSelectorType;
173    case CHANNEL_UNKNOWN:
174    default:
175        return SkDisplacementMapEffect::kUnknown_ChannelSelectorType;
176    }
177}
178
179PassRefPtr<SkImageFilter> FEDisplacementMap::createImageFilter(SkiaImageFilterBuilder* builder)
180{
181    RefPtr<SkImageFilter> color = builder->build(inputEffect(0), operatingColorSpace());
182    RefPtr<SkImageFilter> displ = builder->build(inputEffect(1), operatingColorSpace());
183    SkDisplacementMapEffect::ChannelSelectorType typeX = toSkiaMode(m_xChannelSelector);
184    SkDisplacementMapEffect::ChannelSelectorType typeY = toSkiaMode(m_yChannelSelector);
185    SkImageFilter::CropRect cropRect = getCropRect(builder->cropOffset());
186    // FIXME : Only applyHorizontalScale is used and applyVerticalScale is ignored
187    // This can be fixed by adding a 2nd scale parameter to SkDisplacementMapEffect
188    return adoptRef(SkDisplacementMapEffect::Create(typeX, typeY, SkFloatToScalar(filter()->applyHorizontalScale(m_scale)), displ.get(), color.get(), &cropRect));
189}
190
191static TextStream& operator<<(TextStream& ts, const ChannelSelectorType& type)
192{
193    switch (type) {
194    case CHANNEL_UNKNOWN:
195        ts << "UNKNOWN";
196        break;
197    case CHANNEL_R:
198        ts << "RED";
199        break;
200    case CHANNEL_G:
201        ts << "GREEN";
202        break;
203    case CHANNEL_B:
204        ts << "BLUE";
205        break;
206    case CHANNEL_A:
207        ts << "ALPHA";
208        break;
209    }
210    return ts;
211}
212
213TextStream& FEDisplacementMap::externalRepresentation(TextStream& ts, int indent) const
214{
215    writeIndent(ts, indent);
216    ts << "[feDisplacementMap";
217    FilterEffect::externalRepresentation(ts);
218    ts << " scale=\"" << m_scale << "\" "
219       << "xChannelSelector=\"" << m_xChannelSelector << "\" "
220       << "yChannelSelector=\"" << m_yChannelSelector << "\"]\n";
221    inputEffect(0)->externalRepresentation(ts, indent + 1);
222    inputEffect(1)->externalRepresentation(ts, indent + 1);
223    return ts;
224}
225
226FloatRect FEDisplacementMap::determineAbsolutePaintRect(const FloatRect& requestedRect)
227{
228    FloatRect rect = requestedRect;
229    if (clipsToBounds())
230        rect.intersect(maxEffectRect());
231
232    if (absolutePaintRect().contains(enclosingIntRect(rect)))
233        return rect;
234
235    rect = mapPaintRect(rect, false);
236    rect = inputEffect(0)->determineAbsolutePaintRect(rect);
237    rect = mapPaintRect(rect, true);
238    rect.intersect(requestedRect);
239
240    addAbsolutePaintRect(rect);
241    return rect;
242}
243
244} // namespace blink
245