blob: 4633bc25e1817f370913ee1c73c9a0c89e8924a7 [file] [log] [blame]
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00002/*
3 * Copyright 2014 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00008
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00009#include "SkTwoPointConicalGradient_gpu.h"
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000010
11#include "SkTwoPointConicalGradient.h"
12
commit-bot@chromium.orgef93d292014-04-10 15:37:52 +000013#if SK_SUPPORT_GPU
joshualitt8ca93e72015-07-08 06:51:43 -070014#include "GrPaint.h"
egdaniel2d721d32015-11-11 13:06:05 -080015#include "glsl/GrGLSLFragmentShaderBuilder.h"
16#include "glsl/GrGLSLProgramBuilder.h"
egdaniel018fb622015-10-28 07:26:40 -070017#include "glsl/GrGLSLProgramDataManager.h"
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000018// For brevity
egdaniel018fb622015-10-28 07:26:40 -070019typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000020
commit-bot@chromium.org80894672014-04-22 21:24:22 +000021static const SkScalar kErrorTol = 0.00001f;
egdaniel8405ef92014-06-09 11:57:28 -070022static const SkScalar kEdgeErrorTol = 5.f * kErrorTol;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000023
24/**
25 * We have three general cases for 2pt conical gradients. First we always assume that
26 * the start radius <= end radius. Our first case (kInside_) is when the start circle
27 * is completely enclosed by the end circle. The second case (kOutside_) is the case
28 * when the start circle is either completely outside the end circle or the circles
29 * overlap. The final case (kEdge_) is when the start circle is inside the end one,
30 * but the two are just barely touching at 1 point along their edges.
31 */
32enum ConicalType {
33 kInside_ConicalType,
34 kOutside_ConicalType,
35 kEdge_ConicalType,
36};
37
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000038//////////////////////////////////////////////////////////////////////////////
39
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000040static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader,
41 SkMatrix* invLMatrix) {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000042 // Inverse of the current local matrix is passed in then,
43 // translate to center1, rotate so center2 is on x axis.
44 const SkPoint& center1 = shader.getStartCenter();
45 const SkPoint& center2 = shader.getEndCenter();
46
47 invLMatrix->postTranslate(-center1.fX, -center1.fY);
48
49 SkPoint diff = center2 - center1;
50 SkScalar diffLen = diff.length();
51 if (0 != diffLen) {
52 SkScalar invDiffLen = SkScalarInvert(diffLen);
53 SkMatrix rot;
54 rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY),
55 SkScalarMul(invDiffLen, diff.fX));
56 invLMatrix->postConcat(rot);
57 }
58}
59
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000060class Edge2PtConicalEffect : public GrGradientEffect {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000061public:
62
joshualittb0a8a372014-09-23 09:50:21 -070063 static GrFragmentProcessor* Create(GrContext* ctx,
64 const SkTwoPointConicalGradient& shader,
65 const SkMatrix& matrix,
66 SkShader::TileMode tm) {
bsalomon4a339522015-10-06 08:40:50 -070067 return new Edge2PtConicalEffect(ctx, shader, matrix, tm);
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000068 }
69
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000070 virtual ~Edge2PtConicalEffect() {}
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000071
mtklein36352bf2015-03-25 18:17:31 -070072 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -080073 return "Two-Point Conical Gradient Edge Touching";
74 }
75
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000076 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000077 SkScalar center() const { return fCenterX1; }
78 SkScalar diffRadius() const { return fDiffRadius; }
79 SkScalar radius() const { return fRadius0; }
80
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000081private:
egdaniel57d3b032015-11-13 11:57:27 -080082 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -070083
egdaniel57d3b032015-11-13 11:57:27 -080084 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -070085
mtklein36352bf2015-03-25 18:17:31 -070086 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -070087 const Edge2PtConicalEffect& s = sBase.cast<Edge2PtConicalEffect>();
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000088 return (INHERITED::onIsEqual(sBase) &&
89 this->fCenterX1 == s.fCenterX1 &&
90 this->fRadius0 == s.fRadius0 &&
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000091 this->fDiffRadius == s.fDiffRadius);
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000092 }
93
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000094 Edge2PtConicalEffect(GrContext* ctx,
95 const SkTwoPointConicalGradient& shader,
96 const SkMatrix& matrix,
97 SkShader::TileMode tm)
bsalomon4a339522015-10-06 08:40:50 -070098 : INHERITED(ctx, shader, matrix, tm),
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000099 fCenterX1(shader.getCenterX1()),
100 fRadius0(shader.getStartRadius()),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000101 fDiffRadius(shader.getDiffRadius()){
joshualitteb2a6762014-12-04 11:35:33 -0800102 this->initClassID<Edge2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000103 // We should only be calling this shader if we are degenerate case with touching circles
egdaniel8405ef92014-06-09 11:57:28 -0700104 // When deciding if we are in edge case, we scaled by the end radius for cases when the
joshualitt01258472014-09-22 10:29:30 -0700105 // start radius was close to zero, otherwise we scaled by the start radius. In addition
106 // Our test for the edge case in set_matrix_circle_conical has a higher tolerance so we
107 // need the sqrt value below
108 SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - fCenterX1) <
109 (fRadius0 < kErrorTol ? shader.getEndRadius() * kEdgeErrorTol :
110 fRadius0 * sqrt(kEdgeErrorTol)));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000111
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000112 // We pass the linear part of the quadratic as a varying.
113 // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
114 fBTransform = this->getCoordTransform();
115 SkMatrix& bMatrix = *fBTransform.accessMatrix();
116 SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius);
117 bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) +
118 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0]));
119 bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) +
120 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1]));
121 bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) +
122 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2]));
123 this->addCoordTransform(&fBTransform);
124 }
125
joshualittb0a8a372014-09-23 09:50:21 -0700126 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000127
128 // @{
129 // Cache of values - these can change arbitrarily, EXCEPT
130 // we shouldn't change between degenerate and non-degenerate?!
131
132 GrCoordTransform fBTransform;
133 SkScalar fCenterX1;
134 SkScalar fRadius0;
135 SkScalar fDiffRadius;
136
137 // @}
138
139 typedef GrGradientEffect INHERITED;
140};
141
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000142class GLEdge2PtConicalEffect : public GrGLGradientEffect {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000143public:
joshualitteb2a6762014-12-04 11:35:33 -0800144 GLEdge2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000145 virtual ~GLEdge2PtConicalEffect() { }
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000146
wangyix7c157a92015-07-22 15:08:53 -0700147 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000148
jvanverthcfc18862015-04-28 08:48:20 -0700149 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000150
151protected:
egdaniel018fb622015-10-28 07:26:40 -0700152 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700153
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000154 UniformHandle fParamUni;
155
156 const char* fVSVaryingName;
157 const char* fFSVaryingName;
158
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000159 // @{
160 /// Values last uploaded as uniforms
161
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000162 SkScalar fCachedRadius;
163 SkScalar fCachedDiffRadius;
164
165 // @}
166
167private:
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000168 typedef GrGLGradientEffect INHERITED;
169
170};
171
egdaniel57d3b032015-11-13 11:57:27 -0800172void Edge2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
173 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800174 GLEdge2PtConicalEffect::GenKey(*this, caps, b);
175}
176
egdaniel57d3b032015-11-13 11:57:27 -0800177GrGLSLFragmentProcessor* Edge2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700178 return new GLEdge2PtConicalEffect(*this);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000179}
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000180
joshualittb0a8a372014-09-23 09:50:21 -0700181GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Edge2PtConicalEffect);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000182
joshualitt01258472014-09-22 10:29:30 -0700183/*
184 * All Two point conical gradient test create functions may occasionally create edge case shaders
185 */
bsalomonc21b09e2015-08-28 18:46:56 -0700186const GrFragmentProcessor* Edge2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700187 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
188 SkScalar radius1 = d->fRandom->nextUScalar1();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000189 SkPoint center2;
190 SkScalar radius2;
191 do {
joshualitt0067ff52015-07-08 14:26:19 -0700192 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000193 // If the circles are identical the factory will give us an empty shader.
194 // This will happen if we pick identical centers
195 } while (center1 == center2);
196
197 // Below makes sure that circle one is contained within circle two
198 // and both circles are touching on an edge
199 SkPoint diff = center2 - center1;
200 SkScalar diffLen = diff.length();
201 radius2 = radius1 + diffLen;
202
203 SkColor colors[kMaxRandomGradientColors];
204 SkScalar stopsArray[kMaxRandomGradientColors];
205 SkScalar* stops = stopsArray;
206 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700207 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000208 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
209 center2, radius2,
210 colors, stops, colorCount,
211 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700212 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700213 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700214 GrAlwaysAssert(fp);
joshualittb0a8a372014-09-23 09:50:21 -0700215 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000216}
217
joshualitteb2a6762014-12-04 11:35:33 -0800218GLEdge2PtConicalEffect::GLEdge2PtConicalEffect(const GrProcessor&)
halcanary96fcdcc2015-08-27 07:41:13 -0700219 : fVSVaryingName(nullptr)
220 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000221 , fCachedRadius(-SK_ScalarMax)
222 , fCachedDiffRadius(-SK_ScalarMax) {}
223
wangyix7c157a92015-07-22 15:08:53 -0700224void GLEdge2PtConicalEffect::emitCode(EmitArgs& args) {
225 const Edge2PtConicalEffect& ge = args.fFp.cast<Edge2PtConicalEffect>();
226 this->emitUniforms(args.fBuilder, ge);
egdaniel2d721d32015-11-11 13:06:05 -0800227 fParamUni = args.fBuilder->addUniformArray(GrGLSLProgramBuilder::kFragment_Visibility,
bsalomon422f56f2014-12-09 10:18:12 -0800228 kFloat_GrSLType, kDefault_GrSLPrecision,
229 "Conical2FSParams", 3);
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
wangyix7c157a92015-07-22 15:08:53 -0700237 args.fBuilder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
238 args.fBuilder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
239 args.fBuilder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000240
241 // We interpolate the linear component in coords[1].
wangyix7c157a92015-07-22 15:08:53 -0700242 SkASSERT(args.fCoords[0].getType() == args.fCoords[1].getType());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000243 const char* coords2D;
244 SkString bVar;
egdaniel4ca2e602015-11-18 08:01:26 -0800245 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
wangyix7c157a92015-07-22 15:08:53 -0700246 if (kVec3f_GrSLType == args.fCoords[0].getType()) {
egdaniel4ca2e602015-11-18 08:01:26 -0800247 fragBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
wangyix7c157a92015-07-22 15:08:53 -0700248 args.fCoords[0].c_str(), args.fCoords[0].c_str(),
249 args.fCoords[1].c_str(), args.fCoords[1].c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000250 coords2D = "interpolants.xy";
251 bVar = "interpolants.z";
252 } else {
wangyix7c157a92015-07-22 15:08:53 -0700253 coords2D = args.fCoords[0].c_str();
254 bVar.printf("%s.x", args.fCoords[1].c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000255 }
256
257 // output will default to transparent black (we simply won't write anything
258 // else to it if invalid, instead of discarding or returning prematurely)
egdaniel4ca2e602015-11-18 08:01:26 -0800259 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 +0000260
261 // c = (x^2)+(y^2) - params[1]
egdaniel4ca2e602015-11-18 08:01:26 -0800262 fragBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000263 cName.c_str(), coords2D, coords2D, p1.c_str());
264
265 // linear case: t = -c/b
egdaniel4ca2e602015-11-18 08:01:26 -0800266 fragBuilder->codeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000267 cName.c_str(), bVar.c_str());
268
269 // if r(t) > 0, then t will be the x coordinate
egdaniel4ca2e602015-11-18 08:01:26 -0800270 fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000271 p2.c_str(), p0.c_str());
egdaniel4ca2e602015-11-18 08:01:26 -0800272 fragBuilder->codeAppend("\t");
273 this->emitColor(args.fBuilder,
274 fragBuilder,
275 ge,
276 tName.c_str(),
277 args.fOutputColor,
278 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700279 args.fSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -0800280 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000281}
282
egdaniel018fb622015-10-28 07:26:40 -0700283void GLEdge2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
284 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700285 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700286 const Edge2PtConicalEffect& data = processor.cast<Edge2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000287 SkScalar radius0 = data.radius();
288 SkScalar diffRadius = data.diffRadius();
289
290 if (fCachedRadius != radius0 ||
291 fCachedDiffRadius != diffRadius) {
292
293 float values[3] = {
294 SkScalarToFloat(radius0),
295 SkScalarToFloat(SkScalarMul(radius0, radius0)),
296 SkScalarToFloat(diffRadius)
297 };
298
kkinnunen7510b222014-07-30 00:04:16 -0700299 pdman.set1fv(fParamUni, 3, values);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000300 fCachedRadius = radius0;
301 fCachedDiffRadius = diffRadius;
302 }
303}
304
joshualittb0a8a372014-09-23 09:50:21 -0700305void GLEdge2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700306 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700307 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000308}
309
310//////////////////////////////////////////////////////////////////////////////
311// Focal Conical Gradients
312//////////////////////////////////////////////////////////////////////////////
313
314static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader,
315 SkMatrix* invLMatrix, SkScalar* focalX) {
316 // Inverse of the current local matrix is passed in then,
317 // translate, scale, and rotate such that endCircle is unit circle on x-axis,
318 // and focal point is at the origin.
319 ConicalType conicalType;
320 const SkPoint& focal = shader.getStartCenter();
321 const SkPoint& centerEnd = shader.getEndCenter();
322 SkScalar radius = shader.getEndRadius();
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000323 SkScalar invRadius = 1.f / radius;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000324
325 SkMatrix matrix;
326
327 matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
328 matrix.postScale(invRadius, invRadius);
329
330 SkPoint focalTrans;
331 matrix.mapPoints(&focalTrans, &focal, 1);
332 *focalX = focalTrans.length();
333
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000334 if (0.f != *focalX) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000335 SkScalar invFocalX = SkScalarInvert(*focalX);
336 SkMatrix rot;
337 rot.setSinCos(-SkScalarMul(invFocalX, focalTrans.fY),
338 SkScalarMul(invFocalX, focalTrans.fX));
339 matrix.postConcat(rot);
340 }
341
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000342 matrix.postTranslate(-(*focalX), 0.f);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000343
344 // If the focal point is touching the edge of the circle it will
345 // cause a degenerate case that must be handled separately
egdaniel8405ef92014-06-09 11:57:28 -0700346 // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
347 // stability trade off versus the linear approx used in the Edge Shader
348 if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000349 return kEdge_ConicalType;
350 }
351
352 // Scale factor 1 / (1 - focalX * focalX)
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000353 SkScalar oneMinusF2 = 1.f - SkScalarMul(*focalX, *focalX);
reed80ea19c2015-05-12 10:37:34 -0700354 SkScalar s = SkScalarInvert(oneMinusF2);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000355
356
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000357 if (s >= 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000358 conicalType = kInside_ConicalType;
359 matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
360 } else {
361 conicalType = kOutside_ConicalType;
362 matrix.postScale(s, s);
363 }
364
365 invLMatrix->postConcat(matrix);
366
367 return conicalType;
368}
369
370//////////////////////////////////////////////////////////////////////////////
371
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000372class FocalOutside2PtConicalEffect : public GrGradientEffect {
373public:
374
joshualittb0a8a372014-09-23 09:50:21 -0700375 static GrFragmentProcessor* Create(GrContext* ctx,
376 const SkTwoPointConicalGradient& shader,
377 const SkMatrix& matrix,
378 SkShader::TileMode tm,
379 SkScalar focalX) {
bsalomon4a339522015-10-06 08:40:50 -0700380 return new FocalOutside2PtConicalEffect(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000381 }
382
383 virtual ~FocalOutside2PtConicalEffect() { }
384
mtklein36352bf2015-03-25 18:17:31 -0700385 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -0800386 return "Two-Point Conical Gradient Focal Outside";
387 }
388
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000389 bool isFlipped() const { return fIsFlipped; }
390 SkScalar focal() const { return fFocalX; }
391
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000392private:
egdaniel57d3b032015-11-13 11:57:27 -0800393 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700394
egdaniel57d3b032015-11-13 11:57:27 -0800395 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700396
mtklein36352bf2015-03-25 18:17:31 -0700397 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700398 const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000399 return (INHERITED::onIsEqual(sBase) &&
400 this->fFocalX == s.fFocalX &&
401 this->fIsFlipped == s.fIsFlipped);
402 }
403
404 FocalOutside2PtConicalEffect(GrContext* ctx,
405 const SkTwoPointConicalGradient& shader,
406 const SkMatrix& matrix,
407 SkShader::TileMode tm,
408 SkScalar focalX)
bsalomon4a339522015-10-06 08:40:50 -0700409 : INHERITED(ctx, shader, matrix, tm)
joshualittb2456052015-07-08 09:36:59 -0700410 , fFocalX(focalX)
411 , fIsFlipped(shader.isFlippedGrad()) {
joshualitteb2a6762014-12-04 11:35:33 -0800412 this->initClassID<FocalOutside2PtConicalEffect>();
413 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000414
joshualittb0a8a372014-09-23 09:50:21 -0700415 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000416
417 SkScalar fFocalX;
418 bool fIsFlipped;
419
420 typedef GrGradientEffect INHERITED;
421};
422
423class GLFocalOutside2PtConicalEffect : public GrGLGradientEffect {
424public:
joshualitteb2a6762014-12-04 11:35:33 -0800425 GLFocalOutside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000426 virtual ~GLFocalOutside2PtConicalEffect() { }
427
wangyix7c157a92015-07-22 15:08:53 -0700428 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000429
jvanverthcfc18862015-04-28 08:48:20 -0700430 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000431
432protected:
egdaniel018fb622015-10-28 07:26:40 -0700433 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700434
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000435 UniformHandle fParamUni;
436
437 const char* fVSVaryingName;
438 const char* fFSVaryingName;
439
440 bool fIsFlipped;
441
442 // @{
443 /// Values last uploaded as uniforms
444
445 SkScalar fCachedFocal;
446
447 // @}
448
449private:
450 typedef GrGLGradientEffect INHERITED;
451
452};
453
egdaniel57d3b032015-11-13 11:57:27 -0800454void FocalOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
455 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800456 GLFocalOutside2PtConicalEffect::GenKey(*this, caps, b);
457}
458
egdaniel57d3b032015-11-13 11:57:27 -0800459GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700460 return new GLFocalOutside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000461}
462
joshualittb0a8a372014-09-23 09:50:21 -0700463GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000464
joshualitt01258472014-09-22 10:29:30 -0700465/*
466 * All Two point conical gradient test create functions may occasionally create edge case shaders
467 */
bsalomonc21b09e2015-08-28 18:46:56 -0700468const GrFragmentProcessor* FocalOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700469 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000470 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000471 SkPoint center2;
472 SkScalar radius2;
473 do {
joshualitt0067ff52015-07-08 14:26:19 -0700474 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000475 // 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 +0000476 } while (center1 == center2);
477 SkPoint diff = center2 - center1;
478 SkScalar diffLen = diff.length();
479 // Below makes sure that the focal point is not contained within circle two
joshualitt0067ff52015-07-08 14:26:19 -0700480 radius2 = d->fRandom->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000481
482 SkColor colors[kMaxRandomGradientColors];
483 SkScalar stopsArray[kMaxRandomGradientColors];
484 SkScalar* stops = stopsArray;
485 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700486 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000487 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
488 center2, radius2,
489 colors, stops, colorCount,
490 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700491 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700492 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700493 GrAlwaysAssert(fp);
494 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000495}
496
joshualitteb2a6762014-12-04 11:35:33 -0800497GLFocalOutside2PtConicalEffect::GLFocalOutside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -0700498 : fVSVaryingName(nullptr)
499 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000500 , fCachedFocal(SK_ScalarMax) {
joshualittb0a8a372014-09-23 09:50:21 -0700501 const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000502 fIsFlipped = data.isFlipped();
503}
504
wangyix7c157a92015-07-22 15:08:53 -0700505void GLFocalOutside2PtConicalEffect::emitCode(EmitArgs& args) {
506 const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>();
507 this->emitUniforms(args.fBuilder, ge);
egdaniel2d721d32015-11-11 13:06:05 -0800508 fParamUni = args.fBuilder->addUniformArray(GrGLSLProgramBuilder::kFragment_Visibility,
bsalomon422f56f2014-12-09 10:18:12 -0800509 kFloat_GrSLType, kDefault_GrSLPrecision,
510 "Conical2FSParams", 2);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000511 SkString tName("t");
512 SkString p0; // focalX
513 SkString p1; // 1 - focalX * focalX
514
wangyix7c157a92015-07-22 15:08:53 -0700515 args.fBuilder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
516 args.fBuilder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000517
518 // if we have a vec3 from being in perspective, convert it to a vec2 first
egdaniel4ca2e602015-11-18 08:01:26 -0800519 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
520 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000521 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000522
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)
egdaniel4ca2e602015-11-18 08:01:26 -0800527 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 +0000528
egdaniel4ca2e602015-11-18 08:01:26 -0800529 fragBuilder->codeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D);
530 fragBuilder->codeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D);
531 fragBuilder->codeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000532
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 if (!fIsFlipped) {
egdaniel4ca2e602015-11-18 08:01:26 -0800536 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + sqrt(d);\n", tName.c_str(),
537 coords2D, p0.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000538 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800539 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s - sqrt(d);\n", tName.c_str(),
540 coords2D, p0.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000541 }
542
egdaniel4ca2e602015-11-18 08:01:26 -0800543 fragBuilder->codeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str());
544 fragBuilder->codeAppend("\t\t");
545 this->emitColor(args.fBuilder,
546 fragBuilder,
547 ge,
548 tName.c_str(),
549 args.fOutputColor,
550 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700551 args.fSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -0800552 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000553}
554
egdaniel018fb622015-10-28 07:26:40 -0700555void GLFocalOutside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
556 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700557 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700558 const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000559 SkASSERT(data.isFlipped() == fIsFlipped);
560 SkScalar focal = data.focal();
561
562 if (fCachedFocal != focal) {
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000563 SkScalar oneMinus2F = 1.f - SkScalarMul(focal, focal);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000564
565 float values[2] = {
566 SkScalarToFloat(focal),
567 SkScalarToFloat(oneMinus2F),
568 };
569
kkinnunen7510b222014-07-30 00:04:16 -0700570 pdman.set1fv(fParamUni, 2, values);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000571 fCachedFocal = focal;
572 }
573}
574
joshualittb0a8a372014-09-23 09:50:21 -0700575void GLFocalOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700576 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -0700577 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -0700578 key[0] = GenBaseGradientKey(processor);
579 key[1] = processor.cast<FocalOutside2PtConicalEffect>().isFlipped();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000580}
581
582//////////////////////////////////////////////////////////////////////////////
583
584class GLFocalInside2PtConicalEffect;
585
586class FocalInside2PtConicalEffect : public GrGradientEffect {
587public:
588
joshualittb0a8a372014-09-23 09:50:21 -0700589 static GrFragmentProcessor* Create(GrContext* ctx,
590 const SkTwoPointConicalGradient& shader,
591 const SkMatrix& matrix,
592 SkShader::TileMode tm,
593 SkScalar focalX) {
bsalomon4a339522015-10-06 08:40:50 -0700594 return new FocalInside2PtConicalEffect(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000595 }
596
597 virtual ~FocalInside2PtConicalEffect() {}
598
mtklein36352bf2015-03-25 18:17:31 -0700599 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -0800600 return "Two-Point Conical Gradient Focal Inside";
601 }
602
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000603 SkScalar focal() const { return fFocalX; }
604
egdaniel57d3b032015-11-13 11:57:27 -0800605 typedef GLFocalInside2PtConicalEffect GLSLProcessor;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000606
607private:
egdaniel57d3b032015-11-13 11:57:27 -0800608 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700609
egdaniel57d3b032015-11-13 11:57:27 -0800610 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700611
mtklein36352bf2015-03-25 18:17:31 -0700612 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700613 const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000614 return (INHERITED::onIsEqual(sBase) &&
615 this->fFocalX == s.fFocalX);
616 }
617
618 FocalInside2PtConicalEffect(GrContext* ctx,
619 const SkTwoPointConicalGradient& shader,
620 const SkMatrix& matrix,
621 SkShader::TileMode tm,
622 SkScalar focalX)
bsalomon4a339522015-10-06 08:40:50 -0700623 : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX) {
joshualitteb2a6762014-12-04 11:35:33 -0800624 this->initClassID<FocalInside2PtConicalEffect>();
625 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000626
joshualittb0a8a372014-09-23 09:50:21 -0700627 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000628
629 SkScalar fFocalX;
630
631 typedef GrGradientEffect INHERITED;
632};
633
634class GLFocalInside2PtConicalEffect : public GrGLGradientEffect {
635public:
joshualitteb2a6762014-12-04 11:35:33 -0800636 GLFocalInside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000637 virtual ~GLFocalInside2PtConicalEffect() {}
638
wangyix7c157a92015-07-22 15:08:53 -0700639 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000640
jvanverthcfc18862015-04-28 08:48:20 -0700641 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000642
643protected:
egdaniel018fb622015-10-28 07:26:40 -0700644 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700645
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000646 UniformHandle fFocalUni;
647
648 const char* fVSVaryingName;
649 const char* fFSVaryingName;
650
651 // @{
652 /// Values last uploaded as uniforms
653
654 SkScalar fCachedFocal;
655
656 // @}
657
658private:
659 typedef GrGLGradientEffect INHERITED;
660
661};
662
egdaniel57d3b032015-11-13 11:57:27 -0800663void FocalInside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
664 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800665 GLFocalInside2PtConicalEffect::GenKey(*this, caps, b);
666}
667
egdaniel57d3b032015-11-13 11:57:27 -0800668GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700669 return new GLFocalInside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000670}
671
joshualittb0a8a372014-09-23 09:50:21 -0700672GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000673
joshualitt01258472014-09-22 10:29:30 -0700674/*
675 * All Two point conical gradient test create functions may occasionally create edge case shaders
676 */
bsalomonc21b09e2015-08-28 18:46:56 -0700677const GrFragmentProcessor* FocalInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700678 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000679 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000680 SkPoint center2;
681 SkScalar radius2;
682 do {
joshualitt0067ff52015-07-08 14:26:19 -0700683 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000684 // Below makes sure radius2 is larger enouch such that the focal point
685 // is inside the end circle
joshualitt0067ff52015-07-08 14:26:19 -0700686 SkScalar increase = d->fRandom->nextUScalar1();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000687 SkPoint diff = center2 - center1;
688 SkScalar diffLen = diff.length();
689 radius2 = diffLen + increase;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000690 // If the circles are identical the factory will give us an empty shader.
691 } while (radius1 == radius2 && center1 == center2);
692
693 SkColor colors[kMaxRandomGradientColors];
694 SkScalar stopsArray[kMaxRandomGradientColors];
695 SkScalar* stops = stopsArray;
696 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700697 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000698 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
699 center2, radius2,
700 colors, stops, colorCount,
701 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700702 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700703 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700704 GrAlwaysAssert(fp);
joshualittb0a8a372014-09-23 09:50:21 -0700705 return fp;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000706}
707
joshualitteb2a6762014-12-04 11:35:33 -0800708GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrProcessor&)
halcanary96fcdcc2015-08-27 07:41:13 -0700709 : fVSVaryingName(nullptr)
710 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000711 , fCachedFocal(SK_ScalarMax) {}
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000712
wangyix7c157a92015-07-22 15:08:53 -0700713void GLFocalInside2PtConicalEffect::emitCode(EmitArgs& args) {
714 const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>();
715 this->emitUniforms(args.fBuilder, ge);
egdaniel2d721d32015-11-11 13:06:05 -0800716 fFocalUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
717 kFloat_GrSLType, kDefault_GrSLPrecision,
718 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000719 SkString tName("t");
720
721 // this is the distance along x-axis from the end center to focal point in
722 // transformed coordinates
egdaniel0d3f0612015-10-21 10:45:48 -0700723 GrGLSLShaderVar focal = args.fBuilder->getUniformVariable(fFocalUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000724
725 // if we have a vec3 from being in perspective, convert it to a vec2 first
egdaniel4ca2e602015-11-18 08:01:26 -0800726 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
727 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000728 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000729
730 // t = p.x * focalX + length(p)
egdaniel4ca2e602015-11-18 08:01:26 -0800731 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000732 coords2D, focal.c_str(), coords2D);
733
egdaniel4ca2e602015-11-18 08:01:26 -0800734 this->emitColor(args.fBuilder,
735 fragBuilder,
736 ge,
737 tName.c_str(),
738 args.fOutputColor,
739 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700740 args.fSamplers);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000741}
742
egdaniel018fb622015-10-28 07:26:40 -0700743void GLFocalInside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
744 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700745 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700746 const FocalInside2PtConicalEffect& data = processor.cast<FocalInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000747 SkScalar focal = data.focal();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000748
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000749 if (fCachedFocal != focal) {
kkinnunen7510b222014-07-30 00:04:16 -0700750 pdman.set1f(fFocalUni, SkScalarToFloat(focal));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000751 fCachedFocal = focal;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000752 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000753}
754
joshualittb0a8a372014-09-23 09:50:21 -0700755void GLFocalInside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700756 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700757 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000758}
759
760//////////////////////////////////////////////////////////////////////////////
761// Circle Conical Gradients
762//////////////////////////////////////////////////////////////////////////////
763
764struct CircleConicalInfo {
765 SkPoint fCenterEnd;
766 SkScalar fA;
767 SkScalar fB;
768 SkScalar fC;
769};
770
771// Returns focal distance along x-axis in transformed coords
772static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
773 SkMatrix* invLMatrix, CircleConicalInfo* info) {
774 // Inverse of the current local matrix is passed in then,
775 // translate and scale such that start circle is on the origin and has radius 1
776 const SkPoint& centerStart = shader.getStartCenter();
777 const SkPoint& centerEnd = shader.getEndCenter();
778 SkScalar radiusStart = shader.getStartRadius();
779 SkScalar radiusEnd = shader.getEndRadius();
780
781 SkMatrix matrix;
782
783 matrix.setTranslate(-centerStart.fX, -centerStart.fY);
784
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000785 SkScalar invStartRad = 1.f / radiusStart;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000786 matrix.postScale(invStartRad, invStartRad);
787
788 radiusEnd /= radiusStart;
789
790 SkPoint centerEndTrans;
791 matrix.mapPoints(&centerEndTrans, &centerEnd, 1);
792
793 SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
794 - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
795
796 // Check to see if start circle is inside end circle with edges touching.
797 // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
egdaniel8405ef92014-06-09 11:57:28 -0700798 // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
799 // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
800 // still accurate.
801 if (SkScalarAbs(A) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000802 return kEdge_ConicalType;
803 }
804
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000805 SkScalar C = 1.f / A;
806 SkScalar B = (radiusEnd - 1.f) * C;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000807
808 matrix.postScale(C, C);
809
810 invLMatrix->postConcat(matrix);
811
812 info->fCenterEnd = centerEndTrans;
813 info->fA = A;
814 info->fB = B;
815 info->fC = C;
816
817 // 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 +0000818 if (A < 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000819 return kInside_ConicalType;
820 }
821 return kOutside_ConicalType;
822}
823
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000824class CircleInside2PtConicalEffect : public GrGradientEffect {
825public:
826
joshualittb0a8a372014-09-23 09:50:21 -0700827 static GrFragmentProcessor* Create(GrContext* ctx,
828 const SkTwoPointConicalGradient& shader,
829 const SkMatrix& matrix,
830 SkShader::TileMode tm,
831 const CircleConicalInfo& info) {
bsalomon4a339522015-10-06 08:40:50 -0700832 return new CircleInside2PtConicalEffect(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000833 }
834
835 virtual ~CircleInside2PtConicalEffect() {}
836
mtklein36352bf2015-03-25 18:17:31 -0700837 const char* name() const override { return "Two-Point Conical Gradient Inside"; }
joshualitteb2a6762014-12-04 11:35:33 -0800838
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000839 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
840 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
841 SkScalar A() const { return fInfo.fA; }
842 SkScalar B() const { return fInfo.fB; }
843 SkScalar C() const { return fInfo.fC; }
844
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000845private:
egdaniel57d3b032015-11-13 11:57:27 -0800846 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700847
egdaniel57d3b032015-11-13 11:57:27 -0800848 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
849 GrProcessorKeyBuilder* b) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700850
mtklein36352bf2015-03-25 18:17:31 -0700851 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700852 const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000853 return (INHERITED::onIsEqual(sBase) &&
854 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
855 this->fInfo.fA == s.fInfo.fA &&
856 this->fInfo.fB == s.fInfo.fB &&
857 this->fInfo.fC == s.fInfo.fC);
858 }
859
860 CircleInside2PtConicalEffect(GrContext* ctx,
861 const SkTwoPointConicalGradient& shader,
862 const SkMatrix& matrix,
863 SkShader::TileMode tm,
864 const CircleConicalInfo& info)
bsalomon4a339522015-10-06 08:40:50 -0700865 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
joshualitteb2a6762014-12-04 11:35:33 -0800866 this->initClassID<CircleInside2PtConicalEffect>();
867 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000868
joshualittb0a8a372014-09-23 09:50:21 -0700869 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000870
871 const CircleConicalInfo fInfo;
872
873 typedef GrGradientEffect INHERITED;
874};
875
876class GLCircleInside2PtConicalEffect : public GrGLGradientEffect {
877public:
joshualitteb2a6762014-12-04 11:35:33 -0800878 GLCircleInside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000879 virtual ~GLCircleInside2PtConicalEffect() {}
880
wangyix7c157a92015-07-22 15:08:53 -0700881 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000882
jvanverthcfc18862015-04-28 08:48:20 -0700883 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000884
885protected:
egdaniel018fb622015-10-28 07:26:40 -0700886 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700887
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000888 UniformHandle fCenterUni;
889 UniformHandle fParamUni;
890
891 const char* fVSVaryingName;
892 const char* fFSVaryingName;
893
894 // @{
895 /// Values last uploaded as uniforms
896
897 SkScalar fCachedCenterX;
898 SkScalar fCachedCenterY;
899 SkScalar fCachedA;
900 SkScalar fCachedB;
901 SkScalar fCachedC;
902
903 // @}
904
905private:
906 typedef GrGLGradientEffect INHERITED;
907
908};
909
egdaniel57d3b032015-11-13 11:57:27 -0800910void CircleInside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
911 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800912 GLCircleInside2PtConicalEffect::GenKey(*this, caps, b);
913}
914
egdaniel57d3b032015-11-13 11:57:27 -0800915GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700916 return new GLCircleInside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000917}
918
joshualittb0a8a372014-09-23 09:50:21 -0700919GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000920
joshualitt01258472014-09-22 10:29:30 -0700921/*
922 * All Two point conical gradient test create functions may occasionally create edge case shaders
923 */
bsalomonc21b09e2015-08-28 18:46:56 -0700924const GrFragmentProcessor* CircleInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700925 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
926 SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000927 SkPoint center2;
928 SkScalar radius2;
929 do {
joshualitt0067ff52015-07-08 14:26:19 -0700930 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000931 // Below makes sure that circle one is contained within circle two
joshualitt0067ff52015-07-08 14:26:19 -0700932 SkScalar increase = d->fRandom->nextUScalar1();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000933 SkPoint diff = center2 - center1;
934 SkScalar diffLen = diff.length();
935 radius2 = radius1 + diffLen + increase;
936 // If the circles are identical the factory will give us an empty shader.
937 } while (radius1 == radius2 && center1 == center2);
938
939 SkColor colors[kMaxRandomGradientColors];
940 SkScalar stopsArray[kMaxRandomGradientColors];
941 SkScalar* stops = stopsArray;
942 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700943 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000944 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
945 center2, radius2,
946 colors, stops, colorCount,
947 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700948 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700949 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700950 GrAlwaysAssert(fp);
joshualitt8ca93e72015-07-08 06:51:43 -0700951 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000952}
953
joshualitteb2a6762014-12-04 11:35:33 -0800954GLCircleInside2PtConicalEffect::GLCircleInside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -0700955 : fVSVaryingName(nullptr)
956 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000957 , fCachedCenterX(SK_ScalarMax)
958 , fCachedCenterY(SK_ScalarMax)
959 , fCachedA(SK_ScalarMax)
960 , fCachedB(SK_ScalarMax)
961 , fCachedC(SK_ScalarMax) {}
962
wangyix7c157a92015-07-22 15:08:53 -0700963void GLCircleInside2PtConicalEffect::emitCode(EmitArgs& args) {
964 const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>();
965 this->emitUniforms(args.fBuilder, ge);
egdaniel2d721d32015-11-11 13:06:05 -0800966 fCenterUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
967 kVec2f_GrSLType, kDefault_GrSLPrecision,
968 "Conical2FSCenter");
969 fParamUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
970 kVec3f_GrSLType, kDefault_GrSLPrecision,
971 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000972 SkString tName("t");
973
egdaniel0d3f0612015-10-21 10:45:48 -0700974 GrGLSLShaderVar center = args.fBuilder->getUniformVariable(fCenterUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000975 // params.x = A
976 // params.y = B
977 // params.z = C
egdaniel0d3f0612015-10-21 10:45:48 -0700978 GrGLSLShaderVar params = args.fBuilder->getUniformVariable(fParamUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000979
980 // if we have a vec3 from being in perspective, convert it to a vec2 first
egdaniel4ca2e602015-11-18 08:01:26 -0800981 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
982 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000983 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000984
985 // p = coords2D
986 // e = center end
987 // r = radius end
988 // A = dot(e, e) - r^2 + 2 * r - 1
989 // B = (r -1) / A
990 // C = 1 / A
991 // d = dot(e, p) + B
992 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
egdaniel4ca2e602015-11-18 08:01:26 -0800993 fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
994 fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
995 params.c_str());
996 fragBuilder->codeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
997 tName.c_str(), params.c_str(), params.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000998
egdaniel4ca2e602015-11-18 08:01:26 -0800999 this->emitColor(args.fBuilder,
1000 fragBuilder,
1001 ge,
1002 tName.c_str(),
1003 args.fOutputColor,
1004 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -07001005 args.fSamplers);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001006}
1007
egdaniel018fb622015-10-28 07:26:40 -07001008void GLCircleInside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1009 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -07001010 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -07001011 const CircleInside2PtConicalEffect& data = processor.cast<CircleInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001012 SkScalar centerX = data.centerX();
1013 SkScalar centerY = data.centerY();
1014 SkScalar A = data.A();
1015 SkScalar B = data.B();
1016 SkScalar C = data.C();
1017
1018 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1019 fCachedA != A || fCachedB != B || fCachedC != C) {
1020
kkinnunen7510b222014-07-30 00:04:16 -07001021 pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1022 pdman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001023
1024 fCachedCenterX = centerX;
1025 fCachedCenterY = centerY;
1026 fCachedA = A;
1027 fCachedB = B;
1028 fCachedC = C;
1029 }
1030}
1031
joshualittb0a8a372014-09-23 09:50:21 -07001032void GLCircleInside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -07001033 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -07001034 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001035}
1036
1037//////////////////////////////////////////////////////////////////////////////
1038
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001039class CircleOutside2PtConicalEffect : public GrGradientEffect {
1040public:
1041
joshualittb0a8a372014-09-23 09:50:21 -07001042 static GrFragmentProcessor* Create(GrContext* ctx,
1043 const SkTwoPointConicalGradient& shader,
1044 const SkMatrix& matrix,
1045 SkShader::TileMode tm,
1046 const CircleConicalInfo& info) {
bsalomon4a339522015-10-06 08:40:50 -07001047 return new CircleOutside2PtConicalEffect(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001048 }
1049
1050 virtual ~CircleOutside2PtConicalEffect() {}
1051
mtklein36352bf2015-03-25 18:17:31 -07001052 const char* name() const override { return "Two-Point Conical Gradient Outside"; }
joshualitteb2a6762014-12-04 11:35:33 -08001053
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001054 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
1055 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
1056 SkScalar A() const { return fInfo.fA; }
1057 SkScalar B() const { return fInfo.fB; }
1058 SkScalar C() const { return fInfo.fC; }
1059 SkScalar tLimit() const { return fTLimit; }
1060 bool isFlipped() const { return fIsFlipped; }
1061
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001062private:
egdaniel57d3b032015-11-13 11:57:27 -08001063 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -07001064
egdaniel57d3b032015-11-13 11:57:27 -08001065 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -07001066
mtklein36352bf2015-03-25 18:17:31 -07001067 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -07001068 const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001069 return (INHERITED::onIsEqual(sBase) &&
1070 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
1071 this->fInfo.fA == s.fInfo.fA &&
1072 this->fInfo.fB == s.fInfo.fB &&
1073 this->fInfo.fC == s.fInfo.fC &&
1074 this->fTLimit == s.fTLimit &&
1075 this->fIsFlipped == s.fIsFlipped);
1076 }
1077
1078 CircleOutside2PtConicalEffect(GrContext* ctx,
1079 const SkTwoPointConicalGradient& shader,
1080 const SkMatrix& matrix,
1081 SkShader::TileMode tm,
1082 const CircleConicalInfo& info)
bsalomon4a339522015-10-06 08:40:50 -07001083 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
joshualitteb2a6762014-12-04 11:35:33 -08001084 this->initClassID<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001085 if (shader.getStartRadius() != shader.getEndRadius()) {
reed80ea19c2015-05-12 10:37:34 -07001086 fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001087 } else {
1088 fTLimit = SK_ScalarMin;
1089 }
1090
1091 fIsFlipped = shader.isFlippedGrad();
1092 }
1093
joshualittb0a8a372014-09-23 09:50:21 -07001094 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001095
1096 const CircleConicalInfo fInfo;
1097 SkScalar fTLimit;
1098 bool fIsFlipped;
1099
1100 typedef GrGradientEffect INHERITED;
1101};
1102
1103class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect {
1104public:
joshualitteb2a6762014-12-04 11:35:33 -08001105 GLCircleOutside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001106 virtual ~GLCircleOutside2PtConicalEffect() {}
1107
wangyix7c157a92015-07-22 15:08:53 -07001108 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001109
jvanverthcfc18862015-04-28 08:48:20 -07001110 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001111
1112protected:
egdaniel018fb622015-10-28 07:26:40 -07001113 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -07001114
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001115 UniformHandle fCenterUni;
1116 UniformHandle fParamUni;
1117
1118 const char* fVSVaryingName;
1119 const char* fFSVaryingName;
1120
1121 bool fIsFlipped;
1122
1123 // @{
1124 /// Values last uploaded as uniforms
1125
1126 SkScalar fCachedCenterX;
1127 SkScalar fCachedCenterY;
1128 SkScalar fCachedA;
1129 SkScalar fCachedB;
1130 SkScalar fCachedC;
1131 SkScalar fCachedTLimit;
1132
1133 // @}
1134
1135private:
1136 typedef GrGLGradientEffect INHERITED;
1137
1138};
1139
egdaniel57d3b032015-11-13 11:57:27 -08001140void CircleOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
1141 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -08001142 GLCircleOutside2PtConicalEffect::GenKey(*this, caps, b);
1143}
1144
egdaniel57d3b032015-11-13 11:57:27 -08001145GrGLSLFragmentProcessor* CircleOutside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -07001146 return new GLCircleOutside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001147}
1148
joshualittb0a8a372014-09-23 09:50:21 -07001149GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001150
joshualitt01258472014-09-22 10:29:30 -07001151/*
1152 * All Two point conical gradient test create functions may occasionally create edge case shaders
1153 */
bsalomonc21b09e2015-08-28 18:46:56 -07001154const GrFragmentProcessor* CircleOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -07001155 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
1156 SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001157 SkPoint center2;
1158 SkScalar radius2;
1159 SkScalar diffLen;
1160 do {
joshualitt0067ff52015-07-08 14:26:19 -07001161 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001162 // If the circles share a center than we can't be in the outside case
1163 } while (center1 == center2);
joshualitt01258472014-09-22 10:29:30 -07001164 SkPoint diff = center2 - center1;
1165 diffLen = diff.length();
1166 // Below makes sure that circle one is not contained within circle two
1167 // and have radius2 >= radius to match sorting on cpu side
joshualitt0067ff52015-07-08 14:26:19 -07001168 radius2 = radius1 + d->fRandom->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001169
1170 SkColor colors[kMaxRandomGradientColors];
1171 SkScalar stopsArray[kMaxRandomGradientColors];
1172 SkScalar* stops = stopsArray;
1173 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -07001174 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001175 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
1176 center2, radius2,
1177 colors, stops, colorCount,
1178 tm));
bsalomonc21b09e2015-08-28 18:46:56 -07001179 const GrFragmentProcessor* fp = shader->asFragmentProcessor(
bsalomon4a339522015-10-06 08:40:50 -07001180 d->fContext,GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -07001181 GrAlwaysAssert(fp);
joshualitt8ca93e72015-07-08 06:51:43 -07001182 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001183}
1184
joshualitteb2a6762014-12-04 11:35:33 -08001185GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -07001186 : fVSVaryingName(nullptr)
1187 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001188 , fCachedCenterX(SK_ScalarMax)
1189 , fCachedCenterY(SK_ScalarMax)
1190 , fCachedA(SK_ScalarMax)
1191 , fCachedB(SK_ScalarMax)
1192 , fCachedC(SK_ScalarMax)
1193 , fCachedTLimit(SK_ScalarMax) {
joshualittb0a8a372014-09-23 09:50:21 -07001194 const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001195 fIsFlipped = data.isFlipped();
1196 }
1197
wangyix7c157a92015-07-22 15:08:53 -07001198void GLCircleOutside2PtConicalEffect::emitCode(EmitArgs& args) {
1199 const CircleOutside2PtConicalEffect& ge = args.fFp.cast<CircleOutside2PtConicalEffect>();
1200 this->emitUniforms(args.fBuilder, ge);
egdaniel2d721d32015-11-11 13:06:05 -08001201 fCenterUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
1202 kVec2f_GrSLType, kDefault_GrSLPrecision,
1203 "Conical2FSCenter");
1204 fParamUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
1205 kVec4f_GrSLType, kDefault_GrSLPrecision,
1206 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001207 SkString tName("t");
1208
egdaniel0d3f0612015-10-21 10:45:48 -07001209 GrGLSLShaderVar center = args.fBuilder->getUniformVariable(fCenterUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001210 // params.x = A
1211 // params.y = B
1212 // params.z = C
egdaniel0d3f0612015-10-21 10:45:48 -07001213 GrGLSLShaderVar params = args.fBuilder->getUniformVariable(fParamUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001214
1215 // if we have a vec3 from being in perspective, convert it to a vec2 first
egdaniel4ca2e602015-11-18 08:01:26 -08001216 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
1217 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +00001218 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001219
1220 // output will default to transparent black (we simply won't write anything
1221 // else to it if invalid, instead of discarding or returning prematurely)
egdaniel4ca2e602015-11-18 08:01:26 -08001222 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 +00001223
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001224 // p = coords2D
1225 // e = center end
1226 // r = radius end
1227 // A = dot(e, e) - r^2 + 2 * r - 1
1228 // B = (r -1) / A
1229 // C = 1 / A
1230 // d = dot(e, p) + B
1231 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001232
egdaniel4ca2e602015-11-18 08:01:26 -08001233 fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
1234 fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
1235 params.c_str());
1236 fragBuilder->codeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(),
1237 params.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001238
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001239 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
1240 // If so we must also flip sign on sqrt
1241 if (!fIsFlipped) {
egdaniel4ca2e602015-11-18 08:01:26 -08001242 fragBuilder->codeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001243 } else {
egdaniel4ca2e602015-11-18 08:01:26 -08001244 fragBuilder->codeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001245 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001246
egdaniel4ca2e602015-11-18 08:01:26 -08001247 fragBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", tName.c_str(), params.c_str());
1248 fragBuilder->codeAppend("\t\t");
1249 this->emitColor(args.fBuilder,
1250 fragBuilder,
1251 ge,
1252 tName.c_str(),
1253 args.fOutputColor,
1254 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -07001255 args.fSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -08001256 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001257}
1258
egdaniel018fb622015-10-28 07:26:40 -07001259void GLCircleOutside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1260 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -07001261 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -07001262 const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +00001263 SkASSERT(data.isFlipped() == fIsFlipped);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001264 SkScalar centerX = data.centerX();
1265 SkScalar centerY = data.centerY();
1266 SkScalar A = data.A();
1267 SkScalar B = data.B();
1268 SkScalar C = data.C();
1269 SkScalar tLimit = data.tLimit();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001270
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001271 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1272 fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001273
kkinnunen7510b222014-07-30 00:04:16 -07001274 pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1275 pdman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001276 SkScalarToFloat(tLimit));
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001277
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001278 fCachedCenterX = centerX;
1279 fCachedCenterY = centerY;
1280 fCachedA = A;
1281 fCachedB = B;
1282 fCachedC = C;
1283 fCachedTLimit = tLimit;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001284 }
1285}
1286
joshualittb0a8a372014-09-23 09:50:21 -07001287void GLCircleOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -07001288 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -07001289 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -07001290 key[0] = GenBaseGradientKey(processor);
1291 key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001292}
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001293
1294//////////////////////////////////////////////////////////////////////////////
1295
joshualittb0a8a372014-09-23 09:50:21 -07001296GrFragmentProcessor* Gr2PtConicalGradientEffect::Create(GrContext* ctx,
1297 const SkTwoPointConicalGradient& shader,
1298 SkShader::TileMode tm,
1299 const SkMatrix* localMatrix) {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001300 SkMatrix matrix;
1301 if (!shader.getLocalMatrix().invert(&matrix)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001302 return nullptr;
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001303 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001304 if (localMatrix) {
1305 SkMatrix inv;
1306 if (!localMatrix->invert(&inv)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001307 return nullptr;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001308 }
1309 matrix.postConcat(inv);
1310 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001311
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001312 if (shader.getStartRadius() < kErrorTol) {
1313 SkScalar focalX;
1314 ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
1315 if (type == kInside_ConicalType) {
bsalomon4a339522015-10-06 08:40:50 -07001316 return FocalInside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001317 } else if(type == kEdge_ConicalType) {
1318 set_matrix_edge_conical(shader, &matrix);
bsalomon4a339522015-10-06 08:40:50 -07001319 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001320 } else {
bsalomon4a339522015-10-06 08:40:50 -07001321 return FocalOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001322 }
1323 }
1324
1325 CircleConicalInfo info;
1326 ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
1327
1328 if (type == kInside_ConicalType) {
bsalomon4a339522015-10-06 08:40:50 -07001329 return CircleInside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001330 } else if (type == kEdge_ConicalType) {
1331 set_matrix_edge_conical(shader, &matrix);
bsalomon4a339522015-10-06 08:40:50 -07001332 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001333 } else {
bsalomon4a339522015-10-06 08:40:50 -07001334 return CircleOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001335 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001336}
1337
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001338#endif