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 |
11 | 11 |
12 #include "GrContext.h" | 12 #include "GrContext.h" |
13 #include "GrInvariantOutput.h" | 13 #include "GrInvariantOutput.h" |
14 #include "GrTextureProvider.h" | 14 #include "GrTextureProvider.h" |
15 | 15 |
16 #include "glsl/GrGLSLFragmentProcessor.h" | 16 #include "glsl/GrGLSLFragmentProcessor.h" |
17 #include "glsl/GrGLSLFragmentShaderBuilder.h" | 17 #include "glsl/GrGLSLFragmentShaderBuilder.h" |
18 #include "glsl/GrGLSLProgramDataManager.h" | 18 #include "glsl/GrGLSLProgramDataManager.h" |
19 #include "glsl/GrGLSLUniformHandler.h" | 19 #include "glsl/GrGLSLUniformHandler.h" |
20 | 20 |
21 #include "SkFixed.h" | 21 #include "SkFixed.h" |
22 | 22 |
23 class GrGLCircleBlurFragmentProcessor : public GrGLSLFragmentProcessor { | 23 class GrCircleBlurFragmentProcessor::GLSLProcessor : public GrGLSLFragmentProces sor { |
24 public: | 24 public: |
25 void emitCode(EmitArgs&) override; | 25 void emitCode(EmitArgs&) override; |
26 | 26 |
27 protected: | 27 protected: |
28 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override ; | 28 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override ; |
29 | 29 |
30 private: | 30 private: |
31 GrGLSLProgramDataManager::UniformHandle fDataUniform; | 31 GrGLSLProgramDataManager::UniformHandle fDataUniform; |
32 | 32 |
33 typedef GrGLSLFragmentProcessor INHERITED; | 33 typedef GrGLSLFragmentProcessor INHERITED; |
34 }; | 34 }; |
35 | 35 |
36 void GrGLCircleBlurFragmentProcessor::emitCode(EmitArgs& args) { | 36 void GrCircleBlurFragmentProcessor::GLSLProcessor::emitCode(EmitArgs& args) { |
37 | 37 |
38 const char *dataName; | 38 const char *dataName; |
39 | 39 |
40 // The data is formatted as: | 40 // The data is formatted as: |
41 // x,y - the center of the circle | 41 // x,y - the center of the circle |
42 // z - the distance at which the intensity starts falling off (e.g., the start of the table) | 42 // z - the distance at which the intensity starts falling off (e.g., the start of the table) |
43 // w - the inverse of the profile texture size | 43 // w - the inverse of the distance over which the texture is stretched. |
44 fDataUniform = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, | 44 fDataUniform = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, |
45 kVec4f_GrSLType, | 45 kVec4f_GrSLType, |
46 kDefault_GrSLPrecision, | 46 kDefault_GrSLPrecision, |
47 "data", | 47 "data", |
48 &dataName); | 48 &dataName); |
49 | 49 |
50 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; | 50 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
51 const char *fragmentPos = fragBuilder->fragmentPosition(); | 51 const char *fragmentPos = fragBuilder->fragmentPosition(); |
52 | 52 |
53 if (args.fInputColor) { | 53 if (args.fInputColor) { |
(...skipping 10 matching lines...) Expand all Loading... | |
64 fragBuilder->codeAppendf("float dist = length(vec) + ( 0.5 - %s.z ) * %s.w;" , | 64 fragBuilder->codeAppendf("float dist = length(vec) + ( 0.5 - %s.z ) * %s.w;" , |
65 dataName, dataName); | 65 dataName, dataName); |
66 | 66 |
67 fragBuilder->codeAppendf("float intensity = "); | 67 fragBuilder->codeAppendf("float intensity = "); |
68 fragBuilder->appendTextureLookup(args.fTexSamplers[0], "vec2(dist, 0.5)"); | 68 fragBuilder->appendTextureLookup(args.fTexSamplers[0], "vec2(dist, 0.5)"); |
69 fragBuilder->codeAppend(".a;"); | 69 fragBuilder->codeAppend(".a;"); |
70 | 70 |
71 fragBuilder->codeAppendf("%s = src * intensity;\n", args.fOutputColor ); | 71 fragBuilder->codeAppendf("%s = src * intensity;\n", args.fOutputColor ); |
72 } | 72 } |
73 | 73 |
74 void GrGLCircleBlurFragmentProcessor::onSetData(const GrGLSLProgramDataManager& pdman, | 74 void GrCircleBlurFragmentProcessor::GLSLProcessor::onSetData(const GrGLSLProgram DataManager& pdman, |
75 const GrProcessor& proc) { | 75 const GrProcessor& proc) { |
76 const GrCircleBlurFragmentProcessor& cbfp = proc.cast<GrCircleBlurFragmentPr ocessor>(); | 76 const GrCircleBlurFragmentProcessor& cbfp = proc.cast<GrCircleBlurFragmentPr ocessor>(); |
77 const SkRect& circle = cbfp.circle(); | 77 const SkRect& circle = cbfp.fCircle; |
78 | 78 |
79 // The data is formatted as: | 79 // The data is formatted as: |
80 // x,y - the center of the circle | 80 // x,y - the center of the circle |
81 // z - the distance at which the intensity starts falling off (e.g., the start of the table) | 81 // z - the distance at which the intensity starts falling off (e.g., the start of the table) |
82 // w - the inverse of the profile texture size | 82 // w - the inverse of the distance over which the profile texture is stre tched. |
83 pdman.set4f(fDataUniform, circle.centerX(), circle.centerY(), cbfp.offset(), | 83 pdman.set4f(fDataUniform, circle.centerX(), circle.centerY(), cbfp.fSolidRad ius, |
84 1.0f / cbfp.profileSize()); | 84 1.f / cbfp.fTextureRadius); |
85 } | 85 } |
86 | 86 |
87 /////////////////////////////////////////////////////////////////////////////// | 87 /////////////////////////////////////////////////////////////////////////////// |
88 | 88 |
89 GrCircleBlurFragmentProcessor::GrCircleBlurFragmentProcessor(const SkRect& circl e, | 89 GrCircleBlurFragmentProcessor::GrCircleBlurFragmentProcessor(const SkRect& circl e, |
90 float sigma, | 90 float sigma, |
91 float offset, | 91 float solidRadius, |
92 float textureRadius , | |
92 GrTexture* blurProf ile) | 93 GrTexture* blurProf ile) |
93 : fCircle(circle) | 94 : fCircle(circle) |
94 , fSigma(sigma) | 95 , fSigma(sigma) |
95 , fOffset(offset) | 96 , fSolidRadius(solidRadius) |
97 , fTextureRadius(textureRadius) | |
96 , fBlurProfileAccess(blurProfile, GrTextureParams::kBilerp_FilterMode) { | 98 , fBlurProfileAccess(blurProfile, GrTextureParams::kBilerp_FilterMode) { |
97 this->initClassID<GrCircleBlurFragmentProcessor>(); | 99 this->initClassID<GrCircleBlurFragmentProcessor>(); |
98 this->addTextureAccess(&fBlurProfileAccess); | 100 this->addTextureAccess(&fBlurProfileAccess); |
99 this->setWillReadFragmentPosition(); | 101 this->setWillReadFragmentPosition(); |
100 } | 102 } |
101 | 103 |
102 GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() c onst { | 104 GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() c onst { |
103 return new GrGLCircleBlurFragmentProcessor; | 105 return new GLSLProcessor; |
104 } | 106 } |
105 | 107 |
106 void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrGLSLCaps& caps , | 108 void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrGLSLCaps& caps , |
107 GrProcessorKeyBuilder* b) const { | 109 GrProcessorKeyBuilder* b) const { |
108 GrGLCircleBlurFragmentProcessor::GenKey(*this, caps, b); | 110 return; |
jvanverth1
2016/06/14 18:51:15
Won't this return garbage (whatever was there befo
bsalomon
2016/06/14 19:16:51
This just leaves the key empty (since this process
| |
109 } | 111 } |
110 | 112 |
111 void GrCircleBlurFragmentProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const { | 113 void GrCircleBlurFragmentProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const { |
112 inout->mulByUnknownSingleComponent(); | 114 inout->mulByUnknownSingleComponent(); |
113 } | 115 } |
114 | 116 |
115 // Create a Gaussian half-kernel and a summed area table given a sigma and numbe r of discrete | 117 // Create a Gaussian half-kernel and a summed area table given a sigma and numbe r of discrete |
116 // steps. The half kernel is normalized to sum to 0.5. | 118 // steps. The half kernel is normalized to sum to 0.5. |
117 static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHa lfKernel, | 119 static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHa lfKernel, |
118 int halfKernelSize, float sigma) { | 120 int halfKernelSize, float sigma) { |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
186 continue; | 188 continue; |
187 } | 189 } |
188 float verticalEval = yKernelEvaluations[i + halfKernelSize]; | 190 float verticalEval = yKernelEvaluations[i + halfKernelSize]; |
189 acc += verticalEval * halfKernel[i]; | 191 acc += verticalEval * halfKernel[i]; |
190 } | 192 } |
191 // Since we applied a half kernel in y we multiply acc by 2 (the circle is s ymmetric about the | 193 // Since we applied a half kernel in y we multiply acc by 2 (the circle is s ymmetric about the |
192 // x axis). | 194 // x axis). |
193 return SkUnitScalarClampToByte(2.f * acc); | 195 return SkUnitScalarClampToByte(2.f * acc); |
194 } | 196 } |
195 | 197 |
196 static inline void compute_profile_offset_and_size(float circleR, float sigma, | |
197 float* offset, int* size) { | |
198 if (3*sigma <= circleR) { | |
199 // The circle is bigger than the Gaussian. In this case we know the inte rior of the | |
200 // blurred circle is solid. | |
201 *offset = circleR - 3 * sigma; // This location maps to 0.5f in the weig hts texture. | |
202 // It should always be 255. | |
203 *size = SkScalarCeilToInt(6*sigma); | |
204 } else { | |
205 // The Gaussian is bigger than the circle. | |
206 *offset = 0.0f; | |
207 *size = SkScalarCeilToInt(circleR + 3*sigma); | |
208 } | |
209 } | |
210 | |
211 // This function creates a profile of a blurred circle. It does this by computin g a kernel for | 198 // This function creates a profile of a blurred circle. It does this by computin g a kernel for |
212 // half the Gaussian and a matching summed area table. The summed area table is used to compute | 199 // half the Gaussian and a matching summed area table. The summed area table is used to compute |
213 // an array of vertical applications of the half kernel to the circle along the x axis. The table | 200 // an array of vertical applications of the half kernel to the circle along the x axis. The table |
214 // of y evaluations has 2 * k + n entries where k is the size of the half kernel and n is the size | 201 // of y evaluations has 2 * k + n entries where k is the size of the half kernel and n is the size |
215 // of the profile being computed. Then for each of the n profile entries we walk out k steps in each | 202 // of the profile being computed. Then for each of the n profile entries we walk out k steps in each |
216 // horizontal direction multiplying the corresponding y evaluation by the half k ernel entry and | 203 // horizontal direction multiplying the corresponding y evaluation by the half k ernel entry and |
217 // sum these values to compute the profile entry. | 204 // sum these values to compute the profile entry. |
218 static uint8_t* create_profile(float circleR, float sigma) { | 205 static uint8_t* create_profile(float sigma, float circleR, float offset, int pro fileTextureWidth) { |
219 float offset; | 206 int numSteps = profileTextureWidth; |
220 int numSteps; | 207 // compute_profile_offset_and_size(circleR, sigma, &offset, &numSteps); |
jvanverth1
2016/06/14 18:51:15
Delete this line?
bsalomon
2016/06/14 19:16:51
oops, done.
| |
221 compute_profile_offset_and_size(circleR, sigma, &offset, &numSteps); | |
222 | 208 |
223 uint8_t* weights = new uint8_t[numSteps]; | 209 uint8_t* weights = new uint8_t[numSteps]; |
224 | 210 |
225 // The full kernel is 6 sigmas wide. | 211 // The full kernel is 6 sigmas wide. |
226 int halfKernelSize = SkScalarCeilToInt(6.0f*sigma); | 212 int halfKernelSize = SkScalarCeilToInt(6.0f*sigma); |
227 // round up to next multiple of 2 and then divide by 2 | 213 // round up to next multiple of 2 and then divide by 2 |
228 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; | 214 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1; |
229 | 215 |
230 // Number of x steps at which to apply kernel in y to cover all the profile samples in x. | 216 // Number of x steps at which to apply kernel in y to cover all the profile samples in x. |
231 int numYSteps = numSteps + 2 * halfKernelSize; | 217 int numYSteps = numSteps + 2 * halfKernelSize; |
232 | 218 |
233 SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps); | 219 SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps); |
234 float* halfKernel = bulkAlloc.get(); | 220 float* halfKernel = bulkAlloc.get(); |
235 float* summedKernel = bulkAlloc.get() + halfKernelSize; | 221 float* summedKernel = bulkAlloc.get() + halfKernelSize; |
236 float* yEvals = bulkAlloc.get() + 2 * halfKernelSize; | 222 float* yEvals = bulkAlloc.get() + 2 * halfKernelSize; |
237 make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma); | 223 make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma); |
238 | 224 |
239 float firstX = offset - halfKernelSize + 0.5f; | 225 float firstX = offset - halfKernelSize + 0.5f; |
240 apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summed Kernel); | 226 apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summed Kernel); |
241 | 227 |
242 for (int i = 0; i < numSteps - 1; ++i) { | 228 for (int i = 0; i < numSteps - 1; ++i) { |
243 float evalX = offset + i + 0.5f; | 229 float evalX = offset + i + 0.5f; |
244 weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i); | 230 weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i); |
245 } | 231 } |
246 // Ensure the tail of the Gaussian goes to zero. | 232 // Ensure the tail of the Gaussian goes to zero. |
247 weights[numSteps - 1] = 0; | 233 weights[numSteps - 1] = 0; |
248 return weights; | 234 return weights; |
249 } | 235 } |
250 | 236 |
237 static int next_pow2_16bits(int x) { | |
238 SkASSERT(x > 0); | |
239 SkASSERT(x <= SK_MaxS16); | |
240 x--; | |
241 x |= x >> 1; | |
242 x |= x >> 2; | |
243 x |= x >> 4; | |
244 x |= x >> 8; | |
245 return x + 1; | |
246 } | |
247 | |
251 GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture( | 248 GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture( |
252 GrTextureProvide r* textureProvider, | 249 GrTextureProvide r* textureProvider, |
253 const SkRect& ci rcle, | 250 const SkRect& ci rcle, |
254 float sigma, | 251 float sigma, |
255 float* offset) { | 252 float* solidRadi us, |
253 float* textureRa dius) { | |
256 float circleR = circle.width() / 2.0f; | 254 float circleR = circle.width() / 2.0f; |
255 // Profile textures are cached by the ratio of sigma to circle radius and by the size of the | |
256 // profile texture (binned by powers of 2). | |
257 SkScalar sigmaToCircleRRatio = sigma / circleR; | |
258 // When sigma is really small this becomes a equivalent to convolving a Gaus sian with a half- | |
259 // plane. We could do that simpler computation. However, right now we're jus t using a lower | |
260 // bound off the ratio. Similarly, in the extreme high ratio cases circle be comes a point WRT to | |
261 // the Guassian and the profile texture is a just a Gaussian evaluation. | |
262 sigmaToCircleRRatio = SkTPin(sigmaToCircleRRatio, 0.05f, 8.f); | |
263 // Convert to fixed point for the key. | |
264 SkFixed sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio); | |
265 // We shave off some bits to reduce the number of unique entries. We could p robably shave off | |
266 // more than we do. | |
267 sigmaToCircleRRatioFixed &= ~0xff; | |
268 // From the circle center to solidRadius is all 1s and represented by the le ftmost pixel (with | |
269 // value 255) in the profile texture. If it is zero then there is no solid c enter to the | |
270 // blurred circle. | |
271 if (3*sigma <= circleR) { | |
272 // The circle is bigger than the Gaussian. In this case we know the inte rior of the | |
273 // blurred circle is solid. | |
274 *solidRadius = circleR - 3 * sigma; // This location maps to 0.5f in the weights texture. | |
275 // It should always be 255. | |
276 *textureRadius = SkScalarCeilToInt(6*sigma); | |
277 } else { | |
278 // The Gaussian is bigger than the circle. | |
279 *solidRadius = 0.0f; | |
280 *textureRadius = SkScalarCeilToInt(circleR + 3*sigma); | |
281 } | |
282 int profileTextureWidth = SkScalarCeilToInt(*textureRadius); | |
283 profileTextureWidth = (profileTextureWidth >= 1024) ? 1024 : | |
284 next_pow2_16bits(profileTextureWidth); | |
257 | 285 |
258 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); | 286 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); |
259 GrUniqueKey key; | 287 GrUniqueKey key; |
260 GrUniqueKey::Builder builder(&key, kDomain, 2); | 288 GrUniqueKey::Builder builder(&key, kDomain, 2); |
261 // The profile curve varies with both the sigma of the Gaussian and the size of the | 289 builder[0] = sigmaToCircleRRatioFixed; |
262 // disk. Quantizing to 16.16 should be close enough though. | 290 builder[1] = profileTextureWidth; |
263 builder[0] = SkScalarToFixed(sigma); | |
264 builder[1] = SkScalarToFixed(circleR); | |
265 builder.finish(); | 291 builder.finish(); |
266 | 292 |
267 GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key); | 293 GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key); |
268 | 294 |
269 int profileSize; | |
270 compute_profile_offset_and_size(circleR, sigma, offset, &profileSize); | |
271 | |
272 if (!blurProfile) { | 295 if (!blurProfile) { |
273 | |
274 GrSurfaceDesc texDesc; | 296 GrSurfaceDesc texDesc; |
275 texDesc.fWidth = profileSize; | 297 texDesc.fWidth = profileTextureWidth; |
276 texDesc.fHeight = 1; | 298 texDesc.fHeight = 1; |
277 texDesc.fConfig = kAlpha_8_GrPixelConfig; | 299 texDesc.fConfig = kAlpha_8_GrPixelConfig; |
278 | 300 |
279 SkAutoTDeleteArray<uint8_t> profile(create_profile(circleR, sigma)); | 301 // Rescale params to the size of the texture we're creating. |
302 SkScalar scale = profileTextureWidth / *textureRadius; | |
303 SkAutoTDeleteArray<uint8_t> profile(create_profile(sigma * scale, circle R * scale, | |
304 *solidRadius * scale, | |
305 profileTextureWidth)) ; | |
280 | 306 |
281 blurProfile = textureProvider->createTexture(texDesc, SkBudgeted::kYes, profile.get(), 0); | 307 blurProfile = textureProvider->createTexture(texDesc, SkBudgeted::kYes, profile.get(), 0); |
282 if (blurProfile) { | 308 if (blurProfile) { |
283 textureProvider->assignUniqueKeyToTexture(key, blurProfile); | 309 textureProvider->assignUniqueKeyToTexture(key, blurProfile); |
284 } | 310 } |
285 } | 311 } |
286 | 312 |
287 return blurProfile; | 313 return blurProfile; |
288 } | 314 } |
289 | 315 |
290 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor); | 316 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor); |
291 | 317 |
292 sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(GrProcessor TestData* d) { | 318 sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(GrProcessor TestData* d) { |
293 SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f); | 319 SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f); |
294 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f); | 320 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f); |
295 SkRect circle = SkRect::MakeWH(wh, wh); | 321 SkRect circle = SkRect::MakeWH(wh, wh); |
296 return GrCircleBlurFragmentProcessor::Make(d->fContext->textureProvider(), c ircle, sigma); | 322 return GrCircleBlurFragmentProcessor::Make(d->fContext->textureProvider(), c ircle, sigma); |
297 } | 323 } |
298 | 324 |
299 #endif | 325 #endif |
OLD | NEW |