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