Index: src/effects/GrCircleBlurFragmentProcessor.cpp |
diff --git a/src/effects/GrCircleBlurFragmentProcessor.cpp b/src/effects/GrCircleBlurFragmentProcessor.cpp |
index ff6985f7c3d56b5ea13b836c786acd9a4dd5a73e..bd8adeee811bd0f402a281c066de7dd4f4be8cf7 100644 |
--- a/src/effects/GrCircleBlurFragmentProcessor.cpp |
+++ b/src/effects/GrCircleBlurFragmentProcessor.cpp |
@@ -137,47 +137,55 @@ static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHa |
} |
} |
-// Applies the 1D half kernel vertically at a point (x, 0) to a circle centered at the origin with |
-// radius circleR. |
-static float eval_vertically(float x, float circleR, const float* summedHalfKernelTable, |
- int halfKernelSize) { |
- // Given x find the positive y that is on the edge of the circle. |
- float y = sqrtf(fabs(circleR * circleR - x * x)); |
- // In the column at x we exit the circle at +y and -y |
- // table entry j is actually the kernel evaluated at j + 0.5. |
- y -= 0.5f; |
- int yInt = SkScalarFloorToInt(y); |
- SkASSERT(yInt >= -1); |
- if (y < 0) { |
- return (y + 0.5f) * summedHalfKernelTable[0]; |
- } else if (yInt >= halfKernelSize - 1) { |
- return 0.5f; |
- } else { |
- float yFrac = y - yInt; |
- return (1.f - yFrac) * summedHalfKernelTable[yInt] + |
- yFrac * summedHalfKernelTable[yInt + 1]; |
+// Applies the 1D half kernel vertically at points along the x axis to a circle centered at the |
+// origin with radius circleR. |
+void apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR, |
+ int halfKernelSize, const float* summedHalfKernelTable) { |
+ float x = firstX; |
+ for (int i = 0; i < numSteps; ++i, x += 1.f) { |
+ if (x < -circleR || x > circleR) { |
+ results[i] = 0; |
+ continue; |
+ } |
+ float y = sqrtf(circleR * circleR - x * x); |
+ // In the column at x we exit the circle at +y and -y |
+ // The summed table entry j is actually reflects an offset of j + 0.5. |
+ y -= 0.5f; |
+ int yInt = SkScalarFloorToInt(y); |
+ SkASSERT(yInt >= -1); |
+ if (y < 0) { |
+ results[i] = (y + 0.5f) * summedHalfKernelTable[0]; |
+ } else if (yInt >= halfKernelSize - 1) { |
+ results[i] = 0.5f; |
+ } else { |
+ float yFrac = y - yInt; |
+ results[i] = (1.f - yFrac) * summedHalfKernelTable[yInt] + |
+ yFrac * summedHalfKernelTable[yInt + 1]; |
+ } |
} |
} |
-// Apply the kernel at point (t, 0) to a circle centered at the origin with radius circleR. |
-static uint8_t eval_at(float t, float circleR, const float* halfKernel, |
- const float* summedHalfKernelTable, int halfKernelSize) { |
+// Apply a Gaussian at point (evalX, 0) to a circle centered at the origin with radius circleR. |
+// This relies on having a half kernel computed for the Gaussian and a table of applications of |
+// the half kernel in y to columns at (evalX - halfKernel, evalX - halfKernel + 1, ..., evalX + |
+// halfKernel) passed in as yKernelEvaluations. |
+static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int halfKernelSize, |
+ const float* yKernelEvaluations) { |
float acc = 0; |
- for (int i = 0; i < halfKernelSize; ++i) { |
- float x = t - i - 0.5f; |
+ float x = evalX - halfKernelSize; |
+ for (int i = 0; i < halfKernelSize; ++i, x += 1.f) { |
if (x < -circleR || x > circleR) { |
continue; |
} |
- float verticalEval = eval_vertically(x, circleR, summedHalfKernelTable, halfKernelSize); |
- acc += verticalEval * halfKernel[i]; |
+ float verticalEval = yKernelEvaluations[i]; |
+ acc += verticalEval * halfKernel[halfKernelSize - i - 1]; |
} |
- for (int i = 0; i < halfKernelSize; ++i) { |
- float x = t + i + 0.5f; |
+ for (int i = 0; i < halfKernelSize; ++i, x += 1.f) { |
if (x < -circleR || x > circleR) { |
continue; |
} |
- float verticalEval = eval_vertically(x, circleR, summedHalfKernelTable, halfKernelSize); |
+ float verticalEval = yKernelEvaluations[i + halfKernelSize]; |
acc += verticalEval * halfKernel[i]; |
} |
// Since we applied a half kernel in y we multiply acc by 2 (the circle is symmetric about the |
@@ -201,11 +209,12 @@ static inline void compute_profile_offset_and_size(float circleR, float sigma, |
} |
// This function creates a profile of a blurred circle. It does this by computing a kernel for |
-// half the Gaussian and a matching summed area table. To compute a profile value at x = r it steps |
-// outward in x from (r, 0) in both directions. There is a step for each direction for each entry |
-// in the half kernel. The y contribution at each step is computed from the summed area table using |
-// the height of the circle above the step point. Each y contribution is multiplied by the half |
-// kernel value corresponding to the step in x. |
+// half the Gaussian and a matching summed area table. The summed area table is used to compute |
+// an array of vertical applications of the half kernel to the circle along the x axis. The table |
+// of y evaluations has 2 * k + n entries where k is the size of the half kernel and n is the size |
+// of the profile being computed. Then for each of the n profile entries we walk out k steps in each |
+// horizontal direction multiplying the corresponding y evaluation by the half kernel entry and |
+// sum these values to compute the profile entry. |
static uint8_t* create_profile(float circleR, float sigma) { |
float offset; |
int numSteps; |
@@ -217,13 +226,22 @@ static uint8_t* create_profile(float circleR, float sigma) { |
int halfKernelSize = SkScalarCeilToInt(6.0f*sigma); |
// round up to next multiple of 2 and then divide by 2 |
halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; |
- SkAutoTArray<float> halfKernel(halfKernelSize); |
- SkAutoTArray<float> summedKernel(halfKernelSize); |
- make_half_kernel_and_summed_table(halfKernel.get(), summedKernel.get(), halfKernelSize, |
- sigma); |
+ |
+ // Number of x steps at which to apply kernel in y to cover all the profile samples in x. |
+ int numYSteps = numSteps + 2 * halfKernelSize; |
+ |
+ SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps); |
+ float* halfKernel = bulkAlloc.get(); |
+ float* summedKernel = bulkAlloc.get() + halfKernelSize; |
+ float* yEvals = bulkAlloc.get() + 2 * halfKernelSize; |
+ make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma); |
+ |
+ float firstX = offset - halfKernelSize + 0.5f; |
+ apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summedKernel); |
+ |
for (int i = 0; i < numSteps - 1; ++i) { |
- weights[i] = eval_at(offset+i, circleR, halfKernel.get(), summedKernel.get(), |
- halfKernelSize); |
+ float evalX = offset + i + 0.5f; |
+ weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i); |
} |
// Ensure the tail of the Gaussian goes to zero. |
weights[numSteps - 1] = 0; |