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 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include "config.h"
25
26#if ENABLE(FILTERS)
27#include "FEMorphology.h"
28
29#include "Filter.h"
30#include "RenderTreeAsText.h"
31#include "TextStream.h"
32
33#include <wtf/ByteArray.h>
34#include <wtf/Vector.h>
35
36using std::min;
37using std::max;
38
39namespace WebCore {
40
41FEMorphology::FEMorphology(Filter* filter, MorphologyOperatorType type, float radiusX, float radiusY)
42    : FilterEffect(filter)
43    , m_type(type)
44    , m_radiusX(radiusX)
45    , m_radiusY(radiusY)
46{
47}
48
49PassRefPtr<FEMorphology> FEMorphology::create(Filter* filter, MorphologyOperatorType type, float radiusX, float radiusY)
50{
51    return adoptRef(new FEMorphology(filter, type, radiusX, radiusY));
52}
53
54MorphologyOperatorType FEMorphology::morphologyOperator() const
55{
56    return m_type;
57}
58
59bool FEMorphology::setMorphologyOperator(MorphologyOperatorType type)
60{
61    if (m_type == type)
62        return false;
63    m_type = type;
64    return true;
65}
66
67float FEMorphology::radiusX() const
68{
69    return m_radiusX;
70}
71
72bool FEMorphology::setRadiusX(float radiusX)
73{
74    if (m_radiusX == radiusX)
75        return false;
76    m_radiusX = radiusX;
77    return true;
78}
79
80float FEMorphology::radiusY() const
81{
82    return m_radiusY;
83}
84
85void FEMorphology::determineAbsolutePaintRect()
86{
87    FloatRect paintRect = inputEffect(0)->absolutePaintRect();
88    Filter* filter = this->filter();
89    paintRect.inflateX(filter->applyHorizontalScale(m_radiusX));
90    paintRect.inflateY(filter->applyVerticalScale(m_radiusY));
91    paintRect.intersect(maxEffectRect());
92    setAbsolutePaintRect(enclosingIntRect(paintRect));
93}
94
95bool FEMorphology::setRadiusY(float radiusY)
96{
97    if (m_radiusY == radiusY)
98        return false;
99    m_radiusY = radiusY;
100    return true;
101}
102
103void FEMorphology::apply()
104{
105    if (hasResult())
106        return;
107    FilterEffect* in = inputEffect(0);
108    in->apply();
109    if (!in->hasResult())
110        return;
111
112    ByteArray* dstPixelArray = createPremultipliedImageResult();
113    if (!dstPixelArray)
114        return;
115
116    setIsAlphaImage(in->isAlphaImage());
117    if (m_radiusX <= 0 || m_radiusY <= 0)
118        return;
119
120    Filter* filter = this->filter();
121    int radiusX = static_cast<int>(floorf(filter->applyHorizontalScale(m_radiusX)));
122    int radiusY = static_cast<int>(floorf(filter->applyVerticalScale(m_radiusY)));
123
124    IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
125    RefPtr<ByteArray> srcPixelArray = in->asPremultipliedImage(effectDrawingRect);
126
127    int effectWidth = effectDrawingRect.width() * 4;
128
129    // Limit the radius size to effect dimensions
130    radiusX = min(effectDrawingRect.width() - 1, radiusX);
131    radiusY = min(effectDrawingRect.height() - 1, radiusY);
132
133    Vector<unsigned char> extrema;
134    for (int y = 0; y < effectDrawingRect.height(); ++y) {
135        int startY = max(0, y - radiusY);
136        int endY = min(effectDrawingRect.height() - 1, y + radiusY);
137        for (unsigned channel = 0; channel < 4; ++channel) {
138            // Fill the kernel
139            extrema.clear();
140            for (int j = 0; j <= radiusX; ++j) {
141                unsigned char columnExtrema = srcPixelArray->get(startY * effectWidth + 4 * j + channel);
142                for (int i = startY; i <= endY; ++i) {
143                    unsigned char pixel = srcPixelArray->get(i * effectWidth + 4 * j + channel);
144                    if ((m_type == FEMORPHOLOGY_OPERATOR_ERODE && pixel <= columnExtrema) ||
145                        (m_type == FEMORPHOLOGY_OPERATOR_DILATE && pixel >= columnExtrema))
146                        columnExtrema = pixel;
147                }
148                extrema.append(columnExtrema);
149            }
150
151            // Kernel is filled, get extrema of next column
152            for (int x = 0; x < effectDrawingRect.width(); ++x) {
153                unsigned endX = min(x + radiusX, effectDrawingRect.width() - 1);
154                unsigned char columnExtrema = srcPixelArray->get(startY * effectWidth + endX * 4 + channel);
155                for (int i = startY; i <= endY; ++i) {
156                    unsigned char pixel = srcPixelArray->get(i * effectWidth + endX * 4 + channel);
157                    if ((m_type == FEMORPHOLOGY_OPERATOR_ERODE && pixel <= columnExtrema) ||
158                        (m_type == FEMORPHOLOGY_OPERATOR_DILATE && pixel >= columnExtrema))
159                        columnExtrema = pixel;
160                }
161                if (x - radiusX >= 0)
162                    extrema.remove(0);
163                if (x + radiusX <= effectDrawingRect.width())
164                    extrema.append(columnExtrema);
165                unsigned char entireExtrema = extrema[0];
166                for (unsigned kernelIndex = 0; kernelIndex < extrema.size(); ++kernelIndex) {
167                    if ((m_type == FEMORPHOLOGY_OPERATOR_ERODE && extrema[kernelIndex] <= entireExtrema) ||
168                        (m_type == FEMORPHOLOGY_OPERATOR_DILATE && extrema[kernelIndex] >= entireExtrema))
169                        entireExtrema = extrema[kernelIndex];
170                }
171                dstPixelArray->set(y * effectWidth + 4 * x + channel, entireExtrema);
172            }
173        }
174    }
175}
176
177void FEMorphology::dump()
178{
179}
180
181static TextStream& operator<<(TextStream& ts, const MorphologyOperatorType& type)
182{
183    switch (type) {
184    case FEMORPHOLOGY_OPERATOR_UNKNOWN:
185        ts << "UNKNOWN";
186        break;
187    case FEMORPHOLOGY_OPERATOR_ERODE:
188        ts << "ERODE";
189        break;
190    case FEMORPHOLOGY_OPERATOR_DILATE:
191        ts << "DILATE";
192        break;
193    }
194    return ts;
195}
196
197TextStream& FEMorphology::externalRepresentation(TextStream& ts, int indent) const
198{
199    writeIndent(ts, indent);
200    ts << "[feMorphology";
201    FilterEffect::externalRepresentation(ts);
202    ts << " operator=\"" << morphologyOperator() << "\" "
203       << "radius=\"" << radiusX() << ", " << radiusY() << "\"]\n";
204    inputEffect(0)->externalRepresentation(ts, indent + 1);
205    return ts;
206}
207
208} // namespace WebCore
209
210#endif // ENABLE(FILTERS)
211