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

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

Issue 1981923002: Reformat circle blur profile computation and add comments. (Closed) Base URL: https://chromium.googlesource.com/skia.git@blurbench
Patch Set: compute offset always 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 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
108 GrGLCircleBlurFragmentProcessor::GenKey(*this, caps, b); 108 GrGLCircleBlurFragmentProcessor::GenKey(*this, caps, b);
109 } 109 }
110 110
111 void GrCircleBlurFragmentProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const { 111 void GrCircleBlurFragmentProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const {
112 inout->mulByUnknownSingleComponent(); 112 inout->mulByUnknownSingleComponent();
113 } 113 }
114 114
115 // Evaluate an AA circle function centered at the origin with 'radius' at (x,y) 115 // Evaluate an AA circle function centered at the origin with 'radius' at (x,y)
116 static inline float disk(float x, float y, float radius) { 116 static inline float disk(float x, float y, float radius) {
117 float distSq = x*x + y*y; 117 float distSq = x*x + y*y;
118 if (distSq <= (radius-0.5f)*(radius-0.5f)) { 118 if (distSq <= (radius - 0.5f) * (radius - 0.5f)) {
119 return 1.0f; 119 return 1.0f;
120 } else if (distSq >= (radius+0.5f)*(radius+0.5f)) { 120 } else if (distSq >= (radius + 0.5f) * (radius + 0.5f)) {
121 return 0.0f; 121 return 0.0f;
122 } else { 122 } else {
123 float ramp = radius + 0.5f - sqrtf(distSq); 123 float ramp = radius + 0.5f - sqrtf(distSq);
124 SkASSERT(ramp >= 0.0f && ramp <= 1.0f); 124 SkASSERT(ramp >= 0.0f && ramp <= 1.0f);
125 return ramp; 125 return ramp;
126 } 126 }
127 } 127 }
128 128
129 // Create the top half of an even-sized Gaussian kernel 129 // Create the top half of an even-sized Gaussian kernel
130 static void make_half_kernel(float* kernel, int kernelWH, float sigma) { 130 static void make_half_kernel(float* kernel, int kernelWH, float sigma) {
131 SkASSERT(!(kernelWH & 1)); 131 SkASSERT(!(kernelWH & 1));
132 132
133 const float kernelOff = (kernelWH-1)/2.0f; 133 // We treat each cell in the half-kernel as a 1x1 window and evaluate it
134 // at the center. So the evaluations go from -kernelOff to kernelOff in x
135 // and -kernelOff to -.5 in y (since this is a top-half kernel).
136 const float kernelOff = (kernelWH - 1) / 2.0f;
134 137
135 float b = 1.0f / (2.0f * sigma * sigma); 138 float b = 1.0f / (2.0f * sigma * sigma);
136 // omit the scale term since we're just going to renormalize 139 // omit the scale term since we're just going to renormalize
137 140
138 float tot = 0.0f; 141 float tot = 0.0f;
139 for (int y = 0; y < kernelWH/2; ++y) { 142 for (int y = 0; y < kernelWH / 2; ++y) {
140 for (int x = 0; x < kernelWH/2; ++x) { 143 for (int x = 0; x < kernelWH / 2; ++x) {
141 // TODO: use a cheap approximation of the 2D Guassian? 144 // TODO: use a cheap approximation of the 2D Guassian?
142 float x2 = (x-kernelOff) * (x-kernelOff); 145 float x2 = (x - kernelOff) * (x - kernelOff);
143 float y2 = (y-kernelOff) * (y-kernelOff); 146 float y2 = (y - kernelOff) * (y - kernelOff);
144 // The kernel is symmetric so only compute it once for both sides 147 // The kernel is symmetric so only compute it once for both sides
145 kernel[y*kernelWH+(kernelWH-x-1)] = kernel[y*kernelWH+x] = expf(-(x2 + y2) * b); 148 float value = expf(-(x2 + y2) * b);
146 tot += 2.0f * kernel[y*kernelWH+x]; 149 kernel[y * kernelWH + x] = value;
150 kernel[y * kernelWH + (kernelWH - x - 1)] = value;
151 tot += 2.0f * value;
147 } 152 }
148 } 153 }
149 // Still normalize the half kernel to 1.0 (rather than 0.5) so we don't 154 // Normalize the half kernel to 1.0 (rather than 0.5) so we don't have to sc ale by 2.0 after
150 // have to scale by 2.0 after convolution. 155 // convolution.
151 for (int y = 0; y < kernelWH/2; ++y) { 156 for (int y = 0; y < kernelWH / 2; ++y) {
152 for (int x = 0; x < kernelWH; ++x) { 157 for (int x = 0; x < kernelWH; ++x) {
153 kernel[y*kernelWH+x] /= tot; 158 kernel[y * kernelWH + x] /= tot;
154 } 159 }
155 } 160 }
156 } 161 }
157 162
158 // Apply the half-kernel at 't' away from the center of the circle 163 // Apply the half-kernel at 't' away from the center of the circle
159 static uint8_t eval_at(float t, float halfWidth, float* halfKernel, int kernelWH ) { 164 static uint8_t eval_at(float t, float circleR, float* halfKernel, int kernelWH) {
160 SkASSERT(!(kernelWH & 1)); 165 SkASSERT(!(kernelWH & 1));
161 166
162 const float kernelOff = (kernelWH-1)/2.0f;
163
164 float acc = 0; 167 float acc = 0;
165 168
166 for (int y = 0; y < kernelWH/2; ++y) { 169 // We evaluate the kernel application at (x=t, y=0) using halfKernel which r epresents the top
167 if (kernelOff-y > halfWidth+0.5f) { 170 // half of a 2D Guassian kernel. The full kernel is symmetric so evaluating just the upper half
168 // All disk() samples in this row will be 0.0f 171 // is sufficient. The half kernel has been normalized to 1 rather than 0.5 s o there is no need
172 // to double after evaluation.
173
174 // The sample positions relative to (t, 0) match the sampling used to create the half kernel.
175 const float kernelOff = (kernelWH - 1) / 2.0f;
176
177 for (int j = 0; j < kernelWH / 2; ++j) {
178 float y = (kernelOff - j);
179 if (y > circleR + 0.5f) {
180 // The entire row is above the circle.
169 continue; 181 continue;
170 } 182 }
171 183
172 for (int x = 0; x < kernelWH; ++x) { 184 for (int i = 0; i < kernelWH; ++i) {
173 float image = disk(t - kernelOff + x, -kernelOff + y, halfWidth); 185 float x = t - kernelOff + i;
174 float kernel = halfKernel[y*kernelWH+x]; 186 if (x > circleR + 0.5f) {
187 // Stop evaluation once x crosses outside the circle.
188 break;
189 }
190 float image = disk(x, y, circleR);
191 float kernel = halfKernel[j * kernelWH + i];
175 acc += kernel * image; 192 acc += kernel * image;
176 } 193 }
177 } 194 }
178 195
179 return SkUnitScalarClampToByte(acc); 196 return SkUnitScalarClampToByte(acc);
180 } 197 }
181 198
182 static inline void compute_profile_offset_and_size(float halfWH, float sigma, 199 static inline void compute_profile_offset_and_size(float circleR, float sigma,
183 float* offset, int* size) { 200 float* offset, int* size) {
184 201
185 if (3*sigma <= halfWH) { 202 if (3*sigma <= circleR) {
186 // The circle is bigger than the Gaussian. In this case we know the inte rior of the 203 // The circle is bigger than the Gaussian. In this case we know the inte rior of the
187 // blurred circle is solid. 204 // blurred circle is solid.
188 *offset = halfWH - 3 * sigma; // This location maps to 0.5f in the weigh ts texture. 205 *offset = circleR - 3 * sigma; // This location maps to 0.5f in the weig hts texture.
189 // It should always be 255. 206 // It should always be 255.
190 *size = SkScalarCeilToInt(6*sigma); 207 *size = SkScalarCeilToInt(6*sigma);
191 } else { 208 } else {
192 // The Gaussian is bigger than the circle. 209 // The Gaussian is bigger than the circle.
193 *offset = 0.0f; 210 *offset = 0.0f;
194 *size = SkScalarCeilToInt(halfWH + 3*sigma); 211 *size = SkScalarCeilToInt(circleR + 3*sigma);
195 } 212 }
196 } 213 }
197 214
198 static uint8_t* create_profile(float halfWH, float sigma) { 215 static uint8_t* create_profile(float circleR, float sigma) {
199 216
200 int kernelWH = SkScalarCeilToInt(6.0f*sigma); 217 int kernelWH = SkScalarCeilToInt(6.0f*sigma);
201 kernelWH = (kernelWH + 1) & ~1; // make it the next even number up 218 kernelWH = (kernelWH + 1) & ~1; // make it the next even number up
202 219
203 SkAutoTArray<float> halfKernel(kernelWH*kernelWH/2); 220 SkAutoTArray<float> halfKernel(kernelWH * kernelWH / 2);
204 221
205 make_half_kernel(halfKernel.get(), kernelWH, sigma); 222 make_half_kernel(halfKernel.get(), kernelWH, sigma);
206 223
207 float offset; 224 float offset;
208 int numSteps; 225 int numSteps;
209 226
210 compute_profile_offset_and_size(halfWH, sigma, &offset, &numSteps); 227 compute_profile_offset_and_size(circleR, sigma, &offset, &numSteps);
211 228
212 uint8_t* weights = new uint8_t[numSteps]; 229 uint8_t* weights = new uint8_t[numSteps];
213 for (int i = 0; i < numSteps - 1; ++i) { 230 for (int i = 0; i < numSteps - 1; ++i) {
214 weights[i] = eval_at(offset+i, halfWH, halfKernel.get(), kernelWH); 231 weights[i] = eval_at(offset+i, circleR, halfKernel.get(), kernelWH);
215 } 232 }
216 // Ensure the tail of the Gaussian goes to zero. 233 // Ensure the tail of the Gaussian goes to zero.
217 weights[numSteps-1] = 0; 234 weights[numSteps - 1] = 0;
218 235
219 return weights; 236 return weights;
220 } 237 }
221 238
222 GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture( 239 GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture(
223 GrTextureProvide r* textureProvider, 240 GrTextureProvide r* textureProvider,
224 const SkRect& ci rcle, 241 const SkRect& ci rcle,
225 float sigma, 242 float sigma,
226 float* offset) { 243 float* offset) {
227 float halfWH = circle.width() / 2.0f; 244 float circleR = circle.width() / 2.0f;
228
229 int size;
230 compute_profile_offset_and_size(halfWH, sigma, offset, &size);
231
232 GrSurfaceDesc texDesc;
233 texDesc.fWidth = size;
234 texDesc.fHeight = 1;
235 texDesc.fConfig = kAlpha_8_GrPixelConfig;
236 245
237 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); 246 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
238 GrUniqueKey key; 247 GrUniqueKey key;
239 GrUniqueKey::Builder builder(&key, kDomain, 2); 248 GrUniqueKey::Builder builder(&key, kDomain, 2);
240 // The profile curve varies with both the sigma of the Gaussian and the size of the 249 // The profile curve varies with both the sigma of the Gaussian and the size of the
241 // disk. Quantizing to 16.16 should be close enough though. 250 // disk. Quantizing to 16.16 should be close enough though.
242 builder[0] = SkScalarToFixed(sigma); 251 builder[0] = SkScalarToFixed(sigma);
243 builder[1] = SkScalarToFixed(halfWH); 252 builder[1] = SkScalarToFixed(circleR);
244 builder.finish(); 253 builder.finish();
245 254
246 GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key); 255 GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key);
247 256
257 int profileSize;
258 compute_profile_offset_and_size(circleR, sigma, offset, &profileSize);
259
248 if (!blurProfile) { 260 if (!blurProfile) {
249 SkAutoTDeleteArray<uint8_t> profile(create_profile(halfWH, sigma)); 261
262 GrSurfaceDesc texDesc;
263 texDesc.fWidth = profileSize;
264 texDesc.fHeight = 1;
265 texDesc.fConfig = kAlpha_8_GrPixelConfig;
266
267 SkAutoTDeleteArray<uint8_t> profile(create_profile(circleR, sigma));
250 268
251 blurProfile = textureProvider->createTexture(texDesc, SkBudgeted::kYes, profile.get(), 0); 269 blurProfile = textureProvider->createTexture(texDesc, SkBudgeted::kYes, profile.get(), 0);
252 if (blurProfile) { 270 if (blurProfile) {
253 textureProvider->assignUniqueKeyToTexture(key, blurProfile); 271 textureProvider->assignUniqueKeyToTexture(key, blurProfile);
254 } 272 }
255 } 273 }
256 274
257 return blurProfile; 275 return blurProfile;
258 } 276 }
259 277
260 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor); 278 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor);
261 279
262 const GrFragmentProcessor* GrCircleBlurFragmentProcessor::TestCreate(GrProcessor TestData* d) { 280 const GrFragmentProcessor* GrCircleBlurFragmentProcessor::TestCreate(GrProcessor TestData* d) {
263 SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f); 281 SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f);
264 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f); 282 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f);
265 SkRect circle = SkRect::MakeWH(wh, wh); 283 SkRect circle = SkRect::MakeWH(wh, wh);
266 return GrCircleBlurFragmentProcessor::Create(d->fContext->textureProvider(), circle, sigma); 284 return GrCircleBlurFragmentProcessor::Create(d->fContext->textureProvider(), circle, sigma);
267 } 285 }
268 286
269 #endif 287 #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