Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "GrCircleBlurFragmentProcessor.h" | 8 #include "GrCircleBlurFragmentProcessor.h" |
| 9 | 9 |
| 10 #if SK_SUPPORT_GPU | 10 #if SK_SUPPORT_GPU |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 129 } | 129 } |
| 130 float sum = 0.f; | 130 float sum = 0.f; |
| 131 // The half kernel should sum to 0.5 not 1.0. | 131 // The half kernel should sum to 0.5 not 1.0. |
| 132 tot *= 2.f; | 132 tot *= 2.f; |
| 133 for (int i = 0; i < halfKernelSize; ++i) { | 133 for (int i = 0; i < halfKernelSize; ++i) { |
| 134 halfKernel[i] /= tot; | 134 halfKernel[i] /= tot; |
| 135 sum += halfKernel[i]; | 135 sum += halfKernel[i]; |
| 136 summedHalfKernel[i] = sum; | 136 summedHalfKernel[i] = sum; |
| 137 } | 137 } |
| 138 } | 138 } |
| 139 | 139 |
|
robertphillips
2016/05/20 14:44:18
at a points ?
Maybe something more like:
Create
bsalomon
2016/05/20 15:17:14
Done.
| |
| 140 // Applies the 1D half kernel vertically at a point (x, 0) to a circle centered at the origin with | 140 // Applies the 1D half kernel vertically at a points (x, 0) to a circle centered at the origin with |
| 141 // radius circleR. | 141 // radius circleR. |
| 142 static float eval_vertically(float x, float circleR, const float* summedHalfKern elTable, | 142 void apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR , |
| 143 int halfKernelSize) { | 143 int halfKernelSize, const float* summedHalfKernelTable) { |
| 144 // Given x find the positive y that is on the edge of the circle. | 144 float x = firstX; |
| 145 float y = sqrtf(fabs(circleR * circleR - x * x)); | 145 for (int i = 0; i < numSteps; ++i, x += 1.f) { |
| 146 // In the column at x we exit the circle at +y and -y | 146 if (x < -circleR || x > circleR) { |
|
bsalomon
2016/05/20 00:24:45
I tried breaking this loop up into five parts:
1)
| |
| 147 // table entry j is actually the kernel evaluated at j + 0.5. | 147 results[i] = 0; |
| 148 y -= 0.5f; | 148 continue; |
| 149 int yInt = SkScalarFloorToInt(y); | 149 } |
| 150 SkASSERT(yInt >= -1); | 150 float y = sqrtf(circleR * circleR - x * x); |
| 151 if (y < 0) { | 151 // In the column at x we exit the circle at +y and -y |
| 152 return (y + 0.5f) * summedHalfKernelTable[0]; | 152 // The summed table entry j is actually reflects an offset of j + 0.5. |
| 153 } else if (yInt >= halfKernelSize - 1) { | 153 y -= 0.5f; |
| 154 return 0.5f; | 154 int yInt = SkScalarFloorToInt(y); |
| 155 } else { | 155 SkASSERT(yInt >= -1); |
| 156 float yFrac = y - yInt; | 156 if (y < 0) { |
| 157 return (1.f - yFrac) * summedHalfKernelTable[yInt] + | 157 results[i] = (y + 0.5f) * summedHalfKernelTable[0]; |
| 158 yFrac * summedHalfKernelTable[yInt + 1]; | 158 } else if (yInt >= halfKernelSize - 1) { |
| 159 results[i] = 0.5f; | |
| 160 } else { | |
| 161 float yFrac = y - yInt; | |
| 162 results[i] = (1.f - yFrac) * summedHalfKernelTable[yInt] + | |
| 163 yFrac * summedHalfKernelTable[yInt + 1]; | |
| 164 } | |
| 159 } | 165 } |
| 160 } | 166 } |
| 161 | 167 |
|
robertphillips
2016/05/20 14:44:19
// Conceptually combine the 2D Gaussian kernel pos
bsalomon
2016/05/20 15:17:14
Done.
| |
| 162 // Apply the kernel at point (t, 0) to a circle centered at the origin with radi us circleR. | 168 // Apply the kernel at point (evalX, 0) to a circle centered at the origin with radius circleR. |
| 163 static uint8_t eval_at(float t, float circleR, const float* halfKernel, | 169 static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int halfKernelSize, |
| 164 const float* summedHalfKernelTable, int halfKernelSize) { | 170 const float* yKernelEvaluations) { |
| 165 float acc = 0; | 171 float acc = 0; |
| 166 | 172 |
| 167 for (int i = 0; i < halfKernelSize; ++i) { | 173 float x = evalX - halfKernelSize; |
| 168 float x = t - i - 0.5f; | 174 for (int i = 0; i < halfKernelSize; ++i, x += 1.f) { |
| 169 if (x < -circleR || x > circleR) { | 175 if (x < -circleR || x > circleR) { |
| 170 continue; | 176 continue; |
| 171 } | 177 } |
| 172 float verticalEval = eval_vertically(x, circleR, summedHalfKernelTable, halfKernelSize); | 178 float verticalEval = yKernelEvaluations[i]; |
| 173 acc += verticalEval * halfKernel[i]; | 179 acc += verticalEval * halfKernel[halfKernelSize - i - 1]; |
| 174 } | 180 } |
| 175 for (int i = 0; i < halfKernelSize; ++i) { | 181 for (int i = 0; i < halfKernelSize; ++i, x += 1.f) { |
| 176 float x = t + i + 0.5f; | |
| 177 if (x < -circleR || x > circleR) { | 182 if (x < -circleR || x > circleR) { |
| 178 continue; | 183 continue; |
| 179 } | 184 } |
| 180 float verticalEval = eval_vertically(x, circleR, summedHalfKernelTable, halfKernelSize); | 185 float verticalEval = yKernelEvaluations[i + halfKernelSize]; |
| 181 acc += verticalEval * halfKernel[i]; | 186 acc += verticalEval * halfKernel[i]; |
| 182 } | 187 } |
| 183 // Since we applied a half kernel in y we multiply acc by 2 (the circle is s ymmetric about the | 188 // Since we applied a half kernel in y we multiply acc by 2 (the circle is s ymmetric about the |
| 184 // x axis). | 189 // x axis). |
| 185 return SkUnitScalarClampToByte(2.f * acc); | 190 return SkUnitScalarClampToByte(2.f * acc); |
| 186 } | 191 } |
| 187 | 192 |
| 188 static inline void compute_profile_offset_and_size(float circleR, float sigma, | 193 static inline void compute_profile_offset_and_size(float circleR, float sigma, |
| 189 float* offset, int* size) { | 194 float* offset, int* size) { |
| 190 if (3*sigma <= circleR) { | 195 if (3*sigma <= circleR) { |
| 191 // The circle is bigger than the Gaussian. In this case we know the inte rior of the | 196 // The circle is bigger than the Gaussian. In this case we know the inte rior of the |
| 192 // blurred circle is solid. | 197 // blurred circle is solid. |
| 193 *offset = circleR - 3 * sigma; // This location maps to 0.5f in the weig hts texture. | 198 *offset = circleR - 3 * sigma; // This location maps to 0.5f in the weig hts texture. |
| 194 // It should always be 255. | 199 // It should always be 255. |
| 195 *size = SkScalarCeilToInt(6*sigma); | 200 *size = SkScalarCeilToInt(6*sigma); |
| 196 } else { | 201 } else { |
| 197 // The Gaussian is bigger than the circle. | 202 // The Gaussian is bigger than the circle. |
| 198 *offset = 0.0f; | 203 *offset = 0.0f; |
| 199 *size = SkScalarCeilToInt(circleR + 3*sigma); | 204 *size = SkScalarCeilToInt(circleR + 3*sigma); |
| 200 } | 205 } |
| 201 } | 206 } |
| 202 | 207 |
|
robertphillips
2016/05/20 14:44:19
Seems like this might need updating
bsalomon
2016/05/20 15:17:14
Done.
| |
| 203 // This function creates a profile of a blurred circle. It does this by computin g a kernel for | 208 // This function creates a profile of a blurred circle. It does this by computin g a kernel for |
| 204 // half the Gaussian and a matching summed area table. To compute a profile valu e at x = r it steps | 209 // half the Gaussian and a matching summed area table. To compute a profile valu e at x = r it steps |
| 205 // outward in x from (r, 0) in both directions. There is a step for each directi on for each entry | 210 // outward in x from (r, 0) in both directions. There is a step for each directi on for each entry |
| 206 // in the half kernel. The y contribution at each step is computed from the summ ed area table using | 211 // in the half kernel. The y contribution at each step is computed from the summ ed area table using |
| 207 // the height of the circle above the step point. Each y contribution is multipl ied by the half | 212 // the height of the circle above the step point. Each y contribution is multipl ied by the half |
| 208 // kernel value corresponding to the step in x. | 213 // kernel value corresponding to the step in x. |
| 209 static uint8_t* create_profile(float circleR, float sigma) { | 214 static uint8_t* create_profile(float circleR, float sigma) { |
| 210 float offset; | 215 float offset; |
| 211 int numSteps; | 216 int numSteps; |
| 212 compute_profile_offset_and_size(circleR, sigma, &offset, &numSteps); | 217 compute_profile_offset_and_size(circleR, sigma, &offset, &numSteps); |
| 213 | 218 |
| 214 uint8_t* weights = new uint8_t[numSteps]; | 219 uint8_t* weights = new uint8_t[numSteps]; |
| 215 | 220 |
| 216 // The full kernel is 6 sigmas wide. | 221 // The full kernel is 6 sigmas wide. |
| 217 int halfKernelSize = SkScalarCeilToInt(6.0f*sigma); | 222 int halfKernelSize = SkScalarCeilToInt(6.0f*sigma); |
| 218 // round up to next multiple of 2 and then divide by 2 | 223 // round up to next multiple of 2 and then divide by 2 |
| 219 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; | 224 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; |
| 220 SkAutoTArray<float> halfKernel(halfKernelSize); | 225 |
| 221 SkAutoTArray<float> summedKernel(halfKernelSize); | 226 // Number of x steps at which to apply kernel in y to cover all the profile samples in x. |
| 222 make_half_kernel_and_summed_table(halfKernel.get(), summedKernel.get(), half KernelSize, | 227 int numYSteps = numSteps + 2 * halfKernelSize; |
| 223 sigma); | 228 |
| 229 SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps); | |
| 230 float* halfKernel = bulkAlloc.get(); | |
| 231 float* summedKernel = bulkAlloc.get() + halfKernelSize; | |
| 232 float* yEvals = bulkAlloc.get() + 2 * halfKernelSize; | |
| 233 make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma); | |
| 234 | |
| 235 float firstX = offset - halfKernelSize + 0.5; | |
| 236 apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summed Kernel); | |
| 237 | |
| 224 for (int i = 0; i < numSteps - 1; ++i) { | 238 for (int i = 0; i < numSteps - 1; ++i) { |
| 225 weights[i] = eval_at(offset+i, circleR, halfKernel.get(), summedKernel.g et(), | 239 float evalX = offset + i + 0.5f; |
| 226 halfKernelSize); | 240 weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i); |
| 227 } | 241 } |
| 228 // Ensure the tail of the Gaussian goes to zero. | 242 // Ensure the tail of the Gaussian goes to zero. |
| 229 weights[numSteps - 1] = 0; | 243 weights[numSteps - 1] = 0; |
| 230 return weights; | 244 return weights; |
| 231 } | 245 } |
| 232 | 246 |
| 233 GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture( | 247 GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture( |
| 234 GrTextureProvide r* textureProvider, | 248 GrTextureProvide r* textureProvider, |
| 235 const SkRect& ci rcle, | 249 const SkRect& ci rcle, |
| 236 float sigma, | 250 float sigma, |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 272 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor); | 286 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor); |
| 273 | 287 |
| 274 const GrFragmentProcessor* GrCircleBlurFragmentProcessor::TestCreate(GrProcessor TestData* d) { | 288 const GrFragmentProcessor* GrCircleBlurFragmentProcessor::TestCreate(GrProcessor TestData* d) { |
| 275 SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f); | 289 SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f); |
| 276 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f); | 290 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f); |
| 277 SkRect circle = SkRect::MakeWH(wh, wh); | 291 SkRect circle = SkRect::MakeWH(wh, wh); |
| 278 return GrCircleBlurFragmentProcessor::Create(d->fContext->textureProvider(), circle, sigma); | 292 return GrCircleBlurFragmentProcessor::Create(d->fContext->textureProvider(), circle, sigma); |
| 279 } | 293 } |
| 280 | 294 |
| 281 #endif | 295 #endif |
| OLD | NEW |