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 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 |
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 points along the x axis to a circle
centered at the |
141 // radius circleR. | 141 // origin with 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) { |
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 |
162 // Apply the kernel at point (t, 0) to a circle centered at the origin with radi
us circleR. | 168 // Apply a Gaussian 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 // This relies on having a half kernel computed for the Gaussian and a table of
applications of |
164 const float* summedHalfKernelTable, int halfKernelSize) { | 170 // the half kernel in y to columns at (evalX - halfKernel, evalX - halfKernel +
1, ..., evalX + |
| 171 // halfKernel) passed in as yKernelEvaluations. |
| 172 static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int
halfKernelSize, |
| 173 const float* yKernelEvaluations) { |
165 float acc = 0; | 174 float acc = 0; |
166 | 175 |
167 for (int i = 0; i < halfKernelSize; ++i) { | 176 float x = evalX - halfKernelSize; |
168 float x = t - i - 0.5f; | 177 for (int i = 0; i < halfKernelSize; ++i, x += 1.f) { |
169 if (x < -circleR || x > circleR) { | 178 if (x < -circleR || x > circleR) { |
170 continue; | 179 continue; |
171 } | 180 } |
172 float verticalEval = eval_vertically(x, circleR, summedHalfKernelTable,
halfKernelSize); | 181 float verticalEval = yKernelEvaluations[i]; |
173 acc += verticalEval * halfKernel[i]; | 182 acc += verticalEval * halfKernel[halfKernelSize - i - 1]; |
174 } | 183 } |
175 for (int i = 0; i < halfKernelSize; ++i) { | 184 for (int i = 0; i < halfKernelSize; ++i, x += 1.f) { |
176 float x = t + i + 0.5f; | |
177 if (x < -circleR || x > circleR) { | 185 if (x < -circleR || x > circleR) { |
178 continue; | 186 continue; |
179 } | 187 } |
180 float verticalEval = eval_vertically(x, circleR, summedHalfKernelTable,
halfKernelSize); | 188 float verticalEval = yKernelEvaluations[i + halfKernelSize]; |
181 acc += verticalEval * halfKernel[i]; | 189 acc += verticalEval * halfKernel[i]; |
182 } | 190 } |
183 // Since we applied a half kernel in y we multiply acc by 2 (the circle is s
ymmetric about the | 191 // Since we applied a half kernel in y we multiply acc by 2 (the circle is s
ymmetric about the |
184 // x axis). | 192 // x axis). |
185 return SkUnitScalarClampToByte(2.f * acc); | 193 return SkUnitScalarClampToByte(2.f * acc); |
186 } | 194 } |
187 | 195 |
188 static inline void compute_profile_offset_and_size(float circleR, float sigma, | 196 static inline void compute_profile_offset_and_size(float circleR, float sigma, |
189 float* offset, int* size) { | 197 float* offset, int* size) { |
190 if (3*sigma <= circleR) { | 198 if (3*sigma <= circleR) { |
191 // The circle is bigger than the Gaussian. In this case we know the inte
rior of the | 199 // The circle is bigger than the Gaussian. In this case we know the inte
rior of the |
192 // blurred circle is solid. | 200 // blurred circle is solid. |
193 *offset = circleR - 3 * sigma; // This location maps to 0.5f in the weig
hts texture. | 201 *offset = circleR - 3 * sigma; // This location maps to 0.5f in the weig
hts texture. |
194 // It should always be 255. | 202 // It should always be 255. |
195 *size = SkScalarCeilToInt(6*sigma); | 203 *size = SkScalarCeilToInt(6*sigma); |
196 } else { | 204 } else { |
197 // The Gaussian is bigger than the circle. | 205 // The Gaussian is bigger than the circle. |
198 *offset = 0.0f; | 206 *offset = 0.0f; |
199 *size = SkScalarCeilToInt(circleR + 3*sigma); | 207 *size = SkScalarCeilToInt(circleR + 3*sigma); |
200 } | 208 } |
201 } | 209 } |
202 | 210 |
203 // This function creates a profile of a blurred circle. It does this by computin
g a kernel for | 211 // 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 | 212 // half the Gaussian and a matching summed area table. The summed area table is
used to compute |
205 // outward in x from (r, 0) in both directions. There is a step for each directi
on for each entry | 213 // an array of vertical applications of the half kernel to the circle along the
x axis. The table |
206 // in the half kernel. The y contribution at each step is computed from the summ
ed area table using | 214 // of y evaluations has 2 * k + n entries where k is the size of the half kernel
and n is the size |
207 // the height of the circle above the step point. Each y contribution is multipl
ied by the half | 215 // of the profile being computed. Then for each of the n profile entries we walk
out k steps in each |
208 // kernel value corresponding to the step in x. | 216 // horizontal direction multiplying the corresponding y evaluation by the half k
ernel entry and |
| 217 // sum these values to compute the profile entry. |
209 static uint8_t* create_profile(float circleR, float sigma) { | 218 static uint8_t* create_profile(float circleR, float sigma) { |
210 float offset; | 219 float offset; |
211 int numSteps; | 220 int numSteps; |
212 compute_profile_offset_and_size(circleR, sigma, &offset, &numSteps); | 221 compute_profile_offset_and_size(circleR, sigma, &offset, &numSteps); |
213 | 222 |
214 uint8_t* weights = new uint8_t[numSteps]; | 223 uint8_t* weights = new uint8_t[numSteps]; |
215 | 224 |
216 // The full kernel is 6 sigmas wide. | 225 // The full kernel is 6 sigmas wide. |
217 int halfKernelSize = SkScalarCeilToInt(6.0f*sigma); | 226 int halfKernelSize = SkScalarCeilToInt(6.0f*sigma); |
218 // round up to next multiple of 2 and then divide by 2 | 227 // round up to next multiple of 2 and then divide by 2 |
219 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; | 228 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; |
220 SkAutoTArray<float> halfKernel(halfKernelSize); | 229 |
221 SkAutoTArray<float> summedKernel(halfKernelSize); | 230 // 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, | 231 int numYSteps = numSteps + 2 * halfKernelSize; |
223 sigma); | 232 |
| 233 SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps); |
| 234 float* halfKernel = bulkAlloc.get(); |
| 235 float* summedKernel = bulkAlloc.get() + halfKernelSize; |
| 236 float* yEvals = bulkAlloc.get() + 2 * halfKernelSize; |
| 237 make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize,
sigma); |
| 238 |
| 239 float firstX = offset - halfKernelSize + 0.5f; |
| 240 apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summed
Kernel); |
| 241 |
224 for (int i = 0; i < numSteps - 1; ++i) { | 242 for (int i = 0; i < numSteps - 1; ++i) { |
225 weights[i] = eval_at(offset+i, circleR, halfKernel.get(), summedKernel.g
et(), | 243 float evalX = offset + i + 0.5f; |
226 halfKernelSize); | 244 weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals
+ i); |
227 } | 245 } |
228 // Ensure the tail of the Gaussian goes to zero. | 246 // Ensure the tail of the Gaussian goes to zero. |
229 weights[numSteps - 1] = 0; | 247 weights[numSteps - 1] = 0; |
230 return weights; | 248 return weights; |
231 } | 249 } |
232 | 250 |
233 GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture( | 251 GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture( |
234 GrTextureProvide
r* textureProvider, | 252 GrTextureProvide
r* textureProvider, |
235 const SkRect& ci
rcle, | 253 const SkRect& ci
rcle, |
236 float sigma, | 254 float sigma, |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
272 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor); | 290 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor); |
273 | 291 |
274 const GrFragmentProcessor* GrCircleBlurFragmentProcessor::TestCreate(GrProcessor
TestData* d) { | 292 const GrFragmentProcessor* GrCircleBlurFragmentProcessor::TestCreate(GrProcessor
TestData* d) { |
275 SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f); | 293 SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f); |
276 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f); | 294 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f); |
277 SkRect circle = SkRect::MakeWH(wh, wh); | 295 SkRect circle = SkRect::MakeWH(wh, wh); |
278 return GrCircleBlurFragmentProcessor::Create(d->fContext->textureProvider(),
circle, sigma); | 296 return GrCircleBlurFragmentProcessor::Create(d->fContext->textureProvider(),
circle, sigma); |
279 } | 297 } |
280 | 298 |
281 #endif | 299 #endif |
OLD | NEW |