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