1 | /* |
2 | * Copyright 2013 Google Inc. |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #include "src/core/SkDistanceFieldGen.h" |
9 | #include "src/gpu/GrCaps.h" |
10 | #include "src/gpu/GrShaderCaps.h" |
11 | #include "src/gpu/GrTexture.h" |
12 | #include "src/gpu/effects/GrAtlasedShaderHelpers.h" |
13 | #include "src/gpu/effects/GrDistanceFieldGeoProc.h" |
14 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
15 | #include "src/gpu/glsl/GrGLSLGeometryProcessor.h" |
16 | #include "src/gpu/glsl/GrGLSLProgramDataManager.h" |
17 | #include "src/gpu/glsl/GrGLSLUniformHandler.h" |
18 | #include "src/gpu/glsl/GrGLSLVarying.h" |
19 | #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" |
20 | |
21 | // Assuming a radius of a little less than the diagonal of the fragment |
22 | #define SK_DistanceFieldAAFactor "0.65" |
23 | |
24 | class GrGLDistanceFieldA8TextGeoProc : public GrGLSLGeometryProcessor { |
25 | public: |
26 | GrGLDistanceFieldA8TextGeoProc() = default; |
27 | |
28 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ |
29 | const GrDistanceFieldA8TextGeoProc& dfTexEffect = |
30 | args.fGP.cast<GrDistanceFieldA8TextGeoProc>(); |
31 | GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
32 | |
33 | GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; |
34 | GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; |
35 | GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
36 | |
37 | // emit attributes |
38 | varyingHandler->emitAttributes(dfTexEffect); |
39 | |
40 | const char* atlasDimensionsInvName; |
41 | fAtlasDimensionsInvUniform = uniformHandler->addUniform(nullptr, |
42 | kVertex_GrShaderFlag, |
43 | kFloat2_GrSLType, |
44 | "AtlasDimensionsInv" , |
45 | &atlasDimensionsInvName); |
46 | #ifdef SK_GAMMA_APPLY_TO_A8 |
47 | // adjust based on gamma |
48 | const char* distanceAdjustUniName = nullptr; |
49 | // width, height, 1/(3*width) |
50 | fDistanceAdjustUni = uniformHandler->addUniform(nullptr, kFragment_GrShaderFlag, |
51 | kHalf_GrSLType, "DistanceAdjust" , |
52 | &distanceAdjustUniName); |
53 | #endif |
54 | |
55 | // Setup pass through color |
56 | varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); |
57 | |
58 | // Setup position |
59 | gpArgs->fPositionVar = dfTexEffect.inPosition().asShaderVar(); |
60 | |
61 | // emit transforms |
62 | this->emitTransforms(vertBuilder, |
63 | varyingHandler, |
64 | uniformHandler, |
65 | dfTexEffect.inPosition().asShaderVar(), |
66 | dfTexEffect.localMatrix(), |
67 | args.fFPCoordTransformHandler); |
68 | |
69 | // add varyings |
70 | GrGLSLVarying uv(kFloat2_GrSLType); |
71 | GrSLType texIdxType = args.fShaderCaps->integerSupport() ? kInt_GrSLType : kFloat_GrSLType; |
72 | GrGLSLVarying texIdx(texIdxType); |
73 | GrGLSLVarying st(kFloat2_GrSLType); |
74 | append_index_uv_varyings(args, dfTexEffect.numTextureSamplers(), |
75 | dfTexEffect.inTextureCoords().name(), atlasDimensionsInvName, &uv, |
76 | &texIdx, &st); |
77 | |
78 | bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == |
79 | kUniformScale_DistanceFieldEffectMask; |
80 | bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); |
81 | bool isGammaCorrect = |
82 | SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag); |
83 | bool isAliased = |
84 | SkToBool(dfTexEffect.getFlags() & kAliased_DistanceFieldEffectFlag); |
85 | |
86 | // Use highp to work around aliasing issues |
87 | fragBuilder->codeAppendf("float2 uv = %s;\n" , uv.fsIn()); |
88 | fragBuilder->codeAppend("half4 texColor;" ); |
89 | append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(), |
90 | texIdx, "uv" , "texColor" ); |
91 | |
92 | fragBuilder->codeAppend("half distance = " |
93 | SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");" ); |
94 | #ifdef SK_GAMMA_APPLY_TO_A8 |
95 | // adjust width based on gamma |
96 | fragBuilder->codeAppendf("distance -= %s;" , distanceAdjustUniName); |
97 | #endif |
98 | |
99 | fragBuilder->codeAppend("half afwidth;" ); |
100 | if (isUniformScale) { |
101 | // For uniform scale, we adjust for the effect of the transformation on the distance |
102 | // by using the length of the gradient of the t coordinate in the y direction. |
103 | // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space. |
104 | |
105 | // this gives us a smooth step across approximately one fragment |
106 | #ifdef SK_VULKAN |
107 | fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor |
108 | "*half(dFdx(%s.x)));" , st.fsIn()); |
109 | #else |
110 | // We use the y gradient because there is a bug in the Mali 400 in the x direction. |
111 | fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor |
112 | "*half(dFdy(%s.y)));" , st.fsIn()); |
113 | #endif |
114 | } else if (isSimilarity) { |
115 | // For similarity transform, we adjust the effect of the transformation on the distance |
116 | // by using the length of the gradient of the texture coordinates. We use st coordinates |
117 | // to ensure we're mapping 1:1 from texel space to pixel space. |
118 | // We use the y gradient because there is a bug in the Mali 400 in the x direction. |
119 | |
120 | // this gives us a smooth step across approximately one fragment |
121 | #ifdef SK_VULKAN |
122 | fragBuilder->codeAppendf("half st_grad_len = length(half2(dFdx(%s)));" , st.fsIn()); |
123 | #else |
124 | // We use the y gradient because there is a bug in the Mali 400 in the x direction. |
125 | fragBuilder->codeAppendf("half st_grad_len = length(half2(dFdy(%s)));" , st.fsIn()); |
126 | #endif |
127 | fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);" ); |
128 | } else { |
129 | // For general transforms, to determine the amount of correction we multiply a unit |
130 | // vector pointing along the SDF gradient direction by the Jacobian of the st coords |
131 | // (which is the inverse transform for this fragment) and take the length of the result. |
132 | fragBuilder->codeAppend("half2 dist_grad = half2(float2(dFdx(distance), " |
133 | "dFdy(distance)));" ); |
134 | // the length of the gradient may be 0, so we need to check for this |
135 | // this also compensates for the Adreno, which likes to drop tiles on division by 0 |
136 | fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);" ); |
137 | fragBuilder->codeAppend("if (dg_len2 < 0.0001) {" ); |
138 | fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);" ); |
139 | fragBuilder->codeAppend("} else {" ); |
140 | fragBuilder->codeAppend("dist_grad = dist_grad*half(inversesqrt(dg_len2));" ); |
141 | fragBuilder->codeAppend("}" ); |
142 | |
143 | fragBuilder->codeAppendf("half2 Jdx = half2(dFdx(%s));" , st.fsIn()); |
144 | fragBuilder->codeAppendf("half2 Jdy = half2(dFdy(%s));" , st.fsIn()); |
145 | fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x," ); |
146 | fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);" ); |
147 | |
148 | // this gives us a smooth step across approximately one fragment |
149 | fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);" ); |
150 | } |
151 | |
152 | if (isAliased) { |
153 | fragBuilder->codeAppend("half val = distance > 0 ? 1.0 : 0.0;" ); |
154 | } else if (isGammaCorrect) { |
155 | // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are |
156 | // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want |
157 | // distance mapped linearly to coverage, so use a linear step: |
158 | fragBuilder->codeAppend( |
159 | "half val = saturate((distance + afwidth) / (2.0 * afwidth));" ); |
160 | } else { |
161 | fragBuilder->codeAppend("half val = smoothstep(-afwidth, afwidth, distance);" ); |
162 | } |
163 | |
164 | fragBuilder->codeAppendf("%s = half4(val);" , args.fOutputCoverage); |
165 | } |
166 | |
167 | void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc, |
168 | const CoordTransformRange& transformRange) override { |
169 | const GrDistanceFieldA8TextGeoProc& dfa8gp = proc.cast<GrDistanceFieldA8TextGeoProc>(); |
170 | |
171 | #ifdef SK_GAMMA_APPLY_TO_A8 |
172 | float distanceAdjust = dfa8gp.getDistanceAdjust(); |
173 | if (distanceAdjust != fDistanceAdjust) { |
174 | fDistanceAdjust = distanceAdjust; |
175 | pdman.set1f(fDistanceAdjustUni, distanceAdjust); |
176 | } |
177 | #endif |
178 | |
179 | const SkISize& atlasDimensions = dfa8gp.atlasDimensions(); |
180 | SkASSERT(SkIsPow2(atlasDimensions.fWidth) && SkIsPow2(atlasDimensions.fHeight)); |
181 | |
182 | if (fAtlasDimensions != atlasDimensions) { |
183 | pdman.set2f(fAtlasDimensionsInvUniform, |
184 | 1.0f / atlasDimensions.fWidth, |
185 | 1.0f / atlasDimensions.fHeight); |
186 | fAtlasDimensions = atlasDimensions; |
187 | } |
188 | this->setTransformDataHelper(dfa8gp.localMatrix(), pdman, transformRange); |
189 | } |
190 | |
191 | static inline void GenKey(const GrGeometryProcessor& gp, |
192 | const GrShaderCaps&, |
193 | GrProcessorKeyBuilder* b) { |
194 | const GrDistanceFieldA8TextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldA8TextGeoProc>(); |
195 | uint32_t key = dfTexEffect.getFlags(); |
196 | b->add32(key); |
197 | b->add32(dfTexEffect.numTextureSamplers()); |
198 | } |
199 | |
200 | private: |
201 | #ifdef SK_GAMMA_APPLY_TO_A8 |
202 | float fDistanceAdjust = -1.f; |
203 | UniformHandle fDistanceAdjustUni; |
204 | #endif |
205 | SkISize fAtlasDimensions = {0, 0}; |
206 | UniformHandle fAtlasDimensionsInvUniform; |
207 | |
208 | typedef GrGLSLGeometryProcessor INHERITED; |
209 | }; |
210 | |
211 | /////////////////////////////////////////////////////////////////////////////// |
212 | |
213 | GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(const GrShaderCaps& caps, |
214 | const GrSurfaceProxyView* views, |
215 | int numViews, |
216 | GrSamplerState params, |
217 | #ifdef SK_GAMMA_APPLY_TO_A8 |
218 | float distanceAdjust, |
219 | #endif |
220 | uint32_t flags, |
221 | const SkMatrix& localMatrix) |
222 | : INHERITED(kGrDistanceFieldA8TextGeoProc_ClassID) |
223 | , fLocalMatrix(localMatrix) |
224 | , fFlags(flags & kNonLCD_DistanceFieldEffectMask) |
225 | #ifdef SK_GAMMA_APPLY_TO_A8 |
226 | , fDistanceAdjust(distanceAdjust) |
227 | #endif |
228 | { |
229 | SkASSERT(numViews <= kMaxTextures); |
230 | SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask)); |
231 | |
232 | if (flags & kPerspective_DistanceFieldEffectFlag) { |
233 | fInPosition = {"inPosition" , kFloat3_GrVertexAttribType, kFloat3_GrSLType}; |
234 | } else { |
235 | fInPosition = {"inPosition" , kFloat2_GrVertexAttribType, kFloat2_GrSLType}; |
236 | } |
237 | fInColor = {"inColor" , kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType }; |
238 | fInTextureCoords = {"inTextureCoords" , kUShort2_GrVertexAttribType, |
239 | caps.integerSupport() ? kUShort2_GrSLType : kFloat2_GrSLType}; |
240 | this->setVertexAttributes(&fInPosition, 3); |
241 | |
242 | if (numViews) { |
243 | fAtlasDimensions = views[0].proxy()->dimensions(); |
244 | } |
245 | for (int i = 0; i < numViews; ++i) { |
246 | const GrSurfaceProxy* proxy = views[i].proxy(); |
247 | SkASSERT(proxy); |
248 | SkASSERT(proxy->dimensions() == fAtlasDimensions); |
249 | fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle()); |
250 | } |
251 | this->setTextureSamplerCnt(numViews); |
252 | } |
253 | |
254 | void GrDistanceFieldA8TextGeoProc::addNewViews(const GrSurfaceProxyView* views, |
255 | int numViews, |
256 | GrSamplerState params) { |
257 | SkASSERT(numViews <= kMaxTextures); |
258 | // Just to make sure we don't try to add too many proxies |
259 | numViews = std::min(numViews, kMaxTextures); |
260 | |
261 | if (!fTextureSamplers[0].isInitialized()) { |
262 | fAtlasDimensions = views[0].proxy()->dimensions(); |
263 | } |
264 | |
265 | for (int i = 0; i < numViews; ++i) { |
266 | const GrSurfaceProxy* proxy = views[i].proxy(); |
267 | SkASSERT(proxy); |
268 | SkASSERT(proxy->dimensions() == fAtlasDimensions); |
269 | if (!fTextureSamplers[i].isInitialized()) { |
270 | fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle()); |
271 | } |
272 | } |
273 | this->setTextureSamplerCnt(numViews); |
274 | } |
275 | |
276 | void GrDistanceFieldA8TextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps, |
277 | GrProcessorKeyBuilder* b) const { |
278 | GrGLDistanceFieldA8TextGeoProc::GenKey(*this, caps, b); |
279 | } |
280 | |
281 | GrGLSLPrimitiveProcessor* |
282 | GrDistanceFieldA8TextGeoProc::createGLSLInstance(const GrShaderCaps&) const { |
283 | return new GrGLDistanceFieldA8TextGeoProc(); |
284 | } |
285 | |
286 | /////////////////////////////////////////////////////////////////////////////// |
287 | |
288 | GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldA8TextGeoProc); |
289 | |
290 | #if GR_TEST_UTILS |
291 | GrGeometryProcessor* GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorTestData* d) { |
292 | auto [view, ct, at] = d->randomAlphaOnlyView(); |
293 | |
294 | GrSamplerState::WrapMode wrapModes[2]; |
295 | GrTest::TestWrapModes(d->fRandom, wrapModes); |
296 | GrSamplerState samplerState(wrapModes, d->fRandom->nextBool() |
297 | ? GrSamplerState::Filter::kBilerp |
298 | : GrSamplerState::Filter::kNearest); |
299 | |
300 | uint32_t flags = 0; |
301 | flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0; |
302 | if (flags & kSimilarity_DistanceFieldEffectFlag) { |
303 | flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0; |
304 | } |
305 | SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom); |
306 | #ifdef SK_GAMMA_APPLY_TO_A8 |
307 | float lum = d->fRandom->nextF(); |
308 | #endif |
309 | return GrDistanceFieldA8TextGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(), |
310 | &view, 1, |
311 | samplerState, |
312 | #ifdef SK_GAMMA_APPLY_TO_A8 |
313 | lum, |
314 | #endif |
315 | flags, localMatrix); |
316 | } |
317 | #endif |
318 | |
319 | /////////////////////////////////////////////////////////////////////////////// |
320 | |
321 | class GrGLDistanceFieldPathGeoProc : public GrGLSLGeometryProcessor { |
322 | public: |
323 | GrGLDistanceFieldPathGeoProc() : fMatrix(SkMatrix::InvalidMatrix()), fAtlasDimensions{0,0} {} |
324 | |
325 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ |
326 | const GrDistanceFieldPathGeoProc& dfPathEffect = |
327 | args.fGP.cast<GrDistanceFieldPathGeoProc>(); |
328 | |
329 | GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
330 | |
331 | GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; |
332 | GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; |
333 | GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
334 | |
335 | // emit attributes |
336 | varyingHandler->emitAttributes(dfPathEffect); |
337 | |
338 | const char* atlasDimensionsInvName; |
339 | fAtlasDimensionsInvUniform = uniformHandler->addUniform(nullptr, |
340 | kVertex_GrShaderFlag, |
341 | kFloat2_GrSLType, |
342 | "AtlasDimensionsInv" , |
343 | &atlasDimensionsInvName); |
344 | |
345 | GrGLSLVarying uv(kFloat2_GrSLType); |
346 | GrSLType texIdxType = args.fShaderCaps->integerSupport() ? kInt_GrSLType : kFloat_GrSLType; |
347 | GrGLSLVarying texIdx(texIdxType); |
348 | GrGLSLVarying st(kFloat2_GrSLType); |
349 | append_index_uv_varyings(args, dfPathEffect.numTextureSamplers(), |
350 | dfPathEffect.inTextureCoords().name(), atlasDimensionsInvName, &uv, |
351 | &texIdx, &st); |
352 | |
353 | // setup pass through color |
354 | varyingHandler->addPassThroughAttribute(dfPathEffect.inColor(), args.fOutputColor); |
355 | |
356 | if (dfPathEffect.matrix().hasPerspective()) { |
357 | // Setup position |
358 | this->writeOutputPosition(vertBuilder, |
359 | uniformHandler, |
360 | gpArgs, |
361 | dfPathEffect.inPosition().name(), |
362 | dfPathEffect.matrix(), |
363 | &fMatrixUniform); |
364 | |
365 | // emit transforms |
366 | this->emitTransforms(vertBuilder, |
367 | varyingHandler, |
368 | uniformHandler, |
369 | dfPathEffect.inPosition().asShaderVar(), |
370 | args.fFPCoordTransformHandler); |
371 | } else { |
372 | // Setup position |
373 | this->writeOutputPosition(vertBuilder, gpArgs, dfPathEffect.inPosition().name()); |
374 | |
375 | // emit transforms |
376 | this->emitTransforms(vertBuilder, |
377 | varyingHandler, |
378 | uniformHandler, |
379 | dfPathEffect.inPosition().asShaderVar(), |
380 | dfPathEffect.matrix(), |
381 | args.fFPCoordTransformHandler); |
382 | } |
383 | |
384 | // Use highp to work around aliasing issues |
385 | fragBuilder->codeAppendf("float2 uv = %s;" , uv.fsIn()); |
386 | fragBuilder->codeAppend("half4 texColor;" ); |
387 | append_multitexture_lookup(args, dfPathEffect.numTextureSamplers(), texIdx, "uv" , |
388 | "texColor" ); |
389 | |
390 | fragBuilder->codeAppend("half distance = " |
391 | SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");" ); |
392 | |
393 | fragBuilder->codeAppend("half afwidth;" ); |
394 | bool isUniformScale = (dfPathEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == |
395 | kUniformScale_DistanceFieldEffectMask; |
396 | bool isSimilarity = SkToBool(dfPathEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); |
397 | bool isGammaCorrect = |
398 | SkToBool(dfPathEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag); |
399 | if (isUniformScale) { |
400 | // For uniform scale, we adjust for the effect of the transformation on the distance |
401 | // by using the length of the gradient of the t coordinate in the y direction. |
402 | // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space. |
403 | |
404 | // this gives us a smooth step across approximately one fragment |
405 | #ifdef SK_VULKAN |
406 | fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor |
407 | "*half(dFdx(%s.x)));" , st.fsIn()); |
408 | #else |
409 | // We use the y gradient because there is a bug in the Mali 400 in the x direction. |
410 | fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor |
411 | "*half(dFdy(%s.y)));" , st.fsIn()); |
412 | #endif |
413 | } else if (isSimilarity) { |
414 | // For similarity transform, we adjust the effect of the transformation on the distance |
415 | // by using the length of the gradient of the texture coordinates. We use st coordinates |
416 | // to ensure we're mapping 1:1 from texel space to pixel space. |
417 | |
418 | // this gives us a smooth step across approximately one fragment |
419 | #ifdef SK_VULKAN |
420 | fragBuilder->codeAppendf("half st_grad_len = half(length(dFdx(%s)));" , st.fsIn()); |
421 | #else |
422 | // We use the y gradient because there is a bug in the Mali 400 in the x direction. |
423 | fragBuilder->codeAppendf("half st_grad_len = half(length(dFdy(%s)));" , st.fsIn()); |
424 | #endif |
425 | fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);" ); |
426 | } else { |
427 | // For general transforms, to determine the amount of correction we multiply a unit |
428 | // vector pointing along the SDF gradient direction by the Jacobian of the st coords |
429 | // (which is the inverse transform for this fragment) and take the length of the result. |
430 | fragBuilder->codeAppend("half2 dist_grad = half2(dFdx(distance), " |
431 | "dFdy(distance));" ); |
432 | // the length of the gradient may be 0, so we need to check for this |
433 | // this also compensates for the Adreno, which likes to drop tiles on division by 0 |
434 | fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);" ); |
435 | fragBuilder->codeAppend("if (dg_len2 < 0.0001) {" ); |
436 | fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);" ); |
437 | fragBuilder->codeAppend("} else {" ); |
438 | fragBuilder->codeAppend("dist_grad = dist_grad*half(inversesqrt(dg_len2));" ); |
439 | fragBuilder->codeAppend("}" ); |
440 | |
441 | fragBuilder->codeAppendf("half2 Jdx = half2(dFdx(%s));" , st.fsIn()); |
442 | fragBuilder->codeAppendf("half2 Jdy = half2(dFdy(%s));" , st.fsIn()); |
443 | fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x," ); |
444 | fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);" ); |
445 | |
446 | // this gives us a smooth step across approximately one fragment |
447 | fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);" ); |
448 | } |
449 | // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are |
450 | // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance |
451 | // mapped linearly to coverage, so use a linear step: |
452 | if (isGammaCorrect) { |
453 | fragBuilder->codeAppend( |
454 | "half val = saturate((distance + afwidth) / (2.0 * afwidth));" ); |
455 | } else { |
456 | fragBuilder->codeAppend("half val = smoothstep(-afwidth, afwidth, distance);" ); |
457 | } |
458 | |
459 | fragBuilder->codeAppendf("%s = half4(val);" , args.fOutputCoverage); |
460 | } |
461 | |
462 | void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc, |
463 | const CoordTransformRange& transformRange) override { |
464 | const GrDistanceFieldPathGeoProc& dfpgp = proc.cast<GrDistanceFieldPathGeoProc>(); |
465 | |
466 | if (dfpgp.matrix().hasPerspective() && !SkMatrixPriv::CheapEqual(fMatrix, dfpgp.matrix())) { |
467 | fMatrix = dfpgp.matrix(); |
468 | pdman.setSkMatrix(fMatrixUniform, fMatrix); |
469 | } |
470 | |
471 | const SkISize& atlasDimensions = dfpgp.atlasDimensions(); |
472 | SkASSERT(SkIsPow2(atlasDimensions.fWidth) && SkIsPow2(atlasDimensions.fHeight)); |
473 | if (fAtlasDimensions != atlasDimensions) { |
474 | pdman.set2f(fAtlasDimensionsInvUniform, |
475 | 1.0f / atlasDimensions.fWidth, |
476 | 1.0f / atlasDimensions.fHeight); |
477 | fAtlasDimensions = atlasDimensions; |
478 | } |
479 | |
480 | if (dfpgp.matrix().hasPerspective()) { |
481 | this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange); |
482 | } else { |
483 | this->setTransformDataHelper(dfpgp.matrix(), pdman, transformRange); |
484 | } |
485 | } |
486 | |
487 | static inline void GenKey(const GrGeometryProcessor& gp, |
488 | const GrShaderCaps&, |
489 | GrProcessorKeyBuilder* b) { |
490 | const GrDistanceFieldPathGeoProc& dfTexEffect = gp.cast<GrDistanceFieldPathGeoProc>(); |
491 | |
492 | uint32_t key = dfTexEffect.getFlags(); |
493 | key |= ComputePosKey(dfTexEffect.matrix()) << 16; |
494 | b->add32(key); |
495 | b->add32(dfTexEffect.matrix().hasPerspective()); |
496 | b->add32(dfTexEffect.numTextureSamplers()); |
497 | } |
498 | |
499 | private: |
500 | SkMatrix fMatrix; // view matrix if perspective, local matrix otherwise |
501 | UniformHandle fMatrixUniform; |
502 | |
503 | SkISize fAtlasDimensions; |
504 | UniformHandle fAtlasDimensionsInvUniform; |
505 | |
506 | typedef GrGLSLGeometryProcessor INHERITED; |
507 | }; |
508 | |
509 | /////////////////////////////////////////////////////////////////////////////// |
510 | |
511 | GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc(const GrShaderCaps& caps, |
512 | const SkMatrix& matrix, |
513 | bool wideColor, |
514 | const GrSurfaceProxyView* views, |
515 | int numViews, |
516 | GrSamplerState params, |
517 | uint32_t flags) |
518 | : INHERITED(kGrDistanceFieldPathGeoProc_ClassID) |
519 | , fMatrix(matrix) |
520 | , fFlags(flags & kNonLCD_DistanceFieldEffectMask) { |
521 | SkASSERT(numViews <= kMaxTextures); |
522 | SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask)); |
523 | |
524 | fInPosition = {"inPosition" , kFloat2_GrVertexAttribType, kFloat2_GrSLType}; |
525 | fInColor = MakeColorAttribute("inColor" , wideColor); |
526 | fInTextureCoords = {"inTextureCoords" , kUShort2_GrVertexAttribType, |
527 | caps.integerSupport() ? kUShort2_GrSLType : kFloat2_GrSLType}; |
528 | this->setVertexAttributes(&fInPosition, 3); |
529 | |
530 | if (numViews) { |
531 | fAtlasDimensions = views[0].proxy()->dimensions(); |
532 | } |
533 | |
534 | for (int i = 0; i < numViews; ++i) { |
535 | const GrSurfaceProxy* proxy = views[i].proxy(); |
536 | SkASSERT(proxy); |
537 | SkASSERT(proxy->dimensions() == fAtlasDimensions); |
538 | fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle()); |
539 | } |
540 | this->setTextureSamplerCnt(numViews); |
541 | } |
542 | |
543 | void GrDistanceFieldPathGeoProc::addNewViews(const GrSurfaceProxyView* views, |
544 | int numViews, |
545 | GrSamplerState params) { |
546 | SkASSERT(numViews <= kMaxTextures); |
547 | // Just to make sure we don't try to add too many proxies |
548 | numViews = std::min(numViews, kMaxTextures); |
549 | |
550 | if (!fTextureSamplers[0].isInitialized()) { |
551 | fAtlasDimensions = views[0].proxy()->dimensions(); |
552 | } |
553 | |
554 | for (int i = 0; i < numViews; ++i) { |
555 | const GrSurfaceProxy* proxy = views[i].proxy(); |
556 | SkASSERT(proxy); |
557 | SkASSERT(proxy->dimensions() == fAtlasDimensions); |
558 | if (!fTextureSamplers[i].isInitialized()) { |
559 | fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle()); |
560 | } |
561 | } |
562 | this->setTextureSamplerCnt(numViews); |
563 | } |
564 | |
565 | void GrDistanceFieldPathGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps, |
566 | GrProcessorKeyBuilder* b) const { |
567 | GrGLDistanceFieldPathGeoProc::GenKey(*this, caps, b); |
568 | } |
569 | |
570 | GrGLSLPrimitiveProcessor* |
571 | GrDistanceFieldPathGeoProc::createGLSLInstance(const GrShaderCaps&) const { |
572 | return new GrGLDistanceFieldPathGeoProc(); |
573 | } |
574 | |
575 | /////////////////////////////////////////////////////////////////////////////// |
576 | |
577 | GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldPathGeoProc); |
578 | |
579 | #if GR_TEST_UTILS |
580 | GrGeometryProcessor* GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTestData* d) { |
581 | auto [view, ct, at] = d->randomAlphaOnlyView(); |
582 | |
583 | GrSamplerState::WrapMode wrapModes[2]; |
584 | GrTest::TestWrapModes(d->fRandom, wrapModes); |
585 | GrSamplerState samplerState(wrapModes, d->fRandom->nextBool() |
586 | ? GrSamplerState::Filter::kBilerp |
587 | : GrSamplerState::Filter::kNearest); |
588 | |
589 | uint32_t flags = 0; |
590 | flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0; |
591 | if (flags & kSimilarity_DistanceFieldEffectFlag) { |
592 | flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0; |
593 | } |
594 | |
595 | return GrDistanceFieldPathGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(), |
596 | GrTest::TestMatrix(d->fRandom), |
597 | d->fRandom->nextBool(), |
598 | &view, 1, |
599 | samplerState, |
600 | flags); |
601 | } |
602 | #endif |
603 | |
604 | /////////////////////////////////////////////////////////////////////////////// |
605 | |
606 | class GrGLDistanceFieldLCDTextGeoProc : public GrGLSLGeometryProcessor { |
607 | public: |
608 | GrGLDistanceFieldLCDTextGeoProc() : fAtlasDimensions({0, 0}) { |
609 | fDistanceAdjust = GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(1.0f, 1.0f, 1.0f); |
610 | } |
611 | |
612 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ |
613 | const GrDistanceFieldLCDTextGeoProc& dfTexEffect = |
614 | args.fGP.cast<GrDistanceFieldLCDTextGeoProc>(); |
615 | |
616 | GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; |
617 | GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; |
618 | GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
619 | |
620 | // emit attributes |
621 | varyingHandler->emitAttributes(dfTexEffect); |
622 | |
623 | const char* atlasDimensionsInvName; |
624 | fAtlasDimensionsInvUniform = uniformHandler->addUniform(nullptr, |
625 | kVertex_GrShaderFlag, |
626 | kFloat2_GrSLType, |
627 | "AtlasDimensionsInv" , |
628 | &atlasDimensionsInvName); |
629 | |
630 | GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
631 | |
632 | // setup pass through color |
633 | varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); |
634 | |
635 | // Setup position |
636 | gpArgs->fPositionVar = dfTexEffect.inPosition().asShaderVar(); |
637 | |
638 | // emit transforms |
639 | this->emitTransforms(vertBuilder, |
640 | varyingHandler, |
641 | uniformHandler, |
642 | dfTexEffect.inPosition().asShaderVar(), |
643 | dfTexEffect.localMatrix(), |
644 | args.fFPCoordTransformHandler); |
645 | |
646 | // set up varyings |
647 | GrGLSLVarying uv(kFloat2_GrSLType); |
648 | GrSLType texIdxType = args.fShaderCaps->integerSupport() ? kInt_GrSLType : kFloat_GrSLType; |
649 | GrGLSLVarying texIdx(texIdxType); |
650 | GrGLSLVarying st(kFloat2_GrSLType); |
651 | append_index_uv_varyings(args, dfTexEffect.numTextureSamplers(), |
652 | dfTexEffect.inTextureCoords().name(), atlasDimensionsInvName, &uv, |
653 | &texIdx, &st); |
654 | |
655 | GrGLSLVarying delta(kFloat_GrSLType); |
656 | varyingHandler->addVarying("Delta" , &delta); |
657 | if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) { |
658 | vertBuilder->codeAppendf("%s = -%s.x/3.0;" , delta.vsOut(), atlasDimensionsInvName); |
659 | } else { |
660 | vertBuilder->codeAppendf("%s = %s.x/3.0;" , delta.vsOut(), atlasDimensionsInvName); |
661 | } |
662 | |
663 | // add frag shader code |
664 | bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == |
665 | kUniformScale_DistanceFieldEffectMask; |
666 | bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); |
667 | bool isGammaCorrect = |
668 | SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag); |
669 | |
670 | // create LCD offset adjusted by inverse of transform |
671 | // Use highp to work around aliasing issues |
672 | fragBuilder->codeAppendf("float2 uv = %s;\n" , uv.fsIn()); |
673 | |
674 | if (isUniformScale) { |
675 | #ifdef SK_VULKAN |
676 | fragBuilder->codeAppendf("half st_grad_len = half(abs(dFdx(%s.x)));" , st.fsIn()); |
677 | #else |
678 | // We use the y gradient because there is a bug in the Mali 400 in the x direction. |
679 | fragBuilder->codeAppendf("half st_grad_len = half(abs(dFdy(%s.y)));" , st.fsIn()); |
680 | #endif |
681 | fragBuilder->codeAppendf("half2 offset = half2(half(st_grad_len*%s), 0.0);" , |
682 | delta.fsIn()); |
683 | } else if (isSimilarity) { |
684 | // For a similarity matrix with rotation, the gradient will not be aligned |
685 | // with the texel coordinate axes, so we need to calculate it. |
686 | #ifdef SK_VULKAN |
687 | fragBuilder->codeAppendf("half2 st_grad = half2(dFdx(%s));" , st.fsIn()); |
688 | fragBuilder->codeAppendf("half2 offset = half(%s)*st_grad;" , delta.fsIn()); |
689 | #else |
690 | // We use dFdy because of a Mali 400 bug, and rotate -90 degrees to |
691 | // get the gradient in the x direction. |
692 | fragBuilder->codeAppendf("half2 st_grad = half2(dFdy(%s));" , st.fsIn()); |
693 | fragBuilder->codeAppendf("half2 offset = half2(%s*float2(st_grad.y, -st_grad.x));" , |
694 | delta.fsIn()); |
695 | #endif |
696 | fragBuilder->codeAppend("half st_grad_len = length(st_grad);" ); |
697 | } else { |
698 | fragBuilder->codeAppendf("half2 st = half2(%s);\n" , st.fsIn()); |
699 | |
700 | fragBuilder->codeAppend("half2 Jdx = half2(dFdx(st));" ); |
701 | fragBuilder->codeAppend("half2 Jdy = half2(dFdy(st));" ); |
702 | fragBuilder->codeAppendf("half2 offset = half2(half(%s))*Jdx;" , delta.fsIn()); |
703 | } |
704 | |
705 | // sample the texture by index |
706 | fragBuilder->codeAppend("half4 texColor;" ); |
707 | append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(), |
708 | texIdx, "uv" , "texColor" ); |
709 | |
710 | // green is distance to uv center |
711 | fragBuilder->codeAppend("half3 distance;" ); |
712 | fragBuilder->codeAppend("distance.y = texColor.r;" ); |
713 | // red is distance to left offset |
714 | fragBuilder->codeAppend("half2 uv_adjusted = half2(uv) - offset;" ); |
715 | append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(), |
716 | texIdx, "uv_adjusted" , "texColor" ); |
717 | fragBuilder->codeAppend("distance.x = texColor.r;" ); |
718 | // blue is distance to right offset |
719 | fragBuilder->codeAppend("uv_adjusted = half2(uv) + offset;" ); |
720 | append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(), |
721 | texIdx, "uv_adjusted" , "texColor" ); |
722 | fragBuilder->codeAppend("distance.z = texColor.r;" ); |
723 | |
724 | fragBuilder->codeAppend("distance = " |
725 | "half3(" SK_DistanceFieldMultiplier ")*(distance - half3(" SK_DistanceFieldThreshold"));" ); |
726 | |
727 | // adjust width based on gamma |
728 | const char* distanceAdjustUniName = nullptr; |
729 | fDistanceAdjustUni = uniformHandler->addUniform(nullptr, kFragment_GrShaderFlag, |
730 | kHalf3_GrSLType, "DistanceAdjust" , |
731 | &distanceAdjustUniName); |
732 | fragBuilder->codeAppendf("distance -= %s;" , distanceAdjustUniName); |
733 | |
734 | // To be strictly correct, we should compute the anti-aliasing factor separately |
735 | // for each color component. However, this is only important when using perspective |
736 | // transformations, and even then using a single factor seems like a reasonable |
737 | // trade-off between quality and speed. |
738 | fragBuilder->codeAppend("half afwidth;" ); |
739 | if (isSimilarity) { |
740 | // For similarity transform (uniform scale-only is a subset of this), we adjust for the |
741 | // effect of the transformation on the distance by using the length of the gradient of |
742 | // the texture coordinates. We use st coordinates to ensure we're mapping 1:1 from texel |
743 | // space to pixel space. |
744 | |
745 | // this gives us a smooth step across approximately one fragment |
746 | fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*st_grad_len;" ); |
747 | } else { |
748 | // For general transforms, to determine the amount of correction we multiply a unit |
749 | // vector pointing along the SDF gradient direction by the Jacobian of the st coords |
750 | // (which is the inverse transform for this fragment) and take the length of the result. |
751 | fragBuilder->codeAppend("half2 dist_grad = half2(half(dFdx(distance.r)), " |
752 | "half(dFdy(distance.r)));" ); |
753 | // the length of the gradient may be 0, so we need to check for this |
754 | // this also compensates for the Adreno, which likes to drop tiles on division by 0 |
755 | fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);" ); |
756 | fragBuilder->codeAppend("if (dg_len2 < 0.0001) {" ); |
757 | fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);" ); |
758 | fragBuilder->codeAppend("} else {" ); |
759 | fragBuilder->codeAppend("dist_grad = dist_grad*half(inversesqrt(dg_len2));" ); |
760 | fragBuilder->codeAppend("}" ); |
761 | fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x," ); |
762 | fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);" ); |
763 | |
764 | // this gives us a smooth step across approximately one fragment |
765 | fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);" ); |
766 | } |
767 | |
768 | // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are |
769 | // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance |
770 | // mapped linearly to coverage, so use a linear step: |
771 | if (isGammaCorrect) { |
772 | fragBuilder->codeAppendf("%s = " |
773 | "half4(saturate((distance + half3(afwidth)) / half3(2.0 * afwidth)), 1.0);" , |
774 | args.fOutputCoverage); |
775 | } else { |
776 | fragBuilder->codeAppendf( |
777 | "%s = half4(smoothstep(half3(-afwidth), half3(afwidth), distance), 1.0);" , |
778 | args.fOutputCoverage); |
779 | } |
780 | } |
781 | |
782 | void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& processor, |
783 | const CoordTransformRange& transformRange) override { |
784 | SkASSERT(fDistanceAdjustUni.isValid()); |
785 | |
786 | const GrDistanceFieldLCDTextGeoProc& dflcd = processor.cast<GrDistanceFieldLCDTextGeoProc>(); |
787 | GrDistanceFieldLCDTextGeoProc::DistanceAdjust wa = dflcd.getDistanceAdjust(); |
788 | if (wa != fDistanceAdjust) { |
789 | pdman.set3f(fDistanceAdjustUni, |
790 | wa.fR, |
791 | wa.fG, |
792 | wa.fB); |
793 | fDistanceAdjust = wa; |
794 | } |
795 | |
796 | const SkISize& atlasDimensions = dflcd.atlasDimensions(); |
797 | SkASSERT(SkIsPow2(atlasDimensions.fWidth) && SkIsPow2(atlasDimensions.fHeight)); |
798 | if (fAtlasDimensions != atlasDimensions) { |
799 | pdman.set2f(fAtlasDimensionsInvUniform, |
800 | 1.0f / atlasDimensions.fWidth, |
801 | 1.0f / atlasDimensions.fHeight); |
802 | fAtlasDimensions = atlasDimensions; |
803 | } |
804 | this->setTransformDataHelper(dflcd.localMatrix(), pdman, transformRange); |
805 | } |
806 | |
807 | static inline void GenKey(const GrGeometryProcessor& gp, |
808 | const GrShaderCaps&, |
809 | GrProcessorKeyBuilder* b) { |
810 | const GrDistanceFieldLCDTextGeoProc& dfTexEffect = gp.cast<GrDistanceFieldLCDTextGeoProc>(); |
811 | |
812 | uint32_t key = dfTexEffect.getFlags(); |
813 | b->add32(key); |
814 | b->add32(dfTexEffect.numTextureSamplers()); |
815 | } |
816 | |
817 | private: |
818 | GrDistanceFieldLCDTextGeoProc::DistanceAdjust fDistanceAdjust; |
819 | UniformHandle fDistanceAdjustUni; |
820 | |
821 | SkISize fAtlasDimensions; |
822 | UniformHandle fAtlasDimensionsInvUniform; |
823 | |
824 | typedef GrGLSLGeometryProcessor INHERITED; |
825 | }; |
826 | |
827 | /////////////////////////////////////////////////////////////////////////////// |
828 | |
829 | GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc(const GrShaderCaps& caps, |
830 | const GrSurfaceProxyView* views, |
831 | int numViews, |
832 | GrSamplerState params, |
833 | DistanceAdjust distanceAdjust, |
834 | uint32_t flags, |
835 | const SkMatrix& localMatrix) |
836 | : INHERITED(kGrDistanceFieldLCDTextGeoProc_ClassID) |
837 | , fLocalMatrix(localMatrix) |
838 | , fDistanceAdjust(distanceAdjust) |
839 | , fFlags(flags & kLCD_DistanceFieldEffectMask) { |
840 | SkASSERT(numViews <= kMaxTextures); |
841 | SkASSERT(!(flags & ~kLCD_DistanceFieldEffectMask) && (flags & kUseLCD_DistanceFieldEffectFlag)); |
842 | |
843 | if (fFlags & kPerspective_DistanceFieldEffectFlag) { |
844 | fInPosition = {"inPosition" , kFloat3_GrVertexAttribType, kFloat3_GrSLType}; |
845 | } else { |
846 | fInPosition = {"inPosition" , kFloat2_GrVertexAttribType, kFloat2_GrSLType}; |
847 | } |
848 | fInColor = {"inColor" , kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType}; |
849 | fInTextureCoords = {"inTextureCoords" , kUShort2_GrVertexAttribType, |
850 | caps.integerSupport() ? kUShort2_GrSLType : kFloat2_GrSLType}; |
851 | this->setVertexAttributes(&fInPosition, 3); |
852 | |
853 | if (numViews) { |
854 | fAtlasDimensions = views[0].proxy()->dimensions(); |
855 | } |
856 | |
857 | for (int i = 0; i < numViews; ++i) { |
858 | const GrSurfaceProxy* proxy = views[i].proxy(); |
859 | SkASSERT(proxy); |
860 | SkASSERT(proxy->dimensions() == fAtlasDimensions); |
861 | fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle()); |
862 | } |
863 | this->setTextureSamplerCnt(numViews); |
864 | } |
865 | |
866 | void GrDistanceFieldLCDTextGeoProc::addNewViews(const GrSurfaceProxyView* views, |
867 | int numViews, |
868 | GrSamplerState params) { |
869 | SkASSERT(numViews <= kMaxTextures); |
870 | // Just to make sure we don't try to add too many proxies |
871 | numViews = std::min(numViews, kMaxTextures); |
872 | |
873 | if (!fTextureSamplers[0].isInitialized()) { |
874 | fAtlasDimensions = views[0].proxy()->dimensions(); |
875 | } |
876 | |
877 | for (int i = 0; i < numViews; ++i) { |
878 | const GrSurfaceProxy* proxy = views[i].proxy(); |
879 | SkASSERT(proxy); |
880 | SkASSERT(proxy->dimensions() == fAtlasDimensions); |
881 | if (!fTextureSamplers[i].isInitialized()) { |
882 | fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle()); |
883 | } |
884 | } |
885 | this->setTextureSamplerCnt(numViews); |
886 | } |
887 | |
888 | void GrDistanceFieldLCDTextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps, |
889 | GrProcessorKeyBuilder* b) const { |
890 | GrGLDistanceFieldLCDTextGeoProc::GenKey(*this, caps, b); |
891 | } |
892 | |
893 | GrGLSLPrimitiveProcessor* GrDistanceFieldLCDTextGeoProc::createGLSLInstance(const GrShaderCaps&) const { |
894 | return new GrGLDistanceFieldLCDTextGeoProc(); |
895 | } |
896 | |
897 | /////////////////////////////////////////////////////////////////////////////// |
898 | |
899 | GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldLCDTextGeoProc); |
900 | |
901 | #if GR_TEST_UTILS |
902 | GrGeometryProcessor* GrDistanceFieldLCDTextGeoProc::TestCreate(GrProcessorTestData* d) { |
903 | auto [view, ct, at] = d->randomView(); |
904 | |
905 | GrSamplerState::WrapMode wrapModes[2]; |
906 | GrTest::TestWrapModes(d->fRandom, wrapModes); |
907 | GrSamplerState samplerState(wrapModes, d->fRandom->nextBool() |
908 | ? GrSamplerState::Filter::kBilerp |
909 | : GrSamplerState::Filter::kNearest); |
910 | DistanceAdjust wa = { 0.0f, 0.1f, -0.1f }; |
911 | uint32_t flags = kUseLCD_DistanceFieldEffectFlag; |
912 | flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0; |
913 | if (flags & kSimilarity_DistanceFieldEffectFlag) { |
914 | flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0; |
915 | } |
916 | flags |= d->fRandom->nextBool() ? kBGR_DistanceFieldEffectFlag : 0; |
917 | SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom); |
918 | |
919 | return GrDistanceFieldLCDTextGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(), &view, |
920 | 1, samplerState, wa, flags, localMatrix); |
921 | } |
922 | #endif |
923 | |