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