OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 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 "GrConvolutionEffect.h" | 8 #include "GrConvolutionEffect.h" |
9 #include "glsl/GrGLSLFragmentProcessor.h" | 9 #include "glsl/GrGLSLFragmentProcessor.h" |
10 #include "glsl/GrGLSLFragmentShaderBuilder.h" | 10 #include "glsl/GrGLSLFragmentShaderBuilder.h" |
11 #include "glsl/GrGLSLProgramDataManager.h" | 11 #include "glsl/GrGLSLProgramDataManager.h" |
12 #include "glsl/GrGLSLUniformHandler.h" | 12 #include "glsl/GrGLSLUniformHandler.h" |
13 | 13 |
14 // For brevity | 14 // For brevity |
15 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; | 15 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; |
16 | 16 |
| 17 /** |
| 18 * Base class with shared functionality for GrGLBoundedConvolutionEffect and |
| 19 * GrGLLerpConvolutionEffect. |
| 20 */ |
17 class GrGLConvolutionEffect : public GrGLSLFragmentProcessor { | 21 class GrGLConvolutionEffect : public GrGLSLFragmentProcessor { |
18 public: | 22 public: |
19 void emitCode(EmitArgs&) override; | |
20 | |
21 static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessor
KeyBuilder*); | 23 static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessor
KeyBuilder*); |
22 | 24 |
23 protected: | 25 protected: |
| 26 void getImageIncrement(const GrConvolutionEffect&, float (*)[2]) const; |
| 27 |
| 28 private: |
| 29 typedef GrGLSLFragmentProcessor INHERITED; |
| 30 }; |
| 31 |
| 32 void GrGLConvolutionEffect::GenKey(const GrProcessor& processor, |
| 33 const GrGLSLCaps&, |
| 34 GrProcessorKeyBuilder* b) { |
| 35 const GrConvolutionEffect& ce = processor.cast<GrConvolutionEffect>(); |
| 36 uint32_t key = ce.radius(); |
| 37 key <<= 2; |
| 38 if (ce.useBounds()) { |
| 39 key |= 0x2; |
| 40 key |= GrConvolutionEffect::kY_Direction == ce.direction() ? 0x1 : 0x0; |
| 41 } |
| 42 b->add32(key); |
| 43 } |
| 44 |
| 45 void GrGLConvolutionEffect::getImageIncrement(const GrConvolutionEffect& ce, |
| 46 float (*imageIncrement)[2]) const
{ |
| 47 GrTexture& texture = *ce.texture(0); |
| 48 (*imageIncrement)[0] = (*imageIncrement)[1] = 0; |
| 49 float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f; |
| 50 switch (ce.direction()) { |
| 51 case Gr1DKernelEffect::kX_Direction: |
| 52 (*imageIncrement)[0] = 1.0f / texture.width(); |
| 53 break; |
| 54 case Gr1DKernelEffect::kY_Direction: |
| 55 (*imageIncrement)[1] = ySign / texture.height(); |
| 56 break; |
| 57 default: |
| 58 SkFAIL("Unknown filter direction."); |
| 59 } |
| 60 } |
| 61 |
| 62 /////////////////////////////////////////////////////////////////////////////// |
| 63 |
| 64 /** |
| 65 * Applies a ceolution effect which restricts samples to the provided bounds |
| 66 * using shader logic. |
| 67 */ |
| 68 class GrGLBoundedConvolutionEffect : public GrGLConvolutionEffect { |
| 69 public: |
| 70 virtual void emitCode(EmitArgs&) override; |
| 71 |
24 void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor&) ov
erride; | 72 void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor&) ov
erride; |
25 | 73 |
26 private: | 74 private: |
27 UniformHandle fKernelUni; | 75 UniformHandle fKernelUni; |
28 UniformHandle fImageIncrementUni; | 76 UniformHandle fImageIncrementUni; |
29 UniformHandle fBoundsUni; | 77 UniformHandle fBoundsUni; |
30 | 78 |
31 typedef GrGLSLFragmentProcessor INHERITED; | 79 typedef GrGLConvolutionEffect INHERITED; |
32 }; | 80 }; |
33 | 81 |
34 void GrGLConvolutionEffect::emitCode(EmitArgs& args) { | 82 void GrGLBoundedConvolutionEffect::emitCode(EmitArgs& args) { |
35 const GrConvolutionEffect& ce = args.fFp.cast<GrConvolutionEffect>(); | 83 const GrConvolutionEffect& ce = args.fFp.cast<GrConvolutionEffect>(); |
36 | 84 |
37 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; | 85 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
38 fImageIncrementUni = uniformHandler->addUniform(kFragment_GrShaderFlag, | 86 fImageIncrementUni = |
39 kVec2f_GrSLType, kDefault_Gr
SLPrecision, | 87 uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, |
40 "ImageIncrement"); | 88 kDefault_GrSLPrecision, "ImageIncrement"); |
41 if (ce.useBounds()) { | 89 fBoundsUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLT
ype, |
42 fBoundsUni = uniformHandler->addUniform(kFragment_GrShaderFlag, | 90 kDefault_GrSLPrecision, "Bounds"); |
43 kVec2f_GrSLType, kDefault_GrSLPr
ecision, | |
44 "Bounds"); | |
45 } | |
46 | 91 |
47 int width = Gr1DKernelEffect::WidthFromRadius(ce.radius()); | 92 int width = Gr1DKernelEffect::WidthFromRadius(ce.radius()); |
| 93 fKernelUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag, kFloat_
GrSLType, |
| 94 kDefault_GrSLPrecision, "Kernel", widt
h); |
48 | 95 |
49 int arrayCount = (width + 3) / 4; | 96 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; |
50 SkASSERT(4 * arrayCount >= width); | |
51 | |
52 fKernelUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag, | |
53 kVec4f_GrSLType, kDefault_GrSLP
recision, | |
54 "Kernel", arrayCount); | |
55 | |
56 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; | |
57 SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); | 97 SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); |
58 | 98 |
59 fragBuilder->codeAppendf("%s = vec4(0, 0, 0, 0);", args.fOutputColor); | 99 fragBuilder->codeAppendf("%s = vec4(0, 0, 0, 0);\n", args.fOutputColor); |
60 | 100 |
61 const GrGLSLShaderVar& kernel = uniformHandler->getUniformVariable(fKernelUn
i); | 101 const GrGLSLShaderVar& kernel = uniformHandler->getUniformVariable(fKernelUn
i); |
62 const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni); | 102 const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni); |
63 | 103 |
64 fragBuilder->codeAppendf("vec2 coord = %s - %d.0 * %s;", coords2D.c_str(), c
e.radius(), imgInc); | 104 fragBuilder->codeAppendf("vec2 coord = %s - %d.0 * %s;\n", coords2D.c_str(),
ce.radius(), |
| 105 imgInc); |
65 | 106 |
66 // Manually unroll loop because some drivers don't; yields 20-30% speedup. | 107 // Manually unroll loop because some drivers don't; yields 20-30% speedup. |
67 const char* kVecSuffix[4] = { ".x", ".y", ".z", ".w" }; | |
68 for (int i = 0; i < width; i++) { | 108 for (int i = 0; i < width; i++) { |
69 SkString index; | 109 SkString index; |
70 SkString kernelIndex; | 110 SkString kernelIndex; |
71 index.appendS32(i/4); | 111 index.appendS32(i); |
72 kernel.appendArrayAccess(index.c_str(), &kernelIndex); | 112 kernel.appendArrayAccess(index.c_str(), &kernelIndex); |
73 kernelIndex.append(kVecSuffix[i & 0x3]); | 113 // We used to compute a bool indicating whether we're in bounds or not,
cast it to a |
74 | 114 // float, and then mul weight*texture_sample by the float. However, the
Adreno 430 seems |
75 if (ce.useBounds()) { | 115 // to have a bug that caused corruption. |
76 // We used to compute a bool indicating whether we're in bounds or n
ot, cast it to a | 116 const char* bounds = uniformHandler->getUniformCStr(fBoundsUni); |
77 // float, and then mul weight*texture_sample by the float. However,
the Adreno 430 seems | 117 const char* component = ce.direction() == Gr1DKernelEffect::kY_Direction
? "y" : "x"; |
78 // to have a bug that caused corruption. | 118 fragBuilder->codeAppendf("if (coord.%s >= %s.x && coord.%s <= %s.y) {", |
79 const char* bounds = uniformHandler->getUniformCStr(fBoundsUni); | 119 component, bounds, component, bounds); |
80 const char* component = ce.direction() == Gr1DKernelEffect::kY_Direc
tion ? "y" : "x"; | 120 fragBuilder->codeAppendf("%s += ", args.fOutputColor); |
81 fragBuilder->codeAppendf("if (coord.%s >= %s.x && coord.%s <= %s.y)
{", | |
82 component, bounds, component, bounds); | |
83 } | |
84 fragBuilder->codeAppendf("\t\t%s += ", args.fOutputColor); | |
85 fragBuilder->appendTextureLookup(args.fSamplers[0], "coord"); | 121 fragBuilder->appendTextureLookup(args.fSamplers[0], "coord"); |
86 fragBuilder->codeAppendf(" * %s;\n", kernelIndex.c_str()); | 122 fragBuilder->codeAppendf(" * %s;\n", kernelIndex.c_str()); |
87 if (ce.useBounds()) { | 123 fragBuilder->codeAppend("}"); |
88 fragBuilder->codeAppend("}"); | 124 fragBuilder->codeAppendf("coord += %s;\n", imgInc); |
89 } | |
90 fragBuilder->codeAppendf("\t\tcoord += %s;\n", imgInc); | |
91 } | 125 } |
92 | 126 |
93 SkString modulate; | 127 SkString modulate; |
94 GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); | 128 GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); |
95 fragBuilder->codeAppend(modulate.c_str()); | 129 fragBuilder->codeAppend(modulate.c_str()); |
96 } | 130 } |
97 | 131 |
98 void GrGLConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdman, | 132 void GrGLBoundedConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdm
an, |
99 const GrProcessor& processor) { | 133 const GrProcessor& processor) { |
100 const GrConvolutionEffect& conv = processor.cast<GrConvolutionEffect>(); | 134 const GrConvolutionEffect& ce = processor.cast<GrConvolutionEffect>(); |
101 GrTexture& texture = *conv.texture(0); | |
102 | 135 |
103 float imageIncrement[2] = { 0 }; | 136 // the code we generated was for a specific bounding mode. |
104 float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f; | 137 SkASSERT(ce.useBounds()); |
105 switch (conv.direction()) { | 138 |
106 case Gr1DKernelEffect::kX_Direction: | 139 GrTexture& texture = *ce.texture(0); |
107 imageIncrement[0] = 1.0f / texture.width(); | 140 float imageIncrement[2]; |
108 break; | 141 getImageIncrement(ce, &imageIncrement); |
109 case Gr1DKernelEffect::kY_Direction: | 142 pdman.set2fv(fImageIncrementUni, 1, imageIncrement); |
110 imageIncrement[1] = ySign / texture.height(); | 143 const float* bounds = ce.bounds(); |
111 break; | 144 if (Gr1DKernelEffect::kY_Direction == ce.direction() && |
112 default: | 145 texture.origin() != kTopLeft_GrSurfaceOrigin) { |
113 SkFAIL("Unknown filter direction."); | 146 pdman.set2f(fBoundsUni, 1.0f - bounds[1], 1.0f - bounds[0]); |
| 147 } else { |
| 148 pdman.set2f(fBoundsUni, bounds[0], bounds[1]); |
114 } | 149 } |
115 pdman.set2fv(fImageIncrementUni, 1, imageIncrement); | |
116 if (conv.useBounds()) { | |
117 const float* bounds = conv.bounds(); | |
118 if (Gr1DKernelEffect::kY_Direction == conv.direction() && | |
119 texture.origin() != kTopLeft_GrSurfaceOrigin) { | |
120 pdman.set2f(fBoundsUni, 1.0f - bounds[1], 1.0f - bounds[0]); | |
121 } else { | |
122 pdman.set2f(fBoundsUni, bounds[0], bounds[1]); | |
123 } | |
124 } | |
125 int width = Gr1DKernelEffect::WidthFromRadius(conv.radius()); | |
126 | 150 |
127 int arrayCount = (width + 3) / 4; | 151 int width = Gr1DKernelEffect::WidthFromRadius(ce.radius()); |
128 SkASSERT(4 * arrayCount >= width); | 152 pdman.set1fv(fKernelUni, width, ce.kernel()); |
129 pdman.set4fv(fKernelUni, arrayCount, conv.kernel()); | |
130 } | |
131 | |
132 void GrGLConvolutionEffect::GenKey(const GrProcessor& processor, const GrGLSLCap
s&, | |
133 GrProcessorKeyBuilder* b) { | |
134 const GrConvolutionEffect& conv = processor.cast<GrConvolutionEffect>(); | |
135 uint32_t key = conv.radius(); | |
136 key <<= 2; | |
137 if (conv.useBounds()) { | |
138 key |= 0x2; | |
139 key |= GrConvolutionEffect::kY_Direction == conv.direction() ? 0x1 : 0x0
; | |
140 } | |
141 b->add32(key); | |
142 } | 153 } |
143 | 154 |
144 /////////////////////////////////////////////////////////////////////////////// | 155 /////////////////////////////////////////////////////////////////////////////// |
| 156 |
| 157 /** |
| 158 * Applies a ceolution effect which applies the ceolution using a linear |
| 159 * interpolation optimization to use half as many samples. |
| 160 */ |
| 161 class GrGLLerpConvolutionEffect : public GrGLConvolutionEffect { |
| 162 public: |
| 163 virtual void emitCode(EmitArgs&) override; |
| 164 |
| 165 void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor&) ov
erride; |
| 166 |
| 167 private: |
| 168 int bilerpSampleCount(int width) const; |
| 169 |
| 170 // Bounded uniforms |
| 171 UniformHandle fSampleWeightUni; |
| 172 UniformHandle fSampleOffsetUni; |
| 173 |
| 174 typedef GrGLConvolutionEffect INHERITED; |
| 175 }; |
| 176 |
| 177 void GrGLLerpConvolutionEffect::emitCode(EmitArgs& args) { |
| 178 const GrConvolutionEffect& ce = args.fFp.cast<GrConvolutionEffect>(); |
| 179 |
| 180 int width = Gr1DKernelEffect::WidthFromRadius(ce.radius()); |
| 181 int sampleCount = bilerpSampleCount(width); |
| 182 // We use 2 * sampleCount uniforms. The maximum allowed by PS2.0 is 32, so |
| 183 // ensure we don't exceed this. Note that it is currently impossible to |
| 184 // exceed this as bilerpSampleCount = (kernelWidth + 1) / 2, and kernelWidth |
| 185 // maxes out at 25, resulting in a max sampleCount of 26. |
| 186 SkASSERT(sampleCount < 16); |
| 187 |
| 188 |
| 189 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
| 190 fSampleOffsetUni = |
| 191 uniformHandler->addUniformArray(kFragment_GrShaderFlag, kVec2f_GrSLType, |
| 192 kDefault_GrSLPrecision, "SampleOffset", sampleC
ount); |
| 193 fSampleWeightUni = |
| 194 uniformHandler->addUniformArray(kFragment_GrShaderFlag, kFloat_GrSLType, |
| 195 kDefault_GrSLPrecision, "SampleWeight", sampleC
ount); |
| 196 |
| 197 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; |
| 198 SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); |
| 199 |
| 200 fragBuilder->codeAppendf("%s = vec4(0, 0, 0, 0);\n", args.fOutputColor); |
| 201 |
| 202 const GrGLSLShaderVar& kernel = uniformHandler->getUniformVariable(fSampleWe
ightUni); |
| 203 const GrGLSLShaderVar& imgInc = uniformHandler->getUniformVariable(fSampleOf
fsetUni); |
| 204 |
| 205 fragBuilder->codeAppendf("vec2 coord; \n"); |
| 206 |
| 207 // Manually unroll loop because some drivers don't; yields 20-30% speedup. |
| 208 for (int i = 0; i < sampleCount; i++) { |
| 209 SkString index; |
| 210 SkString weightIndex; |
| 211 SkString offsetIndex; |
| 212 index.appendS32(i); |
| 213 kernel.appendArrayAccess(index.c_str(), &weightIndex); |
| 214 imgInc.appendArrayAccess(index.c_str(), &offsetIndex); |
| 215 fragBuilder->codeAppendf("coord = %s + %s;\n", coords2D.c_str(), offsetI
ndex.c_str()); |
| 216 fragBuilder->codeAppendf("%s += ", args.fOutputColor); |
| 217 fragBuilder->appendTextureLookup(args.fSamplers[0], "coord"); |
| 218 fragBuilder->codeAppendf(" * %s;\n", weightIndex.c_str()); |
| 219 } |
| 220 |
| 221 SkString modulate; |
| 222 GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); |
| 223 fragBuilder->codeAppend(modulate.c_str()); |
| 224 } |
| 225 |
| 226 void GrGLLerpConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdman, |
| 227 const GrProcessor& processor) { |
| 228 const GrConvolutionEffect& ce = processor.cast<GrConvolutionEffect>(); |
| 229 |
| 230 // the code we generated was for a specific kernel radius |
| 231 SkASSERT(ce.radius() == this->radius()); |
| 232 |
| 233 // the code we generated was for a specific bounding mode. |
| 234 SkASSERT(!ce.useBounds()); |
| 235 |
| 236 int width = Gr1DKernelEffect::WidthFromRadius(ce.radius()); |
| 237 int sampleCount = bilerpSampleCount(width); |
| 238 SkAutoTArray<float> imageIncrements(sampleCount * 2); // X and Y floats per
sample. |
| 239 SkAutoTArray<float> kernel(sampleCount); |
| 240 |
| 241 float baseImageIncrement[2]; |
| 242 getImageIncrement(ce, &baseImageIncrement); |
| 243 |
| 244 for (int i = 0; i < sampleCount; i++) { |
| 245 int sampleIndex1 = i * 2; |
| 246 int sampleIndex2 = sampleIndex1 + 1; |
| 247 |
| 248 // If we have an odd number of samples in our filter, the last sample wo
n't use |
| 249 // the linear interpolation optimization (it will be pixel aligned). |
| 250 if (sampleIndex2 >= width) { |
| 251 sampleIndex2 = sampleIndex1; |
| 252 } |
| 253 |
| 254 float kernelWeight1 = ce.kernel()[sampleIndex1]; |
| 255 float kernelWeight2 = ce.kernel()[sampleIndex2]; |
| 256 |
| 257 float totalKernelWeight = |
| 258 (sampleIndex1 == sampleIndex2) ? kernelWeight1 : (kernelWeight1 + ke
rnelWeight2); |
| 259 |
| 260 float sampleRatio = |
| 261 (sampleIndex1 == sampleIndex2) ? 0 : kernelWeight2 / (kernelWeight1
+ kernelWeight2); |
| 262 |
| 263 imageIncrements[i * 2] = (-ce.radius() + i * 2 + sampleRatio) * baseImag
eIncrement[0]; |
| 264 imageIncrements[i * 2 + 1] = |
| 265 (-ce.radius() + i * 2 + sampleRatio) * baseImageIncrement[1]; |
| 266 |
| 267 kernel[i] = totalKernelWeight; |
| 268 } |
| 269 pdman.set2fv(fSampleOffsetUni, sampleCount, imageIncrements.get()); |
| 270 pdman.set1fv(fSampleWeightUni, sampleCount, kernel.get()); |
| 271 } |
| 272 |
| 273 int GrGLLerpConvolutionEffect::bilerpSampleCount(int width) const { |
| 274 // We use a linear interpolation optimization to only sample once for each |
| 275 // two pixel aligned samples in the kernel. If we have an odd number of |
| 276 // samples, we will have to skip this optimization for the last sample. |
| 277 // Because of this we always round up our sample count (by adding 1 before |
| 278 // dividing). |
| 279 return (width + 1) / 2; |
| 280 } |
| 281 |
| 282 /////////////////////////////////////////////////////////////////////////////// |
145 | 283 |
146 GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture, | 284 GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture, |
147 Direction direction, | 285 Direction direction, |
148 int radius, | 286 int radius, |
149 const float* kernel, | 287 const float* kernel, |
150 bool useBounds, | 288 bool useBounds, |
151 float bounds[2]) | 289 float bounds[2]) |
152 : INHERITED(texture, direction, radius), fUseBounds(useBounds) { | 290 : INHERITED(texture, |
| 291 direction, |
| 292 radius, |
| 293 useBounds ? GrTextureParams::FilterMode::kNone_FilterMode |
| 294 : GrTextureParams::FilterMode::kBilerp_FilterMode) |
| 295 , fUseBounds(useBounds) { |
153 this->initClassID<GrConvolutionEffect>(); | 296 this->initClassID<GrConvolutionEffect>(); |
154 SkASSERT(radius <= kMaxKernelRadius); | 297 SkASSERT(radius <= kMaxKernelRadius); |
155 SkASSERT(kernel); | 298 SkASSERT(kernel); |
156 int width = this->width(); | 299 int width = this->width(); |
157 for (int i = 0; i < width; i++) { | 300 for (int i = 0; i < width; i++) { |
158 fKernel[i] = kernel[i]; | 301 fKernel[i] = kernel[i]; |
159 } | 302 } |
160 memcpy(fBounds, bounds, sizeof(fBounds)); | 303 memcpy(fBounds, bounds, sizeof(fBounds)); |
161 } | 304 } |
162 | 305 |
163 GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture, | 306 GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture, |
164 Direction direction, | 307 Direction direction, |
165 int radius, | 308 int radius, |
166 float gaussianSigma, | 309 float gaussianSigma, |
167 bool useBounds, | 310 bool useBounds, |
168 float bounds[2]) | 311 float bounds[2]) |
169 : INHERITED(texture, direction, radius), fUseBounds(useBounds) { | 312 : INHERITED(texture, |
| 313 direction, |
| 314 radius, |
| 315 useBounds ? GrTextureParams::FilterMode::kNone_FilterMode |
| 316 : GrTextureParams::FilterMode::kBilerp_FilterMode) |
| 317 , fUseBounds(useBounds) { |
170 this->initClassID<GrConvolutionEffect>(); | 318 this->initClassID<GrConvolutionEffect>(); |
171 SkASSERT(radius <= kMaxKernelRadius); | 319 SkASSERT(radius <= kMaxKernelRadius); |
172 int width = this->width(); | 320 int width = this->width(); |
173 | 321 |
174 float sum = 0.0f; | 322 float sum = 0.0f; |
175 float denom = 1.0f / (2.0f * gaussianSigma * gaussianSigma); | 323 float denom = 1.0f / (2.0f * gaussianSigma * gaussianSigma); |
176 for (int i = 0; i < width; ++i) { | 324 for (int i = 0; i < width; ++i) { |
177 float x = static_cast<float>(i - this->radius()); | 325 float x = static_cast<float>(i - this->radius()); |
178 // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian | 326 // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian |
179 // is dropped here, since we renormalize the kernel below. | 327 // is dropped here, since we renormalize the kernel below. |
180 fKernel[i] = sk_float_exp(- x * x * denom); | 328 fKernel[i] = sk_float_exp(- x * x * denom); |
181 sum += fKernel[i]; | 329 sum += fKernel[i]; |
182 } | 330 } |
183 // Normalize the kernel | 331 // Normalize the kernel |
184 float scale = 1.0f / sum; | 332 float scale = 1.0f / sum; |
185 for (int i = 0; i < width; ++i) { | 333 for (int i = 0; i < width; ++i) { |
186 fKernel[i] *= scale; | 334 fKernel[i] *= scale; |
187 } | 335 } |
188 memcpy(fBounds, bounds, sizeof(fBounds)); | 336 memcpy(fBounds, bounds, sizeof(fBounds)); |
189 } | 337 } |
190 | 338 |
191 GrConvolutionEffect::~GrConvolutionEffect() { | 339 GrConvolutionEffect::~GrConvolutionEffect() { |
192 } | 340 } |
193 | 341 |
194 void GrConvolutionEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps, | 342 void GrConvolutionEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps, |
195 GrProcessorKeyBuilder* b) const
{ | 343 GrProcessorKeyBuilder* b) const { |
196 GrGLConvolutionEffect::GenKey(*this, caps, b); | 344 GrGLConvolutionEffect::GenKey(*this, caps, b); |
197 } | 345 } |
198 | 346 |
199 GrGLSLFragmentProcessor* GrConvolutionEffect::onCreateGLSLInstance() const { | 347 GrGLSLFragmentProcessor* GrConvolutionEffect::onCreateGLSLInstance() const { |
200 return new GrGLConvolutionEffect; | 348 // We support a linear interpolation optimization which (when feasible) uses |
| 349 // half the number of samples to apply the kernel. This is not always |
| 350 // applicable, as the linear interpolation optimization does not support |
| 351 // bounded sampling. |
| 352 if (this->useBounds()) { |
| 353 return new GrGLBoundedConvolutionEffect; |
| 354 } else { |
| 355 return new GrGLLerpConvolutionEffect; |
| 356 } |
201 } | 357 } |
202 | 358 |
203 bool GrConvolutionEffect::onIsEqual(const GrFragmentProcessor& sBase) const { | 359 bool GrConvolutionEffect::onIsEqual(const GrFragmentProcessor& sBase) const { |
204 const GrConvolutionEffect& s = sBase.cast<GrConvolutionEffect>(); | 360 const GrConvolutionEffect& s = sBase.cast<GrConvolutionEffect>(); |
205 return (this->radius() == s.radius() && | 361 return (this->radius() == s.radius() && |
206 this->direction() == s.direction() && | 362 this->direction() == s.direction() && |
207 this->useBounds() == s.useBounds() && | 363 this->useBounds() == s.useBounds() && |
208 0 == memcmp(fBounds, s.fBounds, sizeof(fBounds)) && | 364 0 == memcmp(fBounds, s.fBounds, sizeof(fBounds)) && |
209 0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float))); | 365 0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float))); |
210 } | 366 } |
(...skipping 17 matching lines...) Expand all Loading... |
228 } | 384 } |
229 | 385 |
230 bool useBounds = d->fRandom->nextBool(); | 386 bool useBounds = d->fRandom->nextBool(); |
231 return GrConvolutionEffect::Create(d->fTextures[texIdx], | 387 return GrConvolutionEffect::Create(d->fTextures[texIdx], |
232 dir, | 388 dir, |
233 radius, | 389 radius, |
234 kernel, | 390 kernel, |
235 useBounds, | 391 useBounds, |
236 bounds); | 392 bounds); |
237 } | 393 } |
OLD | NEW |