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