1 /*
2 * Copyright 2014 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
9 #include "SkTwoPointConicalGradient.h"
10
11 #if SK_SUPPORT_GPU
12 #include "GrCoordTransform.h"
13 #include "GrPaint.h"
14 #include "glsl/GrGLSLFragmentShaderBuilder.h"
15 #include "glsl/GrGLSLProgramDataManager.h"
16 #include "glsl/GrGLSLUniformHandler.h"
17 #include "SkTwoPointConicalGradient_gpu.h"
18
19 // For brevity
20 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
21
22 static const SkScalar kErrorTol = 0.00001f;
23 static const SkScalar kEdgeErrorTol = 5.f * kErrorTol;
24
25 /**
26 * We have three general cases for 2pt conical gradients. First we always assume that
27 * the start radius <= end radius. Our first case (kInside_) is when the start circle
28 * is completely enclosed by the end circle. The second case (kOutside_) is the case
29 * when the start circle is either completely outside the end circle or the circles
30 * overlap. The final case (kEdge_) is when the start circle is inside the end one,
31 * but the two are just barely touching at 1 point along their edges.
32 */
33 enum ConicalType {
34 kInside_ConicalType,
35 kOutside_ConicalType,
36 kEdge_ConicalType,
37 };
38
39 //////////////////////////////////////////////////////////////////////////////
40
set_matrix_edge_conical(const SkTwoPointConicalGradient & shader,SkMatrix * invLMatrix)41 static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader,
42 SkMatrix* invLMatrix) {
43 // Inverse of the current local matrix is passed in then,
44 // translate to center1, rotate so center2 is on x axis.
45 const SkPoint& center1 = shader.getStartCenter();
46 const SkPoint& center2 = shader.getEndCenter();
47
48 invLMatrix->postTranslate(-center1.fX, -center1.fY);
49
50 SkPoint diff = center2 - center1;
51 SkScalar diffLen = diff.length();
52 if (0 != diffLen) {
53 SkScalar invDiffLen = SkScalarInvert(diffLen);
54 SkMatrix rot;
55 rot.setSinCos(-invDiffLen * diff.fY, invDiffLen * diff.fX);
56 invLMatrix->postConcat(rot);
57 }
58 }
59
60 class Edge2PtConicalEffect : public GrGradientEffect {
61 public:
62 class GLSLEdge2PtConicalProcessor;
63
Make(const CreateArgs & args)64 static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) {
65 return sk_sp<GrFragmentProcessor>(new Edge2PtConicalEffect(args));
66 }
67
~Edge2PtConicalEffect()68 ~Edge2PtConicalEffect() override {}
69
name() const70 const char* name() const override {
71 return "Two-Point Conical Gradient Edge Touching";
72 }
73
74 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
center() const75 SkScalar center() const { return fCenterX1; }
diffRadius() const76 SkScalar diffRadius() const { return fDiffRadius; }
radius() const77 SkScalar radius() const { return fRadius0; }
78
79 private:
80 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
81
82 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
83
onIsEqual(const GrFragmentProcessor & sBase) const84 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
85 const Edge2PtConicalEffect& s = sBase.cast<Edge2PtConicalEffect>();
86 return (INHERITED::onIsEqual(sBase) &&
87 this->fCenterX1 == s.fCenterX1 &&
88 this->fRadius0 == s.fRadius0 &&
89 this->fDiffRadius == s.fDiffRadius);
90 }
91
Edge2PtConicalEffect(const CreateArgs & args)92 Edge2PtConicalEffect(const CreateArgs& args)
93 : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */) {
94 const SkTwoPointConicalGradient& shader =
95 *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
96 fCenterX1 = shader.getCenterX1();
97 fRadius0 = shader.getStartRadius();
98 fDiffRadius = shader.getDiffRadius();
99 this->initClassID<Edge2PtConicalEffect>();
100 // We should only be calling this shader if we are degenerate case with touching circles
101 // When deciding if we are in edge case, we scaled by the end radius for cases when the
102 // start radius was close to zero, otherwise we scaled by the start radius. In addition
103 // Our test for the edge case in set_matrix_circle_conical has a higher tolerance so we
104 // need the sqrt value below
105 SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - fCenterX1) <
106 (fRadius0 < kErrorTol ? shader.getEndRadius() * kEdgeErrorTol :
107 fRadius0 * sqrt(kEdgeErrorTol)));
108
109 // We pass the linear part of the quadratic as a varying.
110 // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
111 fBTransform = this->getCoordTransform();
112 SkMatrix& bMatrix = *fBTransform.accessMatrix();
113 SkScalar r0dr = fRadius0 * fDiffRadius;
114 bMatrix[SkMatrix::kMScaleX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMScaleX] +
115 r0dr * bMatrix[SkMatrix::kMPersp0]);
116 bMatrix[SkMatrix::kMSkewX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMSkewX] +
117 r0dr * bMatrix[SkMatrix::kMPersp1]);
118 bMatrix[SkMatrix::kMTransX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMTransX] +
119 r0dr * bMatrix[SkMatrix::kMPersp2]);
120 this->addCoordTransform(&fBTransform);
121 }
122
123 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
124
125 // @{
126 // Cache of values - these can change arbitrarily, EXCEPT
127 // we shouldn't change between degenerate and non-degenerate?!
128
129 GrCoordTransform fBTransform;
130 SkScalar fCenterX1;
131 SkScalar fRadius0;
132 SkScalar fDiffRadius;
133
134 // @}
135
136 typedef GrGradientEffect INHERITED;
137 };
138
139 class Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor : public GrGradientEffect::GLSLProcessor {
140 public:
141 GLSLEdge2PtConicalProcessor(const GrProcessor&);
~GLSLEdge2PtConicalProcessor()142 ~GLSLEdge2PtConicalProcessor() override {}
143
144 virtual void emitCode(EmitArgs&) override;
145
146 static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
147
148 protected:
149 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
150
151 UniformHandle fParamUni;
152
153 const char* fVSVaryingName;
154 const char* fFSVaryingName;
155
156 // @{
157 /// Values last uploaded as uniforms
158
159 SkScalar fCachedRadius;
160 SkScalar fCachedDiffRadius;
161
162 // @}
163
164 private:
165 typedef GrGradientEffect::GLSLProcessor INHERITED;
166
167 };
168
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const169 void Edge2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
170 GrProcessorKeyBuilder* b) const {
171 Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GenKey(*this, caps, b);
172 }
173
onCreateGLSLInstance() const174 GrGLSLFragmentProcessor* Edge2PtConicalEffect::onCreateGLSLInstance() const {
175 return new Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor(*this);
176 }
177
178 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Edge2PtConicalEffect);
179
180 /*
181 * All Two point conical gradient test create functions may occasionally create edge case shaders
182 */
183 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)184 sk_sp<GrFragmentProcessor> Edge2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
185 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
186 SkScalar radius1 = d->fRandom->nextUScalar1();
187 SkPoint center2;
188 SkScalar radius2;
189 do {
190 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
191 // If the circles are identical the factory will give us an empty shader.
192 // This will happen if we pick identical centers
193 } while (center1 == center2);
194
195 // Below makes sure that circle one is contained within circle two
196 // and both circles are touching on an edge
197 SkPoint diff = center2 - center1;
198 SkScalar diffLen = diff.length();
199 radius2 = radius1 + diffLen;
200
201 RandomGradientParams params(d->fRandom);
202 auto shader = params.fUseColors4f ?
203 SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
204 params.fColors4f, params.fColorSpace, params.fStops,
205 params.fColorCount, params.fTileMode) :
206 SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
207 params.fColors, params.fStops,
208 params.fColorCount, params.fTileMode);
209 GrTest::TestAsFPArgs asFPArgs(d);
210 sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
211 GrAlwaysAssert(fp);
212 return fp;
213 }
214 #endif
215
GLSLEdge2PtConicalProcessor(const GrProcessor &)216 Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GLSLEdge2PtConicalProcessor(const GrProcessor&)
217 : fVSVaryingName(nullptr)
218 , fFSVaryingName(nullptr)
219 , fCachedRadius(-SK_ScalarMax)
220 , fCachedDiffRadius(-SK_ScalarMax) {}
221
emitCode(EmitArgs & args)222 void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::emitCode(EmitArgs& args) {
223 const Edge2PtConicalEffect& ge = args.fFp.cast<Edge2PtConicalEffect>();
224 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
225 this->emitUniforms(uniformHandler, ge);
226 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
227 kVec3f_GrSLType, kDefault_GrSLPrecision,
228 "Conical2FSParams");
229
230 SkString cName("c");
231 SkString tName("t");
232 SkString p0; // start radius
233 SkString p1; // start radius squared
234 SkString p2; // difference in radii (r1 - r0)
235
236
237 p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
238 p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
239 p2.appendf("%s.z", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
240
241 // We interpolate the linear component in coords[1].
242 SkASSERT(args.fTransformedCoords[0].getType() == args.fTransformedCoords[1].getType());
243 const char* coords2D;
244 SkString bVar;
245 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
246 if (kVec3f_GrSLType == args.fTransformedCoords[0].getType()) {
247 fragBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
248 args.fTransformedCoords[0].c_str(),
249 args.fTransformedCoords[0].c_str(),
250 args.fTransformedCoords[1].c_str(),
251 args.fTransformedCoords[1].c_str());
252 coords2D = "interpolants.xy";
253 bVar = "interpolants.z";
254 } else {
255 coords2D = args.fTransformedCoords[0].c_str();
256 bVar.printf("%s.x", args.fTransformedCoords[1].c_str());
257 }
258
259 // output will default to transparent black (we simply won't write anything
260 // else to it if invalid, instead of discarding or returning prematurely)
261 fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
262
263 // c = (x^2)+(y^2) - params[1]
264 fragBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
265 cName.c_str(), coords2D, coords2D, p1.c_str());
266
267 // linear case: t = -c/b
268 fragBuilder->codeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
269 cName.c_str(), bVar.c_str());
270
271 // if r(t) > 0, then t will be the x coordinate
272 fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
273 p2.c_str(), p0.c_str());
274 fragBuilder->codeAppend("\t");
275 this->emitColor(fragBuilder,
276 uniformHandler,
277 args.fShaderCaps,
278 ge,
279 tName.c_str(),
280 args.fOutputColor,
281 args.fInputColor,
282 args.fTexSamplers);
283 fragBuilder->codeAppend("\t}\n");
284 }
285
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)286 void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::onSetData(
287 const GrGLSLProgramDataManager& pdman,
288 const GrProcessor& processor) {
289 INHERITED::onSetData(pdman, processor);
290 const Edge2PtConicalEffect& data = processor.cast<Edge2PtConicalEffect>();
291 SkScalar radius0 = data.radius();
292 SkScalar diffRadius = data.diffRadius();
293
294 if (fCachedRadius != radius0 ||
295 fCachedDiffRadius != diffRadius) {
296
297 pdman.set3f(fParamUni, radius0, radius0 * radius0, diffRadius);
298 fCachedRadius = radius0;
299 fCachedDiffRadius = diffRadius;
300 }
301 }
302
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)303 void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GenKey(const GrProcessor& processor,
304 const GrShaderCaps&, GrProcessorKeyBuilder* b) {
305 b->add32(GenBaseGradientKey(processor));
306 }
307
308 //////////////////////////////////////////////////////////////////////////////
309 // Focal Conical Gradients
310 //////////////////////////////////////////////////////////////////////////////
311
set_matrix_focal_conical(const SkTwoPointConicalGradient & shader,SkMatrix * invLMatrix,SkScalar * focalX)312 static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader,
313 SkMatrix* invLMatrix, SkScalar* focalX) {
314 // Inverse of the current local matrix is passed in then,
315 // translate, scale, and rotate such that endCircle is unit circle on x-axis,
316 // and focal point is at the origin.
317 ConicalType conicalType;
318 const SkPoint& focal = shader.getStartCenter();
319 const SkPoint& centerEnd = shader.getEndCenter();
320 SkScalar radius = shader.getEndRadius();
321 SkScalar invRadius = 1.f / radius;
322
323 SkMatrix matrix;
324
325 matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
326 matrix.postScale(invRadius, invRadius);
327
328 SkPoint focalTrans;
329 matrix.mapPoints(&focalTrans, &focal, 1);
330 *focalX = focalTrans.length();
331
332 if (0.f != *focalX) {
333 SkScalar invFocalX = SkScalarInvert(*focalX);
334 SkMatrix rot;
335 rot.setSinCos(-invFocalX * focalTrans.fY, invFocalX * focalTrans.fX);
336 matrix.postConcat(rot);
337 }
338
339 matrix.postTranslate(-(*focalX), 0.f);
340
341 // If the focal point is touching the edge of the circle it will
342 // cause a degenerate case that must be handled separately
343 // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
344 // stability trade off versus the linear approx used in the Edge Shader
345 if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
346 return kEdge_ConicalType;
347 }
348
349 // Scale factor 1 / (1 - focalX * focalX)
350 SkScalar oneMinusF2 = 1.f - *focalX * *focalX;
351 SkScalar s = SkScalarInvert(oneMinusF2);
352
353
354 if (s >= 0.f) {
355 conicalType = kInside_ConicalType;
356 matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
357 } else {
358 conicalType = kOutside_ConicalType;
359 matrix.postScale(s, s);
360 }
361
362 invLMatrix->postConcat(matrix);
363
364 return conicalType;
365 }
366
367 //////////////////////////////////////////////////////////////////////////////
368
369 class FocalOutside2PtConicalEffect : public GrGradientEffect {
370 public:
371 class GLSLFocalOutside2PtConicalProcessor;
372
Make(const CreateArgs & args,SkScalar focalX)373 static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) {
374 return sk_sp<GrFragmentProcessor>(
375 new FocalOutside2PtConicalEffect(args, focalX));
376 }
377
~FocalOutside2PtConicalEffect()378 ~FocalOutside2PtConicalEffect() override {}
379
name() const380 const char* name() const override {
381 return "Two-Point Conical Gradient Focal Outside";
382 }
383
isFlipped() const384 bool isFlipped() const { return fIsFlipped; }
focal() const385 SkScalar focal() const { return fFocalX; }
386
387 private:
388 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
389
390 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
391
onIsEqual(const GrFragmentProcessor & sBase) const392 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
393 const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>();
394 return (INHERITED::onIsEqual(sBase) &&
395 this->fFocalX == s.fFocalX &&
396 this->fIsFlipped == s.fIsFlipped);
397 }
398
IsFlipped(const CreateArgs & args)399 static bool IsFlipped(const CreateArgs& args) {
400 // eww.
401 return static_cast<const SkTwoPointConicalGradient*>(args.fShader)->isFlippedGrad();
402 }
403
FocalOutside2PtConicalEffect(const CreateArgs & args,SkScalar focalX)404 FocalOutside2PtConicalEffect(const CreateArgs& args, SkScalar focalX)
405 : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */)
406 , fFocalX(focalX)
407 , fIsFlipped(IsFlipped(args)) {
408 this->initClassID<FocalOutside2PtConicalEffect>();
409 }
410
411 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
412
413 SkScalar fFocalX;
414 bool fIsFlipped;
415
416 typedef GrGradientEffect INHERITED;
417 };
418
419 class FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor
420 : public GrGradientEffect::GLSLProcessor {
421 public:
422 GLSLFocalOutside2PtConicalProcessor(const GrProcessor&);
~GLSLFocalOutside2PtConicalProcessor()423 ~GLSLFocalOutside2PtConicalProcessor() override {}
424
425 virtual void emitCode(EmitArgs&) override;
426
427 static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
428
429 protected:
430 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
431
432 UniformHandle fParamUni;
433
434 const char* fVSVaryingName;
435 const char* fFSVaryingName;
436
437 bool fIsFlipped;
438
439 // @{
440 /// Values last uploaded as uniforms
441
442 SkScalar fCachedFocal;
443
444 // @}
445
446 private:
447 typedef GrGradientEffect::GLSLProcessor INHERITED;
448
449 };
450
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const451 void FocalOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
452 GrProcessorKeyBuilder* b) const {
453 FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::GenKey(*this, caps, b);
454 }
455
onCreateGLSLInstance() const456 GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const {
457 return new FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor(*this);
458 }
459
460 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect);
461
462 /*
463 * All Two point conical gradient test create functions may occasionally create edge case shaders
464 */
465 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)466 sk_sp<GrFragmentProcessor> FocalOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
467 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
468 SkScalar radius1 = 0.f;
469 SkPoint center2;
470 SkScalar radius2;
471 do {
472 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
473 // Need to make sure the centers are not the same or else focal point will be inside
474 } while (center1 == center2);
475
476 SkPoint diff = center2 - center1;
477 SkScalar diffLen = diff.length();
478 // Below makes sure that the focal point is not contained within circle two
479 radius2 = d->fRandom->nextRangeF(0.f, diffLen);
480
481 RandomGradientParams params(d->fRandom);
482 auto shader = params.fUseColors4f ?
483 SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
484 params.fColors4f, params.fColorSpace, params.fStops,
485 params.fColorCount, params.fTileMode) :
486 SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
487 params.fColors, params.fStops,
488 params.fColorCount, params.fTileMode);
489 GrTest::TestAsFPArgs asFPArgs(d);
490 sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
491 GrAlwaysAssert(fp);
492 return fp;
493 }
494 #endif
495
496 FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor
GLSLFocalOutside2PtConicalProcessor(const GrProcessor & processor)497 ::GLSLFocalOutside2PtConicalProcessor(const GrProcessor& processor)
498 : fVSVaryingName(nullptr)
499 , fFSVaryingName(nullptr)
500 , fCachedFocal(SK_ScalarMax) {
501 const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
502 fIsFlipped = data.isFlipped();
503 }
504
emitCode(EmitArgs & args)505 void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::emitCode(EmitArgs& args) {
506 const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>();
507 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
508 this->emitUniforms(uniformHandler, ge);
509 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
510 kVec2f_GrSLType, kDefault_GrSLPrecision,
511 "Conical2FSParams");
512 SkString tName("t");
513 SkString p0; // focalX
514 SkString p1; // 1 - focalX * focalX
515
516 p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
517 p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
518
519 // if we have a vec3 from being in perspective, convert it to a vec2 first
520 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
521 SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
522 const char* coords2D = coords2DString.c_str();
523
524 // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
525
526 // output will default to transparent black (we simply won't write anything
527 // else to it if invalid, instead of discarding or returning prematurely)
528 fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
529
530 fragBuilder->codeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D);
531 fragBuilder->codeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D);
532 fragBuilder->codeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str());
533
534 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
535 // If so we must also flip sign on sqrt
536 if (!fIsFlipped) {
537 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + sqrt(d);\n", tName.c_str(),
538 coords2D, p0.c_str());
539 } else {
540 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s - sqrt(d);\n", tName.c_str(),
541 coords2D, p0.c_str());
542 }
543
544 fragBuilder->codeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str());
545 fragBuilder->codeAppend("\t\t");
546 this->emitColor(fragBuilder,
547 uniformHandler,
548 args.fShaderCaps,
549 ge,
550 tName.c_str(),
551 args.fOutputColor,
552 args.fInputColor,
553 args.fTexSamplers);
554 fragBuilder->codeAppend("\t}\n");
555 }
556
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)557 void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::onSetData(
558 const GrGLSLProgramDataManager& pdman,
559 const GrProcessor& processor) {
560 INHERITED::onSetData(pdman, processor);
561 const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
562 SkASSERT(data.isFlipped() == fIsFlipped);
563 SkScalar focal = data.focal();
564
565 if (fCachedFocal != focal) {
566 SkScalar oneMinus2F = 1.f - focal * focal;
567
568 pdman.set2f(fParamUni, SkScalarToFloat(focal), SkScalarToFloat(oneMinus2F));
569 fCachedFocal = focal;
570 }
571 }
572
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)573 void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::GenKey(
574 const GrProcessor& processor,
575 const GrShaderCaps&, GrProcessorKeyBuilder* b) {
576 uint32_t* key = b->add32n(2);
577 key[0] = GenBaseGradientKey(processor);
578 key[1] = processor.cast<FocalOutside2PtConicalEffect>().isFlipped();
579 }
580
581 //////////////////////////////////////////////////////////////////////////////
582
583 class FocalInside2PtConicalEffect : public GrGradientEffect {
584 public:
585 class GLSLFocalInside2PtConicalProcessor;
586
Make(const CreateArgs & args,SkScalar focalX)587 static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) {
588 return sk_sp<GrFragmentProcessor>(
589 new FocalInside2PtConicalEffect(args, focalX));
590 }
591
~FocalInside2PtConicalEffect()592 ~FocalInside2PtConicalEffect() override {}
593
name() const594 const char* name() const override {
595 return "Two-Point Conical Gradient Focal Inside";
596 }
597
focal() const598 SkScalar focal() const { return fFocalX; }
599
600 typedef FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor GLSLProcessor;
601
602 private:
603 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
604
605 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
606
onIsEqual(const GrFragmentProcessor & sBase) const607 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
608 const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
609 return (INHERITED::onIsEqual(sBase) &&
610 this->fFocalX == s.fFocalX);
611 }
612
FocalInside2PtConicalEffect(const CreateArgs & args,SkScalar focalX)613 FocalInside2PtConicalEffect(const CreateArgs& args, SkScalar focalX)
614 : INHERITED(args, args.fShader->colorsAreOpaque()), fFocalX(focalX) {
615 this->initClassID<FocalInside2PtConicalEffect>();
616 }
617
618 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
619
620 SkScalar fFocalX;
621
622 typedef GrGradientEffect INHERITED;
623 };
624
625 class FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor
626 : public GrGradientEffect::GLSLProcessor {
627 public:
628 GLSLFocalInside2PtConicalProcessor(const GrProcessor&);
~GLSLFocalInside2PtConicalProcessor()629 ~GLSLFocalInside2PtConicalProcessor() override {}
630
631 virtual void emitCode(EmitArgs&) override;
632
633 static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
634
635 protected:
636 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
637
638 UniformHandle fFocalUni;
639
640 const char* fVSVaryingName;
641 const char* fFSVaryingName;
642
643 // @{
644 /// Values last uploaded as uniforms
645
646 SkScalar fCachedFocal;
647
648 // @}
649
650 private:
651 typedef GrGradientEffect::GLSLProcessor INHERITED;
652
653 };
654
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const655 void FocalInside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
656 GrProcessorKeyBuilder* b) const {
657 FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::GenKey(*this, caps, b);
658 }
659
onCreateGLSLInstance() const660 GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const {
661 return new FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor(*this);
662 }
663
664 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect);
665
666 /*
667 * All Two point conical gradient test create functions may occasionally create edge case shaders
668 */
669 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)670 sk_sp<GrFragmentProcessor> FocalInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
671 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
672 SkScalar radius1 = 0.f;
673 SkPoint center2;
674 SkScalar radius2;
675 do {
676 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
677 // Below makes sure radius2 is larger enouch such that the focal point
678 // is inside the end circle
679 SkScalar increase = d->fRandom->nextUScalar1();
680 SkPoint diff = center2 - center1;
681 SkScalar diffLen = diff.length();
682 radius2 = diffLen + increase;
683 // If the circles are identical the factory will give us an empty shader.
684 } while (radius1 == radius2 && center1 == center2);
685
686 RandomGradientParams params(d->fRandom);
687 auto shader = params.fUseColors4f ?
688 SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
689 params.fColors4f, params.fColorSpace, params.fStops,
690 params.fColorCount, params.fTileMode) :
691 SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
692 params.fColors, params.fStops,
693 params.fColorCount, params.fTileMode);
694 GrTest::TestAsFPArgs asFPArgs(d);
695 sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
696 GrAlwaysAssert(fp);
697 return fp;
698 }
699 #endif
700
701 FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor
GLSLFocalInside2PtConicalProcessor(const GrProcessor &)702 ::GLSLFocalInside2PtConicalProcessor(const GrProcessor&)
703 : fVSVaryingName(nullptr)
704 , fFSVaryingName(nullptr)
705 , fCachedFocal(SK_ScalarMax) {}
706
emitCode(EmitArgs & args)707 void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::emitCode(EmitArgs& args) {
708 const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>();
709 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
710 this->emitUniforms(uniformHandler, ge);
711 fFocalUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
712 kFloat_GrSLType, kDefault_GrSLPrecision,
713 "Conical2FSParams");
714 SkString tName("t");
715
716 // this is the distance along x-axis from the end center to focal point in
717 // transformed coordinates
718 GrShaderVar focal = uniformHandler->getUniformVariable(fFocalUni);
719
720 // if we have a vec3 from being in perspective, convert it to a vec2 first
721 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
722 SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
723 const char* coords2D = coords2DString.c_str();
724
725 // t = p.x * focalX + length(p)
726 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(),
727 coords2D, focal.c_str(), coords2D);
728
729 this->emitColor(fragBuilder,
730 uniformHandler,
731 args.fShaderCaps,
732 ge,
733 tName.c_str(),
734 args.fOutputColor,
735 args.fInputColor,
736 args.fTexSamplers);
737 }
738
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)739 void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::onSetData(
740 const GrGLSLProgramDataManager& pdman,
741 const GrProcessor& processor) {
742 INHERITED::onSetData(pdman, processor);
743 const FocalInside2PtConicalEffect& data = processor.cast<FocalInside2PtConicalEffect>();
744 SkScalar focal = data.focal();
745
746 if (fCachedFocal != focal) {
747 pdman.set1f(fFocalUni, SkScalarToFloat(focal));
748 fCachedFocal = focal;
749 }
750 }
751
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)752 void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::GenKey(
753 const GrProcessor& processor,
754 const GrShaderCaps&, GrProcessorKeyBuilder* b) {
755 b->add32(GenBaseGradientKey(processor));
756 }
757
758 //////////////////////////////////////////////////////////////////////////////
759 // Circle Conical Gradients
760 //////////////////////////////////////////////////////////////////////////////
761
762 struct CircleConicalInfo {
763 SkPoint fCenterEnd;
764 SkScalar fA;
765 SkScalar fB;
766 SkScalar fC;
767 };
768
769 // Returns focal distance along x-axis in transformed coords
set_matrix_circle_conical(const SkTwoPointConicalGradient & shader,SkMatrix * invLMatrix,CircleConicalInfo * info)770 static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
771 SkMatrix* invLMatrix, CircleConicalInfo* info) {
772 // Inverse of the current local matrix is passed in then,
773 // translate and scale such that start circle is on the origin and has radius 1
774 const SkPoint& centerStart = shader.getStartCenter();
775 const SkPoint& centerEnd = shader.getEndCenter();
776 SkScalar radiusStart = shader.getStartRadius();
777 SkScalar radiusEnd = shader.getEndRadius();
778
779 SkMatrix matrix;
780
781 matrix.setTranslate(-centerStart.fX, -centerStart.fY);
782
783 SkScalar invStartRad = 1.f / radiusStart;
784 matrix.postScale(invStartRad, invStartRad);
785
786 radiusEnd /= radiusStart;
787
788 SkPoint centerEndTrans;
789 matrix.mapPoints(¢erEndTrans, ¢erEnd, 1);
790
791 SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
792 - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
793
794 // Check to see if start circle is inside end circle with edges touching.
795 // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
796 // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
797 // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
798 // still accurate.
799 if (SkScalarAbs(A) < kEdgeErrorTol) {
800 return kEdge_ConicalType;
801 }
802
803 SkScalar C = 1.f / A;
804 SkScalar B = (radiusEnd - 1.f) * C;
805
806 matrix.postScale(C, C);
807
808 invLMatrix->postConcat(matrix);
809
810 info->fCenterEnd = centerEndTrans;
811 info->fA = A;
812 info->fB = B;
813 info->fC = C;
814
815 // if A ends up being negative, the start circle is contained completely inside the end cirlce
816 if (A < 0.f) {
817 return kInside_ConicalType;
818 }
819 return kOutside_ConicalType;
820 }
821
822 class CircleInside2PtConicalEffect : public GrGradientEffect {
823 public:
824 class GLSLCircleInside2PtConicalProcessor;
825
Make(const CreateArgs & args,const CircleConicalInfo & info)826 static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, const CircleConicalInfo& info) {
827 return sk_sp<GrFragmentProcessor>(
828 new CircleInside2PtConicalEffect(args, info));
829 }
830
~CircleInside2PtConicalEffect()831 ~CircleInside2PtConicalEffect() override {}
832
name() const833 const char* name() const override { return "Two-Point Conical Gradient Inside"; }
834
centerX() const835 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
centerY() const836 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
A() const837 SkScalar A() const { return fInfo.fA; }
B() const838 SkScalar B() const { return fInfo.fB; }
C() const839 SkScalar C() const { return fInfo.fC; }
840
841 private:
842 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
843
844 virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
845 GrProcessorKeyBuilder* b) const override;
846
onIsEqual(const GrFragmentProcessor & sBase) const847 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
848 const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>();
849 return (INHERITED::onIsEqual(sBase) &&
850 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
851 this->fInfo.fA == s.fInfo.fA &&
852 this->fInfo.fB == s.fInfo.fB &&
853 this->fInfo.fC == s.fInfo.fC);
854 }
855
CircleInside2PtConicalEffect(const CreateArgs & args,const CircleConicalInfo & info)856 CircleInside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info)
857 : INHERITED(args, args.fShader->colorsAreOpaque()), fInfo(info) {
858 this->initClassID<CircleInside2PtConicalEffect>();
859 }
860
861 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
862
863 const CircleConicalInfo fInfo;
864
865 typedef GrGradientEffect INHERITED;
866 };
867
868 class CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor
869 : public GrGradientEffect::GLSLProcessor {
870 public:
871 GLSLCircleInside2PtConicalProcessor(const GrProcessor&);
~GLSLCircleInside2PtConicalProcessor()872 ~GLSLCircleInside2PtConicalProcessor() override {}
873
874 virtual void emitCode(EmitArgs&) override;
875
876 static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
877
878 protected:
879 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
880
881 UniformHandle fCenterUni;
882 UniformHandle fParamUni;
883
884 const char* fVSVaryingName;
885 const char* fFSVaryingName;
886
887 // @{
888 /// Values last uploaded as uniforms
889
890 SkScalar fCachedCenterX;
891 SkScalar fCachedCenterY;
892 SkScalar fCachedA;
893 SkScalar fCachedB;
894 SkScalar fCachedC;
895
896 // @}
897
898 private:
899 typedef GrGradientEffect::GLSLProcessor INHERITED;
900
901 };
902
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const903 void CircleInside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
904 GrProcessorKeyBuilder* b) const {
905 CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::GenKey(*this, caps, b);
906 }
907
onCreateGLSLInstance() const908 GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const {
909 return new CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor(*this);
910 }
911
912 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect);
913
914 /*
915 * All Two point conical gradient test create functions may occasionally create edge case shaders
916 */
917 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)918 sk_sp<GrFragmentProcessor> CircleInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
919 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
920 SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
921 SkPoint center2;
922 SkScalar radius2;
923 do {
924 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
925 // Below makes sure that circle one is contained within circle two
926 SkScalar increase = d->fRandom->nextUScalar1();
927 SkPoint diff = center2 - center1;
928 SkScalar diffLen = diff.length();
929 radius2 = radius1 + diffLen + increase;
930 // If the circles are identical the factory will give us an empty shader.
931 } while (radius1 == radius2 && center1 == center2);
932
933 RandomGradientParams params(d->fRandom);
934 auto shader = params.fUseColors4f ?
935 SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
936 params.fColors4f, params.fColorSpace, params.fStops,
937 params.fColorCount, params.fTileMode) :
938 SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
939 params.fColors, params.fStops,
940 params.fColorCount, params.fTileMode);
941 GrTest::TestAsFPArgs asFPArgs(d);
942 sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
943 GrAlwaysAssert(fp);
944 return fp;
945 }
946 #endif
947
948 CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor
GLSLCircleInside2PtConicalProcessor(const GrProcessor & processor)949 ::GLSLCircleInside2PtConicalProcessor(const GrProcessor& processor)
950 : fVSVaryingName(nullptr)
951 , fFSVaryingName(nullptr)
952 , fCachedCenterX(SK_ScalarMax)
953 , fCachedCenterY(SK_ScalarMax)
954 , fCachedA(SK_ScalarMax)
955 , fCachedB(SK_ScalarMax)
956 , fCachedC(SK_ScalarMax) {}
957
emitCode(EmitArgs & args)958 void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::emitCode(EmitArgs& args) {
959 const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>();
960 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
961 this->emitUniforms(uniformHandler, ge);
962 fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
963 kVec2f_GrSLType, kDefault_GrSLPrecision,
964 "Conical2FSCenter");
965 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
966 kVec3f_GrSLType, kDefault_GrSLPrecision,
967 "Conical2FSParams");
968 SkString tName("t");
969
970 GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
971 // params.x = A
972 // params.y = B
973 // params.z = C
974 GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
975
976 // if we have a vec3 from being in perspective, convert it to a vec2 first
977 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
978 SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
979 const char* coords2D = coords2DString.c_str();
980
981 // p = coords2D
982 // e = center end
983 // r = radius end
984 // A = dot(e, e) - r^2 + 2 * r - 1
985 // B = (r -1) / A
986 // C = 1 / A
987 // d = dot(e, p) + B
988 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
989 fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
990 fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
991 params.c_str());
992 fragBuilder->codeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
993 tName.c_str(), params.c_str(), params.c_str());
994
995 this->emitColor(fragBuilder,
996 uniformHandler,
997 args.fShaderCaps,
998 ge,
999 tName.c_str(),
1000 args.fOutputColor,
1001 args.fInputColor,
1002 args.fTexSamplers);
1003 }
1004
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)1005 void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::onSetData(
1006 const GrGLSLProgramDataManager& pdman,
1007 const GrProcessor& processor) {
1008 INHERITED::onSetData(pdman, processor);
1009 const CircleInside2PtConicalEffect& data = processor.cast<CircleInside2PtConicalEffect>();
1010 SkScalar centerX = data.centerX();
1011 SkScalar centerY = data.centerY();
1012 SkScalar A = data.A();
1013 SkScalar B = data.B();
1014 SkScalar C = data.C();
1015
1016 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1017 fCachedA != A || fCachedB != B || fCachedC != C) {
1018
1019 pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1020 pdman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C));
1021
1022 fCachedCenterX = centerX;
1023 fCachedCenterY = centerY;
1024 fCachedA = A;
1025 fCachedB = B;
1026 fCachedC = C;
1027 }
1028 }
1029
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)1030 void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::GenKey(
1031 const GrProcessor& processor,
1032 const GrShaderCaps&, GrProcessorKeyBuilder* b) {
1033 b->add32(GenBaseGradientKey(processor));
1034 }
1035
1036 //////////////////////////////////////////////////////////////////////////////
1037
1038 class CircleOutside2PtConicalEffect : public GrGradientEffect {
1039 public:
1040 class GLSLCircleOutside2PtConicalProcessor;
1041
Make(const CreateArgs & args,const CircleConicalInfo & info)1042 static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args, const CircleConicalInfo& info) {
1043 return sk_sp<GrFragmentProcessor>(
1044 new CircleOutside2PtConicalEffect(args, info));
1045 }
1046
~CircleOutside2PtConicalEffect()1047 ~CircleOutside2PtConicalEffect() override {}
1048
name() const1049 const char* name() const override { return "Two-Point Conical Gradient Outside"; }
1050
centerX() const1051 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
centerY() const1052 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
A() const1053 SkScalar A() const { return fInfo.fA; }
B() const1054 SkScalar B() const { return fInfo.fB; }
C() const1055 SkScalar C() const { return fInfo.fC; }
tLimit() const1056 SkScalar tLimit() const { return fTLimit; }
isFlipped() const1057 bool isFlipped() const { return fIsFlipped; }
1058
1059 private:
1060 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
1061
1062 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
1063
onIsEqual(const GrFragmentProcessor & sBase) const1064 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
1065 const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>();
1066 return (INHERITED::onIsEqual(sBase) &&
1067 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
1068 this->fInfo.fA == s.fInfo.fA &&
1069 this->fInfo.fB == s.fInfo.fB &&
1070 this->fInfo.fC == s.fInfo.fC &&
1071 this->fTLimit == s.fTLimit &&
1072 this->fIsFlipped == s.fIsFlipped);
1073 }
1074
CircleOutside2PtConicalEffect(const CreateArgs & args,const CircleConicalInfo & info)1075 CircleOutside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info)
1076 : INHERITED(args, false /* opaque: draws transparent black outside of the cone. */)
1077 , fInfo(info) {
1078 this->initClassID<CircleOutside2PtConicalEffect>();
1079 const SkTwoPointConicalGradient& shader =
1080 *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
1081 if (shader.getStartRadius() != shader.getEndRadius()) {
1082 fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius());
1083 } else {
1084 fTLimit = SK_ScalarMin;
1085 }
1086
1087 fIsFlipped = shader.isFlippedGrad();
1088 }
1089
1090 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
1091
1092 const CircleConicalInfo fInfo;
1093 SkScalar fTLimit;
1094 bool fIsFlipped;
1095
1096 typedef GrGradientEffect INHERITED;
1097 };
1098
1099 class CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor
1100 : public GrGradientEffect::GLSLProcessor {
1101 public:
1102 GLSLCircleOutside2PtConicalProcessor(const GrProcessor&);
~GLSLCircleOutside2PtConicalProcessor()1103 ~GLSLCircleOutside2PtConicalProcessor() override {}
1104
1105 virtual void emitCode(EmitArgs&) override;
1106
1107 static void GenKey(const GrProcessor&, const GrShaderCaps& caps, GrProcessorKeyBuilder* b);
1108
1109 protected:
1110 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
1111
1112 UniformHandle fCenterUni;
1113 UniformHandle fParamUni;
1114
1115 const char* fVSVaryingName;
1116 const char* fFSVaryingName;
1117
1118 bool fIsFlipped;
1119
1120 // @{
1121 /// Values last uploaded as uniforms
1122
1123 SkScalar fCachedCenterX;
1124 SkScalar fCachedCenterY;
1125 SkScalar fCachedA;
1126 SkScalar fCachedB;
1127 SkScalar fCachedC;
1128 SkScalar fCachedTLimit;
1129
1130 // @}
1131
1132 private:
1133 typedef GrGradientEffect::GLSLProcessor INHERITED;
1134
1135 };
1136
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const1137 void CircleOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
1138 GrProcessorKeyBuilder* b) const {
1139 CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::GenKey(*this, caps, b);
1140 }
1141
onCreateGLSLInstance() const1142 GrGLSLFragmentProcessor* CircleOutside2PtConicalEffect::onCreateGLSLInstance() const {
1143 return new CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor(*this);
1144 }
1145
1146 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect);
1147
1148 /*
1149 * All Two point conical gradient test create functions may occasionally create edge case shaders
1150 */
1151 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)1152 sk_sp<GrFragmentProcessor> CircleOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
1153 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
1154 SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
1155 SkPoint center2;
1156 SkScalar radius2;
1157 SkScalar diffLen;
1158 do {
1159 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
1160 // If the circles share a center than we can't be in the outside case
1161 } while (center1 == center2);
1162 SkPoint diff = center2 - center1;
1163 diffLen = diff.length();
1164 // Below makes sure that circle one is not contained within circle two
1165 // and have radius2 >= radius to match sorting on cpu side
1166 radius2 = radius1 + d->fRandom->nextRangeF(0.f, diffLen);
1167
1168 RandomGradientParams params(d->fRandom);
1169 auto shader = params.fUseColors4f ?
1170 SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
1171 params.fColors4f, params.fColorSpace, params.fStops,
1172 params.fColorCount, params.fTileMode) :
1173 SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
1174 params.fColors, params.fStops,
1175 params.fColorCount, params.fTileMode);
1176 GrTest::TestAsFPArgs asFPArgs(d);
1177 sk_sp<GrFragmentProcessor> fp = shader->asFragmentProcessor(asFPArgs.args());
1178 GrAlwaysAssert(fp);
1179 return fp;
1180 }
1181 #endif
1182
1183 CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor
GLSLCircleOutside2PtConicalProcessor(const GrProcessor & processor)1184 ::GLSLCircleOutside2PtConicalProcessor(const GrProcessor& processor)
1185 : fVSVaryingName(nullptr)
1186 , fFSVaryingName(nullptr)
1187 , fCachedCenterX(SK_ScalarMax)
1188 , fCachedCenterY(SK_ScalarMax)
1189 , fCachedA(SK_ScalarMax)
1190 , fCachedB(SK_ScalarMax)
1191 , fCachedC(SK_ScalarMax)
1192 , fCachedTLimit(SK_ScalarMax) {
1193 const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
1194 fIsFlipped = data.isFlipped();
1195 }
1196
emitCode(EmitArgs & args)1197 void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::emitCode(EmitArgs& args) {
1198 const CircleOutside2PtConicalEffect& ge = args.fFp.cast<CircleOutside2PtConicalEffect>();
1199 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
1200 this->emitUniforms(uniformHandler, ge);
1201 fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
1202 kVec2f_GrSLType, kDefault_GrSLPrecision,
1203 "Conical2FSCenter");
1204 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
1205 kVec4f_GrSLType, kDefault_GrSLPrecision,
1206 "Conical2FSParams");
1207 SkString tName("t");
1208
1209 GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
1210 // params.x = A
1211 // params.y = B
1212 // params.z = C
1213 GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
1214
1215 // if we have a vec3 from being in perspective, convert it to a vec2 first
1216 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
1217 SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
1218 const char* coords2D = coords2DString.c_str();
1219
1220 // output will default to transparent black (we simply won't write anything
1221 // else to it if invalid, instead of discarding or returning prematurely)
1222 fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
1223
1224 // p = coords2D
1225 // e = center end
1226 // r = radius end
1227 // A = dot(e, e) - r^2 + 2 * r - 1
1228 // B = (r -1) / A
1229 // C = 1 / A
1230 // d = dot(e, p) + B
1231 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
1232
1233 fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
1234 fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
1235 params.c_str());
1236 fragBuilder->codeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(),
1237 params.c_str());
1238
1239 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
1240 // If so we must also flip sign on sqrt
1241 if (!fIsFlipped) {
1242 fragBuilder->codeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
1243 } else {
1244 fragBuilder->codeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str());
1245 }
1246
1247 fragBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n",
1248 tName.c_str(), params.c_str());
1249 fragBuilder->codeAppend("\t\t");
1250 this->emitColor(fragBuilder,
1251 uniformHandler,
1252 args.fShaderCaps,
1253 ge,
1254 tName.c_str(),
1255 args.fOutputColor,
1256 args.fInputColor,
1257 args.fTexSamplers);
1258 fragBuilder->codeAppend("\t}\n");
1259 }
1260
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)1261 void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::onSetData(
1262 const GrGLSLProgramDataManager& pdman,
1263 const GrProcessor& processor) {
1264 INHERITED::onSetData(pdman, processor);
1265 const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
1266 SkASSERT(data.isFlipped() == fIsFlipped);
1267 SkScalar centerX = data.centerX();
1268 SkScalar centerY = data.centerY();
1269 SkScalar A = data.A();
1270 SkScalar B = data.B();
1271 SkScalar C = data.C();
1272 SkScalar tLimit = data.tLimit();
1273
1274 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1275 fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
1276
1277 pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1278 pdman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
1279 SkScalarToFloat(tLimit));
1280
1281 fCachedCenterX = centerX;
1282 fCachedCenterY = centerY;
1283 fCachedA = A;
1284 fCachedB = B;
1285 fCachedC = C;
1286 fCachedTLimit = tLimit;
1287 }
1288 }
1289
GenKey(const GrProcessor & processor,const GrShaderCaps &,GrProcessorKeyBuilder * b)1290 void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::GenKey(
1291 const GrProcessor& processor,
1292 const GrShaderCaps&, GrProcessorKeyBuilder* b) {
1293 uint32_t* key = b->add32n(2);
1294 key[0] = GenBaseGradientKey(processor);
1295 key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped();
1296 }
1297
1298 //////////////////////////////////////////////////////////////////////////////
1299
Make(const GrGradientEffect::CreateArgs & args)1300 sk_sp<GrFragmentProcessor> Gr2PtConicalGradientEffect::Make(
1301 const GrGradientEffect::CreateArgs& args) {
1302 const SkTwoPointConicalGradient& shader =
1303 *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
1304
1305 SkMatrix matrix;
1306 if (!shader.getLocalMatrix().invert(&matrix)) {
1307 return nullptr;
1308 }
1309 if (args.fMatrix) {
1310 SkMatrix inv;
1311 if (!args.fMatrix->invert(&inv)) {
1312 return nullptr;
1313 }
1314 matrix.postConcat(inv);
1315 }
1316
1317 GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fTileMode,
1318 std::move(args.fColorSpaceXform), args.fGammaCorrect);
1319
1320 if (shader.getStartRadius() < kErrorTol) {
1321 SkScalar focalX;
1322 ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
1323 if (type == kInside_ConicalType) {
1324 return FocalInside2PtConicalEffect::Make(newArgs, focalX);
1325 } else if(type == kEdge_ConicalType) {
1326 set_matrix_edge_conical(shader, &matrix);
1327 return Edge2PtConicalEffect::Make(newArgs);
1328 } else {
1329 return FocalOutside2PtConicalEffect::Make(newArgs, focalX);
1330 }
1331 }
1332
1333 CircleConicalInfo info;
1334 ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
1335
1336 if (type == kInside_ConicalType) {
1337 return CircleInside2PtConicalEffect::Make(newArgs, info);
1338 } else if (type == kEdge_ConicalType) {
1339 set_matrix_edge_conical(shader, &matrix);
1340 return Edge2PtConicalEffect::Make(newArgs);
1341 } else {
1342 return CircleOutside2PtConicalEffect::Make(newArgs, info);
1343 }
1344 }
1345
1346 #endif
1347