bd75e2ba8d0f93f623a8067d385ac68beb59b149
[WebKit-https.git] / Source / WebCore / platform / graphics / filters / FEMorphology.cpp
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/ParallelJobs.h>
35 #include <wtf/Vector.h>
36
37 using std::min;
38 using std::max;
39
40 namespace WebCore {
41
42 FEMorphology::FEMorphology(Filter* filter, MorphologyOperatorType type, float radiusX, float radiusY)
43     : FilterEffect(filter)
44     , m_type(type)
45     , m_radiusX(radiusX)
46     , m_radiusY(radiusY)
47 {
48 }
49
50 PassRefPtr<FEMorphology> FEMorphology::create(Filter* filter, MorphologyOperatorType type, float radiusX, float radiusY)
51 {
52     return adoptRef(new FEMorphology(filter, type, radiusX, radiusY));
53 }
54
55 MorphologyOperatorType FEMorphology::morphologyOperator() const
56 {
57     return m_type;
58 }
59
60 bool FEMorphology::setMorphologyOperator(MorphologyOperatorType type)
61 {
62     if (m_type == type)
63         return false;
64     m_type = type;
65     return true;
66 }
67
68 float FEMorphology::radiusX() const
69 {
70     return m_radiusX;
71 }
72
73 bool FEMorphology::setRadiusX(float radiusX)
74 {
75     if (m_radiusX == radiusX)
76         return false;
77     m_radiusX = radiusX;
78     return true;
79 }
80
81 float FEMorphology::radiusY() const
82 {
83     return m_radiusY;
84 }
85
86 void FEMorphology::determineAbsolutePaintRect()
87 {
88     FloatRect paintRect = inputEffect(0)->absolutePaintRect();
89     Filter* filter = this->filter();
90     paintRect.inflateX(filter->applyHorizontalScale(m_radiusX));
91     paintRect.inflateY(filter->applyVerticalScale(m_radiusY));
92     paintRect.intersect(maxEffectRect());
93     setAbsolutePaintRect(enclosingIntRect(paintRect));
94 }
95
96 bool FEMorphology::setRadiusY(float radiusY)
97 {
98     if (m_radiusY == radiusY)
99         return false;
100     m_radiusY = radiusY;
101     return true;
102 }
103
104 void FEMorphology::platformApplyGeneric(PaintingData* paintingData, int yStart, int yEnd)
105 {
106     ByteArray* srcPixelArray = paintingData->srcPixelArray;
107     ByteArray* dstPixelArray = paintingData->dstPixelArray;
108     const int width = paintingData->width;
109     const int height = paintingData->height;
110     const int effectWidth = width * 4;
111     const int radiusX = paintingData->radiusX;
112     const int radiusY = paintingData->radiusY;
113
114     Vector<unsigned char> extrema;
115     for (int y = yStart; y < yEnd; ++y) {
116         int extremaStartY = max(0, y - radiusY);
117         int extremaEndY = min(height - 1, y + radiusY);
118         for (unsigned int clrChannel = 0; clrChannel < 4; ++clrChannel) {
119             extrema.clear();
120             // Compute extremas for each columns
121             for (int x = 0; x <= radiusX; ++x) {
122                 unsigned char columnExtrema = srcPixelArray->get(extremaStartY * effectWidth + 4 * x + clrChannel);
123                 for (int eY = extremaStartY + 1; eY < extremaEndY; ++eY) {
124                     unsigned char pixel = srcPixelArray->get(eY * effectWidth + 4 * x + clrChannel);
125                     if ((m_type == FEMORPHOLOGY_OPERATOR_ERODE && pixel <= columnExtrema)
126                         || (m_type == FEMORPHOLOGY_OPERATOR_DILATE && pixel >= columnExtrema)) {
127                         columnExtrema = pixel;
128                     }
129                 }
130
131                 extrema.append(columnExtrema);
132             }
133
134             // Kernel is filled, get extrema of next column
135             for (int x = 0; x < width; ++x) {
136                 const int endX = min(x + radiusX, width - 1);
137                 unsigned char columnExtrema = srcPixelArray->get(extremaStartY * effectWidth + endX * 4 + clrChannel);
138                 for (int i = extremaStartY + 1; i <= extremaEndY; ++i) {
139                     unsigned char pixel = srcPixelArray->get(i * effectWidth + endX * 4 + clrChannel);
140                     if ((m_type == FEMORPHOLOGY_OPERATOR_ERODE && pixel <= columnExtrema)
141                         || (m_type == FEMORPHOLOGY_OPERATOR_DILATE && pixel >= columnExtrema))
142                         columnExtrema = pixel;
143                 }
144                 if (x - radiusX >= 0)
145                     extrema.remove(0);
146                 if (x + radiusX <= width)
147                     extrema.append(columnExtrema);
148
149                 unsigned char entireExtrema = extrema[0];
150                 for (unsigned kernelIndex = 1; kernelIndex < extrema.size(); ++kernelIndex) {
151                     if ((m_type == FEMORPHOLOGY_OPERATOR_ERODE && extrema[kernelIndex] <= entireExtrema)
152                         || (m_type == FEMORPHOLOGY_OPERATOR_DILATE && extrema[kernelIndex] >= entireExtrema))
153                         entireExtrema = extrema[kernelIndex];
154                 }
155                 dstPixelArray->set(y * effectWidth + 4 * x + clrChannel, entireExtrema);
156             }
157         }
158     }
159 }
160
161 void FEMorphology::platformApplyWorker(PlatformApplyParameters* param)
162 {
163     param->filter->platformApplyGeneric(param->paintingData, param->startY, param->endY);
164 }
165
166 void FEMorphology::platformApply(PaintingData* paintingData)
167 {
168     int optimalThreadNumber = (paintingData->width * paintingData->height) / s_minimalArea;
169     if (optimalThreadNumber > 1) {
170         ParallelJobs<PlatformApplyParameters> parallelJobs(&WebCore::FEMorphology::platformApplyWorker, optimalThreadNumber);
171         int numOfThreads = parallelJobs.numberOfJobs();
172         if (numOfThreads > 1) {
173             const int deltaY = 1 + paintingData->height / numOfThreads;
174             int currentY = 0;
175             for (int job = numOfThreads - 1; job >= 0; --job) {
176                 PlatformApplyParameters& param = parallelJobs.parameter(job);
177                 param.filter = this;
178                 param.startY = currentY;
179                 currentY += deltaY;
180                 param.endY = job ? currentY : paintingData->height;
181                 param.paintingData = paintingData;
182             }
183             parallelJobs.execute();
184             return;
185         }
186         // Fallback to single thread model
187     }
188
189     platformApplyGeneric(paintingData, 0, paintingData->height);
190 }
191
192
193 void FEMorphology::platformApplySoftware()
194 {
195     FilterEffect* in = inputEffect(0);
196
197     ByteArray* dstPixelArray = createPremultipliedImageResult();
198     if (!dstPixelArray)
199         return;
200
201     setIsAlphaImage(in->isAlphaImage());
202     if (m_radiusX <= 0 || m_radiusY <= 0) {
203         dstPixelArray->clear();
204         return;
205     }
206
207     Filter* filter = this->filter();
208     int radiusX = static_cast<int>(floorf(filter->applyHorizontalScale(m_radiusX)));
209     int radiusY = static_cast<int>(floorf(filter->applyVerticalScale(m_radiusY)));
210
211     IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
212     RefPtr<ByteArray> srcPixelArray = in->asPremultipliedImage(effectDrawingRect);
213
214     PaintingData paintingData;
215     paintingData.srcPixelArray = srcPixelArray.get();
216     paintingData.dstPixelArray = dstPixelArray;
217     paintingData.width = effectDrawingRect.width();
218     paintingData.height = effectDrawingRect.height();
219     paintingData.radiusX = min(effectDrawingRect.width() - 1, radiusX);
220     paintingData.radiusY = min(effectDrawingRect.height() - 1, radiusY);
221
222     platformApply(&paintingData);
223 }
224
225 void FEMorphology::dump()
226 {
227 }
228
229 static TextStream& operator<<(TextStream& ts, const MorphologyOperatorType& type)
230 {
231     switch (type) {
232     case FEMORPHOLOGY_OPERATOR_UNKNOWN:
233         ts << "UNKNOWN";
234         break;
235     case FEMORPHOLOGY_OPERATOR_ERODE:
236         ts << "ERODE";
237         break;
238     case FEMORPHOLOGY_OPERATOR_DILATE:
239         ts << "DILATE";
240         break;
241     }
242     return ts;
243 }
244
245 TextStream& FEMorphology::externalRepresentation(TextStream& ts, int indent) const
246 {
247     writeIndent(ts, indent);
248     ts << "[feMorphology";
249     FilterEffect::externalRepresentation(ts);
250     ts << " operator=\"" << morphologyOperator() << "\" "
251        << "radius=\"" << radiusX() << ", " << radiusY() << "\"]\n";
252     inputEffect(0)->externalRepresentation(ts, indent + 1);
253     return ts;
254 }
255
256 } // namespace WebCore
257
258 #endif // ENABLE(FILTERS)