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