blob: b89f88a3a6c018ee12fef2311c46ff23aeebd516 [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
egdaniel7ea439b2015-12-03 09:20:44 -080014#include "GrCoordTransform.h"
15#include "GrInvariantOutput.h"
joshualitt8ca93e72015-07-08 06:51:43 -070016#include "GrPaint.h"
egdaniel2d721d32015-11-11 13:06:05 -080017#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniel018fb622015-10-28 07:26:40 -070018#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080019#include "glsl/GrGLSLUniformHandler.h"
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000020// For brevity
egdaniel018fb622015-10-28 07:26:40 -070021typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000022
commit-bot@chromium.org80894672014-04-22 21:24:22 +000023static const SkScalar kErrorTol = 0.00001f;
egdaniel8405ef92014-06-09 11:57:28 -070024static const SkScalar kEdgeErrorTol = 5.f * kErrorTol;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000025
26/**
27 * We have three general cases for 2pt conical gradients. First we always assume that
28 * the start radius <= end radius. Our first case (kInside_) is when the start circle
29 * is completely enclosed by the end circle. The second case (kOutside_) is the case
30 * when the start circle is either completely outside the end circle or the circles
31 * overlap. The final case (kEdge_) is when the start circle is inside the end one,
32 * but the two are just barely touching at 1 point along their edges.
33 */
34enum ConicalType {
35 kInside_ConicalType,
36 kOutside_ConicalType,
37 kEdge_ConicalType,
38};
39
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000040//////////////////////////////////////////////////////////////////////////////
41
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000042static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader,
43 SkMatrix* invLMatrix) {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000044 // Inverse of the current local matrix is passed in then,
45 // translate to center1, rotate so center2 is on x axis.
46 const SkPoint& center1 = shader.getStartCenter();
47 const SkPoint& center2 = shader.getEndCenter();
48
49 invLMatrix->postTranslate(-center1.fX, -center1.fY);
50
51 SkPoint diff = center2 - center1;
52 SkScalar diffLen = diff.length();
53 if (0 != diffLen) {
54 SkScalar invDiffLen = SkScalarInvert(diffLen);
55 SkMatrix rot;
56 rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY),
57 SkScalarMul(invDiffLen, diff.fX));
58 invLMatrix->postConcat(rot);
59 }
60}
61
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000062class Edge2PtConicalEffect : public GrGradientEffect {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000063public:
64
joshualittb0a8a372014-09-23 09:50:21 -070065 static GrFragmentProcessor* Create(GrContext* ctx,
66 const SkTwoPointConicalGradient& shader,
67 const SkMatrix& matrix,
68 SkShader::TileMode tm) {
bsalomon4a339522015-10-06 08:40:50 -070069 return new Edge2PtConicalEffect(ctx, shader, matrix, tm);
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000070 }
71
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000072 virtual ~Edge2PtConicalEffect() {}
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000073
mtklein36352bf2015-03-25 18:17:31 -070074 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -080075 return "Two-Point Conical Gradient Edge Touching";
76 }
77
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000078 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000079 SkScalar center() const { return fCenterX1; }
80 SkScalar diffRadius() const { return fDiffRadius; }
81 SkScalar radius() const { return fRadius0; }
82
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000083private:
egdaniel57d3b032015-11-13 11:57:27 -080084 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -070085
egdaniel57d3b032015-11-13 11:57:27 -080086 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -070087
mtklein36352bf2015-03-25 18:17:31 -070088 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -070089 const Edge2PtConicalEffect& s = sBase.cast<Edge2PtConicalEffect>();
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000090 return (INHERITED::onIsEqual(sBase) &&
91 this->fCenterX1 == s.fCenterX1 &&
92 this->fRadius0 == s.fRadius0 &&
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000093 this->fDiffRadius == s.fDiffRadius);
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000094 }
95
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000096 Edge2PtConicalEffect(GrContext* ctx,
97 const SkTwoPointConicalGradient& shader,
98 const SkMatrix& matrix,
99 SkShader::TileMode tm)
bsalomon4a339522015-10-06 08:40:50 -0700100 : INHERITED(ctx, shader, matrix, tm),
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000101 fCenterX1(shader.getCenterX1()),
102 fRadius0(shader.getStartRadius()),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000103 fDiffRadius(shader.getDiffRadius()){
joshualitteb2a6762014-12-04 11:35:33 -0800104 this->initClassID<Edge2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000105 // We should only be calling this shader if we are degenerate case with touching circles
egdaniel8405ef92014-06-09 11:57:28 -0700106 // When deciding if we are in edge case, we scaled by the end radius for cases when the
joshualitt01258472014-09-22 10:29:30 -0700107 // start radius was close to zero, otherwise we scaled by the start radius. In addition
108 // Our test for the edge case in set_matrix_circle_conical has a higher tolerance so we
109 // need the sqrt value below
110 SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - fCenterX1) <
111 (fRadius0 < kErrorTol ? shader.getEndRadius() * kEdgeErrorTol :
112 fRadius0 * sqrt(kEdgeErrorTol)));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000113
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000114 // We pass the linear part of the quadratic as a varying.
115 // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
116 fBTransform = this->getCoordTransform();
117 SkMatrix& bMatrix = *fBTransform.accessMatrix();
118 SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius);
119 bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) +
120 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0]));
121 bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) +
122 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1]));
123 bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) +
124 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2]));
125 this->addCoordTransform(&fBTransform);
126 }
127
joshualittb0a8a372014-09-23 09:50:21 -0700128 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000129
130 // @{
131 // Cache of values - these can change arbitrarily, EXCEPT
132 // we shouldn't change between degenerate and non-degenerate?!
133
134 GrCoordTransform fBTransform;
135 SkScalar fCenterX1;
136 SkScalar fRadius0;
137 SkScalar fDiffRadius;
138
139 // @}
140
141 typedef GrGradientEffect INHERITED;
142};
143
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000144class GLEdge2PtConicalEffect : public GrGLGradientEffect {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000145public:
joshualitteb2a6762014-12-04 11:35:33 -0800146 GLEdge2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000147 virtual ~GLEdge2PtConicalEffect() { }
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000148
wangyix7c157a92015-07-22 15:08:53 -0700149 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000150
jvanverthcfc18862015-04-28 08:48:20 -0700151 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000152
153protected:
egdaniel018fb622015-10-28 07:26:40 -0700154 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700155
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000156 UniformHandle fParamUni;
157
158 const char* fVSVaryingName;
159 const char* fFSVaryingName;
160
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000161 // @{
162 /// Values last uploaded as uniforms
163
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000164 SkScalar fCachedRadius;
165 SkScalar fCachedDiffRadius;
166
167 // @}
168
169private:
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000170 typedef GrGLGradientEffect INHERITED;
171
172};
173
egdaniel57d3b032015-11-13 11:57:27 -0800174void Edge2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
175 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800176 GLEdge2PtConicalEffect::GenKey(*this, caps, b);
177}
178
egdaniel57d3b032015-11-13 11:57:27 -0800179GrGLSLFragmentProcessor* Edge2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700180 return new GLEdge2PtConicalEffect(*this);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000181}
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000182
joshualittb0a8a372014-09-23 09:50:21 -0700183GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Edge2PtConicalEffect);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000184
joshualitt01258472014-09-22 10:29:30 -0700185/*
186 * All Two point conical gradient test create functions may occasionally create edge case shaders
187 */
bsalomonc21b09e2015-08-28 18:46:56 -0700188const GrFragmentProcessor* Edge2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700189 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
190 SkScalar radius1 = d->fRandom->nextUScalar1();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000191 SkPoint center2;
192 SkScalar radius2;
193 do {
joshualitt0067ff52015-07-08 14:26:19 -0700194 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000195 // If the circles are identical the factory will give us an empty shader.
196 // This will happen if we pick identical centers
197 } while (center1 == center2);
198
199 // Below makes sure that circle one is contained within circle two
200 // and both circles are touching on an edge
201 SkPoint diff = center2 - center1;
202 SkScalar diffLen = diff.length();
203 radius2 = radius1 + diffLen;
204
205 SkColor colors[kMaxRandomGradientColors];
206 SkScalar stopsArray[kMaxRandomGradientColors];
207 SkScalar* stops = stopsArray;
208 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700209 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000210 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
211 center2, radius2,
212 colors, stops, colorCount,
213 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700214 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700215 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700216 GrAlwaysAssert(fp);
joshualittb0a8a372014-09-23 09:50:21 -0700217 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000218}
219
joshualitteb2a6762014-12-04 11:35:33 -0800220GLEdge2PtConicalEffect::GLEdge2PtConicalEffect(const GrProcessor&)
halcanary96fcdcc2015-08-27 07:41:13 -0700221 : fVSVaryingName(nullptr)
222 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000223 , fCachedRadius(-SK_ScalarMax)
224 , fCachedDiffRadius(-SK_ScalarMax) {}
225
wangyix7c157a92015-07-22 15:08:53 -0700226void GLEdge2PtConicalEffect::emitCode(EmitArgs& args) {
227 const Edge2PtConicalEffect& ge = args.fFp.cast<Edge2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -0800228 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
229 this->emitUniforms(uniformHandler, ge);
cdalton5e58cee2016-02-11 12:49:47 -0800230 fParamUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800231 kFloat_GrSLType, kDefault_GrSLPrecision,
232 "Conical2FSParams", 3);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000233
234 SkString cName("c");
235 SkString tName("t");
236 SkString p0; // start radius
237 SkString p1; // start radius squared
238 SkString p2; // difference in radii (r1 - r0)
239
egdaniel7ea439b2015-12-03 09:20:44 -0800240 uniformHandler->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
241 uniformHandler->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
242 uniformHandler->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000243
244 // We interpolate the linear component in coords[1].
wangyix7c157a92015-07-22 15:08:53 -0700245 SkASSERT(args.fCoords[0].getType() == args.fCoords[1].getType());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000246 const char* coords2D;
247 SkString bVar;
egdaniel4ca2e602015-11-18 08:01:26 -0800248 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
wangyix7c157a92015-07-22 15:08:53 -0700249 if (kVec3f_GrSLType == args.fCoords[0].getType()) {
egdaniel4ca2e602015-11-18 08:01:26 -0800250 fragBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
wangyix7c157a92015-07-22 15:08:53 -0700251 args.fCoords[0].c_str(), args.fCoords[0].c_str(),
252 args.fCoords[1].c_str(), args.fCoords[1].c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000253 coords2D = "interpolants.xy";
254 bVar = "interpolants.z";
255 } else {
wangyix7c157a92015-07-22 15:08:53 -0700256 coords2D = args.fCoords[0].c_str();
257 bVar.printf("%s.x", args.fCoords[1].c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000258 }
259
260 // output will default to transparent black (we simply won't write anything
261 // else to it if invalid, instead of discarding or returning prematurely)
egdaniel4ca2e602015-11-18 08:01:26 -0800262 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 +0000263
264 // c = (x^2)+(y^2) - params[1]
egdaniel4ca2e602015-11-18 08:01:26 -0800265 fragBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000266 cName.c_str(), coords2D, coords2D, p1.c_str());
267
268 // linear case: t = -c/b
egdaniel4ca2e602015-11-18 08:01:26 -0800269 fragBuilder->codeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000270 cName.c_str(), bVar.c_str());
271
272 // if r(t) > 0, then t will be the x coordinate
egdaniel4ca2e602015-11-18 08:01:26 -0800273 fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000274 p2.c_str(), p0.c_str());
egdaniel4ca2e602015-11-18 08:01:26 -0800275 fragBuilder->codeAppend("\t");
egdaniel7ea439b2015-12-03 09:20:44 -0800276 this->emitColor(fragBuilder,
277 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -0800278 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800279 ge,
280 tName.c_str(),
281 args.fOutputColor,
282 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700283 args.fSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -0800284 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000285}
286
egdaniel018fb622015-10-28 07:26:40 -0700287void GLEdge2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
288 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700289 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700290 const Edge2PtConicalEffect& data = processor.cast<Edge2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000291 SkScalar radius0 = data.radius();
292 SkScalar diffRadius = data.diffRadius();
293
294 if (fCachedRadius != radius0 ||
295 fCachedDiffRadius != diffRadius) {
296
297 float values[3] = {
298 SkScalarToFloat(radius0),
299 SkScalarToFloat(SkScalarMul(radius0, radius0)),
300 SkScalarToFloat(diffRadius)
301 };
302
kkinnunen7510b222014-07-30 00:04:16 -0700303 pdman.set1fv(fParamUni, 3, values);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000304 fCachedRadius = radius0;
305 fCachedDiffRadius = diffRadius;
306 }
307}
308
joshualittb0a8a372014-09-23 09:50:21 -0700309void GLEdge2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700310 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700311 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000312}
313
314//////////////////////////////////////////////////////////////////////////////
315// Focal Conical Gradients
316//////////////////////////////////////////////////////////////////////////////
317
318static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader,
319 SkMatrix* invLMatrix, SkScalar* focalX) {
320 // Inverse of the current local matrix is passed in then,
321 // translate, scale, and rotate such that endCircle is unit circle on x-axis,
322 // and focal point is at the origin.
323 ConicalType conicalType;
324 const SkPoint& focal = shader.getStartCenter();
325 const SkPoint& centerEnd = shader.getEndCenter();
326 SkScalar radius = shader.getEndRadius();
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000327 SkScalar invRadius = 1.f / radius;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000328
329 SkMatrix matrix;
330
331 matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
332 matrix.postScale(invRadius, invRadius);
333
334 SkPoint focalTrans;
335 matrix.mapPoints(&focalTrans, &focal, 1);
336 *focalX = focalTrans.length();
337
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000338 if (0.f != *focalX) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000339 SkScalar invFocalX = SkScalarInvert(*focalX);
340 SkMatrix rot;
341 rot.setSinCos(-SkScalarMul(invFocalX, focalTrans.fY),
342 SkScalarMul(invFocalX, focalTrans.fX));
343 matrix.postConcat(rot);
344 }
345
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000346 matrix.postTranslate(-(*focalX), 0.f);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000347
348 // If the focal point is touching the edge of the circle it will
349 // cause a degenerate case that must be handled separately
egdaniel8405ef92014-06-09 11:57:28 -0700350 // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
351 // stability trade off versus the linear approx used in the Edge Shader
352 if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000353 return kEdge_ConicalType;
354 }
355
356 // Scale factor 1 / (1 - focalX * focalX)
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000357 SkScalar oneMinusF2 = 1.f - SkScalarMul(*focalX, *focalX);
reed80ea19c2015-05-12 10:37:34 -0700358 SkScalar s = SkScalarInvert(oneMinusF2);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000359
360
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000361 if (s >= 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000362 conicalType = kInside_ConicalType;
363 matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
364 } else {
365 conicalType = kOutside_ConicalType;
366 matrix.postScale(s, s);
367 }
368
369 invLMatrix->postConcat(matrix);
370
371 return conicalType;
372}
373
374//////////////////////////////////////////////////////////////////////////////
375
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000376class FocalOutside2PtConicalEffect : public GrGradientEffect {
377public:
378
joshualittb0a8a372014-09-23 09:50:21 -0700379 static GrFragmentProcessor* Create(GrContext* ctx,
380 const SkTwoPointConicalGradient& shader,
381 const SkMatrix& matrix,
382 SkShader::TileMode tm,
383 SkScalar focalX) {
bsalomon4a339522015-10-06 08:40:50 -0700384 return new FocalOutside2PtConicalEffect(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000385 }
386
387 virtual ~FocalOutside2PtConicalEffect() { }
388
mtklein36352bf2015-03-25 18:17:31 -0700389 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -0800390 return "Two-Point Conical Gradient Focal Outside";
391 }
392
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000393 bool isFlipped() const { return fIsFlipped; }
394 SkScalar focal() const { return fFocalX; }
395
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000396private:
egdaniel57d3b032015-11-13 11:57:27 -0800397 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700398
egdaniel57d3b032015-11-13 11:57:27 -0800399 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700400
mtklein36352bf2015-03-25 18:17:31 -0700401 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700402 const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000403 return (INHERITED::onIsEqual(sBase) &&
404 this->fFocalX == s.fFocalX &&
405 this->fIsFlipped == s.fIsFlipped);
406 }
407
408 FocalOutside2PtConicalEffect(GrContext* ctx,
409 const SkTwoPointConicalGradient& shader,
410 const SkMatrix& matrix,
411 SkShader::TileMode tm,
412 SkScalar focalX)
bsalomon4a339522015-10-06 08:40:50 -0700413 : INHERITED(ctx, shader, matrix, tm)
joshualittb2456052015-07-08 09:36:59 -0700414 , fFocalX(focalX)
415 , fIsFlipped(shader.isFlippedGrad()) {
joshualitteb2a6762014-12-04 11:35:33 -0800416 this->initClassID<FocalOutside2PtConicalEffect>();
417 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000418
joshualittb0a8a372014-09-23 09:50:21 -0700419 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000420
421 SkScalar fFocalX;
422 bool fIsFlipped;
423
424 typedef GrGradientEffect INHERITED;
425};
426
427class GLFocalOutside2PtConicalEffect : public GrGLGradientEffect {
428public:
joshualitteb2a6762014-12-04 11:35:33 -0800429 GLFocalOutside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000430 virtual ~GLFocalOutside2PtConicalEffect() { }
431
wangyix7c157a92015-07-22 15:08:53 -0700432 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000433
jvanverthcfc18862015-04-28 08:48:20 -0700434 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000435
436protected:
egdaniel018fb622015-10-28 07:26:40 -0700437 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700438
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000439 UniformHandle fParamUni;
440
441 const char* fVSVaryingName;
442 const char* fFSVaryingName;
443
444 bool fIsFlipped;
445
446 // @{
447 /// Values last uploaded as uniforms
448
449 SkScalar fCachedFocal;
450
451 // @}
452
453private:
454 typedef GrGLGradientEffect INHERITED;
455
456};
457
egdaniel57d3b032015-11-13 11:57:27 -0800458void FocalOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
459 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800460 GLFocalOutside2PtConicalEffect::GenKey(*this, caps, b);
461}
462
egdaniel57d3b032015-11-13 11:57:27 -0800463GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700464 return new GLFocalOutside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000465}
466
joshualittb0a8a372014-09-23 09:50:21 -0700467GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000468
joshualitt01258472014-09-22 10:29:30 -0700469/*
470 * All Two point conical gradient test create functions may occasionally create edge case shaders
471 */
bsalomonc21b09e2015-08-28 18:46:56 -0700472const GrFragmentProcessor* FocalOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700473 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000474 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000475 SkPoint center2;
476 SkScalar radius2;
477 do {
joshualitt0067ff52015-07-08 14:26:19 -0700478 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000479 // 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 +0000480 } while (center1 == center2);
481 SkPoint diff = center2 - center1;
482 SkScalar diffLen = diff.length();
483 // Below makes sure that the focal point is not contained within circle two
joshualitt0067ff52015-07-08 14:26:19 -0700484 radius2 = d->fRandom->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000485
486 SkColor colors[kMaxRandomGradientColors];
487 SkScalar stopsArray[kMaxRandomGradientColors];
488 SkScalar* stops = stopsArray;
489 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700490 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000491 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
492 center2, radius2,
493 colors, stops, colorCount,
494 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700495 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700496 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700497 GrAlwaysAssert(fp);
498 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000499}
500
joshualitteb2a6762014-12-04 11:35:33 -0800501GLFocalOutside2PtConicalEffect::GLFocalOutside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -0700502 : fVSVaryingName(nullptr)
503 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000504 , fCachedFocal(SK_ScalarMax) {
joshualittb0a8a372014-09-23 09:50:21 -0700505 const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000506 fIsFlipped = data.isFlipped();
507}
508
wangyix7c157a92015-07-22 15:08:53 -0700509void GLFocalOutside2PtConicalEffect::emitCode(EmitArgs& args) {
510 const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -0800511 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
512 this->emitUniforms(uniformHandler, ge);
cdalton5e58cee2016-02-11 12:49:47 -0800513 fParamUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800514 kFloat_GrSLType, kDefault_GrSLPrecision,
515 "Conical2FSParams", 2);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000516 SkString tName("t");
517 SkString p0; // focalX
518 SkString p1; // 1 - focalX * focalX
519
egdaniel7ea439b2015-12-03 09:20:44 -0800520 uniformHandler->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
521 uniformHandler->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000522
523 // if we have a vec3 from being in perspective, convert it to a vec2 first
egdaniel4ca2e602015-11-18 08:01:26 -0800524 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
525 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000526 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000527
528 // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
529
530 // output will default to transparent black (we simply won't write anything
531 // else to it if invalid, instead of discarding or returning prematurely)
egdaniel4ca2e602015-11-18 08:01:26 -0800532 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 +0000533
egdaniel4ca2e602015-11-18 08:01:26 -0800534 fragBuilder->codeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D);
535 fragBuilder->codeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D);
536 fragBuilder->codeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000537
538 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
539 // If so we must also flip sign on sqrt
540 if (!fIsFlipped) {
egdaniel4ca2e602015-11-18 08:01:26 -0800541 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + sqrt(d);\n", tName.c_str(),
542 coords2D, p0.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000543 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800544 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s - sqrt(d);\n", tName.c_str(),
545 coords2D, p0.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000546 }
547
egdaniel4ca2e602015-11-18 08:01:26 -0800548 fragBuilder->codeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str());
549 fragBuilder->codeAppend("\t\t");
egdaniel7ea439b2015-12-03 09:20:44 -0800550 this->emitColor(fragBuilder,
551 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -0800552 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800553 ge,
554 tName.c_str(),
555 args.fOutputColor,
556 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700557 args.fSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -0800558 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000559}
560
egdaniel018fb622015-10-28 07:26:40 -0700561void GLFocalOutside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
562 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700563 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700564 const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000565 SkASSERT(data.isFlipped() == fIsFlipped);
566 SkScalar focal = data.focal();
567
568 if (fCachedFocal != focal) {
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000569 SkScalar oneMinus2F = 1.f - SkScalarMul(focal, focal);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000570
571 float values[2] = {
572 SkScalarToFloat(focal),
573 SkScalarToFloat(oneMinus2F),
574 };
575
kkinnunen7510b222014-07-30 00:04:16 -0700576 pdman.set1fv(fParamUni, 2, values);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000577 fCachedFocal = focal;
578 }
579}
580
joshualittb0a8a372014-09-23 09:50:21 -0700581void GLFocalOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700582 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -0700583 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -0700584 key[0] = GenBaseGradientKey(processor);
585 key[1] = processor.cast<FocalOutside2PtConicalEffect>().isFlipped();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000586}
587
588//////////////////////////////////////////////////////////////////////////////
589
590class GLFocalInside2PtConicalEffect;
591
592class FocalInside2PtConicalEffect : public GrGradientEffect {
593public:
594
joshualittb0a8a372014-09-23 09:50:21 -0700595 static GrFragmentProcessor* Create(GrContext* ctx,
596 const SkTwoPointConicalGradient& shader,
597 const SkMatrix& matrix,
598 SkShader::TileMode tm,
599 SkScalar focalX) {
bsalomon4a339522015-10-06 08:40:50 -0700600 return new FocalInside2PtConicalEffect(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000601 }
602
603 virtual ~FocalInside2PtConicalEffect() {}
604
mtklein36352bf2015-03-25 18:17:31 -0700605 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -0800606 return "Two-Point Conical Gradient Focal Inside";
607 }
608
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000609 SkScalar focal() const { return fFocalX; }
610
egdaniel57d3b032015-11-13 11:57:27 -0800611 typedef GLFocalInside2PtConicalEffect GLSLProcessor;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000612
613private:
egdaniel57d3b032015-11-13 11:57:27 -0800614 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700615
egdaniel57d3b032015-11-13 11:57:27 -0800616 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700617
mtklein36352bf2015-03-25 18:17:31 -0700618 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700619 const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000620 return (INHERITED::onIsEqual(sBase) &&
621 this->fFocalX == s.fFocalX);
622 }
623
624 FocalInside2PtConicalEffect(GrContext* ctx,
625 const SkTwoPointConicalGradient& shader,
626 const SkMatrix& matrix,
627 SkShader::TileMode tm,
628 SkScalar focalX)
bsalomon4a339522015-10-06 08:40:50 -0700629 : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX) {
joshualitteb2a6762014-12-04 11:35:33 -0800630 this->initClassID<FocalInside2PtConicalEffect>();
631 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000632
joshualittb0a8a372014-09-23 09:50:21 -0700633 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000634
635 SkScalar fFocalX;
636
637 typedef GrGradientEffect INHERITED;
638};
639
640class GLFocalInside2PtConicalEffect : public GrGLGradientEffect {
641public:
joshualitteb2a6762014-12-04 11:35:33 -0800642 GLFocalInside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000643 virtual ~GLFocalInside2PtConicalEffect() {}
644
wangyix7c157a92015-07-22 15:08:53 -0700645 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000646
jvanverthcfc18862015-04-28 08:48:20 -0700647 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000648
649protected:
egdaniel018fb622015-10-28 07:26:40 -0700650 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700651
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000652 UniformHandle fFocalUni;
653
654 const char* fVSVaryingName;
655 const char* fFSVaryingName;
656
657 // @{
658 /// Values last uploaded as uniforms
659
660 SkScalar fCachedFocal;
661
662 // @}
663
664private:
665 typedef GrGLGradientEffect INHERITED;
666
667};
668
egdaniel57d3b032015-11-13 11:57:27 -0800669void FocalInside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
670 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800671 GLFocalInside2PtConicalEffect::GenKey(*this, caps, b);
672}
673
egdaniel57d3b032015-11-13 11:57:27 -0800674GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700675 return new GLFocalInside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000676}
677
joshualittb0a8a372014-09-23 09:50:21 -0700678GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000679
joshualitt01258472014-09-22 10:29:30 -0700680/*
681 * All Two point conical gradient test create functions may occasionally create edge case shaders
682 */
bsalomonc21b09e2015-08-28 18:46:56 -0700683const GrFragmentProcessor* FocalInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700684 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000685 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000686 SkPoint center2;
687 SkScalar radius2;
688 do {
joshualitt0067ff52015-07-08 14:26:19 -0700689 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000690 // Below makes sure radius2 is larger enouch such that the focal point
691 // is inside the end circle
joshualitt0067ff52015-07-08 14:26:19 -0700692 SkScalar increase = d->fRandom->nextUScalar1();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000693 SkPoint diff = center2 - center1;
694 SkScalar diffLen = diff.length();
695 radius2 = diffLen + increase;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000696 // If the circles are identical the factory will give us an empty shader.
697 } while (radius1 == radius2 && center1 == center2);
698
699 SkColor colors[kMaxRandomGradientColors];
700 SkScalar stopsArray[kMaxRandomGradientColors];
701 SkScalar* stops = stopsArray;
702 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700703 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000704 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
705 center2, radius2,
706 colors, stops, colorCount,
707 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700708 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700709 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700710 GrAlwaysAssert(fp);
joshualittb0a8a372014-09-23 09:50:21 -0700711 return fp;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000712}
713
joshualitteb2a6762014-12-04 11:35:33 -0800714GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrProcessor&)
halcanary96fcdcc2015-08-27 07:41:13 -0700715 : fVSVaryingName(nullptr)
716 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000717 , fCachedFocal(SK_ScalarMax) {}
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000718
wangyix7c157a92015-07-22 15:08:53 -0700719void GLFocalInside2PtConicalEffect::emitCode(EmitArgs& args) {
720 const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -0800721 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
722 this->emitUniforms(uniformHandler, ge);
cdalton5e58cee2016-02-11 12:49:47 -0800723 fFocalUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800724 kFloat_GrSLType, kDefault_GrSLPrecision,
725 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000726 SkString tName("t");
727
728 // this is the distance along x-axis from the end center to focal point in
729 // transformed coordinates
egdaniel7ea439b2015-12-03 09:20:44 -0800730 GrGLSLShaderVar focal = uniformHandler->getUniformVariable(fFocalUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000731
732 // if we have a vec3 from being in perspective, convert it to a vec2 first
egdaniel4ca2e602015-11-18 08:01:26 -0800733 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
734 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000735 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000736
737 // t = p.x * focalX + length(p)
egdaniel4ca2e602015-11-18 08:01:26 -0800738 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(),
egdaniel7ea439b2015-12-03 09:20:44 -0800739 coords2D, focal.c_str(), coords2D);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000740
egdaniel7ea439b2015-12-03 09:20:44 -0800741 this->emitColor(fragBuilder,
742 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -0800743 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800744 ge,
745 tName.c_str(),
746 args.fOutputColor,
747 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700748 args.fSamplers);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000749}
750
egdaniel018fb622015-10-28 07:26:40 -0700751void GLFocalInside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
752 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700753 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700754 const FocalInside2PtConicalEffect& data = processor.cast<FocalInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000755 SkScalar focal = data.focal();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000756
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000757 if (fCachedFocal != focal) {
kkinnunen7510b222014-07-30 00:04:16 -0700758 pdman.set1f(fFocalUni, SkScalarToFloat(focal));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000759 fCachedFocal = focal;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000760 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000761}
762
joshualittb0a8a372014-09-23 09:50:21 -0700763void GLFocalInside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700764 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700765 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000766}
767
768//////////////////////////////////////////////////////////////////////////////
769// Circle Conical Gradients
770//////////////////////////////////////////////////////////////////////////////
771
772struct CircleConicalInfo {
773 SkPoint fCenterEnd;
774 SkScalar fA;
775 SkScalar fB;
776 SkScalar fC;
777};
778
779// Returns focal distance along x-axis in transformed coords
780static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
781 SkMatrix* invLMatrix, CircleConicalInfo* info) {
782 // Inverse of the current local matrix is passed in then,
783 // translate and scale such that start circle is on the origin and has radius 1
784 const SkPoint& centerStart = shader.getStartCenter();
785 const SkPoint& centerEnd = shader.getEndCenter();
786 SkScalar radiusStart = shader.getStartRadius();
787 SkScalar radiusEnd = shader.getEndRadius();
788
789 SkMatrix matrix;
790
791 matrix.setTranslate(-centerStart.fX, -centerStart.fY);
792
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000793 SkScalar invStartRad = 1.f / radiusStart;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000794 matrix.postScale(invStartRad, invStartRad);
795
796 radiusEnd /= radiusStart;
797
798 SkPoint centerEndTrans;
799 matrix.mapPoints(&centerEndTrans, &centerEnd, 1);
800
801 SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
802 - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
803
804 // Check to see if start circle is inside end circle with edges touching.
805 // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
egdaniel8405ef92014-06-09 11:57:28 -0700806 // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
807 // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
808 // still accurate.
809 if (SkScalarAbs(A) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000810 return kEdge_ConicalType;
811 }
812
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000813 SkScalar C = 1.f / A;
814 SkScalar B = (radiusEnd - 1.f) * C;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000815
816 matrix.postScale(C, C);
817
818 invLMatrix->postConcat(matrix);
819
820 info->fCenterEnd = centerEndTrans;
821 info->fA = A;
822 info->fB = B;
823 info->fC = C;
824
825 // if A ends up being negative, the start circle is contained completely inside the end cirlce
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000826 if (A < 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000827 return kInside_ConicalType;
828 }
829 return kOutside_ConicalType;
830}
831
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000832class CircleInside2PtConicalEffect : public GrGradientEffect {
833public:
834
joshualittb0a8a372014-09-23 09:50:21 -0700835 static GrFragmentProcessor* Create(GrContext* ctx,
836 const SkTwoPointConicalGradient& shader,
837 const SkMatrix& matrix,
838 SkShader::TileMode tm,
839 const CircleConicalInfo& info) {
bsalomon4a339522015-10-06 08:40:50 -0700840 return new CircleInside2PtConicalEffect(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000841 }
842
843 virtual ~CircleInside2PtConicalEffect() {}
844
mtklein36352bf2015-03-25 18:17:31 -0700845 const char* name() const override { return "Two-Point Conical Gradient Inside"; }
joshualitteb2a6762014-12-04 11:35:33 -0800846
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000847 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
848 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
849 SkScalar A() const { return fInfo.fA; }
850 SkScalar B() const { return fInfo.fB; }
851 SkScalar C() const { return fInfo.fC; }
852
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000853private:
egdaniel57d3b032015-11-13 11:57:27 -0800854 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700855
egdaniel57d3b032015-11-13 11:57:27 -0800856 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
857 GrProcessorKeyBuilder* b) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700858
mtklein36352bf2015-03-25 18:17:31 -0700859 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700860 const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000861 return (INHERITED::onIsEqual(sBase) &&
862 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
863 this->fInfo.fA == s.fInfo.fA &&
864 this->fInfo.fB == s.fInfo.fB &&
865 this->fInfo.fC == s.fInfo.fC);
866 }
867
868 CircleInside2PtConicalEffect(GrContext* ctx,
869 const SkTwoPointConicalGradient& shader,
870 const SkMatrix& matrix,
871 SkShader::TileMode tm,
872 const CircleConicalInfo& info)
bsalomon4a339522015-10-06 08:40:50 -0700873 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
joshualitteb2a6762014-12-04 11:35:33 -0800874 this->initClassID<CircleInside2PtConicalEffect>();
875 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000876
joshualittb0a8a372014-09-23 09:50:21 -0700877 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000878
879 const CircleConicalInfo fInfo;
880
881 typedef GrGradientEffect INHERITED;
882};
883
884class GLCircleInside2PtConicalEffect : public GrGLGradientEffect {
885public:
joshualitteb2a6762014-12-04 11:35:33 -0800886 GLCircleInside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000887 virtual ~GLCircleInside2PtConicalEffect() {}
888
wangyix7c157a92015-07-22 15:08:53 -0700889 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000890
jvanverthcfc18862015-04-28 08:48:20 -0700891 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000892
893protected:
egdaniel018fb622015-10-28 07:26:40 -0700894 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700895
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000896 UniformHandle fCenterUni;
897 UniformHandle fParamUni;
898
899 const char* fVSVaryingName;
900 const char* fFSVaryingName;
901
902 // @{
903 /// Values last uploaded as uniforms
904
905 SkScalar fCachedCenterX;
906 SkScalar fCachedCenterY;
907 SkScalar fCachedA;
908 SkScalar fCachedB;
909 SkScalar fCachedC;
910
911 // @}
912
913private:
914 typedef GrGLGradientEffect INHERITED;
915
916};
917
egdaniel57d3b032015-11-13 11:57:27 -0800918void CircleInside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
919 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800920 GLCircleInside2PtConicalEffect::GenKey(*this, caps, b);
921}
922
egdaniel57d3b032015-11-13 11:57:27 -0800923GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700924 return new GLCircleInside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000925}
926
joshualittb0a8a372014-09-23 09:50:21 -0700927GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000928
joshualitt01258472014-09-22 10:29:30 -0700929/*
930 * All Two point conical gradient test create functions may occasionally create edge case shaders
931 */
bsalomonc21b09e2015-08-28 18:46:56 -0700932const GrFragmentProcessor* CircleInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700933 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
934 SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000935 SkPoint center2;
936 SkScalar radius2;
937 do {
joshualitt0067ff52015-07-08 14:26:19 -0700938 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000939 // Below makes sure that circle one is contained within circle two
joshualitt0067ff52015-07-08 14:26:19 -0700940 SkScalar increase = d->fRandom->nextUScalar1();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000941 SkPoint diff = center2 - center1;
942 SkScalar diffLen = diff.length();
943 radius2 = radius1 + diffLen + increase;
944 // If the circles are identical the factory will give us an empty shader.
945 } while (radius1 == radius2 && center1 == center2);
946
947 SkColor colors[kMaxRandomGradientColors];
948 SkScalar stopsArray[kMaxRandomGradientColors];
949 SkScalar* stops = stopsArray;
950 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700951 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000952 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
953 center2, radius2,
954 colors, stops, colorCount,
955 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700956 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700957 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700958 GrAlwaysAssert(fp);
joshualitt8ca93e72015-07-08 06:51:43 -0700959 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000960}
961
joshualitteb2a6762014-12-04 11:35:33 -0800962GLCircleInside2PtConicalEffect::GLCircleInside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -0700963 : fVSVaryingName(nullptr)
964 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000965 , fCachedCenterX(SK_ScalarMax)
966 , fCachedCenterY(SK_ScalarMax)
967 , fCachedA(SK_ScalarMax)
968 , fCachedB(SK_ScalarMax)
969 , fCachedC(SK_ScalarMax) {}
970
wangyix7c157a92015-07-22 15:08:53 -0700971void GLCircleInside2PtConicalEffect::emitCode(EmitArgs& args) {
972 const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -0800973 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
974 this->emitUniforms(uniformHandler, ge);
cdalton5e58cee2016-02-11 12:49:47 -0800975 fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800976 kVec2f_GrSLType, kDefault_GrSLPrecision,
977 "Conical2FSCenter");
cdalton5e58cee2016-02-11 12:49:47 -0800978 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800979 kVec3f_GrSLType, kDefault_GrSLPrecision,
980 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000981 SkString tName("t");
982
egdaniel7ea439b2015-12-03 09:20:44 -0800983 GrGLSLShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000984 // params.x = A
985 // params.y = B
986 // params.z = C
egdaniel7ea439b2015-12-03 09:20:44 -0800987 GrGLSLShaderVar params = uniformHandler->getUniformVariable(fParamUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000988
989 // if we have a vec3 from being in perspective, convert it to a vec2 first
egdaniel4ca2e602015-11-18 08:01:26 -0800990 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
991 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000992 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000993
994 // p = coords2D
995 // e = center end
996 // r = radius end
997 // A = dot(e, e) - r^2 + 2 * r - 1
998 // B = (r -1) / A
999 // C = 1 / A
1000 // d = dot(e, p) + B
1001 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
egdaniel4ca2e602015-11-18 08:01:26 -08001002 fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
1003 fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
1004 params.c_str());
1005 fragBuilder->codeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
1006 tName.c_str(), params.c_str(), params.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001007
egdaniel7ea439b2015-12-03 09:20:44 -08001008 this->emitColor(fragBuilder,
1009 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -08001010 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -08001011 ge,
1012 tName.c_str(),
1013 args.fOutputColor,
1014 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -07001015 args.fSamplers);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001016}
1017
egdaniel018fb622015-10-28 07:26:40 -07001018void GLCircleInside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1019 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -07001020 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -07001021 const CircleInside2PtConicalEffect& data = processor.cast<CircleInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001022 SkScalar centerX = data.centerX();
1023 SkScalar centerY = data.centerY();
1024 SkScalar A = data.A();
1025 SkScalar B = data.B();
1026 SkScalar C = data.C();
1027
1028 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1029 fCachedA != A || fCachedB != B || fCachedC != C) {
1030
kkinnunen7510b222014-07-30 00:04:16 -07001031 pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1032 pdman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001033
1034 fCachedCenterX = centerX;
1035 fCachedCenterY = centerY;
1036 fCachedA = A;
1037 fCachedB = B;
1038 fCachedC = C;
1039 }
1040}
1041
joshualittb0a8a372014-09-23 09:50:21 -07001042void GLCircleInside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -07001043 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -07001044 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001045}
1046
1047//////////////////////////////////////////////////////////////////////////////
1048
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001049class CircleOutside2PtConicalEffect : public GrGradientEffect {
1050public:
1051
joshualittb0a8a372014-09-23 09:50:21 -07001052 static GrFragmentProcessor* Create(GrContext* ctx,
1053 const SkTwoPointConicalGradient& shader,
1054 const SkMatrix& matrix,
1055 SkShader::TileMode tm,
1056 const CircleConicalInfo& info) {
bsalomon4a339522015-10-06 08:40:50 -07001057 return new CircleOutside2PtConicalEffect(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001058 }
1059
1060 virtual ~CircleOutside2PtConicalEffect() {}
1061
mtklein36352bf2015-03-25 18:17:31 -07001062 const char* name() const override { return "Two-Point Conical Gradient Outside"; }
joshualitteb2a6762014-12-04 11:35:33 -08001063
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001064 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
1065 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
1066 SkScalar A() const { return fInfo.fA; }
1067 SkScalar B() const { return fInfo.fB; }
1068 SkScalar C() const { return fInfo.fC; }
1069 SkScalar tLimit() const { return fTLimit; }
1070 bool isFlipped() const { return fIsFlipped; }
1071
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001072private:
egdaniel57d3b032015-11-13 11:57:27 -08001073 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -07001074
egdaniel57d3b032015-11-13 11:57:27 -08001075 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -07001076
mtklein36352bf2015-03-25 18:17:31 -07001077 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -07001078 const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001079 return (INHERITED::onIsEqual(sBase) &&
1080 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
1081 this->fInfo.fA == s.fInfo.fA &&
1082 this->fInfo.fB == s.fInfo.fB &&
1083 this->fInfo.fC == s.fInfo.fC &&
1084 this->fTLimit == s.fTLimit &&
1085 this->fIsFlipped == s.fIsFlipped);
1086 }
1087
1088 CircleOutside2PtConicalEffect(GrContext* ctx,
1089 const SkTwoPointConicalGradient& shader,
1090 const SkMatrix& matrix,
1091 SkShader::TileMode tm,
1092 const CircleConicalInfo& info)
bsalomon4a339522015-10-06 08:40:50 -07001093 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
joshualitteb2a6762014-12-04 11:35:33 -08001094 this->initClassID<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001095 if (shader.getStartRadius() != shader.getEndRadius()) {
reed80ea19c2015-05-12 10:37:34 -07001096 fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001097 } else {
1098 fTLimit = SK_ScalarMin;
1099 }
1100
1101 fIsFlipped = shader.isFlippedGrad();
1102 }
1103
joshualittb0a8a372014-09-23 09:50:21 -07001104 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001105
1106 const CircleConicalInfo fInfo;
1107 SkScalar fTLimit;
1108 bool fIsFlipped;
1109
1110 typedef GrGradientEffect INHERITED;
1111};
1112
1113class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect {
1114public:
joshualitteb2a6762014-12-04 11:35:33 -08001115 GLCircleOutside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001116 virtual ~GLCircleOutside2PtConicalEffect() {}
1117
wangyix7c157a92015-07-22 15:08:53 -07001118 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001119
jvanverthcfc18862015-04-28 08:48:20 -07001120 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001121
1122protected:
egdaniel018fb622015-10-28 07:26:40 -07001123 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -07001124
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001125 UniformHandle fCenterUni;
1126 UniformHandle fParamUni;
1127
1128 const char* fVSVaryingName;
1129 const char* fFSVaryingName;
1130
1131 bool fIsFlipped;
1132
1133 // @{
1134 /// Values last uploaded as uniforms
1135
1136 SkScalar fCachedCenterX;
1137 SkScalar fCachedCenterY;
1138 SkScalar fCachedA;
1139 SkScalar fCachedB;
1140 SkScalar fCachedC;
1141 SkScalar fCachedTLimit;
1142
1143 // @}
1144
1145private:
1146 typedef GrGLGradientEffect INHERITED;
1147
1148};
1149
egdaniel57d3b032015-11-13 11:57:27 -08001150void CircleOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
1151 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -08001152 GLCircleOutside2PtConicalEffect::GenKey(*this, caps, b);
1153}
1154
egdaniel57d3b032015-11-13 11:57:27 -08001155GrGLSLFragmentProcessor* CircleOutside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -07001156 return new GLCircleOutside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001157}
1158
joshualittb0a8a372014-09-23 09:50:21 -07001159GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001160
joshualitt01258472014-09-22 10:29:30 -07001161/*
1162 * All Two point conical gradient test create functions may occasionally create edge case shaders
1163 */
bsalomonc21b09e2015-08-28 18:46:56 -07001164const GrFragmentProcessor* CircleOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -07001165 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
1166 SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001167 SkPoint center2;
1168 SkScalar radius2;
1169 SkScalar diffLen;
1170 do {
joshualitt0067ff52015-07-08 14:26:19 -07001171 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001172 // If the circles share a center than we can't be in the outside case
1173 } while (center1 == center2);
joshualitt01258472014-09-22 10:29:30 -07001174 SkPoint diff = center2 - center1;
1175 diffLen = diff.length();
1176 // Below makes sure that circle one is not contained within circle two
1177 // and have radius2 >= radius to match sorting on cpu side
joshualitt0067ff52015-07-08 14:26:19 -07001178 radius2 = radius1 + d->fRandom->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001179
1180 SkColor colors[kMaxRandomGradientColors];
1181 SkScalar stopsArray[kMaxRandomGradientColors];
1182 SkScalar* stops = stopsArray;
1183 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -07001184 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001185 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
1186 center2, radius2,
1187 colors, stops, colorCount,
1188 tm));
bsalomonc21b09e2015-08-28 18:46:56 -07001189 const GrFragmentProcessor* fp = shader->asFragmentProcessor(
bsalomon4a339522015-10-06 08:40:50 -07001190 d->fContext,GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -07001191 GrAlwaysAssert(fp);
joshualitt8ca93e72015-07-08 06:51:43 -07001192 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001193}
1194
joshualitteb2a6762014-12-04 11:35:33 -08001195GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -07001196 : fVSVaryingName(nullptr)
1197 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001198 , fCachedCenterX(SK_ScalarMax)
1199 , fCachedCenterY(SK_ScalarMax)
1200 , fCachedA(SK_ScalarMax)
1201 , fCachedB(SK_ScalarMax)
1202 , fCachedC(SK_ScalarMax)
1203 , fCachedTLimit(SK_ScalarMax) {
joshualittb0a8a372014-09-23 09:50:21 -07001204 const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001205 fIsFlipped = data.isFlipped();
1206 }
1207
wangyix7c157a92015-07-22 15:08:53 -07001208void GLCircleOutside2PtConicalEffect::emitCode(EmitArgs& args) {
1209 const CircleOutside2PtConicalEffect& ge = args.fFp.cast<CircleOutside2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -08001210 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
1211 this->emitUniforms(uniformHandler, ge);
cdalton5e58cee2016-02-11 12:49:47 -08001212 fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -08001213 kVec2f_GrSLType, kDefault_GrSLPrecision,
1214 "Conical2FSCenter");
cdalton5e58cee2016-02-11 12:49:47 -08001215 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -08001216 kVec4f_GrSLType, kDefault_GrSLPrecision,
1217 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001218 SkString tName("t");
1219
egdaniel7ea439b2015-12-03 09:20:44 -08001220 GrGLSLShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001221 // params.x = A
1222 // params.y = B
1223 // params.z = C
egdaniel7ea439b2015-12-03 09:20:44 -08001224 GrGLSLShaderVar params = uniformHandler->getUniformVariable(fParamUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001225
1226 // if we have a vec3 from being in perspective, convert it to a vec2 first
egdaniel4ca2e602015-11-18 08:01:26 -08001227 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
1228 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +00001229 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001230
1231 // output will default to transparent black (we simply won't write anything
1232 // else to it if invalid, instead of discarding or returning prematurely)
egdaniel4ca2e602015-11-18 08:01:26 -08001233 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 +00001234
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001235 // p = coords2D
1236 // e = center end
1237 // r = radius end
1238 // A = dot(e, e) - r^2 + 2 * r - 1
1239 // B = (r -1) / A
1240 // C = 1 / A
1241 // d = dot(e, p) + B
1242 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001243
egdaniel4ca2e602015-11-18 08:01:26 -08001244 fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
1245 fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
1246 params.c_str());
1247 fragBuilder->codeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(),
1248 params.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001249
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001250 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
1251 // If so we must also flip sign on sqrt
1252 if (!fIsFlipped) {
egdaniel4ca2e602015-11-18 08:01:26 -08001253 fragBuilder->codeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001254 } else {
egdaniel4ca2e602015-11-18 08:01:26 -08001255 fragBuilder->codeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001256 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001257
egdaniel7ea439b2015-12-03 09:20:44 -08001258 fragBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n",
1259 tName.c_str(), params.c_str());
egdaniel4ca2e602015-11-18 08:01:26 -08001260 fragBuilder->codeAppend("\t\t");
egdaniel7ea439b2015-12-03 09:20:44 -08001261 this->emitColor(fragBuilder,
1262 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -08001263 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -08001264 ge,
1265 tName.c_str(),
1266 args.fOutputColor,
1267 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -07001268 args.fSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -08001269 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001270}
1271
egdaniel018fb622015-10-28 07:26:40 -07001272void GLCircleOutside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1273 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -07001274 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -07001275 const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +00001276 SkASSERT(data.isFlipped() == fIsFlipped);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001277 SkScalar centerX = data.centerX();
1278 SkScalar centerY = data.centerY();
1279 SkScalar A = data.A();
1280 SkScalar B = data.B();
1281 SkScalar C = data.C();
1282 SkScalar tLimit = data.tLimit();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001283
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001284 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1285 fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001286
kkinnunen7510b222014-07-30 00:04:16 -07001287 pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1288 pdman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001289 SkScalarToFloat(tLimit));
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001290
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001291 fCachedCenterX = centerX;
1292 fCachedCenterY = centerY;
1293 fCachedA = A;
1294 fCachedB = B;
1295 fCachedC = C;
1296 fCachedTLimit = tLimit;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001297 }
1298}
1299
joshualittb0a8a372014-09-23 09:50:21 -07001300void GLCircleOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -07001301 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -07001302 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -07001303 key[0] = GenBaseGradientKey(processor);
1304 key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001305}
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001306
1307//////////////////////////////////////////////////////////////////////////////
1308
joshualittb0a8a372014-09-23 09:50:21 -07001309GrFragmentProcessor* Gr2PtConicalGradientEffect::Create(GrContext* ctx,
1310 const SkTwoPointConicalGradient& shader,
1311 SkShader::TileMode tm,
1312 const SkMatrix* localMatrix) {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001313 SkMatrix matrix;
1314 if (!shader.getLocalMatrix().invert(&matrix)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001315 return nullptr;
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001316 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001317 if (localMatrix) {
1318 SkMatrix inv;
1319 if (!localMatrix->invert(&inv)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001320 return nullptr;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001321 }
1322 matrix.postConcat(inv);
1323 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001324
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001325 if (shader.getStartRadius() < kErrorTol) {
1326 SkScalar focalX;
1327 ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
1328 if (type == kInside_ConicalType) {
bsalomon4a339522015-10-06 08:40:50 -07001329 return FocalInside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
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 FocalOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001335 }
1336 }
1337
1338 CircleConicalInfo info;
1339 ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
1340
1341 if (type == kInside_ConicalType) {
bsalomon4a339522015-10-06 08:40:50 -07001342 return CircleInside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001343 } else if (type == kEdge_ConicalType) {
1344 set_matrix_edge_conical(shader, &matrix);
bsalomon4a339522015-10-06 08:40:50 -07001345 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001346 } else {
bsalomon4a339522015-10-06 08:40:50 -07001347 return CircleOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001348 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001349}
1350
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001351#endif