diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/platform/graphics/filters/FEGaussianBlur.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/platform/graphics/filters/FEGaussianBlur.cpp | 257 |
1 files changed, 41 insertions, 216 deletions
diff --git a/chromium/third_party/WebKit/Source/platform/graphics/filters/FEGaussianBlur.cpp b/chromium/third_party/WebKit/Source/platform/graphics/filters/FEGaussianBlur.cpp index 150aac53213..82554826e56 100644 --- a/chromium/third_party/WebKit/Source/platform/graphics/filters/FEGaussianBlur.cpp +++ b/chromium/third_party/WebKit/Source/platform/graphics/filters/FEGaussianBlur.cpp @@ -41,10 +41,10 @@ using namespace std; static inline float gaussianKernelFactor() { - return 3 / 4.f * sqrtf(2 * piFloat); + return 3 / 4.f * sqrtf(twoPiFloat); } -static const unsigned gMaxKernelSize = 1000; +static const int gMaxKernelSize = 1000; namespace WebCore { @@ -80,238 +80,69 @@ void FEGaussianBlur::setStdDeviationY(float y) m_stdY = y; } -inline void boxBlur(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* dstPixelArray, - unsigned dx, int dxLeft, int dxRight, int stride, int strideLine, int effectWidth, int effectHeight, bool alphaImage) +IntSize FEGaussianBlur::calculateUnscaledKernelSize(const FloatPoint& std) { - for (int y = 0; y < effectHeight; ++y) { - int line = y * strideLine; - for (int channel = 3; channel >= 0; --channel) { - int sum = 0; - // Fill the kernel - int maxKernelSize = min(dxRight, effectWidth); - for (int i = 0; i < maxKernelSize; ++i) - sum += srcPixelArray->item(line + i * stride + channel); - - // Blurring - for (int x = 0; x < effectWidth; ++x) { - int pixelByteOffset = line + x * stride + channel; - dstPixelArray->set(pixelByteOffset, static_cast<unsigned char>(sum / dx)); - if (x >= dxLeft) - sum -= srcPixelArray->item(pixelByteOffset - dxLeft * stride); - if (x + dxRight < effectWidth) - sum += srcPixelArray->item(pixelByteOffset + dxRight * stride); - } - if (alphaImage) // Source image is black, it just has different alpha values - break; - } - } -} - -inline void FEGaussianBlur::platformApplyGeneric(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize) -{ - int stride = 4 * paintSize.width(); - int dxLeft = 0; - int dxRight = 0; - int dyLeft = 0; - int dyRight = 0; - Uint8ClampedArray* src = srcPixelArray; - Uint8ClampedArray* dst = tmpPixelArray; - - for (int i = 0; i < 3; ++i) { - if (kernelSizeX) { - kernelPosition(i, kernelSizeX, dxLeft, dxRight); -#if HAVE(ARM_NEON_INTRINSICS) - if (!isAlphaImage()) - boxBlurNEON(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height()); - else - boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), true); -#else - boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), isAlphaImage()); -#endif - swap(src, dst); - } - - if (kernelSizeY) { - kernelPosition(i, kernelSizeY, dyLeft, dyRight); -#if HAVE(ARM_NEON_INTRINSICS) - if (!isAlphaImage()) - boxBlurNEON(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width()); - else - boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), true); -#else - boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), isAlphaImage()); -#endif - swap(src, dst); - } - } - - // The final result should be stored in srcPixelArray. - if (dst == srcPixelArray) { - ASSERT(src->length() == dst->length()); - memcpy(dst->data(), src->data(), src->length()); - } - -} - -void FEGaussianBlur::platformApplyWorker(PlatformApplyParameters* parameters) -{ - IntSize paintSize(parameters->width, parameters->height); - parameters->filter->platformApplyGeneric(parameters->srcPixelArray.get(), parameters->dstPixelArray.get(), - parameters->kernelSizeX, parameters->kernelSizeY, paintSize); -} - -inline void FEGaussianBlur::platformApply(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize) -{ - int scanline = 4 * paintSize.width(); - int extraHeight = 3 * kernelSizeY * 0.5f; - int optimalThreadNumber = (paintSize.width() * paintSize.height()) / (s_minimalRectDimension + extraHeight * paintSize.width()); - - if (optimalThreadNumber > 1) { - ParallelJobs<PlatformApplyParameters> parallelJobs(&platformApplyWorker, optimalThreadNumber); - - int jobs = parallelJobs.numberOfJobs(); - if (jobs > 1) { - // Split the job into "blockHeight"-sized jobs but there a few jobs that need to be slightly larger since - // blockHeight * jobs < total size. These extras are handled by the remainder "jobsWithExtra". - const int blockHeight = paintSize.height() / jobs; - const int jobsWithExtra = paintSize.height() % jobs; - - int currentY = 0; - for (int job = 0; job < jobs; job++) { - PlatformApplyParameters& params = parallelJobs.parameter(job); - params.filter = this; - - int startY = !job ? 0 : currentY - extraHeight; - currentY += job < jobsWithExtra ? blockHeight + 1 : blockHeight; - int endY = job == jobs - 1 ? currentY : currentY + extraHeight; - - int blockSize = (endY - startY) * scanline; - if (!job) { - params.srcPixelArray = srcPixelArray; - params.dstPixelArray = tmpPixelArray; - } else { - params.srcPixelArray = Uint8ClampedArray::createUninitialized(blockSize); - params.dstPixelArray = Uint8ClampedArray::createUninitialized(blockSize); - memcpy(params.srcPixelArray->data(), srcPixelArray->data() + startY * scanline, blockSize); - } - - params.width = paintSize.width(); - params.height = endY - startY; - params.kernelSizeX = kernelSizeX; - params.kernelSizeY = kernelSizeY; - } - - parallelJobs.execute(); - - // Copy together the parts of the image. - currentY = 0; - for (int job = 1; job < jobs; job++) { - PlatformApplyParameters& params = parallelJobs.parameter(job); - int sourceOffset; - int destinationOffset; - int size; - int adjustedBlockHeight = job < jobsWithExtra ? blockHeight + 1 : blockHeight; - - currentY += adjustedBlockHeight; - sourceOffset = extraHeight * scanline; - destinationOffset = currentY * scanline; - size = adjustedBlockHeight * scanline; - - memcpy(srcPixelArray->data() + destinationOffset, params.srcPixelArray->data() + sourceOffset, size); - } - return; - } - // Fallback to single threaded mode. - } - - // The selection here eventually should happen dynamically on some platforms. - platformApplyGeneric(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY, paintSize); -} - -void FEGaussianBlur::calculateUnscaledKernelSize(unsigned& kernelSizeX, unsigned& kernelSizeY, float stdX, float stdY) -{ - ASSERT(stdX >= 0 && stdY >= 0); - - kernelSizeX = 0; - if (stdX) - kernelSizeX = max<unsigned>(2, static_cast<unsigned>(floorf(stdX * gaussianKernelFactor() + 0.5f))); - kernelSizeY = 0; - if (stdY) - kernelSizeY = max<unsigned>(2, static_cast<unsigned>(floorf(stdY * gaussianKernelFactor() + 0.5f))); + ASSERT(std.x() >= 0 && std.y() >= 0); + IntSize kernelSize; // Limit the kernel size to 1000. A bigger radius won't make a big difference for the result image but // inflates the absolute paint rect to much. This is compatible with Firefox' behavior. - if (kernelSizeX > gMaxKernelSize) - kernelSizeX = gMaxKernelSize; - if (kernelSizeY > gMaxKernelSize) - kernelSizeY = gMaxKernelSize; -} + if (std.x()) { + int size = max<unsigned>(2, static_cast<unsigned>(floorf(std.x() * gaussianKernelFactor() + 0.5f))); + kernelSize.setWidth(min(size, gMaxKernelSize)); + } -void FEGaussianBlur::calculateKernelSize(Filter* filter, unsigned& kernelSizeX, unsigned& kernelSizeY, float stdX, float stdY) -{ - stdX = filter->applyHorizontalScale(stdX); - stdY = filter->applyVerticalScale(stdY); + if (std.y()) { + int size = max<unsigned>(2, static_cast<unsigned>(floorf(std.y() * gaussianKernelFactor() + 0.5f))); + kernelSize.setHeight(min(size, gMaxKernelSize)); + } - calculateUnscaledKernelSize(kernelSizeX, kernelSizeY, stdX, stdY); + return kernelSize; } -void FEGaussianBlur::determineAbsolutePaintRect() +IntSize FEGaussianBlur::calculateKernelSize(Filter* filter, const FloatPoint& std) { - FloatRect absolutePaintRect = mapRect(inputEffect(0)->absolutePaintRect()); - - if (clipsToBounds()) - absolutePaintRect.intersect(maxEffectRect()); - else - absolutePaintRect.unite(maxEffectRect()); + FloatPoint stdError(filter->applyHorizontalScale(std.x()), filter->applyVerticalScale(std.y())); - setAbsolutePaintRect(enclosingIntRect(absolutePaintRect)); + return calculateUnscaledKernelSize(stdError); } FloatRect FEGaussianBlur::mapRect(const FloatRect& rect, bool) { FloatRect result = rect; - unsigned kernelSizeX = 0; - unsigned kernelSizeY = 0; - calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY); + IntSize kernelSize = calculateKernelSize(filter(), FloatPoint(m_stdX, m_stdY)); // We take the half kernel size and multiply it with three, because we run box blur three times. - result.inflateX(3 * kernelSizeX * 0.5f); - result.inflateY(3 * kernelSizeY * 0.5f); + result.inflateX(3 * kernelSize.width() * 0.5f); + result.inflateY(3 * kernelSize.height() * 0.5f); return result; } -void FEGaussianBlur::applySoftware() +FloatRect FEGaussianBlur::determineAbsolutePaintRect(const FloatRect& originalRequestedRect) { - FilterEffect* in = inputEffect(0); - - Uint8ClampedArray* srcPixelArray = createPremultipliedImageResult(); - if (!srcPixelArray) - return; - - setIsAlphaImage(in->isAlphaImage()); - - IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); - in->copyPremultipliedImage(srcPixelArray, effectDrawingRect); - - if (!m_stdX && !m_stdY) - return; - - unsigned kernelSizeX = 0; - unsigned kernelSizeY = 0; - calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY); + FloatRect requestedRect = originalRequestedRect; + if (clipsToBounds()) + requestedRect.intersect(maxEffectRect()); - IntSize paintSize = absolutePaintRect().size(); - RefPtr<Uint8ClampedArray> tmpImageData = Uint8ClampedArray::createUninitialized(paintSize.width() * paintSize.height() * 4); - Uint8ClampedArray* tmpPixelArray = tmpImageData.get(); + FilterEffect* input = inputEffect(0); + FloatRect inputRect = input->determineAbsolutePaintRect(mapRect(requestedRect, false)); + FloatRect outputRect = mapRect(inputRect, true); + outputRect.intersect(requestedRect); + addAbsolutePaintRect(outputRect); - platformApply(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY, paintSize); + // Blur needs space for both input and output pixels in the paint area. + // Input is also clipped to subregion. + if (clipsToBounds()) + inputRect.intersect(maxEffectRect()); + addAbsolutePaintRect(inputRect); + return outputRect; } -bool FEGaussianBlur::applySkia() +void FEGaussianBlur::applySoftware() { ImageBuffer* resultImage = createImageBufferResult(); if (!resultImage) - return false; + return; FilterEffect* in = inputEffect(0); @@ -326,13 +157,13 @@ bool FEGaussianBlur::applySkia() SkPaint paint; GraphicsContext* dstContext = resultImage->context(); - paint.setImageFilter(new SkBlurImageFilter(stdX, stdY))->unref(); + paint.setImageFilter(SkBlurImageFilter::Create(stdX, stdY))->unref(); - dstContext->saveLayer(0, &paint); + SkRect bounds = SkRect::MakeWH(absolutePaintRect().width(), absolutePaintRect().height()); + dstContext->saveLayer(&bounds, &paint); paint.setColor(0xFFFFFFFF); dstContext->drawImage(image.get(), drawingRegion.location(), CompositeCopy); dstContext->restoreLayer(); - return true; } PassRefPtr<SkImageFilter> FEGaussianBlur::createImageFilter(SkiaImageFilterBuilder* builder) @@ -341,7 +172,7 @@ PassRefPtr<SkImageFilter> FEGaussianBlur::createImageFilter(SkiaImageFilterBuild float stdX = filter()->applyHorizontalScale(m_stdX); float stdY = filter()->applyVerticalScale(m_stdY); SkImageFilter::CropRect rect = getCropRect(builder->cropOffset()); - return adoptRef(new SkBlurImageFilter(SkFloatToScalar(stdX), SkFloatToScalar(stdY), input.get(), &rect)); + return adoptRef(SkBlurImageFilter::Create(SkFloatToScalar(stdX), SkFloatToScalar(stdY), input.get(), &rect)); } TextStream& FEGaussianBlur::externalRepresentation(TextStream& ts, int indent) const @@ -354,10 +185,4 @@ TextStream& FEGaussianBlur::externalRepresentation(TextStream& ts, int indent) c return ts; } -float FEGaussianBlur::calculateStdDeviation(float radius) -{ - // Blur radius represents 2/3 times the kernel size, the dest pixel is half of the radius applied 3 times - return max((radius * 2 / 3.f - 0.5f) / gaussianKernelFactor(), 0.f); -} - } // namespace WebCore |