| OLD | NEW |
| (Empty) |
| 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) 2010 Igalia, S.L. | |
| 7 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | |
| 8 * Copyright (C) 2013 Google Inc. All rights reserved. | |
| 9 * | |
| 10 * This library is free software; you can redistribute it and/or | |
| 11 * modify it under the terms of the GNU Library General Public | |
| 12 * License as published by the Free Software Foundation; either | |
| 13 * version 2 of the License, or (at your option) any later version. | |
| 14 * | |
| 15 * This library is distributed in the hope that it will be useful, | |
| 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 18 * Library General Public License for more details. | |
| 19 * | |
| 20 * You should have received a copy of the GNU Library General Public License | |
| 21 * along with this library; see the file COPYING.LIB. If not, write to | |
| 22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
| 23 * Boston, MA 02110-1301, USA. | |
| 24 */ | |
| 25 | |
| 26 #include "config.h" | |
| 27 | |
| 28 #include "core/platform/graphics/filters/FEGaussianBlur.h" | |
| 29 | |
| 30 #include "core/platform/graphics/GraphicsContext.h" | |
| 31 #include "core/platform/graphics/cpu/arm/filters/FEGaussianBlurNEON.h" | |
| 32 #include "core/platform/graphics/filters/ParallelJobs.h" | |
| 33 #include "core/platform/graphics/filters/SkiaImageFilterBuilder.h" | |
| 34 #include "platform/text/TextStream.h" | |
| 35 #include "wtf/MathExtras.h" | |
| 36 #include "wtf/Uint8ClampedArray.h" | |
| 37 | |
| 38 #include "SkBlurImageFilter.h" | |
| 39 | |
| 40 using namespace std; | |
| 41 | |
| 42 static inline float gaussianKernelFactor() | |
| 43 { | |
| 44 return 3 / 4.f * sqrtf(2 * piFloat); | |
| 45 } | |
| 46 | |
| 47 static const unsigned gMaxKernelSize = 1000; | |
| 48 | |
| 49 namespace WebCore { | |
| 50 | |
| 51 FEGaussianBlur::FEGaussianBlur(Filter* filter, float x, float y) | |
| 52 : FilterEffect(filter) | |
| 53 , m_stdX(x) | |
| 54 , m_stdY(y) | |
| 55 { | |
| 56 } | |
| 57 | |
| 58 PassRefPtr<FEGaussianBlur> FEGaussianBlur::create(Filter* filter, float x, float
y) | |
| 59 { | |
| 60 return adoptRef(new FEGaussianBlur(filter, x, y)); | |
| 61 } | |
| 62 | |
| 63 float FEGaussianBlur::stdDeviationX() const | |
| 64 { | |
| 65 return m_stdX; | |
| 66 } | |
| 67 | |
| 68 void FEGaussianBlur::setStdDeviationX(float x) | |
| 69 { | |
| 70 m_stdX = x; | |
| 71 } | |
| 72 | |
| 73 float FEGaussianBlur::stdDeviationY() const | |
| 74 { | |
| 75 return m_stdY; | |
| 76 } | |
| 77 | |
| 78 void FEGaussianBlur::setStdDeviationY(float y) | |
| 79 { | |
| 80 m_stdY = y; | |
| 81 } | |
| 82 | |
| 83 inline void boxBlur(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* dstPixe
lArray, | |
| 84 unsigned dx, int dxLeft, int dxRight, int stride, int stride
Line, int effectWidth, int effectHeight, bool alphaImage) | |
| 85 { | |
| 86 for (int y = 0; y < effectHeight; ++y) { | |
| 87 int line = y * strideLine; | |
| 88 for (int channel = 3; channel >= 0; --channel) { | |
| 89 int sum = 0; | |
| 90 // Fill the kernel | |
| 91 int maxKernelSize = min(dxRight, effectWidth); | |
| 92 for (int i = 0; i < maxKernelSize; ++i) | |
| 93 sum += srcPixelArray->item(line + i * stride + channel); | |
| 94 | |
| 95 // Blurring | |
| 96 for (int x = 0; x < effectWidth; ++x) { | |
| 97 int pixelByteOffset = line + x * stride + channel; | |
| 98 dstPixelArray->set(pixelByteOffset, static_cast<unsigned char>(s
um / dx)); | |
| 99 if (x >= dxLeft) | |
| 100 sum -= srcPixelArray->item(pixelByteOffset - dxLeft * stride
); | |
| 101 if (x + dxRight < effectWidth) | |
| 102 sum += srcPixelArray->item(pixelByteOffset + dxRight * strid
e); | |
| 103 } | |
| 104 if (alphaImage) // Source image is black, it just has different alph
a values | |
| 105 break; | |
| 106 } | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 inline void FEGaussianBlur::platformApplyGeneric(Uint8ClampedArray* srcPixelArra
y, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY,
IntSize& paintSize) | |
| 111 { | |
| 112 int stride = 4 * paintSize.width(); | |
| 113 int dxLeft = 0; | |
| 114 int dxRight = 0; | |
| 115 int dyLeft = 0; | |
| 116 int dyRight = 0; | |
| 117 Uint8ClampedArray* src = srcPixelArray; | |
| 118 Uint8ClampedArray* dst = tmpPixelArray; | |
| 119 | |
| 120 for (int i = 0; i < 3; ++i) { | |
| 121 if (kernelSizeX) { | |
| 122 kernelPosition(i, kernelSizeX, dxLeft, dxRight); | |
| 123 #if HAVE(ARM_NEON_INTRINSICS) | |
| 124 if (!isAlphaImage()) | |
| 125 boxBlurNEON(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, p
aintSize.width(), paintSize.height()); | |
| 126 else | |
| 127 boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paint
Size.width(), paintSize.height(), true); | |
| 128 #else | |
| 129 boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize
.width(), paintSize.height(), isAlphaImage()); | |
| 130 #endif | |
| 131 swap(src, dst); | |
| 132 } | |
| 133 | |
| 134 if (kernelSizeY) { | |
| 135 kernelPosition(i, kernelSizeY, dyLeft, dyRight); | |
| 136 #if HAVE(ARM_NEON_INTRINSICS) | |
| 137 if (!isAlphaImage()) | |
| 138 boxBlurNEON(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, p
aintSize.height(), paintSize.width()); | |
| 139 else | |
| 140 boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paint
Size.height(), paintSize.width(), true); | |
| 141 #else | |
| 142 boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize
.height(), paintSize.width(), isAlphaImage()); | |
| 143 #endif | |
| 144 swap(src, dst); | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 // The final result should be stored in srcPixelArray. | |
| 149 if (dst == srcPixelArray) { | |
| 150 ASSERT(src->length() == dst->length()); | |
| 151 memcpy(dst->data(), src->data(), src->length()); | |
| 152 } | |
| 153 | |
| 154 } | |
| 155 | |
| 156 void FEGaussianBlur::platformApplyWorker(PlatformApplyParameters* parameters) | |
| 157 { | |
| 158 IntSize paintSize(parameters->width, parameters->height); | |
| 159 parameters->filter->platformApplyGeneric(parameters->srcPixelArray.get(), pa
rameters->dstPixelArray.get(), | |
| 160 parameters->kernelSizeX, parameters->kernelSizeY, paintSize); | |
| 161 } | |
| 162 | |
| 163 inline void FEGaussianBlur::platformApply(Uint8ClampedArray* srcPixelArray, Uint
8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSiz
e& paintSize) | |
| 164 { | |
| 165 int scanline = 4 * paintSize.width(); | |
| 166 int extraHeight = 3 * kernelSizeY * 0.5f; | |
| 167 int optimalThreadNumber = (paintSize.width() * paintSize.height()) / (s_mini
malRectDimension + extraHeight * paintSize.width()); | |
| 168 | |
| 169 if (optimalThreadNumber > 1) { | |
| 170 ParallelJobs<PlatformApplyParameters> parallelJobs(&platformApplyWorker,
optimalThreadNumber); | |
| 171 | |
| 172 int jobs = parallelJobs.numberOfJobs(); | |
| 173 if (jobs > 1) { | |
| 174 // Split the job into "blockHeight"-sized jobs but there a few jobs
that need to be slightly larger since | |
| 175 // blockHeight * jobs < total size. These extras are handled by the
remainder "jobsWithExtra". | |
| 176 const int blockHeight = paintSize.height() / jobs; | |
| 177 const int jobsWithExtra = paintSize.height() % jobs; | |
| 178 | |
| 179 int currentY = 0; | |
| 180 for (int job = 0; job < jobs; job++) { | |
| 181 PlatformApplyParameters& params = parallelJobs.parameter(job); | |
| 182 params.filter = this; | |
| 183 | |
| 184 int startY = !job ? 0 : currentY - extraHeight; | |
| 185 currentY += job < jobsWithExtra ? blockHeight + 1 : blockHeight; | |
| 186 int endY = job == jobs - 1 ? currentY : currentY + extraHeight; | |
| 187 | |
| 188 int blockSize = (endY - startY) * scanline; | |
| 189 if (!job) { | |
| 190 params.srcPixelArray = srcPixelArray; | |
| 191 params.dstPixelArray = tmpPixelArray; | |
| 192 } else { | |
| 193 params.srcPixelArray = Uint8ClampedArray::createUninitialize
d(blockSize); | |
| 194 params.dstPixelArray = Uint8ClampedArray::createUninitialize
d(blockSize); | |
| 195 memcpy(params.srcPixelArray->data(), srcPixelArray->data() +
startY * scanline, blockSize); | |
| 196 } | |
| 197 | |
| 198 params.width = paintSize.width(); | |
| 199 params.height = endY - startY; | |
| 200 params.kernelSizeX = kernelSizeX; | |
| 201 params.kernelSizeY = kernelSizeY; | |
| 202 } | |
| 203 | |
| 204 parallelJobs.execute(); | |
| 205 | |
| 206 // Copy together the parts of the image. | |
| 207 currentY = 0; | |
| 208 for (int job = 1; job < jobs; job++) { | |
| 209 PlatformApplyParameters& params = parallelJobs.parameter(job); | |
| 210 int sourceOffset; | |
| 211 int destinationOffset; | |
| 212 int size; | |
| 213 int adjustedBlockHeight = job < jobsWithExtra ? blockHeight + 1
: blockHeight; | |
| 214 | |
| 215 currentY += adjustedBlockHeight; | |
| 216 sourceOffset = extraHeight * scanline; | |
| 217 destinationOffset = currentY * scanline; | |
| 218 size = adjustedBlockHeight * scanline; | |
| 219 | |
| 220 memcpy(srcPixelArray->data() + destinationOffset, params.srcPixe
lArray->data() + sourceOffset, size); | |
| 221 } | |
| 222 return; | |
| 223 } | |
| 224 // Fallback to single threaded mode. | |
| 225 } | |
| 226 | |
| 227 // The selection here eventually should happen dynamically on some platforms
. | |
| 228 platformApplyGeneric(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY,
paintSize); | |
| 229 } | |
| 230 | |
| 231 void FEGaussianBlur::calculateUnscaledKernelSize(unsigned& kernelSizeX, unsigned
& kernelSizeY, float stdX, float stdY) | |
| 232 { | |
| 233 ASSERT(stdX >= 0 && stdY >= 0); | |
| 234 | |
| 235 kernelSizeX = 0; | |
| 236 if (stdX) | |
| 237 kernelSizeX = max<unsigned>(2, static_cast<unsigned>(floorf(stdX * gauss
ianKernelFactor() + 0.5f))); | |
| 238 kernelSizeY = 0; | |
| 239 if (stdY) | |
| 240 kernelSizeY = max<unsigned>(2, static_cast<unsigned>(floorf(stdY * gauss
ianKernelFactor() + 0.5f))); | |
| 241 | |
| 242 // Limit the kernel size to 1000. A bigger radius won't make a big differenc
e for the result image but | |
| 243 // inflates the absolute paint rect to much. This is compatible with Firefox
' behavior. | |
| 244 if (kernelSizeX > gMaxKernelSize) | |
| 245 kernelSizeX = gMaxKernelSize; | |
| 246 if (kernelSizeY > gMaxKernelSize) | |
| 247 kernelSizeY = gMaxKernelSize; | |
| 248 } | |
| 249 | |
| 250 void FEGaussianBlur::calculateKernelSize(Filter* filter, unsigned& kernelSizeX,
unsigned& kernelSizeY, float stdX, float stdY) | |
| 251 { | |
| 252 stdX = filter->applyHorizontalScale(stdX); | |
| 253 stdY = filter->applyVerticalScale(stdY); | |
| 254 | |
| 255 calculateUnscaledKernelSize(kernelSizeX, kernelSizeY, stdX, stdY); | |
| 256 } | |
| 257 | |
| 258 void FEGaussianBlur::determineAbsolutePaintRect() | |
| 259 { | |
| 260 FloatRect absolutePaintRect = mapRect(inputEffect(0)->absolutePaintRect()); | |
| 261 | |
| 262 if (clipsToBounds()) | |
| 263 absolutePaintRect.intersect(maxEffectRect()); | |
| 264 else | |
| 265 absolutePaintRect.unite(maxEffectRect()); | |
| 266 | |
| 267 setAbsolutePaintRect(enclosingIntRect(absolutePaintRect)); | |
| 268 } | |
| 269 | |
| 270 FloatRect FEGaussianBlur::mapRect(const FloatRect& rect, bool) | |
| 271 { | |
| 272 FloatRect result = rect; | |
| 273 unsigned kernelSizeX = 0; | |
| 274 unsigned kernelSizeY = 0; | |
| 275 calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY); | |
| 276 | |
| 277 // We take the half kernel size and multiply it with three, because we run b
ox blur three times. | |
| 278 result.inflateX(3 * kernelSizeX * 0.5f); | |
| 279 result.inflateY(3 * kernelSizeY * 0.5f); | |
| 280 return result; | |
| 281 } | |
| 282 | |
| 283 void FEGaussianBlur::applySoftware() | |
| 284 { | |
| 285 FilterEffect* in = inputEffect(0); | |
| 286 | |
| 287 Uint8ClampedArray* srcPixelArray = createPremultipliedImageResult(); | |
| 288 if (!srcPixelArray) | |
| 289 return; | |
| 290 | |
| 291 setIsAlphaImage(in->isAlphaImage()); | |
| 292 | |
| 293 IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePain
tRect()); | |
| 294 in->copyPremultipliedImage(srcPixelArray, effectDrawingRect); | |
| 295 | |
| 296 if (!m_stdX && !m_stdY) | |
| 297 return; | |
| 298 | |
| 299 unsigned kernelSizeX = 0; | |
| 300 unsigned kernelSizeY = 0; | |
| 301 calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY); | |
| 302 | |
| 303 IntSize paintSize = absolutePaintRect().size(); | |
| 304 RefPtr<Uint8ClampedArray> tmpImageData = Uint8ClampedArray::createUninitiali
zed(paintSize.width() * paintSize.height() * 4); | |
| 305 Uint8ClampedArray* tmpPixelArray = tmpImageData.get(); | |
| 306 | |
| 307 platformApply(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY, paintS
ize); | |
| 308 } | |
| 309 | |
| 310 bool FEGaussianBlur::applySkia() | |
| 311 { | |
| 312 ImageBuffer* resultImage = createImageBufferResult(); | |
| 313 if (!resultImage) | |
| 314 return false; | |
| 315 | |
| 316 FilterEffect* in = inputEffect(0); | |
| 317 | |
| 318 IntRect drawingRegion = drawingRegionOfInputImage(in->absolutePaintRect()); | |
| 319 | |
| 320 setIsAlphaImage(in->isAlphaImage()); | |
| 321 | |
| 322 float stdX = filter()->applyHorizontalScale(m_stdX); | |
| 323 float stdY = filter()->applyVerticalScale(m_stdY); | |
| 324 | |
| 325 RefPtr<Image> image = in->asImageBuffer()->copyImage(DontCopyBackingStore); | |
| 326 | |
| 327 SkPaint paint; | |
| 328 GraphicsContext* dstContext = resultImage->context(); | |
| 329 paint.setImageFilter(new SkBlurImageFilter(stdX, stdY))->unref(); | |
| 330 | |
| 331 dstContext->saveLayer(0, &paint); | |
| 332 paint.setColor(0xFFFFFFFF); | |
| 333 dstContext->drawImage(image.get(), drawingRegion.location(), CompositeCopy); | |
| 334 dstContext->restoreLayer(); | |
| 335 return true; | |
| 336 } | |
| 337 | |
| 338 PassRefPtr<SkImageFilter> FEGaussianBlur::createImageFilter(SkiaImageFilterBuild
er* builder) | |
| 339 { | |
| 340 RefPtr<SkImageFilter> input(builder->build(inputEffect(0), operatingColorSpa
ce())); | |
| 341 float stdX = filter()->applyHorizontalScale(m_stdX); | |
| 342 float stdY = filter()->applyVerticalScale(m_stdY); | |
| 343 SkImageFilter::CropRect rect = getCropRect(builder->cropOffset()); | |
| 344 return adoptRef(new SkBlurImageFilter(SkFloatToScalar(stdX), SkFloatToScalar
(stdY), input.get(), &rect)); | |
| 345 } | |
| 346 | |
| 347 TextStream& FEGaussianBlur::externalRepresentation(TextStream& ts, int indent) c
onst | |
| 348 { | |
| 349 writeIndent(ts, indent); | |
| 350 ts << "[feGaussianBlur"; | |
| 351 FilterEffect::externalRepresentation(ts); | |
| 352 ts << " stdDeviation=\"" << m_stdX << ", " << m_stdY << "\"]\n"; | |
| 353 inputEffect(0)->externalRepresentation(ts, indent + 1); | |
| 354 return ts; | |
| 355 } | |
| 356 | |
| 357 float FEGaussianBlur::calculateStdDeviation(float radius) | |
| 358 { | |
| 359 // Blur radius represents 2/3 times the kernel size, the dest pixel is half
of the radius applied 3 times | |
| 360 return max((radius * 2 / 3.f - 0.5f) / gaussianKernelFactor(), 0.f); | |
| 361 } | |
| 362 | |
| 363 } // namespace WebCore | |
| OLD | NEW |