blob: eb4fe0a5ed4259c727aeaadc34290fddcf324eb8 [file] [log] [blame]
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001/*
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 */
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00007
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00008#include "SkTwoPointConicalGradient_gpu.h"
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00009
10#include "SkTwoPointConicalGradient.h"
11
commit-bot@chromium.orgef93d292014-04-10 15:37:52 +000012#if SK_SUPPORT_GPU
egdaniel7ea439b2015-12-03 09:20:44 -080013#include "GrCoordTransform.h"
14#include "GrInvariantOutput.h"
joshualitt8ca93e72015-07-08 06:51:43 -070015#include "GrPaint.h"
egdaniel2d721d32015-11-11 13:06:05 -080016#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniel018fb622015-10-28 07:26:40 -070017#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080018#include "glsl/GrGLSLUniformHandler.h"
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000019// For brevity
egdaniel018fb622015-10-28 07:26:40 -070020typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000021
commit-bot@chromium.org80894672014-04-22 21:24:22 +000022static const SkScalar kErrorTol = 0.00001f;
egdaniel8405ef92014-06-09 11:57:28 -070023static const SkScalar kEdgeErrorTol = 5.f * kErrorTol;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000024
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 */
33enum ConicalType {
34 kInside_ConicalType,
35 kOutside_ConicalType,
36 kEdge_ConicalType,
37};
38
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000039//////////////////////////////////////////////////////////////////////////////
40
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000041static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader,
42 SkMatrix* invLMatrix) {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000043 // Inverse of the current local matrix is passed in then,
44 // translate to center1, rotate so center2 is on x axis.
45 const SkPoint& center1 = shader.getStartCenter();
46 const SkPoint& center2 = shader.getEndCenter();
47
48 invLMatrix->postTranslate(-center1.fX, -center1.fY);
49
50 SkPoint diff = center2 - center1;
51 SkScalar diffLen = diff.length();
52 if (0 != diffLen) {
53 SkScalar invDiffLen = SkScalarInvert(diffLen);
54 SkMatrix rot;
55 rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY),
56 SkScalarMul(invDiffLen, diff.fX));
57 invLMatrix->postConcat(rot);
58 }
59}
60
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000061class Edge2PtConicalEffect : public GrGradientEffect {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000062public:
63
joshualittb0a8a372014-09-23 09:50:21 -070064 static GrFragmentProcessor* Create(GrContext* ctx,
65 const SkTwoPointConicalGradient& shader,
66 const SkMatrix& matrix,
67 SkShader::TileMode tm) {
bsalomon4a339522015-10-06 08:40:50 -070068 return new Edge2PtConicalEffect(ctx, shader, matrix, tm);
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000069 }
70
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000071 virtual ~Edge2PtConicalEffect() {}
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000072
mtklein36352bf2015-03-25 18:17:31 -070073 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -080074 return "Two-Point Conical Gradient Edge Touching";
75 }
76
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000077 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000078 SkScalar center() const { return fCenterX1; }
79 SkScalar diffRadius() const { return fDiffRadius; }
80 SkScalar radius() const { return fRadius0; }
81
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000082private:
egdaniel57d3b032015-11-13 11:57:27 -080083 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -070084
egdaniel57d3b032015-11-13 11:57:27 -080085 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -070086
mtklein36352bf2015-03-25 18:17:31 -070087 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -070088 const Edge2PtConicalEffect& s = sBase.cast<Edge2PtConicalEffect>();
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000089 return (INHERITED::onIsEqual(sBase) &&
90 this->fCenterX1 == s.fCenterX1 &&
91 this->fRadius0 == s.fRadius0 &&
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000092 this->fDiffRadius == s.fDiffRadius);
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000093 }
94
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000095 Edge2PtConicalEffect(GrContext* ctx,
96 const SkTwoPointConicalGradient& shader,
97 const SkMatrix& matrix,
98 SkShader::TileMode tm)
bsalomon4a339522015-10-06 08:40:50 -070099 : INHERITED(ctx, shader, matrix, tm),
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000100 fCenterX1(shader.getCenterX1()),
101 fRadius0(shader.getStartRadius()),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000102 fDiffRadius(shader.getDiffRadius()){
joshualitteb2a6762014-12-04 11:35:33 -0800103 this->initClassID<Edge2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000104 // We should only be calling this shader if we are degenerate case with touching circles
egdaniel8405ef92014-06-09 11:57:28 -0700105 // When deciding if we are in edge case, we scaled by the end radius for cases when the
joshualitt01258472014-09-22 10:29:30 -0700106 // start radius was close to zero, otherwise we scaled by the start radius. In addition
107 // Our test for the edge case in set_matrix_circle_conical has a higher tolerance so we
108 // need the sqrt value below
109 SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - fCenterX1) <
110 (fRadius0 < kErrorTol ? shader.getEndRadius() * kEdgeErrorTol :
111 fRadius0 * sqrt(kEdgeErrorTol)));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000112
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000113 // We pass the linear part of the quadratic as a varying.
114 // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
115 fBTransform = this->getCoordTransform();
116 SkMatrix& bMatrix = *fBTransform.accessMatrix();
117 SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius);
118 bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) +
119 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0]));
120 bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) +
121 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1]));
122 bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) +
123 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2]));
124 this->addCoordTransform(&fBTransform);
125 }
126
joshualittb0a8a372014-09-23 09:50:21 -0700127 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000128
129 // @{
130 // Cache of values - these can change arbitrarily, EXCEPT
131 // we shouldn't change between degenerate and non-degenerate?!
132
133 GrCoordTransform fBTransform;
134 SkScalar fCenterX1;
135 SkScalar fRadius0;
136 SkScalar fDiffRadius;
137
138 // @}
139
140 typedef GrGradientEffect INHERITED;
141};
142
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000143class GLEdge2PtConicalEffect : public GrGLGradientEffect {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000144public:
joshualitteb2a6762014-12-04 11:35:33 -0800145 GLEdge2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000146 virtual ~GLEdge2PtConicalEffect() { }
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000147
wangyix7c157a92015-07-22 15:08:53 -0700148 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000149
jvanverthcfc18862015-04-28 08:48:20 -0700150 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000151
152protected:
egdaniel018fb622015-10-28 07:26:40 -0700153 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700154
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000155 UniformHandle fParamUni;
156
157 const char* fVSVaryingName;
158 const char* fFSVaryingName;
159
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000160 // @{
161 /// Values last uploaded as uniforms
162
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000163 SkScalar fCachedRadius;
164 SkScalar fCachedDiffRadius;
165
166 // @}
167
168private:
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000169 typedef GrGLGradientEffect INHERITED;
170
171};
172
egdaniel57d3b032015-11-13 11:57:27 -0800173void Edge2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
174 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800175 GLEdge2PtConicalEffect::GenKey(*this, caps, b);
176}
177
egdaniel57d3b032015-11-13 11:57:27 -0800178GrGLSLFragmentProcessor* Edge2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700179 return new GLEdge2PtConicalEffect(*this);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000180}
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000181
joshualittb0a8a372014-09-23 09:50:21 -0700182GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Edge2PtConicalEffect);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000183
joshualitt01258472014-09-22 10:29:30 -0700184/*
185 * All Two point conical gradient test create functions may occasionally create edge case shaders
186 */
bsalomonc21b09e2015-08-28 18:46:56 -0700187const GrFragmentProcessor* Edge2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700188 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
189 SkScalar radius1 = d->fRandom->nextUScalar1();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000190 SkPoint center2;
191 SkScalar radius2;
192 do {
joshualitt0067ff52015-07-08 14:26:19 -0700193 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000194 // If the circles are identical the factory will give us an empty shader.
195 // This will happen if we pick identical centers
196 } while (center1 == center2);
197
198 // Below makes sure that circle one is contained within circle two
199 // and both circles are touching on an edge
200 SkPoint diff = center2 - center1;
201 SkScalar diffLen = diff.length();
202 radius2 = radius1 + diffLen;
203
204 SkColor colors[kMaxRandomGradientColors];
205 SkScalar stopsArray[kMaxRandomGradientColors];
206 SkScalar* stops = stopsArray;
207 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700208 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
reed8a21c9f2016-03-08 18:50:00 -0800209 auto shader = SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
210 colors, stops, colorCount, tm);
bsalomonc21b09e2015-08-28 18:46:56 -0700211 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700212 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700213 GrAlwaysAssert(fp);
joshualittb0a8a372014-09-23 09:50:21 -0700214 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000215}
216
joshualitteb2a6762014-12-04 11:35:33 -0800217GLEdge2PtConicalEffect::GLEdge2PtConicalEffect(const GrProcessor&)
halcanary96fcdcc2015-08-27 07:41:13 -0700218 : fVSVaryingName(nullptr)
219 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000220 , fCachedRadius(-SK_ScalarMax)
221 , fCachedDiffRadius(-SK_ScalarMax) {}
222
wangyix7c157a92015-07-22 15:08:53 -0700223void GLEdge2PtConicalEffect::emitCode(EmitArgs& args) {
224 const Edge2PtConicalEffect& ge = args.fFp.cast<Edge2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -0800225 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
226 this->emitUniforms(uniformHandler, ge);
jvanverthde11ee42016-02-26 13:58:40 -0800227 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
228 kVec3f_GrSLType, kDefault_GrSLPrecision,
229 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000230
231 SkString cName("c");
232 SkString tName("t");
233 SkString p0; // start radius
234 SkString p1; // start radius squared
235 SkString p2; // difference in radii (r1 - r0)
236
jvanverthde11ee42016-02-26 13:58:40 -0800237
238 p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
239 p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
240 p2.appendf("%s.z", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000241
242 // We interpolate the linear component in coords[1].
wangyix7c157a92015-07-22 15:08:53 -0700243 SkASSERT(args.fCoords[0].getType() == args.fCoords[1].getType());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000244 const char* coords2D;
245 SkString bVar;
cdalton85285412016-02-18 12:37:07 -0800246 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
wangyix7c157a92015-07-22 15:08:53 -0700247 if (kVec3f_GrSLType == args.fCoords[0].getType()) {
egdaniel4ca2e602015-11-18 08:01:26 -0800248 fragBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
wangyix7c157a92015-07-22 15:08:53 -0700249 args.fCoords[0].c_str(), args.fCoords[0].c_str(),
250 args.fCoords[1].c_str(), args.fCoords[1].c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000251 coords2D = "interpolants.xy";
252 bVar = "interpolants.z";
253 } else {
wangyix7c157a92015-07-22 15:08:53 -0700254 coords2D = args.fCoords[0].c_str();
255 bVar.printf("%s.x", args.fCoords[1].c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000256 }
257
258 // output will default to transparent black (we simply won't write anything
259 // else to it if invalid, instead of discarding or returning prematurely)
egdaniel4ca2e602015-11-18 08:01:26 -0800260 fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000261
262 // c = (x^2)+(y^2) - params[1]
egdaniel4ca2e602015-11-18 08:01:26 -0800263 fragBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000264 cName.c_str(), coords2D, coords2D, p1.c_str());
265
266 // linear case: t = -c/b
egdaniel4ca2e602015-11-18 08:01:26 -0800267 fragBuilder->codeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000268 cName.c_str(), bVar.c_str());
269
270 // if r(t) > 0, then t will be the x coordinate
egdaniel4ca2e602015-11-18 08:01:26 -0800271 fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000272 p2.c_str(), p0.c_str());
egdaniel4ca2e602015-11-18 08:01:26 -0800273 fragBuilder->codeAppend("\t");
egdaniel7ea439b2015-12-03 09:20:44 -0800274 this->emitColor(fragBuilder,
275 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -0800276 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800277 ge,
278 tName.c_str(),
279 args.fOutputColor,
280 args.fInputColor,
cdalton3f6f76f2016-04-11 12:18:09 -0700281 args.fTexSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -0800282 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000283}
284
egdaniel018fb622015-10-28 07:26:40 -0700285void GLEdge2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
286 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700287 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700288 const Edge2PtConicalEffect& data = processor.cast<Edge2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000289 SkScalar radius0 = data.radius();
290 SkScalar diffRadius = data.diffRadius();
291
292 if (fCachedRadius != radius0 ||
293 fCachedDiffRadius != diffRadius) {
294
halcanary9d524f22016-03-29 09:03:52 -0700295 pdman.set3f(fParamUni, SkScalarToFloat(radius0),
jvanverthde11ee42016-02-26 13:58:40 -0800296 SkScalarToFloat(SkScalarMul(radius0, radius0)), SkScalarToFloat(diffRadius));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000297 fCachedRadius = radius0;
298 fCachedDiffRadius = diffRadius;
299 }
300}
301
joshualittb0a8a372014-09-23 09:50:21 -0700302void GLEdge2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700303 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700304 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000305}
306
307//////////////////////////////////////////////////////////////////////////////
308// Focal Conical Gradients
309//////////////////////////////////////////////////////////////////////////////
310
311static 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 const SkPoint& focal = shader.getStartCenter();
318 const SkPoint& centerEnd = shader.getEndCenter();
319 SkScalar radius = shader.getEndRadius();
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000320 SkScalar invRadius = 1.f / radius;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000321
322 SkMatrix matrix;
323
324 matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
325 matrix.postScale(invRadius, invRadius);
326
327 SkPoint focalTrans;
328 matrix.mapPoints(&focalTrans, &focal, 1);
329 *focalX = focalTrans.length();
330
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000331 if (0.f != *focalX) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000332 SkScalar invFocalX = SkScalarInvert(*focalX);
333 SkMatrix rot;
334 rot.setSinCos(-SkScalarMul(invFocalX, focalTrans.fY),
335 SkScalarMul(invFocalX, focalTrans.fX));
336 matrix.postConcat(rot);
337 }
338
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000339 matrix.postTranslate(-(*focalX), 0.f);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000340
341 // If the focal point is touching the edge of the circle it will
342 // cause a degenerate case that must be handled separately
egdaniel8405ef92014-06-09 11:57:28 -0700343 // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
344 // stability trade off versus the linear approx used in the Edge Shader
345 if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000346 return kEdge_ConicalType;
347 }
348
349 // Scale factor 1 / (1 - focalX * focalX)
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000350 SkScalar oneMinusF2 = 1.f - SkScalarMul(*focalX, *focalX);
reed80ea19c2015-05-12 10:37:34 -0700351 SkScalar s = SkScalarInvert(oneMinusF2);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000352
353
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000354 if (s >= 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000355 conicalType = kInside_ConicalType;
356 matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
357 } else {
358 conicalType = kOutside_ConicalType;
359 matrix.postScale(s, s);
360 }
361
362 invLMatrix->postConcat(matrix);
363
364 return conicalType;
365}
366
367//////////////////////////////////////////////////////////////////////////////
368
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000369class FocalOutside2PtConicalEffect : public GrGradientEffect {
370public:
371
joshualittb0a8a372014-09-23 09:50:21 -0700372 static GrFragmentProcessor* Create(GrContext* ctx,
373 const SkTwoPointConicalGradient& shader,
374 const SkMatrix& matrix,
375 SkShader::TileMode tm,
376 SkScalar focalX) {
bsalomon4a339522015-10-06 08:40:50 -0700377 return new FocalOutside2PtConicalEffect(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000378 }
379
380 virtual ~FocalOutside2PtConicalEffect() { }
381
mtklein36352bf2015-03-25 18:17:31 -0700382 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -0800383 return "Two-Point Conical Gradient Focal Outside";
384 }
385
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000386 bool isFlipped() const { return fIsFlipped; }
387 SkScalar focal() const { return fFocalX; }
388
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000389private:
egdaniel57d3b032015-11-13 11:57:27 -0800390 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700391
egdaniel57d3b032015-11-13 11:57:27 -0800392 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700393
mtklein36352bf2015-03-25 18:17:31 -0700394 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700395 const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000396 return (INHERITED::onIsEqual(sBase) &&
397 this->fFocalX == s.fFocalX &&
398 this->fIsFlipped == s.fIsFlipped);
399 }
400
401 FocalOutside2PtConicalEffect(GrContext* ctx,
402 const SkTwoPointConicalGradient& shader,
403 const SkMatrix& matrix,
404 SkShader::TileMode tm,
405 SkScalar focalX)
bsalomon4a339522015-10-06 08:40:50 -0700406 : INHERITED(ctx, shader, matrix, tm)
joshualittb2456052015-07-08 09:36:59 -0700407 , fFocalX(focalX)
408 , fIsFlipped(shader.isFlippedGrad()) {
joshualitteb2a6762014-12-04 11:35:33 -0800409 this->initClassID<FocalOutside2PtConicalEffect>();
410 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000411
joshualittb0a8a372014-09-23 09:50:21 -0700412 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000413
414 SkScalar fFocalX;
415 bool fIsFlipped;
416
417 typedef GrGradientEffect INHERITED;
418};
419
420class GLFocalOutside2PtConicalEffect : public GrGLGradientEffect {
421public:
joshualitteb2a6762014-12-04 11:35:33 -0800422 GLFocalOutside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000423 virtual ~GLFocalOutside2PtConicalEffect() { }
424
wangyix7c157a92015-07-22 15:08:53 -0700425 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000426
jvanverthcfc18862015-04-28 08:48:20 -0700427 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000428
429protected:
egdaniel018fb622015-10-28 07:26:40 -0700430 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700431
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000432 UniformHandle fParamUni;
433
434 const char* fVSVaryingName;
435 const char* fFSVaryingName;
436
437 bool fIsFlipped;
438
439 // @{
440 /// Values last uploaded as uniforms
441
442 SkScalar fCachedFocal;
443
444 // @}
445
446private:
447 typedef GrGLGradientEffect INHERITED;
448
449};
450
egdaniel57d3b032015-11-13 11:57:27 -0800451void FocalOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
452 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800453 GLFocalOutside2PtConicalEffect::GenKey(*this, caps, b);
454}
455
egdaniel57d3b032015-11-13 11:57:27 -0800456GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700457 return new GLFocalOutside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000458}
459
joshualittb0a8a372014-09-23 09:50:21 -0700460GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000461
joshualitt01258472014-09-22 10:29:30 -0700462/*
463 * All Two point conical gradient test create functions may occasionally create edge case shaders
464 */
bsalomonc21b09e2015-08-28 18:46:56 -0700465const GrFragmentProcessor* FocalOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700466 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000467 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000468 SkPoint center2;
469 SkScalar radius2;
470 do {
joshualitt0067ff52015-07-08 14:26:19 -0700471 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000472 // Need to make sure the centers are not the same or else focal point will be inside
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000473 } while (center1 == center2);
474 SkPoint diff = center2 - center1;
475 SkScalar diffLen = diff.length();
476 // Below makes sure that the focal point is not contained within circle two
joshualitt0067ff52015-07-08 14:26:19 -0700477 radius2 = d->fRandom->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000478
479 SkColor colors[kMaxRandomGradientColors];
480 SkScalar stopsArray[kMaxRandomGradientColors];
481 SkScalar* stops = stopsArray;
482 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700483 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
reed8a21c9f2016-03-08 18:50:00 -0800484 auto shader = SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
485 colors, stops, colorCount, tm);
bsalomonc21b09e2015-08-28 18:46:56 -0700486 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700487 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700488 GrAlwaysAssert(fp);
489 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000490}
491
joshualitteb2a6762014-12-04 11:35:33 -0800492GLFocalOutside2PtConicalEffect::GLFocalOutside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -0700493 : fVSVaryingName(nullptr)
494 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000495 , fCachedFocal(SK_ScalarMax) {
joshualittb0a8a372014-09-23 09:50:21 -0700496 const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000497 fIsFlipped = data.isFlipped();
498}
499
wangyix7c157a92015-07-22 15:08:53 -0700500void GLFocalOutside2PtConicalEffect::emitCode(EmitArgs& args) {
501 const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -0800502 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
503 this->emitUniforms(uniformHandler, ge);
jvanverthde11ee42016-02-26 13:58:40 -0800504 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
505 kVec2f_GrSLType, kDefault_GrSLPrecision,
506 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000507 SkString tName("t");
508 SkString p0; // focalX
509 SkString p1; // 1 - focalX * focalX
510
jvanverthde11ee42016-02-26 13:58:40 -0800511 p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
512 p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000513
514 // if we have a vec3 from being in perspective, convert it to a vec2 first
cdalton85285412016-02-18 12:37:07 -0800515 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
egdaniel4ca2e602015-11-18 08:01:26 -0800516 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000517 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000518
519 // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
520
521 // output will default to transparent black (we simply won't write anything
522 // else to it if invalid, instead of discarding or returning prematurely)
egdaniel4ca2e602015-11-18 08:01:26 -0800523 fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000524
egdaniel4ca2e602015-11-18 08:01:26 -0800525 fragBuilder->codeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D);
526 fragBuilder->codeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D);
527 fragBuilder->codeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000528
529 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
530 // If so we must also flip sign on sqrt
531 if (!fIsFlipped) {
egdaniel4ca2e602015-11-18 08:01:26 -0800532 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + sqrt(d);\n", tName.c_str(),
533 coords2D, p0.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000534 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800535 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s - sqrt(d);\n", tName.c_str(),
536 coords2D, p0.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000537 }
538
egdaniel4ca2e602015-11-18 08:01:26 -0800539 fragBuilder->codeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str());
540 fragBuilder->codeAppend("\t\t");
egdaniel7ea439b2015-12-03 09:20:44 -0800541 this->emitColor(fragBuilder,
542 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -0800543 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800544 ge,
545 tName.c_str(),
546 args.fOutputColor,
547 args.fInputColor,
cdalton3f6f76f2016-04-11 12:18:09 -0700548 args.fTexSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -0800549 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000550}
551
egdaniel018fb622015-10-28 07:26:40 -0700552void GLFocalOutside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
553 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700554 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700555 const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000556 SkASSERT(data.isFlipped() == fIsFlipped);
557 SkScalar focal = data.focal();
558
559 if (fCachedFocal != focal) {
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000560 SkScalar oneMinus2F = 1.f - SkScalarMul(focal, focal);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000561
jvanverthde11ee42016-02-26 13:58:40 -0800562 pdman.set2f(fParamUni, SkScalarToFloat(focal), SkScalarToFloat(oneMinus2F));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000563 fCachedFocal = focal;
564 }
565}
566
joshualittb0a8a372014-09-23 09:50:21 -0700567void GLFocalOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700568 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -0700569 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -0700570 key[0] = GenBaseGradientKey(processor);
571 key[1] = processor.cast<FocalOutside2PtConicalEffect>().isFlipped();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000572}
573
574//////////////////////////////////////////////////////////////////////////////
575
576class GLFocalInside2PtConicalEffect;
577
578class FocalInside2PtConicalEffect : public GrGradientEffect {
579public:
580
joshualittb0a8a372014-09-23 09:50:21 -0700581 static GrFragmentProcessor* Create(GrContext* ctx,
582 const SkTwoPointConicalGradient& shader,
583 const SkMatrix& matrix,
584 SkShader::TileMode tm,
585 SkScalar focalX) {
bsalomon4a339522015-10-06 08:40:50 -0700586 return new FocalInside2PtConicalEffect(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000587 }
588
589 virtual ~FocalInside2PtConicalEffect() {}
590
mtklein36352bf2015-03-25 18:17:31 -0700591 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -0800592 return "Two-Point Conical Gradient Focal Inside";
593 }
594
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000595 SkScalar focal() const { return fFocalX; }
596
egdaniel57d3b032015-11-13 11:57:27 -0800597 typedef GLFocalInside2PtConicalEffect GLSLProcessor;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000598
599private:
egdaniel57d3b032015-11-13 11:57:27 -0800600 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700601
egdaniel57d3b032015-11-13 11:57:27 -0800602 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700603
mtklein36352bf2015-03-25 18:17:31 -0700604 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700605 const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000606 return (INHERITED::onIsEqual(sBase) &&
607 this->fFocalX == s.fFocalX);
608 }
609
610 FocalInside2PtConicalEffect(GrContext* ctx,
611 const SkTwoPointConicalGradient& shader,
612 const SkMatrix& matrix,
613 SkShader::TileMode tm,
614 SkScalar focalX)
bsalomon4a339522015-10-06 08:40:50 -0700615 : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX) {
joshualitteb2a6762014-12-04 11:35:33 -0800616 this->initClassID<FocalInside2PtConicalEffect>();
617 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000618
joshualittb0a8a372014-09-23 09:50:21 -0700619 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000620
621 SkScalar fFocalX;
622
623 typedef GrGradientEffect INHERITED;
624};
625
626class GLFocalInside2PtConicalEffect : public GrGLGradientEffect {
627public:
joshualitteb2a6762014-12-04 11:35:33 -0800628 GLFocalInside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000629 virtual ~GLFocalInside2PtConicalEffect() {}
630
wangyix7c157a92015-07-22 15:08:53 -0700631 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000632
jvanverthcfc18862015-04-28 08:48:20 -0700633 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000634
635protected:
egdaniel018fb622015-10-28 07:26:40 -0700636 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700637
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000638 UniformHandle fFocalUni;
639
640 const char* fVSVaryingName;
641 const char* fFSVaryingName;
642
643 // @{
644 /// Values last uploaded as uniforms
645
646 SkScalar fCachedFocal;
647
648 // @}
649
650private:
651 typedef GrGLGradientEffect INHERITED;
652
653};
654
egdaniel57d3b032015-11-13 11:57:27 -0800655void FocalInside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
656 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800657 GLFocalInside2PtConicalEffect::GenKey(*this, caps, b);
658}
659
egdaniel57d3b032015-11-13 11:57:27 -0800660GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700661 return new GLFocalInside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000662}
663
joshualittb0a8a372014-09-23 09:50:21 -0700664GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000665
joshualitt01258472014-09-22 10:29:30 -0700666/*
667 * All Two point conical gradient test create functions may occasionally create edge case shaders
668 */
bsalomonc21b09e2015-08-28 18:46:56 -0700669const GrFragmentProcessor* FocalInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700670 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000671 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000672 SkPoint center2;
673 SkScalar radius2;
674 do {
joshualitt0067ff52015-07-08 14:26:19 -0700675 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000676 // Below makes sure radius2 is larger enouch such that the focal point
677 // is inside the end circle
joshualitt0067ff52015-07-08 14:26:19 -0700678 SkScalar increase = d->fRandom->nextUScalar1();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000679 SkPoint diff = center2 - center1;
680 SkScalar diffLen = diff.length();
681 radius2 = diffLen + increase;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000682 // If the circles are identical the factory will give us an empty shader.
683 } while (radius1 == radius2 && center1 == center2);
684
685 SkColor colors[kMaxRandomGradientColors];
686 SkScalar stopsArray[kMaxRandomGradientColors];
687 SkScalar* stops = stopsArray;
688 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700689 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
reed8a21c9f2016-03-08 18:50:00 -0800690 auto shader = SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
691 colors, stops, colorCount, tm);
bsalomonc21b09e2015-08-28 18:46:56 -0700692 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700693 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700694 GrAlwaysAssert(fp);
joshualittb0a8a372014-09-23 09:50:21 -0700695 return fp;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000696}
697
joshualitteb2a6762014-12-04 11:35:33 -0800698GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrProcessor&)
halcanary96fcdcc2015-08-27 07:41:13 -0700699 : fVSVaryingName(nullptr)
700 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000701 , fCachedFocal(SK_ScalarMax) {}
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000702
wangyix7c157a92015-07-22 15:08:53 -0700703void GLFocalInside2PtConicalEffect::emitCode(EmitArgs& args) {
704 const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -0800705 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
706 this->emitUniforms(uniformHandler, ge);
cdalton5e58cee2016-02-11 12:49:47 -0800707 fFocalUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800708 kFloat_GrSLType, kDefault_GrSLPrecision,
709 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000710 SkString tName("t");
711
712 // this is the distance along x-axis from the end center to focal point in
713 // transformed coordinates
egdaniel7ea439b2015-12-03 09:20:44 -0800714 GrGLSLShaderVar focal = uniformHandler->getUniformVariable(fFocalUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000715
716 // if we have a vec3 from being in perspective, convert it to a vec2 first
cdalton85285412016-02-18 12:37:07 -0800717 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
egdaniel4ca2e602015-11-18 08:01:26 -0800718 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000719 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000720
721 // t = p.x * focalX + length(p)
egdaniel4ca2e602015-11-18 08:01:26 -0800722 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(),
egdaniel7ea439b2015-12-03 09:20:44 -0800723 coords2D, focal.c_str(), coords2D);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000724
egdaniel7ea439b2015-12-03 09:20:44 -0800725 this->emitColor(fragBuilder,
726 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -0800727 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800728 ge,
729 tName.c_str(),
730 args.fOutputColor,
731 args.fInputColor,
cdalton3f6f76f2016-04-11 12:18:09 -0700732 args.fTexSamplers);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000733}
734
egdaniel018fb622015-10-28 07:26:40 -0700735void GLFocalInside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
736 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700737 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700738 const FocalInside2PtConicalEffect& data = processor.cast<FocalInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000739 SkScalar focal = data.focal();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000740
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000741 if (fCachedFocal != focal) {
kkinnunen7510b222014-07-30 00:04:16 -0700742 pdman.set1f(fFocalUni, SkScalarToFloat(focal));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000743 fCachedFocal = focal;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000744 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000745}
746
joshualittb0a8a372014-09-23 09:50:21 -0700747void GLFocalInside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700748 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700749 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000750}
751
752//////////////////////////////////////////////////////////////////////////////
753// Circle Conical Gradients
754//////////////////////////////////////////////////////////////////////////////
755
756struct CircleConicalInfo {
757 SkPoint fCenterEnd;
758 SkScalar fA;
759 SkScalar fB;
760 SkScalar fC;
761};
762
763// Returns focal distance along x-axis in transformed coords
764static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
765 SkMatrix* invLMatrix, CircleConicalInfo* info) {
766 // Inverse of the current local matrix is passed in then,
767 // translate and scale such that start circle is on the origin and has radius 1
768 const SkPoint& centerStart = shader.getStartCenter();
769 const SkPoint& centerEnd = shader.getEndCenter();
770 SkScalar radiusStart = shader.getStartRadius();
771 SkScalar radiusEnd = shader.getEndRadius();
772
773 SkMatrix matrix;
774
775 matrix.setTranslate(-centerStart.fX, -centerStart.fY);
776
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000777 SkScalar invStartRad = 1.f / radiusStart;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000778 matrix.postScale(invStartRad, invStartRad);
779
780 radiusEnd /= radiusStart;
781
782 SkPoint centerEndTrans;
783 matrix.mapPoints(&centerEndTrans, &centerEnd, 1);
784
785 SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
786 - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
787
788 // Check to see if start circle is inside end circle with edges touching.
789 // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
egdaniel8405ef92014-06-09 11:57:28 -0700790 // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
791 // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
792 // still accurate.
793 if (SkScalarAbs(A) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000794 return kEdge_ConicalType;
795 }
796
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000797 SkScalar C = 1.f / A;
798 SkScalar B = (radiusEnd - 1.f) * C;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000799
800 matrix.postScale(C, C);
801
802 invLMatrix->postConcat(matrix);
803
804 info->fCenterEnd = centerEndTrans;
805 info->fA = A;
806 info->fB = B;
807 info->fC = C;
808
809 // if A ends up being negative, the start circle is contained completely inside the end cirlce
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000810 if (A < 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000811 return kInside_ConicalType;
812 }
813 return kOutside_ConicalType;
814}
815
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000816class CircleInside2PtConicalEffect : public GrGradientEffect {
817public:
818
joshualittb0a8a372014-09-23 09:50:21 -0700819 static GrFragmentProcessor* Create(GrContext* ctx,
820 const SkTwoPointConicalGradient& shader,
821 const SkMatrix& matrix,
822 SkShader::TileMode tm,
823 const CircleConicalInfo& info) {
bsalomon4a339522015-10-06 08:40:50 -0700824 return new CircleInside2PtConicalEffect(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000825 }
826
827 virtual ~CircleInside2PtConicalEffect() {}
828
mtklein36352bf2015-03-25 18:17:31 -0700829 const char* name() const override { return "Two-Point Conical Gradient Inside"; }
joshualitteb2a6762014-12-04 11:35:33 -0800830
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000831 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
832 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
833 SkScalar A() const { return fInfo.fA; }
834 SkScalar B() const { return fInfo.fB; }
835 SkScalar C() const { return fInfo.fC; }
836
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000837private:
egdaniel57d3b032015-11-13 11:57:27 -0800838 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700839
egdaniel57d3b032015-11-13 11:57:27 -0800840 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
841 GrProcessorKeyBuilder* b) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700842
mtklein36352bf2015-03-25 18:17:31 -0700843 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700844 const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000845 return (INHERITED::onIsEqual(sBase) &&
846 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
847 this->fInfo.fA == s.fInfo.fA &&
848 this->fInfo.fB == s.fInfo.fB &&
849 this->fInfo.fC == s.fInfo.fC);
850 }
851
852 CircleInside2PtConicalEffect(GrContext* ctx,
853 const SkTwoPointConicalGradient& shader,
854 const SkMatrix& matrix,
855 SkShader::TileMode tm,
856 const CircleConicalInfo& info)
bsalomon4a339522015-10-06 08:40:50 -0700857 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
joshualitteb2a6762014-12-04 11:35:33 -0800858 this->initClassID<CircleInside2PtConicalEffect>();
859 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000860
joshualittb0a8a372014-09-23 09:50:21 -0700861 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000862
863 const CircleConicalInfo fInfo;
864
865 typedef GrGradientEffect INHERITED;
866};
867
868class GLCircleInside2PtConicalEffect : public GrGLGradientEffect {
869public:
joshualitteb2a6762014-12-04 11:35:33 -0800870 GLCircleInside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000871 virtual ~GLCircleInside2PtConicalEffect() {}
872
wangyix7c157a92015-07-22 15:08:53 -0700873 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000874
jvanverthcfc18862015-04-28 08:48:20 -0700875 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000876
877protected:
egdaniel018fb622015-10-28 07:26:40 -0700878 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700879
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000880 UniformHandle fCenterUni;
881 UniformHandle fParamUni;
882
883 const char* fVSVaryingName;
884 const char* fFSVaryingName;
885
886 // @{
887 /// Values last uploaded as uniforms
888
889 SkScalar fCachedCenterX;
890 SkScalar fCachedCenterY;
891 SkScalar fCachedA;
892 SkScalar fCachedB;
893 SkScalar fCachedC;
894
895 // @}
896
897private:
898 typedef GrGLGradientEffect INHERITED;
899
900};
901
egdaniel57d3b032015-11-13 11:57:27 -0800902void CircleInside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
903 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800904 GLCircleInside2PtConicalEffect::GenKey(*this, caps, b);
905}
906
egdaniel57d3b032015-11-13 11:57:27 -0800907GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700908 return new GLCircleInside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000909}
910
joshualittb0a8a372014-09-23 09:50:21 -0700911GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000912
joshualitt01258472014-09-22 10:29:30 -0700913/*
914 * All Two point conical gradient test create functions may occasionally create edge case shaders
915 */
bsalomonc21b09e2015-08-28 18:46:56 -0700916const GrFragmentProcessor* CircleInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700917 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
918 SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000919 SkPoint center2;
920 SkScalar radius2;
921 do {
joshualitt0067ff52015-07-08 14:26:19 -0700922 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000923 // Below makes sure that circle one is contained within circle two
joshualitt0067ff52015-07-08 14:26:19 -0700924 SkScalar increase = d->fRandom->nextUScalar1();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000925 SkPoint diff = center2 - center1;
926 SkScalar diffLen = diff.length();
927 radius2 = radius1 + diffLen + increase;
928 // If the circles are identical the factory will give us an empty shader.
929 } while (radius1 == radius2 && center1 == center2);
930
931 SkColor colors[kMaxRandomGradientColors];
932 SkScalar stopsArray[kMaxRandomGradientColors];
933 SkScalar* stops = stopsArray;
934 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700935 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
reed8a21c9f2016-03-08 18:50:00 -0800936 auto shader = SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
937 colors, stops, colorCount, tm);
bsalomonc21b09e2015-08-28 18:46:56 -0700938 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700939 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700940 GrAlwaysAssert(fp);
joshualitt8ca93e72015-07-08 06:51:43 -0700941 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000942}
943
joshualitteb2a6762014-12-04 11:35:33 -0800944GLCircleInside2PtConicalEffect::GLCircleInside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -0700945 : fVSVaryingName(nullptr)
946 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000947 , fCachedCenterX(SK_ScalarMax)
948 , fCachedCenterY(SK_ScalarMax)
949 , fCachedA(SK_ScalarMax)
950 , fCachedB(SK_ScalarMax)
951 , fCachedC(SK_ScalarMax) {}
952
wangyix7c157a92015-07-22 15:08:53 -0700953void GLCircleInside2PtConicalEffect::emitCode(EmitArgs& args) {
954 const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -0800955 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
956 this->emitUniforms(uniformHandler, ge);
cdalton5e58cee2016-02-11 12:49:47 -0800957 fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800958 kVec2f_GrSLType, kDefault_GrSLPrecision,
959 "Conical2FSCenter");
cdalton5e58cee2016-02-11 12:49:47 -0800960 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800961 kVec3f_GrSLType, kDefault_GrSLPrecision,
962 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000963 SkString tName("t");
964
egdaniel7ea439b2015-12-03 09:20:44 -0800965 GrGLSLShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000966 // params.x = A
967 // params.y = B
968 // params.z = C
egdaniel7ea439b2015-12-03 09:20:44 -0800969 GrGLSLShaderVar params = uniformHandler->getUniformVariable(fParamUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000970
971 // if we have a vec3 from being in perspective, convert it to a vec2 first
cdalton85285412016-02-18 12:37:07 -0800972 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
egdaniel4ca2e602015-11-18 08:01:26 -0800973 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000974 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000975
976 // p = coords2D
977 // e = center end
978 // r = radius end
979 // A = dot(e, e) - r^2 + 2 * r - 1
980 // B = (r -1) / A
981 // C = 1 / A
982 // d = dot(e, p) + B
983 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
egdaniel4ca2e602015-11-18 08:01:26 -0800984 fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
985 fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
986 params.c_str());
987 fragBuilder->codeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
988 tName.c_str(), params.c_str(), params.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000989
egdaniel7ea439b2015-12-03 09:20:44 -0800990 this->emitColor(fragBuilder,
991 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -0800992 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800993 ge,
994 tName.c_str(),
995 args.fOutputColor,
996 args.fInputColor,
cdalton3f6f76f2016-04-11 12:18:09 -0700997 args.fTexSamplers);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000998}
999
egdaniel018fb622015-10-28 07:26:40 -07001000void GLCircleInside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1001 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -07001002 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -07001003 const CircleInside2PtConicalEffect& data = processor.cast<CircleInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001004 SkScalar centerX = data.centerX();
1005 SkScalar centerY = data.centerY();
1006 SkScalar A = data.A();
1007 SkScalar B = data.B();
1008 SkScalar C = data.C();
1009
1010 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1011 fCachedA != A || fCachedB != B || fCachedC != C) {
1012
kkinnunen7510b222014-07-30 00:04:16 -07001013 pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1014 pdman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001015
1016 fCachedCenterX = centerX;
1017 fCachedCenterY = centerY;
1018 fCachedA = A;
1019 fCachedB = B;
1020 fCachedC = C;
1021 }
1022}
1023
joshualittb0a8a372014-09-23 09:50:21 -07001024void GLCircleInside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -07001025 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -07001026 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001027}
1028
1029//////////////////////////////////////////////////////////////////////////////
1030
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001031class CircleOutside2PtConicalEffect : public GrGradientEffect {
1032public:
1033
joshualittb0a8a372014-09-23 09:50:21 -07001034 static GrFragmentProcessor* Create(GrContext* ctx,
1035 const SkTwoPointConicalGradient& shader,
1036 const SkMatrix& matrix,
1037 SkShader::TileMode tm,
1038 const CircleConicalInfo& info) {
bsalomon4a339522015-10-06 08:40:50 -07001039 return new CircleOutside2PtConicalEffect(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001040 }
1041
1042 virtual ~CircleOutside2PtConicalEffect() {}
1043
mtklein36352bf2015-03-25 18:17:31 -07001044 const char* name() const override { return "Two-Point Conical Gradient Outside"; }
joshualitteb2a6762014-12-04 11:35:33 -08001045
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001046 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
1047 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
1048 SkScalar A() const { return fInfo.fA; }
1049 SkScalar B() const { return fInfo.fB; }
1050 SkScalar C() const { return fInfo.fC; }
1051 SkScalar tLimit() const { return fTLimit; }
1052 bool isFlipped() const { return fIsFlipped; }
1053
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001054private:
egdaniel57d3b032015-11-13 11:57:27 -08001055 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -07001056
egdaniel57d3b032015-11-13 11:57:27 -08001057 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -07001058
mtklein36352bf2015-03-25 18:17:31 -07001059 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -07001060 const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001061 return (INHERITED::onIsEqual(sBase) &&
1062 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
1063 this->fInfo.fA == s.fInfo.fA &&
1064 this->fInfo.fB == s.fInfo.fB &&
1065 this->fInfo.fC == s.fInfo.fC &&
1066 this->fTLimit == s.fTLimit &&
1067 this->fIsFlipped == s.fIsFlipped);
1068 }
1069
1070 CircleOutside2PtConicalEffect(GrContext* ctx,
1071 const SkTwoPointConicalGradient& shader,
1072 const SkMatrix& matrix,
1073 SkShader::TileMode tm,
1074 const CircleConicalInfo& info)
bsalomon4a339522015-10-06 08:40:50 -07001075 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
joshualitteb2a6762014-12-04 11:35:33 -08001076 this->initClassID<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001077 if (shader.getStartRadius() != shader.getEndRadius()) {
reed80ea19c2015-05-12 10:37:34 -07001078 fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001079 } else {
1080 fTLimit = SK_ScalarMin;
1081 }
1082
1083 fIsFlipped = shader.isFlippedGrad();
1084 }
1085
joshualittb0a8a372014-09-23 09:50:21 -07001086 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001087
1088 const CircleConicalInfo fInfo;
1089 SkScalar fTLimit;
1090 bool fIsFlipped;
1091
1092 typedef GrGradientEffect INHERITED;
1093};
1094
1095class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect {
1096public:
joshualitteb2a6762014-12-04 11:35:33 -08001097 GLCircleOutside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001098 virtual ~GLCircleOutside2PtConicalEffect() {}
1099
wangyix7c157a92015-07-22 15:08:53 -07001100 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001101
jvanverthcfc18862015-04-28 08:48:20 -07001102 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001103
1104protected:
egdaniel018fb622015-10-28 07:26:40 -07001105 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -07001106
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001107 UniformHandle fCenterUni;
1108 UniformHandle fParamUni;
1109
1110 const char* fVSVaryingName;
1111 const char* fFSVaryingName;
1112
1113 bool fIsFlipped;
1114
1115 // @{
1116 /// Values last uploaded as uniforms
1117
1118 SkScalar fCachedCenterX;
1119 SkScalar fCachedCenterY;
1120 SkScalar fCachedA;
1121 SkScalar fCachedB;
1122 SkScalar fCachedC;
1123 SkScalar fCachedTLimit;
1124
1125 // @}
1126
1127private:
1128 typedef GrGLGradientEffect INHERITED;
1129
1130};
1131
egdaniel57d3b032015-11-13 11:57:27 -08001132void CircleOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
1133 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -08001134 GLCircleOutside2PtConicalEffect::GenKey(*this, caps, b);
1135}
1136
egdaniel57d3b032015-11-13 11:57:27 -08001137GrGLSLFragmentProcessor* CircleOutside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -07001138 return new GLCircleOutside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001139}
1140
joshualittb0a8a372014-09-23 09:50:21 -07001141GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001142
joshualitt01258472014-09-22 10:29:30 -07001143/*
1144 * All Two point conical gradient test create functions may occasionally create edge case shaders
1145 */
bsalomonc21b09e2015-08-28 18:46:56 -07001146const GrFragmentProcessor* CircleOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -07001147 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
1148 SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001149 SkPoint center2;
1150 SkScalar radius2;
1151 SkScalar diffLen;
1152 do {
joshualitt0067ff52015-07-08 14:26:19 -07001153 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001154 // If the circles share a center than we can't be in the outside case
1155 } while (center1 == center2);
joshualitt01258472014-09-22 10:29:30 -07001156 SkPoint diff = center2 - center1;
1157 diffLen = diff.length();
1158 // Below makes sure that circle one is not contained within circle two
1159 // and have radius2 >= radius to match sorting on cpu side
joshualitt0067ff52015-07-08 14:26:19 -07001160 radius2 = radius1 + d->fRandom->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001161
1162 SkColor colors[kMaxRandomGradientColors];
1163 SkScalar stopsArray[kMaxRandomGradientColors];
1164 SkScalar* stops = stopsArray;
1165 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -07001166 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
reed8a21c9f2016-03-08 18:50:00 -08001167 auto shader = SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
1168 colors, stops, colorCount, tm);
bsalomonc21b09e2015-08-28 18:46:56 -07001169 const GrFragmentProcessor* fp = shader->asFragmentProcessor(
bsalomon4a339522015-10-06 08:40:50 -07001170 d->fContext,GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -07001171 GrAlwaysAssert(fp);
joshualitt8ca93e72015-07-08 06:51:43 -07001172 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001173}
1174
joshualitteb2a6762014-12-04 11:35:33 -08001175GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -07001176 : fVSVaryingName(nullptr)
1177 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001178 , fCachedCenterX(SK_ScalarMax)
1179 , fCachedCenterY(SK_ScalarMax)
1180 , fCachedA(SK_ScalarMax)
1181 , fCachedB(SK_ScalarMax)
1182 , fCachedC(SK_ScalarMax)
1183 , fCachedTLimit(SK_ScalarMax) {
joshualittb0a8a372014-09-23 09:50:21 -07001184 const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001185 fIsFlipped = data.isFlipped();
1186 }
1187
wangyix7c157a92015-07-22 15:08:53 -07001188void GLCircleOutside2PtConicalEffect::emitCode(EmitArgs& args) {
1189 const CircleOutside2PtConicalEffect& ge = args.fFp.cast<CircleOutside2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -08001190 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
1191 this->emitUniforms(uniformHandler, ge);
cdalton5e58cee2016-02-11 12:49:47 -08001192 fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -08001193 kVec2f_GrSLType, kDefault_GrSLPrecision,
1194 "Conical2FSCenter");
cdalton5e58cee2016-02-11 12:49:47 -08001195 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -08001196 kVec4f_GrSLType, kDefault_GrSLPrecision,
1197 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001198 SkString tName("t");
1199
egdaniel7ea439b2015-12-03 09:20:44 -08001200 GrGLSLShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001201 // params.x = A
1202 // params.y = B
1203 // params.z = C
egdaniel7ea439b2015-12-03 09:20:44 -08001204 GrGLSLShaderVar params = uniformHandler->getUniformVariable(fParamUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001205
1206 // if we have a vec3 from being in perspective, convert it to a vec2 first
cdalton85285412016-02-18 12:37:07 -08001207 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
egdaniel4ca2e602015-11-18 08:01:26 -08001208 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +00001209 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001210
1211 // output will default to transparent black (we simply won't write anything
1212 // else to it if invalid, instead of discarding or returning prematurely)
egdaniel4ca2e602015-11-18 08:01:26 -08001213 fragBuilder->codeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001214
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001215 // p = coords2D
1216 // e = center end
1217 // r = radius end
1218 // A = dot(e, e) - r^2 + 2 * r - 1
1219 // B = (r -1) / A
1220 // C = 1 / A
1221 // d = dot(e, p) + B
1222 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001223
egdaniel4ca2e602015-11-18 08:01:26 -08001224 fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
1225 fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
1226 params.c_str());
1227 fragBuilder->codeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(),
1228 params.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001229
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001230 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
1231 // If so we must also flip sign on sqrt
1232 if (!fIsFlipped) {
egdaniel4ca2e602015-11-18 08:01:26 -08001233 fragBuilder->codeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001234 } else {
egdaniel4ca2e602015-11-18 08:01:26 -08001235 fragBuilder->codeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001236 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001237
egdaniel7ea439b2015-12-03 09:20:44 -08001238 fragBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n",
1239 tName.c_str(), params.c_str());
egdaniel4ca2e602015-11-18 08:01:26 -08001240 fragBuilder->codeAppend("\t\t");
egdaniel7ea439b2015-12-03 09:20:44 -08001241 this->emitColor(fragBuilder,
1242 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -08001243 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -08001244 ge,
1245 tName.c_str(),
1246 args.fOutputColor,
1247 args.fInputColor,
cdalton3f6f76f2016-04-11 12:18:09 -07001248 args.fTexSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -08001249 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001250}
1251
egdaniel018fb622015-10-28 07:26:40 -07001252void GLCircleOutside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1253 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -07001254 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -07001255 const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +00001256 SkASSERT(data.isFlipped() == fIsFlipped);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001257 SkScalar centerX = data.centerX();
1258 SkScalar centerY = data.centerY();
1259 SkScalar A = data.A();
1260 SkScalar B = data.B();
1261 SkScalar C = data.C();
1262 SkScalar tLimit = data.tLimit();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001263
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001264 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1265 fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001266
kkinnunen7510b222014-07-30 00:04:16 -07001267 pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1268 pdman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001269 SkScalarToFloat(tLimit));
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001270
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001271 fCachedCenterX = centerX;
1272 fCachedCenterY = centerY;
1273 fCachedA = A;
1274 fCachedB = B;
1275 fCachedC = C;
1276 fCachedTLimit = tLimit;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001277 }
1278}
1279
joshualittb0a8a372014-09-23 09:50:21 -07001280void GLCircleOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -07001281 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -07001282 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -07001283 key[0] = GenBaseGradientKey(processor);
1284 key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001285}
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001286
1287//////////////////////////////////////////////////////////////////////////////
1288
joshualittb0a8a372014-09-23 09:50:21 -07001289GrFragmentProcessor* Gr2PtConicalGradientEffect::Create(GrContext* ctx,
1290 const SkTwoPointConicalGradient& shader,
1291 SkShader::TileMode tm,
1292 const SkMatrix* localMatrix) {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001293 SkMatrix matrix;
1294 if (!shader.getLocalMatrix().invert(&matrix)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001295 return nullptr;
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001296 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001297 if (localMatrix) {
1298 SkMatrix inv;
1299 if (!localMatrix->invert(&inv)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001300 return nullptr;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001301 }
1302 matrix.postConcat(inv);
1303 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001304
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001305 if (shader.getStartRadius() < kErrorTol) {
1306 SkScalar focalX;
1307 ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
1308 if (type == kInside_ConicalType) {
bsalomon4a339522015-10-06 08:40:50 -07001309 return FocalInside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001310 } else if(type == kEdge_ConicalType) {
1311 set_matrix_edge_conical(shader, &matrix);
bsalomon4a339522015-10-06 08:40:50 -07001312 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001313 } else {
bsalomon4a339522015-10-06 08:40:50 -07001314 return FocalOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001315 }
1316 }
1317
1318 CircleConicalInfo info;
1319 ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
1320
1321 if (type == kInside_ConicalType) {
bsalomon4a339522015-10-06 08:40:50 -07001322 return CircleInside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001323 } else if (type == kEdge_ConicalType) {
1324 set_matrix_edge_conical(shader, &matrix);
bsalomon4a339522015-10-06 08:40:50 -07001325 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001326 } else {
bsalomon4a339522015-10-06 08:40:50 -07001327 return CircleOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001328 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001329}
1330
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001331#endif