Flutter Engine
The Flutter Engine
GrDistanceFieldGeoProc.cpp
Go to the documentation of this file.
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
9
15#include "src/base/SkRandom.h"
18#include "src/gpu/KeyBuilder.h"
31
32#include <algorithm>
33
34#if !defined(SK_DISABLE_SDF_TEXT)
35
36// Assuming a radius of a little less than the diagonal of the fragment
37#define SK_DistanceFieldAAFactor "0.65"
38
40public:
42 const GrShaderCaps& shaderCaps,
43 const GrGeometryProcessor& geomProc) override {
45
46#ifdef SK_GAMMA_APPLY_TO_A8
47 float distanceAdjust = dfa8gp.fDistanceAdjust;
48 if (distanceAdjust != fDistanceAdjust) {
49 fDistanceAdjust = distanceAdjust;
50 pdman.set1f(fDistanceAdjustUni, distanceAdjust);
51 }
52#endif
53
54 const SkISize& atlasDimensions = dfa8gp.fAtlasDimensions;
55 SkASSERT(SkIsPow2(atlasDimensions.fWidth) && SkIsPow2(atlasDimensions.fHeight));
56
57 if (fAtlasDimensions != atlasDimensions) {
58 pdman.set2f(fAtlasDimensionsInvUniform,
59 1.0f / atlasDimensions.fWidth,
60 1.0f / atlasDimensions.fHeight);
61 fAtlasDimensions = atlasDimensions;
62 }
63 SetTransform(pdman, shaderCaps, fLocalMatrixUniform, dfa8gp.fLocalMatrix, &fLocalMatrix);
64 }
65
66private:
67 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
68 const GrDistanceFieldA8TextGeoProc& dfTexEffect =
69 args.fGeomProc.cast<GrDistanceFieldA8TextGeoProc>();
70 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
71
72 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
73 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
74 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
75
76 // emit attributes
77 varyingHandler->emitAttributes(dfTexEffect);
78
79 const char* atlasDimensionsInvName;
80 fAtlasDimensionsInvUniform = uniformHandler->addUniform(nullptr,
83 "AtlasDimensionsInv",
84 &atlasDimensionsInvName);
85#ifdef SK_GAMMA_APPLY_TO_A8
86 // adjust based on gamma
87 const char* distanceAdjustUniName = nullptr;
88 // width, height, 1/(3*width)
89 fDistanceAdjustUni = uniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
90 SkSLType::kHalf, "DistanceAdjust",
91 &distanceAdjustUniName);
92#endif
93
94 // Setup pass through color
95 fragBuilder->codeAppendf("half4 %s;\n", args.fOutputColor);
96 varyingHandler->addPassThroughAttribute(dfTexEffect.fInColor.asShaderVar(),
97 args.fOutputColor);
98
99 // Setup position
100 gpArgs->fPositionVar = dfTexEffect.fInPosition.asShaderVar();
101 WriteLocalCoord(vertBuilder,
102 uniformHandler,
103 *args.fShaderCaps,
104 gpArgs,
105 gpArgs->fPositionVar,
106 dfTexEffect.fLocalMatrix,
107 &fLocalMatrixUniform);
108
109 // add varyings
110 GrGLSLVarying uv, texIdx, st;
112 dfTexEffect.numTextureSamplers(),
113 dfTexEffect.fInTextureCoords.name(),
114 atlasDimensionsInvName,
115 &uv,
116 &texIdx,
117 &st);
118
119 bool isUniformScale = (dfTexEffect.fFlags & kUniformScale_DistanceFieldEffectMask) ==
121 bool isSimilarity = SkToBool(dfTexEffect.fFlags & kSimilarity_DistanceFieldEffectFlag );
122 bool isGammaCorrect = SkToBool(dfTexEffect.fFlags & kGammaCorrect_DistanceFieldEffectFlag);
123 bool isAliased = SkToBool(dfTexEffect.fFlags & kAliased_DistanceFieldEffectFlag );
124
125 // Use highp to work around aliasing issues
126 fragBuilder->codeAppendf("float2 uv = %s;\n", uv.fsIn());
127 fragBuilder->codeAppend("half4 texColor;");
129 texIdx, "uv", "texColor");
130
131 fragBuilder->codeAppend("half distance = "
133#ifdef SK_GAMMA_APPLY_TO_A8
134 // adjust width based on gamma
135 fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName);
136#endif
137
138 fragBuilder->codeAppend("half afwidth;");
139 if (isUniformScale) {
140 // For uniform scale, we adjust for the effect of the transformation on the distance
141 // by using the length of the gradient of the t coordinate in the y direction.
142 // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space.
143
144 // this gives us a smooth step across approximately one fragment
145 if (args.fShaderCaps->fAvoidDfDxForGradientsWhenPossible) {
146 fragBuilder->codeAppendf(
147 "afwidth = abs(" SK_DistanceFieldAAFactor "*half(dFdy(%s.y)));", st.fsIn());
148 } else {
149 fragBuilder->codeAppendf(
150 "afwidth = abs(" SK_DistanceFieldAAFactor "*half(dFdx(%s.x)));", st.fsIn());
151 }
152 } else if (isSimilarity) {
153 // For similarity transform, we adjust the effect of the transformation on the distance
154 // by using the length of the gradient of the texture coordinates. We use st coordinates
155 // to ensure we're mapping 1:1 from texel space to pixel space.
156 // We use the y gradient because there is a bug in the Mali 400 in the x direction.
157
158 // this gives us a smooth step across approximately one fragment
159 if (args.fShaderCaps->fAvoidDfDxForGradientsWhenPossible) {
160 fragBuilder->codeAppendf("half st_grad_len = length(half2(dFdy(%s)));", st.fsIn());
161 } else {
162 fragBuilder->codeAppendf("half st_grad_len = length(half2(dFdx(%s)));", st.fsIn());
163 }
164 fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);");
165 } else {
166 // For general transforms, to determine the amount of correction we multiply a unit
167 // vector pointing along the SDF gradient direction by the Jacobian of the st coords
168 // (which is the inverse transform for this fragment) and take the length of the result.
169 fragBuilder->codeAppend("half2 dist_grad = half2(dFdx(distance), dFdy(distance));");
170 // the length of the gradient may be 0, so we need to check for this
171 // this also compensates for the Adreno, which likes to drop tiles on division by 0
172 fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);"
173 "if (dg_len2 < 0.0001) {"
174 "dist_grad = half2(0.7071, 0.7071);"
175 "} else {"
176 "dist_grad = dist_grad*half(inversesqrt(dg_len2));"
177 "}");
178
179 fragBuilder->codeAppendf("float2x2 jacobian = float2x2(dFdx(%s), dFdy(%s));",
180 st.fsIn(), st.fsIn());
181 fragBuilder->codeAppend("half2 grad = half2(jacobian * dist_grad);");
182
183 // this gives us a smooth step across approximately one fragment
184 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
185 }
186
187 if (isAliased) {
188 fragBuilder->codeAppend("half val = distance > 0 ? 1.0 : 0.0;");
189 } else if (isGammaCorrect) {
190 // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
191 // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want
192 // distance mapped linearly to coverage, so use a linear step:
193 fragBuilder->codeAppend(
194 "half val = saturate((distance + afwidth) / (2.0 * afwidth));");
195 } else {
196 fragBuilder->codeAppend("half val = smoothstep(-afwidth, afwidth, distance);");
197 }
198
199 fragBuilder->codeAppendf("half4 %s = half4(val);", args.fOutputCoverage);
200 }
201
202private:
203#ifdef SK_GAMMA_APPLY_TO_A8
204 float fDistanceAdjust = -1.f;
205#endif
206 SkISize fAtlasDimensions = {-1, -1};
207 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
208
209 UniformHandle fDistanceAdjustUni;
210 UniformHandle fAtlasDimensionsInvUniform;
211 UniformHandle fLocalMatrixUniform;
212
213 using INHERITED = ProgramImpl;
214};
215
216///////////////////////////////////////////////////////////////////////////////
217
218GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(const GrShaderCaps& caps,
219 const GrSurfaceProxyView* views,
220 int numViews,
222#ifdef SK_GAMMA_APPLY_TO_A8
223 float distanceAdjust,
224#endif
225 uint32_t flags,
226 const SkMatrix& localMatrix)
227 : INHERITED(kGrDistanceFieldA8TextGeoProc_ClassID)
228 , fLocalMatrix(localMatrix)
230#ifdef SK_GAMMA_APPLY_TO_A8
231 , fDistanceAdjust(distanceAdjust)
232#endif
233{
234 SkASSERT(numViews <= kMaxTextures);
236
238 fInPosition = {"inPosition", kFloat3_GrVertexAttribType, SkSLType::kFloat3};
239 } else {
240 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, SkSLType::kFloat2};
241 }
242 fInColor = {"inColor", kUByte4_norm_GrVertexAttribType, SkSLType::kHalf4 };
243 fInTextureCoords = {"inTextureCoords", kUShort2_GrVertexAttribType,
245 this->setVertexAttributesWithImplicitOffsets(&fInPosition, 3);
246
247 if (numViews) {
248 fAtlasDimensions = views[0].proxy()->dimensions();
249 }
250 for (int i = 0; i < numViews; ++i) {
251 const GrSurfaceProxy* proxy = views[i].proxy();
252 SkASSERT(proxy);
253 SkASSERT(proxy->dimensions() == fAtlasDimensions);
254 fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle());
255 }
256 this->setTextureSamplerCnt(numViews);
257}
258
260 int numViews,
262 SkASSERT(numViews <= kMaxTextures);
263 // Just to make sure we don't try to add too many proxies
264 numViews = std::min(numViews, kMaxTextures);
265
266 if (!fTextureSamplers[0].isInitialized()) {
267 fAtlasDimensions = views[0].proxy()->dimensions();
268 }
269
270 for (int i = 0; i < numViews; ++i) {
271 const GrSurfaceProxy* proxy = views[i].proxy();
272 SkASSERT(proxy);
273 SkASSERT(proxy->dimensions() == fAtlasDimensions);
274 if (!fTextureSamplers[i].isInitialized()) {
275 fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle());
276 }
277 }
278 this->setTextureSamplerCnt(numViews);
279}
280
282 skgpu::KeyBuilder* b) const {
283 uint32_t key = 0;
284 key |= fFlags;
285 key |= ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix) << 16;
286 b->add32(key);
287 b->add32(this->numTextureSamplers());
288}
289
290std::unique_ptr<GrGeometryProcessor::ProgramImpl> GrDistanceFieldA8TextGeoProc::makeProgramImpl(
291 const GrShaderCaps&) const {
292 return std::make_unique<Impl>();
293}
294
295///////////////////////////////////////////////////////////////////////////////
296
298
299#if defined(GR_TEST_UTILS)
300GrGeometryProcessor* GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorTestData* d) {
301 auto [view, ct, at] = d->randomAlphaOnlyView();
302
303 GrSamplerState::WrapMode wrapModes[2];
304 GrTest::TestWrapModes(d->fRandom, wrapModes);
305 GrSamplerState samplerState(wrapModes, d->fRandom->nextBool()
308
309 uint32_t flags = 0;
310 flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0;
312 flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
313 }
314 SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
315#ifdef SK_GAMMA_APPLY_TO_A8
316 float lum = d->fRandom->nextF();
317#endif
318 return GrDistanceFieldA8TextGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(),
319 &view, 1,
320 samplerState,
321#ifdef SK_GAMMA_APPLY_TO_A8
322 lum,
323#endif
324 flags, localMatrix);
325}
326#endif
327
328///////////////////////////////////////////////////////////////////////////////
329
331public:
333 const GrShaderCaps& shaderCaps,
334 const GrGeometryProcessor& geomProc) override {
336
337 // We always set the matrix uniform. It's used to transform from from device to local
338 // for the local coord variable.
339 SetTransform(pdman, shaderCaps, fLocalMatrixUniform, dfpgp.fLocalMatrix, &fLocalMatrix);
340
341 const SkISize& atlasDimensions = dfpgp.fAtlasDimensions;
342 SkASSERT(SkIsPow2(atlasDimensions.fWidth) && SkIsPow2(atlasDimensions.fHeight));
343 if (fAtlasDimensions != atlasDimensions) {
344 pdman.set2f(fAtlasDimensionsInvUniform,
345 1.0f / atlasDimensions.fWidth,
346 1.0f / atlasDimensions.fHeight);
347 fAtlasDimensions = atlasDimensions;
348 }
349 }
350
351private:
352 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
353 const GrDistanceFieldPathGeoProc& dfPathEffect =
354 args.fGeomProc.cast<GrDistanceFieldPathGeoProc>();
355
356 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
357
358 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
359 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
360 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
361
362 // emit attributes
363 varyingHandler->emitAttributes(dfPathEffect);
364
365 const char* atlasDimensionsInvName;
366 fAtlasDimensionsInvUniform = uniformHandler->addUniform(nullptr,
369 "AtlasDimensionsInv",
370 &atlasDimensionsInvName);
371
372 GrGLSLVarying uv, texIdx, st;
374 dfPathEffect.numTextureSamplers(),
375 dfPathEffect.fInTextureCoords.name(),
376 atlasDimensionsInvName,
377 &uv,
378 &texIdx,
379 &st);
380
381 // setup pass through color
382 fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
383 varyingHandler->addPassThroughAttribute(dfPathEffect.fInColor.asShaderVar(),
384 args.fOutputColor);
385
386 // Setup position (output position is pass through, local coords are transformed)
387 gpArgs->fPositionVar = dfPathEffect.fInPosition.asShaderVar();
388 WriteLocalCoord(vertBuilder,
389 uniformHandler,
390 *args.fShaderCaps,
391 gpArgs,
392 gpArgs->fPositionVar,
393 dfPathEffect.fLocalMatrix,
394 &fLocalMatrixUniform);
395
396 // Use highp to work around aliasing issues
397 fragBuilder->codeAppendf("float2 uv = %s;", uv.fsIn());
398 fragBuilder->codeAppend("half4 texColor;");
399 append_multitexture_lookup(args, dfPathEffect.numTextureSamplers(), texIdx, "uv",
400 "texColor");
401
402 fragBuilder->codeAppend("half distance = "
404
405 fragBuilder->codeAppend("half afwidth;");
406 bool isUniformScale = (dfPathEffect.fFlags & kUniformScale_DistanceFieldEffectMask) ==
408 bool isSimilarity = SkToBool(dfPathEffect.fFlags & kSimilarity_DistanceFieldEffectFlag );
409 bool isGammaCorrect = SkToBool(dfPathEffect.fFlags & kGammaCorrect_DistanceFieldEffectFlag);
410 if (isUniformScale) {
411 // For uniform scale, we adjust for the effect of the transformation on the distance
412 // by using the length of the gradient of the t coordinate in the y direction.
413 // We use st coordinates to ensure we're mapping 1:1 from texel space to pixel space.
414
415 // this gives us a smooth step across approximately one fragment
416 if (args.fShaderCaps->fAvoidDfDxForGradientsWhenPossible) {
417 fragBuilder->codeAppendf(
418 "afwidth = abs(" SK_DistanceFieldAAFactor "*half(dFdy(%s.y)));", st.fsIn());
419 } else {
420 fragBuilder->codeAppendf(
421 "afwidth = abs(" SK_DistanceFieldAAFactor "*half(dFdx(%s.x)));", st.fsIn());
422 }
423 } else if (isSimilarity) {
424 // For similarity transform, we adjust the effect of the transformation on the distance
425 // by using the length of the gradient of the texture coordinates. We use st coordinates
426 // to ensure we're mapping 1:1 from texel space to pixel space.
427
428 // this gives us a smooth step across approximately one fragment
429 if (args.fShaderCaps->fAvoidDfDxForGradientsWhenPossible) {
430 fragBuilder->codeAppendf("half st_grad_len = half(length(dFdy(%s)));", st.fsIn());
431 } else {
432 fragBuilder->codeAppendf("half st_grad_len = half(length(dFdx(%s)));", st.fsIn());
433 }
434 fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);");
435 } else {
436 // For general transforms, to determine the amount of correction we multiply a unit
437 // vector pointing along the SDF gradient direction by the Jacobian of the st coords
438 // (which is the inverse transform for this fragment) and take the length of the result.
439 fragBuilder->codeAppend("half2 dist_grad = half2(dFdx(distance), "
440 "dFdy(distance));");
441 // the length of the gradient may be 0, so we need to check for this
442 // this also compensates for the Adreno, which likes to drop tiles on division by 0
443 fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);"
444 "if (dg_len2 < 0.0001) {"
445 "dist_grad = half2(0.7071, 0.7071);"
446 "} else {"
447 "dist_grad = dist_grad*half(inversesqrt(dg_len2));"
448 "}");
449
450 fragBuilder->codeAppendf("float2x2 jacobian = float2x2(dFdx(%s), dFdy(%s));",
451 st.fsIn(), st.fsIn());
452 fragBuilder->codeAppend("half2 grad = half2(jacobian * dist_grad);");
453
454 // this gives us a smooth step across approximately one fragment
455 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
456 }
457 // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
458 // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance
459 // mapped linearly to coverage, so use a linear step:
460 if (isGammaCorrect) {
461 fragBuilder->codeAppend(
462 "half val = saturate((distance + afwidth) / (2.0 * afwidth));");
463 } else {
464 fragBuilder->codeAppend("half val = smoothstep(-afwidth, afwidth, distance);");
465 }
466
467 fragBuilder->codeAppendf("half4 %s = half4(val);", args.fOutputCoverage);
468 }
469
470 SkMatrix fLocalMatrix;
471 UniformHandle fLocalMatrixUniform;
472
473 SkISize fAtlasDimensions;
474 UniformHandle fAtlasDimensionsInvUniform;
475
476 using INHERITED = ProgramImpl;
477};
478
479///////////////////////////////////////////////////////////////////////////////
480
481GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc(const GrShaderCaps& caps,
482 const GrSurfaceProxyView* views,
483 int numViews,
485 const SkMatrix& localMatrix,
486 uint32_t flags)
488 , fLocalMatrix(localMatrix)
490 SkASSERT(numViews <= kMaxTextures);
492
493 fInPosition = {"inPosition", kFloat3_GrVertexAttribType, SkSLType::kFloat3};
495 fInTextureCoords = {"inTextureCoords", kUShort2_GrVertexAttribType,
497 this->setVertexAttributesWithImplicitOffsets(&fInPosition, 3);
498
499 if (numViews) {
500 fAtlasDimensions = views[0].proxy()->dimensions();
501 }
502
503 for (int i = 0; i < numViews; ++i) {
504 const GrSurfaceProxy* proxy = views[i].proxy();
505 SkASSERT(proxy);
506 SkASSERT(proxy->dimensions() == fAtlasDimensions);
507 fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle());
508 }
509 this->setTextureSamplerCnt(numViews);
510}
511
513 int numViews,
515 SkASSERT(numViews <= kMaxTextures);
516 // Just to make sure we don't try to add too many proxies
517 numViews = std::min(numViews, kMaxTextures);
518
519 if (!fTextureSamplers[0].isInitialized()) {
520 fAtlasDimensions = views[0].proxy()->dimensions();
521 }
522
523 for (int i = 0; i < numViews; ++i) {
524 const GrSurfaceProxy* proxy = views[i].proxy();
525 SkASSERT(proxy);
526 SkASSERT(proxy->dimensions() == fAtlasDimensions);
527 if (!fTextureSamplers[i].isInitialized()) {
528 fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle());
529 }
530 }
531 this->setTextureSamplerCnt(numViews);
532}
533
535 skgpu::KeyBuilder* b) const {
536 uint32_t key = fFlags;
537 key |= ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix) << 16;
538 key |= fLocalMatrix.hasPerspective() << (16 + ProgramImpl::kMatrixKeyBits);
539 b->add32(key);
540 b->add32(this->numTextureSamplers());
541}
542
543std::unique_ptr<GrGeometryProcessor::ProgramImpl> GrDistanceFieldPathGeoProc::makeProgramImpl(
544 const GrShaderCaps&) const {
545 return std::make_unique<Impl>();
546}
547
548///////////////////////////////////////////////////////////////////////////////
549
551
552#if defined(GR_TEST_UTILS)
553GrGeometryProcessor* GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTestData* d) {
554 auto [view, ct, at] = d->randomAlphaOnlyView();
555
556 GrSamplerState::WrapMode wrapModes[2];
557 GrTest::TestWrapModes(d->fRandom, wrapModes);
558 GrSamplerState samplerState(wrapModes, d->fRandom->nextBool()
561
562 uint32_t flags = 0;
563 flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0;
565 flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
566 }
567 flags |= d->fRandom->nextBool() ? kWideColor_DistanceFieldEffectFlag : 0;
568 SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
569 return GrDistanceFieldPathGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(),
570 &view, 1,
571 samplerState,
572 localMatrix,
573 flags);
574}
575#endif
576
577
578///////////////////////////////////////////////////////////////////////////////
579
581public:
583 const GrShaderCaps& shaderCaps,
584 const GrGeometryProcessor& geomProc) override {
585 SkASSERT(fDistanceAdjustUni.isValid());
586
588 GrDistanceFieldLCDTextGeoProc::DistanceAdjust wa = dflcd.fDistanceAdjust;
589 if (wa != fDistanceAdjust) {
590 pdman.set3f(fDistanceAdjustUni, wa.fR, wa.fG, wa.fB);
591 fDistanceAdjust = wa;
592 }
593
594 const SkISize& atlasDimensions = dflcd.fAtlasDimensions;
595 SkASSERT(SkIsPow2(atlasDimensions.fWidth) && SkIsPow2(atlasDimensions.fHeight));
596 if (fAtlasDimensions != atlasDimensions) {
597 pdman.set2f(fAtlasDimensionsInvUniform,
598 1.0f / atlasDimensions.fWidth,
599 1.0f / atlasDimensions.fHeight);
600 fAtlasDimensions = atlasDimensions;
601 }
602 SetTransform(pdman, shaderCaps, fLocalMatrixUniform, dflcd.fLocalMatrix, &fLocalMatrix);
603 }
604
605private:
606 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
607 const GrDistanceFieldLCDTextGeoProc& dfTexEffect =
608 args.fGeomProc.cast<GrDistanceFieldLCDTextGeoProc>();
609
610 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
611 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
612 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
613
614 // emit attributes
615 varyingHandler->emitAttributes(dfTexEffect);
616
617 const char* atlasDimensionsInvName;
618 fAtlasDimensionsInvUniform = uniformHandler->addUniform(nullptr,
621 "AtlasDimensionsInv",
622 &atlasDimensionsInvName);
623
624 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
625
626 // setup pass through color
627 fragBuilder->codeAppendf("half4 %s;\n", args.fOutputColor);
628 varyingHandler->addPassThroughAttribute(dfTexEffect.fInColor.asShaderVar(),
629 args.fOutputColor);
630
631 // Setup position
632 gpArgs->fPositionVar = dfTexEffect.fInPosition.asShaderVar();
633 WriteLocalCoord(vertBuilder,
634 uniformHandler,
635 *args.fShaderCaps,
636 gpArgs,
637 dfTexEffect.fInPosition.asShaderVar(),
638 dfTexEffect.fLocalMatrix,
639 &fLocalMatrixUniform);
640
641 // set up varyings
642 GrGLSLVarying uv, texIdx, st;
644 dfTexEffect.numTextureSamplers(),
645 dfTexEffect.fInTextureCoords.name(),
646 atlasDimensionsInvName,
647 &uv,
648 &texIdx,
649 &st);
650
652 varyingHandler->addVarying("Delta", &delta);
653 if (dfTexEffect.fFlags & kPortrait_DistanceFieldEffectFlag) {
654 if (dfTexEffect.fFlags & kBGR_DistanceFieldEffectFlag) {
655 vertBuilder->codeAppendf("%s = -%s.y/3.0;", delta.vsOut(), atlasDimensionsInvName);
656 } else {
657 vertBuilder->codeAppendf("%s = %s.y/3.0;", delta.vsOut(), atlasDimensionsInvName);
658 }
659 } else {
660 if (dfTexEffect.fFlags & kBGR_DistanceFieldEffectFlag) {
661 vertBuilder->codeAppendf("%s = -%s.x/3.0;", delta.vsOut(), atlasDimensionsInvName);
662 } else {
663 vertBuilder->codeAppendf("%s = %s.x/3.0;", delta.vsOut(), atlasDimensionsInvName);
664 }
665 }
666
667 // add frag shader code
668 bool isUniformScale = (dfTexEffect.fFlags & kUniformScale_DistanceFieldEffectMask) ==
670 bool isSimilarity = SkToBool(dfTexEffect.fFlags & kSimilarity_DistanceFieldEffectFlag );
671 bool isGammaCorrect = SkToBool(dfTexEffect.fFlags & kGammaCorrect_DistanceFieldEffectFlag);
672
673 // create LCD offset adjusted by inverse of transform
674 // Use highp to work around aliasing issues
675 fragBuilder->codeAppendf("float2 uv = %s;\n", uv.fsIn());
676
677 if (isUniformScale) {
678 if (args.fShaderCaps->fAvoidDfDxForGradientsWhenPossible) {
679 fragBuilder->codeAppendf("half st_grad_len = half(abs(dFdy(%s.y)));", st.fsIn());
680 } else {
681 fragBuilder->codeAppendf("half st_grad_len = half(abs(dFdx(%s.x)));", st.fsIn());
682 }
683 if (dfTexEffect.fFlags & kPortrait_DistanceFieldEffectFlag) {
684 fragBuilder->codeAppendf("half2 offset = half2(0.0, half(st_grad_len*%s));",
685 delta.fsIn());
686 } else {
687 fragBuilder->codeAppendf("half2 offset = half2(half(st_grad_len*%s), 0.0);",
688 delta.fsIn());
689 }
690 } else if (isSimilarity) {
691 // For a similarity matrix with rotation, the gradient will not be aligned
692 // with the texel coordinate axes, so we need to calculate it.
693 if (args.fShaderCaps->fAvoidDfDxForGradientsWhenPossible) {
694 // We use dFdy instead and rotate -90 degrees to get the gradient in the x
695 // direction.
696 fragBuilder->codeAppendf("half2 st_grad = half2(dFdy(%s));", st.fsIn());
697 if (dfTexEffect.fFlags & kPortrait_DistanceFieldEffectFlag) {
698 fragBuilder->codeAppendf("half2 offset = half2(%s)*st_grad;", delta.fsIn());
699 } else {
700 fragBuilder->codeAppendf(
701 "half2 offset = half2(%s*float2(st_grad.y,-st_grad.x));", delta.fsIn());
702 }
703 } else {
704 fragBuilder->codeAppendf("half2 st_grad = half2(dFdx(%s));", st.fsIn());
705 if (dfTexEffect.fFlags & kPortrait_DistanceFieldEffectFlag) {
706 fragBuilder->codeAppendf(
707 "half2 offset = half2(%s*float2(-st_grad.y,st_grad.x));", delta.fsIn());
708 } else {
709 fragBuilder->codeAppendf("half2 offset = half(%s)*st_grad;", delta.fsIn());
710 }
711 }
712 fragBuilder->codeAppend("half st_grad_len = length(st_grad);");
713 } else {
714 fragBuilder->codeAppendf("half2 st = half2(%s);\n", st.fsIn());
715
716 fragBuilder->codeAppend("float2x2 jacobian = float2x2(dFdx(st), dFdy(st));");
717 if (dfTexEffect.fFlags & kPortrait_DistanceFieldEffectFlag) {
718 fragBuilder->codeAppendf("half2 offset = half2(jacobian * half2(0, %s));",
719 delta.fsIn());
720 } else {
721 fragBuilder->codeAppendf("half2 offset = half2(jacobian * half2(%s, 0));",
722 delta.fsIn());
723 }
724 }
725
726 // sample the texture by index
727 fragBuilder->codeAppend("half3 distance;");
729 texIdx, "uv", "offset", "distance");
730 fragBuilder->codeAppend("distance = "
731 "half3(" SK_DistanceFieldMultiplier ")*(distance - half3(" SK_DistanceFieldThreshold"));");
732
733 // adjust width based on gamma
734 const char* distanceAdjustUniName = nullptr;
735 fDistanceAdjustUni = uniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
736 SkSLType::kHalf3, "DistanceAdjust",
737 &distanceAdjustUniName);
738 fragBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName);
739
740 // To be strictly correct, we should compute the anti-aliasing factor separately
741 // for each color component. However, this is only important when using perspective
742 // transformations, and even then using a single factor seems like a reasonable
743 // trade-off between quality and speed.
744 fragBuilder->codeAppend("half afwidth;");
745 if (isSimilarity) {
746 // For similarity transform (uniform scale-only is a subset of this), we adjust for the
747 // effect of the transformation on the distance by using the length of the gradient of
748 // the texture coordinates. We use st coordinates to ensure we're mapping 1:1 from texel
749 // space to pixel space.
750
751 // this gives us a smooth step across approximately one fragment
752 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*st_grad_len;");
753 } else {
754 // For general transforms, to determine the amount of correction we multiply a unit
755 // vector pointing along the SDF gradient direction by the Jacobian of the st coords
756 // (which is the inverse transform for this fragment) and take the length of the result.
757 fragBuilder->codeAppend("half2 dist_grad = half2(dFdx(distance.r), dFdy(distance.r));");
758 // the length of the gradient may be 0, so we need to check for this
759 // this also compensates for the Adreno, which likes to drop tiles on division by 0
760 fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);"
761 "if (dg_len2 < 0.0001) {"
762 "dist_grad = half2(0.7071, 0.7071);"
763 "} else {"
764 "dist_grad = dist_grad*half(inversesqrt(dg_len2));"
765 "}"
766 "half2 grad = half2(jacobian * dist_grad);");
767
768 // this gives us a smooth step across approximately one fragment
769 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);");
770 }
771
772 // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
773 // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance
774 // mapped linearly to coverage, so use a linear step:
775 if (isGammaCorrect) {
776 fragBuilder->codeAppendf("half4 %s = "
777 "half4(saturate((distance + half3(afwidth)) / half3(2.0 * afwidth)), 1.0);",
778 args.fOutputCoverage);
779 } else {
780 fragBuilder->codeAppendf(
781 "half4 %s = half4(smoothstep(half3(-afwidth), half3(afwidth), distance), 1.0);",
782 args.fOutputCoverage);
783 }
784 }
785
786private:
787 DistanceAdjust fDistanceAdjust = DistanceAdjust::Make(1.0f, 1.0f, 1.0f);
788 SkISize fAtlasDimensions = {-1, -1};
789 SkMatrix fLocalMatrix = SkMatrix::InvalidMatrix();
790
791 UniformHandle fDistanceAdjustUni;
792 UniformHandle fAtlasDimensionsInvUniform;
793 UniformHandle fLocalMatrixUniform;
794};
795
796///////////////////////////////////////////////////////////////////////////////
797
798GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc(const GrShaderCaps& caps,
799 const GrSurfaceProxyView* views,
800 int numViews,
802 DistanceAdjust distanceAdjust,
803 uint32_t flags,
804 const SkMatrix& localMatrix)
806 , fLocalMatrix(localMatrix)
807 , fDistanceAdjust(distanceAdjust)
809 SkASSERT(numViews <= kMaxTextures);
811
813 fInPosition = {"inPosition", kFloat3_GrVertexAttribType, SkSLType::kFloat3};
814 } else {
815 fInPosition = {"inPosition", kFloat2_GrVertexAttribType, SkSLType::kFloat2};
816 }
817 fInColor = {"inColor", kUByte4_norm_GrVertexAttribType, SkSLType::kHalf4};
818 fInTextureCoords = {"inTextureCoords", kUShort2_GrVertexAttribType,
820 this->setVertexAttributesWithImplicitOffsets(&fInPosition, 3);
821
822 if (numViews) {
823 fAtlasDimensions = views[0].proxy()->dimensions();
824 }
825
826 for (int i = 0; i < numViews; ++i) {
827 const GrSurfaceProxy* proxy = views[i].proxy();
828 SkASSERT(proxy);
829 SkASSERT(proxy->dimensions() == fAtlasDimensions);
830 fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle());
831 }
832 this->setTextureSamplerCnt(numViews);
833}
834
836 int numViews,
838 SkASSERT(numViews <= kMaxTextures);
839 // Just to make sure we don't try to add too many proxies
840 numViews = std::min(numViews, kMaxTextures);
841
842 if (!fTextureSamplers[0].isInitialized()) {
843 fAtlasDimensions = views[0].proxy()->dimensions();
844 }
845
846 for (int i = 0; i < numViews; ++i) {
847 const GrSurfaceProxy* proxy = views[i].proxy();
848 SkASSERT(proxy);
849 SkASSERT(proxy->dimensions() == fAtlasDimensions);
850 if (!fTextureSamplers[i].isInitialized()) {
851 fTextureSamplers[i].reset(params, proxy->backendFormat(), views[i].swizzle());
852 }
853 }
854 this->setTextureSamplerCnt(numViews);
855}
856
858 skgpu::KeyBuilder* b) const {
859 uint32_t key = 0;
860 key |= ProgramImpl::ComputeMatrixKey(caps, fLocalMatrix);
861 key |= fFlags << 16;
862 b->add32(key);
863 b->add32(this->numTextureSamplers());
864}
865
866std::unique_ptr<GrGeometryProcessor::ProgramImpl> GrDistanceFieldLCDTextGeoProc::makeProgramImpl(
867 const GrShaderCaps&) const {
868 return std::make_unique<Impl>();
869}
870
871///////////////////////////////////////////////////////////////////////////////
872
874
875#if defined(GR_TEST_UTILS)
876GrGeometryProcessor* GrDistanceFieldLCDTextGeoProc::TestCreate(GrProcessorTestData* d) {
877 auto [view, ct, at] = d->randomView();
878
879 GrSamplerState::WrapMode wrapModes[2];
880 GrTest::TestWrapModes(d->fRandom, wrapModes);
881 GrSamplerState samplerState(wrapModes, d->fRandom->nextBool()
884 DistanceAdjust wa = { 0.0f, 0.1f, -0.1f };
886 flags |= d->fRandom->nextBool() ? kSimilarity_DistanceFieldEffectFlag : 0;
888 flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
889 }
890 flags |= d->fRandom->nextBool() ? kBGR_DistanceFieldEffectFlag : 0;
891 SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
892
893 return GrDistanceFieldLCDTextGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(), &view,
894 1, samplerState, wa, flags, localMatrix);
895}
896#endif
897
898#endif // !defined(SK_DISABLE_SDF_TEXT)
static void append_index_uv_varyings(GrGeometryProcessor::ProgramImpl::EmitArgs &args, int numTextureSamplers, const char *inTexCoordsName, const char *atlasDimensionsInvName, GrGLSLVarying *uv, GrGLSLVarying *texIdx, GrGLSLVarying *st)
static void append_multitexture_lookup_lcd(GrGeometryProcessor::ProgramImpl::EmitArgs &args, int numTextureSamplers, const GrGLSLVarying &texIdx, const char *coordName, const char *offsetName, const char *distanceName)
static void append_multitexture_lookup(GrGeometryProcessor::ProgramImpl::EmitArgs &args, int numTextureSamplers, const GrGLSLVarying &texIdx, const char *coordName, const char *colorName)
#define SK_DistanceFieldAAFactor
@ kGammaCorrect_DistanceFieldEffectFlag
@ kWideColor_DistanceFieldEffectFlag
@ kUseLCD_DistanceFieldEffectFlag
@ kPerspective_DistanceFieldEffectFlag
@ kSimilarity_DistanceFieldEffectFlag
@ kBGR_DistanceFieldEffectFlag
@ kScaleOnly_DistanceFieldEffectFlag
@ kAliased_DistanceFieldEffectFlag
@ kUniformScale_DistanceFieldEffectMask
@ kPath_DistanceFieldEffectMask
@ kNonLCD_DistanceFieldEffectMask
@ kPortrait_DistanceFieldEffectFlag
@ kLCD_DistanceFieldEffectMask
GrFragmentProcessor::ProgramImpl ProgramImpl
#define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(...)
@ kVertex_GrShaderFlag
Definition: GrTypesPriv.h:286
@ kFragment_GrShaderFlag
Definition: GrTypesPriv.h:287
@ kFloat2_GrVertexAttribType
Definition: GrTypesPriv.h:314
@ kUShort2_GrVertexAttribType
Definition: GrTypesPriv.h:340
@ kUByte4_norm_GrVertexAttribType
Definition: GrTypesPriv.h:334
@ kFloat3_GrVertexAttribType
Definition: GrTypesPriv.h:315
uint16_t fFlags
Definition: ShapeLayer.cpp:106
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define SK_DistanceFieldMultiplier
#define SK_DistanceFieldThreshold
constexpr bool SkIsPow2(T value)
Definition: SkMath.h:51
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
void setData(const GrGLSLProgramDataManager &pdman, const GrShaderCaps &shaderCaps, const GrGeometryProcessor &geomProc) override
static GrGeometryProcessor * Make(SkArenaAlloc *arena, const GrShaderCaps &caps, const GrSurfaceProxyView *views, int numActiveViews, GrSamplerState params, uint32_t flags, const SkMatrix &localMatrixIfUsesLocalCoords)
void addNewViews(const GrSurfaceProxyView *views, int numViews, GrSamplerState)
void addToKey(const GrShaderCaps &, skgpu::KeyBuilder *) const override
std::unique_ptr< ProgramImpl > makeProgramImpl(const GrShaderCaps &) const override
void setData(const GrGLSLProgramDataManager &pdman, const GrShaderCaps &shaderCaps, const GrGeometryProcessor &geomProc) override
static GrGeometryProcessor * Make(SkArenaAlloc *arena, const GrShaderCaps &caps, const GrSurfaceProxyView *views, int numActiveViews, GrSamplerState params, DistanceAdjust distanceAdjust, uint32_t flags, const SkMatrix &localMatrixIfUsesLocalCoords)
void addNewViews(const GrSurfaceProxyView *, int numActiveViews, GrSamplerState)
std::unique_ptr< ProgramImpl > makeProgramImpl(const GrShaderCaps &) const override
void addToKey(const GrShaderCaps &, skgpu::KeyBuilder *) const override
void setData(const GrGLSLProgramDataManager &pdman, const GrShaderCaps &shaderCaps, const GrGeometryProcessor &geomProc) override
void addNewViews(const GrSurfaceProxyView *, int numActiveViews, GrSamplerState)
void addToKey(const GrShaderCaps &, skgpu::KeyBuilder *) const override
static GrGeometryProcessor * Make(SkArenaAlloc *arena, const GrShaderCaps &caps, const GrSurfaceProxyView *views, int numActiveViews, GrSamplerState params, const SkMatrix &localMatrix, uint32_t flags)
std::unique_ptr< ProgramImpl > makeProgramImpl(const GrShaderCaps &) const override
virtual void set2f(UniformHandle, float, float) const =0
virtual void set3f(UniformHandle, float, float, float) const =0
virtual void set1f(UniformHandle, float v0) const =0
void codeAppend(const char *str)
void codeAppendf(const char format[],...) SK_PRINTF_LIKE(2
UniformHandle addUniform(const GrProcessor *owner, uint32_t visibility, SkSLType type, const char *name, const char **outName=nullptr)
void emitAttributes(const GrGeometryProcessor &)
void addPassThroughAttribute(const GrShaderVar &vsVar, const char *output, Interpolation=Interpolation::kInterpolated)
void addVarying(const char *name, GrGLSLVarying *varying, Interpolation=Interpolation::kInterpolated)
const char * fsIn() const
Definition: GrGLSLVarying.h:68
constexpr const char * name() const
GrGLSLProgramDataManager::UniformHandle UniformHandle
static void WriteLocalCoord(GrGLSLVertexBuilder *, GrGLSLUniformHandler *, const GrShaderCaps &, GrGPArgs *, GrShaderVar localVar, const SkMatrix &localMatrix, UniformHandle *localMatrixUniform)
static uint32_t ComputeMatrixKey(const GrShaderCaps &caps, const SkMatrix &mat)
static void SetTransform(const GrGLSLProgramDataManager &, const GrShaderCaps &, const UniformHandle &uniform, const SkMatrix &matrix, SkMatrix *state=nullptr)
void reset(GrSamplerState, const GrBackendFormat &, const skgpu::Swizzle &)
void setTextureSamplerCnt(int cnt)
static Attribute MakeColorAttribute(const char *name, bool wideColor)
void setVertexAttributesWithImplicitOffsets(const Attribute *attrs, int attrCount)
const T & cast() const
Definition: GrProcessor.h:127
@ kGrDistanceFieldLCDTextGeoProc_ClassID
Definition: GrProcessor.h:57
@ kGrDistanceFieldPathGeoProc_ClassID
Definition: GrProcessor.h:58
skgpu::Swizzle swizzle() const
GrSurfaceProxy * proxy() const
const GrBackendFormat & backendFormat() const
SkISize dimensions() const
bool hasPerspective() const
Definition: SkMatrix.h:312
static const SkMatrix & InvalidMatrix()
Definition: SkMatrix.cpp:1550
const EmbeddedViewParams * params
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
static bool b
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static float lum(float r, float g, float b)
Definition: hsl.cpp:52
static float min(float r, float g, float b)
Definition: hsl.cpp:48
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
Definition: SkSize.h:16
int32_t fHeight
Definition: SkSize.h:18
int32_t fWidth
Definition: SkSize.h:17
bool fIntegerSupport
Definition: SkSLUtil.h:89