--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Timing test for blur filter</title>
+ <style>
+ img {
+ width: 600px;
+ height: 600px;
+ }
+ </style>
+ <script>
+
+ var WIDTH = 600;
+ var HEIGHT = 600;
+ var NUM_ITERATIONS = 10;
+ var MAX_RADIUS = 20;
+ var currentIteration = 0;
+ var currentRadius = 0;
+ var testIsRunning = false;
+ var image = null;
+ var startTime = null;
+
+ function init() {
+ document.querySelector("button").addEventListener("click", run, false);
+ image = document.querySelector("img");
+
+ // Fill the image with generated content. We can't use a canvas directly,
+ // since that gets composited.
+ var canvas = document.createElement("canvas");
+ canvas.width = WIDTH * window.devicePixelRatio;
+ canvas.height = HEIGHT * window.devicePixelRatio;
+
+ // Fill the canvas with some generated content.
+ var ctx = canvas.getContext("2d");
+ ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
+
+ for (var i = 0; i < WIDTH; i += 20) {
+ for (var j = 0; j < HEIGHT; j += 20) {
+ ctx.fillStyle = "rgb(" + Math.round(i / WIDTH * 255) + ", " + Math.round(j / HEIGHT * 255) + ", " + (i % 40 ? 64 : 192) + ")";
+ ctx.fillRect(i, j, 20, 20);
+ }
+ }
+
+ image.src = canvas.toDataURL();
+ }
+
+ function run() {
+ if (testIsRunning)
+ return;
+
+ testIsRunning = true;
+ currentIteration = 0;
+ currentRadius = 0;
+ startTime = Date.now();
+
+ step();
+ }
+
+ function step() {
+ var usedRadius = (currentIteration % 2) ? (MAX_RADIUS - currentRadius) : currentRadius;
+ image.style.webkitFilter = "blur(" + usedRadius + "px)";
+ currentRadius++;
+ if (currentRadius > MAX_RADIUS) {
+ currentIteration++;
+ currentRadius = 0;
+ }
+
+ if (currentIteration < NUM_ITERATIONS)
+ setTimeout(step, 0);
+ else
+ end();
+ }
+
+ function end() {
+ testIsRunning = false;
+ var elapsedTime = (Date.now() - startTime) / 1000;
+ var result = document.createElement("p");
+ result.textContent = (NUM_ITERATIONS * MAX_RADIUS) + " blurs done in " + elapsedTime + " seconds";
+ document.body.appendChild(result);
+ if (window.testRunner)
+ testRunner.notifyDone();
+ }
+
+ window.addEventListener("load", init, false);
+ </script>
+</head>
+<body>
+<img>
+<p>
+ <button>Start</button>
+</p>
+</body>
+</html>
#include "GraphicsContext.h"
#include "TextStream.h"
+#if HAVE(ACCELERATE)
+#include <Accelerate/Accelerate.h>
+#endif
+
#include <runtime/JSCInlines.h>
#include <runtime/TypedArrayInlines.h>
#include <runtime/Uint8ClampedArray.h>
namespace WebCore {
+inline void kernelPosition(int blurIteration, unsigned& radius, int& deltaLeft, int& deltaRight)
+{
+ // Check http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement for details.
+ switch (blurIteration) {
+ case 0:
+ if (!(radius % 2)) {
+ deltaLeft = radius / 2 - 1;
+ deltaRight = radius - deltaLeft;
+ } else {
+ deltaLeft = radius / 2;
+ deltaRight = radius - deltaLeft;
+ }
+ break;
+ case 1:
+ if (!(radius % 2)) {
+ deltaLeft++;
+ deltaRight--;
+ }
+ break;
+ case 2:
+ if (!(radius % 2)) {
+ deltaRight++;
+ radius++;
+ }
+ break;
+ }
+}
+
FEGaussianBlur::FEGaussianBlur(Filter* filter, float x, float y, EdgeModeType edgeMode)
: FilterEffect(filter)
, m_stdX(x)
}
}
-inline void FEGaussianBlur::platformApplyGeneric(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize)
+#if HAVE(ACCELERATE)
+inline void accelerateBoxBlur(const Uint8ClampedArray* src, Uint8ClampedArray* dst, unsigned kernelSize, int stride, int effectWidth, int effectHeight)
+{
+ // We must always use an odd radius.
+ if (kernelSize % 2 != 1)
+ kernelSize += 1;
+
+ vImage_Buffer effectInBuffer;
+ effectInBuffer.data = src->data();
+ effectInBuffer.width = effectWidth;
+ effectInBuffer.height = effectHeight;
+ effectInBuffer.rowBytes = stride;
+
+ vImage_Buffer effectOutBuffer;
+ effectOutBuffer.data = dst->data();
+ effectOutBuffer.width = effectWidth;
+ effectOutBuffer.height = effectHeight;
+ effectOutBuffer.rowBytes = stride;
+
+ // Determine the size of a temporary buffer by calling the function first with a special flag. vImage will return
+ // the size needed, or an error (which are all negative).
+ size_t tmpBufferSize = vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, 0, 0, 0, kernelSize, kernelSize, 0, kvImageEdgeExtend | kvImageGetTempBufferSize);
+ if (tmpBufferSize <= 0)
+ return;
+
+ void* tmpBuffer = fastMalloc(tmpBufferSize);
+ vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, tmpBuffer, 0, 0, kernelSize, kernelSize, 0, kvImageEdgeExtend);
+ vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, tmpBuffer, 0, 0, kernelSize, kernelSize, 0, kvImageEdgeExtend);
+ vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, tmpBuffer, 0, 0, kernelSize, kernelSize, 0, kvImageEdgeExtend);
+ WTF::fastFree(tmpBuffer);
+
+ // The final result should be stored in src.
+ if (dst == src) {
+ ASSERT(src->length() == dst->length());
+ memcpy(dst->data(), src->data(), src->length());
+ }
+}
+#endif
+
+inline void standardBoxBlur(Uint8ClampedArray* src, Uint8ClampedArray* dst, unsigned kernelSizeX, unsigned kernelSizeY, int stride, IntSize& paintSize, bool isAlphaImage, EdgeModeType edgeMode)
{
- 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) {
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, m_edgeMode);
+ boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), true, edgeMode);
#else
- boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), isAlphaImage(), m_edgeMode);
+ boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), isAlphaImage, edgeMode);
#endif
std::swap(src, dst);
}
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, m_edgeMode);
+ boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), true, edgeMode);
#else
- boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), isAlphaImage(), m_edgeMode);
+ boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), isAlphaImage, edgeMode);
#endif
std::swap(src, dst);
}
}
- // The final result should be stored in srcPixelArray.
- if (dst == srcPixelArray) {
+ // The final result should be stored in src.
+ if (dst == src) {
ASSERT(src->length() == dst->length());
memcpy(dst->data(), src->data(), src->length());
}
+}
+
+inline void FEGaussianBlur::platformApplyGeneric(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize)
+{
+ int stride = 4 * paintSize.width();
+
+#if HAVE(ACCELERATE)
+ if (kernelSizeX == kernelSizeY && (m_edgeMode == EDGEMODE_NONE || m_edgeMode == EDGEMODE_DUPLICATE)) {
+ accelerateBoxBlur(srcPixelArray, tmpPixelArray, kernelSizeX, stride, paintSize.width(), paintSize.height());
+ return;
+ }
+#endif
+ standardBoxBlur(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY, stride, paintSize, isAlphaImage(), m_edgeMode);
}
void FEGaussianBlur::platformApplyWorker(PlatformApplyParameters* parameters)
inline void FEGaussianBlur::platformApply(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSize& paintSize)
{
+#if !HAVE(ACCELERATE)
int scanline = 4 * paintSize.width();
int extraHeight = 3 * kernelSizeY * 0.5f;
int optimalThreadNumber = (paintSize.width() * paintSize.height()) / (s_minimalRectDimension + extraHeight * paintSize.width());
}
// Fallback to single threaded mode.
}
+#endif
// The selection here eventually should happen dynamically on some platforms.
platformApplyGeneric(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY, paintSize);