Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(381)

Side by Side Diff: src/effects/GrCircleBlurFragmentProcessor.cpp

Issue 1991413002: When building circle blur profile evaluate kernel vertically once per column (Closed) Base URL: https://skia.googlesource.com/skia.git@blursep
Patch Set: Fix float->double warning in msvs Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698