blob: 31ceda4f2aed45777efb90dd994ff5499a6f81cb [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,
egdaniela2e3e0f2015-11-19 07:23:45 -0800275 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800276 ge,
277 tName.c_str(),
278 args.fOutputColor,
279 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700280 args.fSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -0800281 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000282}
283
egdaniel018fb622015-10-28 07:26:40 -0700284void GLEdge2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
285 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700286 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700287 const Edge2PtConicalEffect& data = processor.cast<Edge2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000288 SkScalar radius0 = data.radius();
289 SkScalar diffRadius = data.diffRadius();
290
291 if (fCachedRadius != radius0 ||
292 fCachedDiffRadius != diffRadius) {
293
294 float values[3] = {
295 SkScalarToFloat(radius0),
296 SkScalarToFloat(SkScalarMul(radius0, radius0)),
297 SkScalarToFloat(diffRadius)
298 };
299
kkinnunen7510b222014-07-30 00:04:16 -0700300 pdman.set1fv(fParamUni, 3, values);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000301 fCachedRadius = radius0;
302 fCachedDiffRadius = diffRadius;
303 }
304}
305
joshualittb0a8a372014-09-23 09:50:21 -0700306void GLEdge2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700307 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700308 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000309}
310
311//////////////////////////////////////////////////////////////////////////////
312// Focal Conical Gradients
313//////////////////////////////////////////////////////////////////////////////
314
315static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader,
316 SkMatrix* invLMatrix, SkScalar* focalX) {
317 // Inverse of the current local matrix is passed in then,
318 // translate, scale, and rotate such that endCircle is unit circle on x-axis,
319 // and focal point is at the origin.
320 ConicalType conicalType;
321 const SkPoint& focal = shader.getStartCenter();
322 const SkPoint& centerEnd = shader.getEndCenter();
323 SkScalar radius = shader.getEndRadius();
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000324 SkScalar invRadius = 1.f / radius;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000325
326 SkMatrix matrix;
327
328 matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
329 matrix.postScale(invRadius, invRadius);
330
331 SkPoint focalTrans;
332 matrix.mapPoints(&focalTrans, &focal, 1);
333 *focalX = focalTrans.length();
334
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000335 if (0.f != *focalX) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000336 SkScalar invFocalX = SkScalarInvert(*focalX);
337 SkMatrix rot;
338 rot.setSinCos(-SkScalarMul(invFocalX, focalTrans.fY),
339 SkScalarMul(invFocalX, focalTrans.fX));
340 matrix.postConcat(rot);
341 }
342
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000343 matrix.postTranslate(-(*focalX), 0.f);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000344
345 // If the focal point is touching the edge of the circle it will
346 // cause a degenerate case that must be handled separately
egdaniel8405ef92014-06-09 11:57:28 -0700347 // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
348 // stability trade off versus the linear approx used in the Edge Shader
349 if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000350 return kEdge_ConicalType;
351 }
352
353 // Scale factor 1 / (1 - focalX * focalX)
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000354 SkScalar oneMinusF2 = 1.f - SkScalarMul(*focalX, *focalX);
reed80ea19c2015-05-12 10:37:34 -0700355 SkScalar s = SkScalarInvert(oneMinusF2);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000356
357
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000358 if (s >= 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000359 conicalType = kInside_ConicalType;
360 matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
361 } else {
362 conicalType = kOutside_ConicalType;
363 matrix.postScale(s, s);
364 }
365
366 invLMatrix->postConcat(matrix);
367
368 return conicalType;
369}
370
371//////////////////////////////////////////////////////////////////////////////
372
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000373class FocalOutside2PtConicalEffect : public GrGradientEffect {
374public:
375
joshualittb0a8a372014-09-23 09:50:21 -0700376 static GrFragmentProcessor* Create(GrContext* ctx,
377 const SkTwoPointConicalGradient& shader,
378 const SkMatrix& matrix,
379 SkShader::TileMode tm,
380 SkScalar focalX) {
bsalomon4a339522015-10-06 08:40:50 -0700381 return new FocalOutside2PtConicalEffect(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000382 }
383
384 virtual ~FocalOutside2PtConicalEffect() { }
385
mtklein36352bf2015-03-25 18:17:31 -0700386 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -0800387 return "Two-Point Conical Gradient Focal Outside";
388 }
389
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000390 bool isFlipped() const { return fIsFlipped; }
391 SkScalar focal() const { return fFocalX; }
392
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000393private:
egdaniel57d3b032015-11-13 11:57:27 -0800394 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700395
egdaniel57d3b032015-11-13 11:57:27 -0800396 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700397
mtklein36352bf2015-03-25 18:17:31 -0700398 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700399 const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000400 return (INHERITED::onIsEqual(sBase) &&
401 this->fFocalX == s.fFocalX &&
402 this->fIsFlipped == s.fIsFlipped);
403 }
404
405 FocalOutside2PtConicalEffect(GrContext* ctx,
406 const SkTwoPointConicalGradient& shader,
407 const SkMatrix& matrix,
408 SkShader::TileMode tm,
409 SkScalar focalX)
bsalomon4a339522015-10-06 08:40:50 -0700410 : INHERITED(ctx, shader, matrix, tm)
joshualittb2456052015-07-08 09:36:59 -0700411 , fFocalX(focalX)
412 , fIsFlipped(shader.isFlippedGrad()) {
joshualitteb2a6762014-12-04 11:35:33 -0800413 this->initClassID<FocalOutside2PtConicalEffect>();
414 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000415
joshualittb0a8a372014-09-23 09:50:21 -0700416 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000417
418 SkScalar fFocalX;
419 bool fIsFlipped;
420
421 typedef GrGradientEffect INHERITED;
422};
423
424class GLFocalOutside2PtConicalEffect : public GrGLGradientEffect {
425public:
joshualitteb2a6762014-12-04 11:35:33 -0800426 GLFocalOutside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000427 virtual ~GLFocalOutside2PtConicalEffect() { }
428
wangyix7c157a92015-07-22 15:08:53 -0700429 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000430
jvanverthcfc18862015-04-28 08:48:20 -0700431 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000432
433protected:
egdaniel018fb622015-10-28 07:26:40 -0700434 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700435
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000436 UniformHandle fParamUni;
437
438 const char* fVSVaryingName;
439 const char* fFSVaryingName;
440
441 bool fIsFlipped;
442
443 // @{
444 /// Values last uploaded as uniforms
445
446 SkScalar fCachedFocal;
447
448 // @}
449
450private:
451 typedef GrGLGradientEffect INHERITED;
452
453};
454
egdaniel57d3b032015-11-13 11:57:27 -0800455void FocalOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
456 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800457 GLFocalOutside2PtConicalEffect::GenKey(*this, caps, b);
458}
459
egdaniel57d3b032015-11-13 11:57:27 -0800460GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700461 return new GLFocalOutside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000462}
463
joshualittb0a8a372014-09-23 09:50:21 -0700464GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000465
joshualitt01258472014-09-22 10:29:30 -0700466/*
467 * All Two point conical gradient test create functions may occasionally create edge case shaders
468 */
bsalomonc21b09e2015-08-28 18:46:56 -0700469const GrFragmentProcessor* FocalOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700470 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000471 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000472 SkPoint center2;
473 SkScalar radius2;
474 do {
joshualitt0067ff52015-07-08 14:26:19 -0700475 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000476 // 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 +0000477 } while (center1 == center2);
478 SkPoint diff = center2 - center1;
479 SkScalar diffLen = diff.length();
480 // Below makes sure that the focal point is not contained within circle two
joshualitt0067ff52015-07-08 14:26:19 -0700481 radius2 = d->fRandom->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000482
483 SkColor colors[kMaxRandomGradientColors];
484 SkScalar stopsArray[kMaxRandomGradientColors];
485 SkScalar* stops = stopsArray;
486 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700487 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000488 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
489 center2, radius2,
490 colors, stops, colorCount,
491 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700492 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700493 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700494 GrAlwaysAssert(fp);
495 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000496}
497
joshualitteb2a6762014-12-04 11:35:33 -0800498GLFocalOutside2PtConicalEffect::GLFocalOutside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -0700499 : fVSVaryingName(nullptr)
500 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000501 , fCachedFocal(SK_ScalarMax) {
joshualittb0a8a372014-09-23 09:50:21 -0700502 const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000503 fIsFlipped = data.isFlipped();
504}
505
wangyix7c157a92015-07-22 15:08:53 -0700506void GLFocalOutside2PtConicalEffect::emitCode(EmitArgs& args) {
507 const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>();
508 this->emitUniforms(args.fBuilder, ge);
egdaniel2d721d32015-11-11 13:06:05 -0800509 fParamUni = args.fBuilder->addUniformArray(GrGLSLProgramBuilder::kFragment_Visibility,
bsalomon422f56f2014-12-09 10:18:12 -0800510 kFloat_GrSLType, kDefault_GrSLPrecision,
511 "Conical2FSParams", 2);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000512 SkString tName("t");
513 SkString p0; // focalX
514 SkString p1; // 1 - focalX * focalX
515
wangyix7c157a92015-07-22 15:08:53 -0700516 args.fBuilder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
517 args.fBuilder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000518
519 // if we have a vec3 from being in perspective, convert it to a vec2 first
egdaniel4ca2e602015-11-18 08:01:26 -0800520 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
521 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000522 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000523
524 // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
525
526 // output will default to transparent black (we simply won't write anything
527 // else to it if invalid, instead of discarding or returning prematurely)
egdaniel4ca2e602015-11-18 08:01:26 -0800528 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 +0000529
egdaniel4ca2e602015-11-18 08:01:26 -0800530 fragBuilder->codeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D);
531 fragBuilder->codeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D);
532 fragBuilder->codeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000533
534 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
535 // If so we must also flip sign on sqrt
536 if (!fIsFlipped) {
egdaniel4ca2e602015-11-18 08:01:26 -0800537 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + sqrt(d);\n", tName.c_str(),
538 coords2D, p0.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000539 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800540 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s - sqrt(d);\n", tName.c_str(),
541 coords2D, p0.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000542 }
543
egdaniel4ca2e602015-11-18 08:01:26 -0800544 fragBuilder->codeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str());
545 fragBuilder->codeAppend("\t\t");
546 this->emitColor(args.fBuilder,
547 fragBuilder,
egdaniela2e3e0f2015-11-19 07:23:45 -0800548 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800549 ge,
550 tName.c_str(),
551 args.fOutputColor,
552 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700553 args.fSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -0800554 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000555}
556
egdaniel018fb622015-10-28 07:26:40 -0700557void GLFocalOutside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
558 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700559 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700560 const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000561 SkASSERT(data.isFlipped() == fIsFlipped);
562 SkScalar focal = data.focal();
563
564 if (fCachedFocal != focal) {
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000565 SkScalar oneMinus2F = 1.f - SkScalarMul(focal, focal);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000566
567 float values[2] = {
568 SkScalarToFloat(focal),
569 SkScalarToFloat(oneMinus2F),
570 };
571
kkinnunen7510b222014-07-30 00:04:16 -0700572 pdman.set1fv(fParamUni, 2, values);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000573 fCachedFocal = focal;
574 }
575}
576
joshualittb0a8a372014-09-23 09:50:21 -0700577void GLFocalOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700578 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -0700579 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -0700580 key[0] = GenBaseGradientKey(processor);
581 key[1] = processor.cast<FocalOutside2PtConicalEffect>().isFlipped();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000582}
583
584//////////////////////////////////////////////////////////////////////////////
585
586class GLFocalInside2PtConicalEffect;
587
588class FocalInside2PtConicalEffect : public GrGradientEffect {
589public:
590
joshualittb0a8a372014-09-23 09:50:21 -0700591 static GrFragmentProcessor* Create(GrContext* ctx,
592 const SkTwoPointConicalGradient& shader,
593 const SkMatrix& matrix,
594 SkShader::TileMode tm,
595 SkScalar focalX) {
bsalomon4a339522015-10-06 08:40:50 -0700596 return new FocalInside2PtConicalEffect(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000597 }
598
599 virtual ~FocalInside2PtConicalEffect() {}
600
mtklein36352bf2015-03-25 18:17:31 -0700601 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -0800602 return "Two-Point Conical Gradient Focal Inside";
603 }
604
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000605 SkScalar focal() const { return fFocalX; }
606
egdaniel57d3b032015-11-13 11:57:27 -0800607 typedef GLFocalInside2PtConicalEffect GLSLProcessor;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000608
609private:
egdaniel57d3b032015-11-13 11:57:27 -0800610 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700611
egdaniel57d3b032015-11-13 11:57:27 -0800612 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700613
mtklein36352bf2015-03-25 18:17:31 -0700614 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700615 const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000616 return (INHERITED::onIsEqual(sBase) &&
617 this->fFocalX == s.fFocalX);
618 }
619
620 FocalInside2PtConicalEffect(GrContext* ctx,
621 const SkTwoPointConicalGradient& shader,
622 const SkMatrix& matrix,
623 SkShader::TileMode tm,
624 SkScalar focalX)
bsalomon4a339522015-10-06 08:40:50 -0700625 : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX) {
joshualitteb2a6762014-12-04 11:35:33 -0800626 this->initClassID<FocalInside2PtConicalEffect>();
627 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000628
joshualittb0a8a372014-09-23 09:50:21 -0700629 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000630
631 SkScalar fFocalX;
632
633 typedef GrGradientEffect INHERITED;
634};
635
636class GLFocalInside2PtConicalEffect : public GrGLGradientEffect {
637public:
joshualitteb2a6762014-12-04 11:35:33 -0800638 GLFocalInside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000639 virtual ~GLFocalInside2PtConicalEffect() {}
640
wangyix7c157a92015-07-22 15:08:53 -0700641 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000642
jvanverthcfc18862015-04-28 08:48:20 -0700643 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000644
645protected:
egdaniel018fb622015-10-28 07:26:40 -0700646 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700647
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000648 UniformHandle fFocalUni;
649
650 const char* fVSVaryingName;
651 const char* fFSVaryingName;
652
653 // @{
654 /// Values last uploaded as uniforms
655
656 SkScalar fCachedFocal;
657
658 // @}
659
660private:
661 typedef GrGLGradientEffect INHERITED;
662
663};
664
egdaniel57d3b032015-11-13 11:57:27 -0800665void FocalInside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
666 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800667 GLFocalInside2PtConicalEffect::GenKey(*this, caps, b);
668}
669
egdaniel57d3b032015-11-13 11:57:27 -0800670GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700671 return new GLFocalInside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000672}
673
joshualittb0a8a372014-09-23 09:50:21 -0700674GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000675
joshualitt01258472014-09-22 10:29:30 -0700676/*
677 * All Two point conical gradient test create functions may occasionally create edge case shaders
678 */
bsalomonc21b09e2015-08-28 18:46:56 -0700679const GrFragmentProcessor* FocalInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700680 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000681 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000682 SkPoint center2;
683 SkScalar radius2;
684 do {
joshualitt0067ff52015-07-08 14:26:19 -0700685 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000686 // Below makes sure radius2 is larger enouch such that the focal point
687 // is inside the end circle
joshualitt0067ff52015-07-08 14:26:19 -0700688 SkScalar increase = d->fRandom->nextUScalar1();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000689 SkPoint diff = center2 - center1;
690 SkScalar diffLen = diff.length();
691 radius2 = diffLen + increase;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000692 // If the circles are identical the factory will give us an empty shader.
693 } while (radius1 == radius2 && center1 == center2);
694
695 SkColor colors[kMaxRandomGradientColors];
696 SkScalar stopsArray[kMaxRandomGradientColors];
697 SkScalar* stops = stopsArray;
698 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700699 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000700 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
701 center2, radius2,
702 colors, stops, colorCount,
703 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700704 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700705 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700706 GrAlwaysAssert(fp);
joshualittb0a8a372014-09-23 09:50:21 -0700707 return fp;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000708}
709
joshualitteb2a6762014-12-04 11:35:33 -0800710GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrProcessor&)
halcanary96fcdcc2015-08-27 07:41:13 -0700711 : fVSVaryingName(nullptr)
712 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000713 , fCachedFocal(SK_ScalarMax) {}
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000714
wangyix7c157a92015-07-22 15:08:53 -0700715void GLFocalInside2PtConicalEffect::emitCode(EmitArgs& args) {
716 const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>();
717 this->emitUniforms(args.fBuilder, ge);
egdaniel2d721d32015-11-11 13:06:05 -0800718 fFocalUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
719 kFloat_GrSLType, kDefault_GrSLPrecision,
720 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000721 SkString tName("t");
722
723 // this is the distance along x-axis from the end center to focal point in
724 // transformed coordinates
egdaniel0d3f0612015-10-21 10:45:48 -0700725 GrGLSLShaderVar focal = args.fBuilder->getUniformVariable(fFocalUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000726
727 // if we have a vec3 from being in perspective, convert it to a vec2 first
egdaniel4ca2e602015-11-18 08:01:26 -0800728 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
729 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000730 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000731
732 // t = p.x * focalX + length(p)
egdaniel4ca2e602015-11-18 08:01:26 -0800733 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000734 coords2D, focal.c_str(), coords2D);
735
egdaniel4ca2e602015-11-18 08:01:26 -0800736 this->emitColor(args.fBuilder,
737 fragBuilder,
egdaniela2e3e0f2015-11-19 07:23:45 -0800738 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800739 ge,
740 tName.c_str(),
741 args.fOutputColor,
742 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700743 args.fSamplers);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000744}
745
egdaniel018fb622015-10-28 07:26:40 -0700746void GLFocalInside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
747 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700748 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700749 const FocalInside2PtConicalEffect& data = processor.cast<FocalInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000750 SkScalar focal = data.focal();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000751
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000752 if (fCachedFocal != focal) {
kkinnunen7510b222014-07-30 00:04:16 -0700753 pdman.set1f(fFocalUni, SkScalarToFloat(focal));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000754 fCachedFocal = focal;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000755 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000756}
757
joshualittb0a8a372014-09-23 09:50:21 -0700758void GLFocalInside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700759 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700760 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000761}
762
763//////////////////////////////////////////////////////////////////////////////
764// Circle Conical Gradients
765//////////////////////////////////////////////////////////////////////////////
766
767struct CircleConicalInfo {
768 SkPoint fCenterEnd;
769 SkScalar fA;
770 SkScalar fB;
771 SkScalar fC;
772};
773
774// Returns focal distance along x-axis in transformed coords
775static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
776 SkMatrix* invLMatrix, CircleConicalInfo* info) {
777 // Inverse of the current local matrix is passed in then,
778 // translate and scale such that start circle is on the origin and has radius 1
779 const SkPoint& centerStart = shader.getStartCenter();
780 const SkPoint& centerEnd = shader.getEndCenter();
781 SkScalar radiusStart = shader.getStartRadius();
782 SkScalar radiusEnd = shader.getEndRadius();
783
784 SkMatrix matrix;
785
786 matrix.setTranslate(-centerStart.fX, -centerStart.fY);
787
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000788 SkScalar invStartRad = 1.f / radiusStart;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000789 matrix.postScale(invStartRad, invStartRad);
790
791 radiusEnd /= radiusStart;
792
793 SkPoint centerEndTrans;
794 matrix.mapPoints(&centerEndTrans, &centerEnd, 1);
795
796 SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
797 - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
798
799 // Check to see if start circle is inside end circle with edges touching.
800 // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
egdaniel8405ef92014-06-09 11:57:28 -0700801 // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
802 // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
803 // still accurate.
804 if (SkScalarAbs(A) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000805 return kEdge_ConicalType;
806 }
807
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000808 SkScalar C = 1.f / A;
809 SkScalar B = (radiusEnd - 1.f) * C;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000810
811 matrix.postScale(C, C);
812
813 invLMatrix->postConcat(matrix);
814
815 info->fCenterEnd = centerEndTrans;
816 info->fA = A;
817 info->fB = B;
818 info->fC = C;
819
820 // 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 +0000821 if (A < 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000822 return kInside_ConicalType;
823 }
824 return kOutside_ConicalType;
825}
826
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000827class CircleInside2PtConicalEffect : public GrGradientEffect {
828public:
829
joshualittb0a8a372014-09-23 09:50:21 -0700830 static GrFragmentProcessor* Create(GrContext* ctx,
831 const SkTwoPointConicalGradient& shader,
832 const SkMatrix& matrix,
833 SkShader::TileMode tm,
834 const CircleConicalInfo& info) {
bsalomon4a339522015-10-06 08:40:50 -0700835 return new CircleInside2PtConicalEffect(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000836 }
837
838 virtual ~CircleInside2PtConicalEffect() {}
839
mtklein36352bf2015-03-25 18:17:31 -0700840 const char* name() const override { return "Two-Point Conical Gradient Inside"; }
joshualitteb2a6762014-12-04 11:35:33 -0800841
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000842 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
843 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
844 SkScalar A() const { return fInfo.fA; }
845 SkScalar B() const { return fInfo.fB; }
846 SkScalar C() const { return fInfo.fC; }
847
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000848private:
egdaniel57d3b032015-11-13 11:57:27 -0800849 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700850
egdaniel57d3b032015-11-13 11:57:27 -0800851 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
852 GrProcessorKeyBuilder* b) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700853
mtklein36352bf2015-03-25 18:17:31 -0700854 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700855 const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000856 return (INHERITED::onIsEqual(sBase) &&
857 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
858 this->fInfo.fA == s.fInfo.fA &&
859 this->fInfo.fB == s.fInfo.fB &&
860 this->fInfo.fC == s.fInfo.fC);
861 }
862
863 CircleInside2PtConicalEffect(GrContext* ctx,
864 const SkTwoPointConicalGradient& shader,
865 const SkMatrix& matrix,
866 SkShader::TileMode tm,
867 const CircleConicalInfo& info)
bsalomon4a339522015-10-06 08:40:50 -0700868 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
joshualitteb2a6762014-12-04 11:35:33 -0800869 this->initClassID<CircleInside2PtConicalEffect>();
870 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000871
joshualittb0a8a372014-09-23 09:50:21 -0700872 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000873
874 const CircleConicalInfo fInfo;
875
876 typedef GrGradientEffect INHERITED;
877};
878
879class GLCircleInside2PtConicalEffect : public GrGLGradientEffect {
880public:
joshualitteb2a6762014-12-04 11:35:33 -0800881 GLCircleInside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000882 virtual ~GLCircleInside2PtConicalEffect() {}
883
wangyix7c157a92015-07-22 15:08:53 -0700884 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000885
jvanverthcfc18862015-04-28 08:48:20 -0700886 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000887
888protected:
egdaniel018fb622015-10-28 07:26:40 -0700889 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700890
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000891 UniformHandle fCenterUni;
892 UniformHandle fParamUni;
893
894 const char* fVSVaryingName;
895 const char* fFSVaryingName;
896
897 // @{
898 /// Values last uploaded as uniforms
899
900 SkScalar fCachedCenterX;
901 SkScalar fCachedCenterY;
902 SkScalar fCachedA;
903 SkScalar fCachedB;
904 SkScalar fCachedC;
905
906 // @}
907
908private:
909 typedef GrGLGradientEffect INHERITED;
910
911};
912
egdaniel57d3b032015-11-13 11:57:27 -0800913void CircleInside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
914 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800915 GLCircleInside2PtConicalEffect::GenKey(*this, caps, b);
916}
917
egdaniel57d3b032015-11-13 11:57:27 -0800918GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700919 return new GLCircleInside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000920}
921
joshualittb0a8a372014-09-23 09:50:21 -0700922GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000923
joshualitt01258472014-09-22 10:29:30 -0700924/*
925 * All Two point conical gradient test create functions may occasionally create edge case shaders
926 */
bsalomonc21b09e2015-08-28 18:46:56 -0700927const GrFragmentProcessor* CircleInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700928 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
929 SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000930 SkPoint center2;
931 SkScalar radius2;
932 do {
joshualitt0067ff52015-07-08 14:26:19 -0700933 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000934 // Below makes sure that circle one is contained within circle two
joshualitt0067ff52015-07-08 14:26:19 -0700935 SkScalar increase = d->fRandom->nextUScalar1();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000936 SkPoint diff = center2 - center1;
937 SkScalar diffLen = diff.length();
938 radius2 = radius1 + diffLen + increase;
939 // If the circles are identical the factory will give us an empty shader.
940 } while (radius1 == radius2 && center1 == center2);
941
942 SkColor colors[kMaxRandomGradientColors];
943 SkScalar stopsArray[kMaxRandomGradientColors];
944 SkScalar* stops = stopsArray;
945 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700946 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000947 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
948 center2, radius2,
949 colors, stops, colorCount,
950 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700951 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700952 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700953 GrAlwaysAssert(fp);
joshualitt8ca93e72015-07-08 06:51:43 -0700954 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000955}
956
joshualitteb2a6762014-12-04 11:35:33 -0800957GLCircleInside2PtConicalEffect::GLCircleInside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -0700958 : fVSVaryingName(nullptr)
959 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000960 , fCachedCenterX(SK_ScalarMax)
961 , fCachedCenterY(SK_ScalarMax)
962 , fCachedA(SK_ScalarMax)
963 , fCachedB(SK_ScalarMax)
964 , fCachedC(SK_ScalarMax) {}
965
wangyix7c157a92015-07-22 15:08:53 -0700966void GLCircleInside2PtConicalEffect::emitCode(EmitArgs& args) {
967 const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>();
968 this->emitUniforms(args.fBuilder, ge);
egdaniel2d721d32015-11-11 13:06:05 -0800969 fCenterUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
970 kVec2f_GrSLType, kDefault_GrSLPrecision,
971 "Conical2FSCenter");
972 fParamUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
973 kVec3f_GrSLType, kDefault_GrSLPrecision,
974 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000975 SkString tName("t");
976
egdaniel0d3f0612015-10-21 10:45:48 -0700977 GrGLSLShaderVar center = args.fBuilder->getUniformVariable(fCenterUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000978 // params.x = A
979 // params.y = B
980 // params.z = C
egdaniel0d3f0612015-10-21 10:45:48 -0700981 GrGLSLShaderVar params = args.fBuilder->getUniformVariable(fParamUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000982
983 // if we have a vec3 from being in perspective, convert it to a vec2 first
egdaniel4ca2e602015-11-18 08:01:26 -0800984 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
985 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000986 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000987
988 // p = coords2D
989 // e = center end
990 // r = radius end
991 // A = dot(e, e) - r^2 + 2 * r - 1
992 // B = (r -1) / A
993 // C = 1 / A
994 // d = dot(e, p) + B
995 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
egdaniel4ca2e602015-11-18 08:01:26 -0800996 fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
997 fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
998 params.c_str());
999 fragBuilder->codeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
1000 tName.c_str(), params.c_str(), params.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001001
egdaniel4ca2e602015-11-18 08:01:26 -08001002 this->emitColor(args.fBuilder,
1003 fragBuilder,
egdaniela2e3e0f2015-11-19 07:23:45 -08001004 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -08001005 ge,
1006 tName.c_str(),
1007 args.fOutputColor,
1008 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -07001009 args.fSamplers);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001010}
1011
egdaniel018fb622015-10-28 07:26:40 -07001012void GLCircleInside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1013 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -07001014 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -07001015 const CircleInside2PtConicalEffect& data = processor.cast<CircleInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001016 SkScalar centerX = data.centerX();
1017 SkScalar centerY = data.centerY();
1018 SkScalar A = data.A();
1019 SkScalar B = data.B();
1020 SkScalar C = data.C();
1021
1022 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1023 fCachedA != A || fCachedB != B || fCachedC != C) {
1024
kkinnunen7510b222014-07-30 00:04:16 -07001025 pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1026 pdman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001027
1028 fCachedCenterX = centerX;
1029 fCachedCenterY = centerY;
1030 fCachedA = A;
1031 fCachedB = B;
1032 fCachedC = C;
1033 }
1034}
1035
joshualittb0a8a372014-09-23 09:50:21 -07001036void GLCircleInside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -07001037 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -07001038 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001039}
1040
1041//////////////////////////////////////////////////////////////////////////////
1042
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001043class CircleOutside2PtConicalEffect : public GrGradientEffect {
1044public:
1045
joshualittb0a8a372014-09-23 09:50:21 -07001046 static GrFragmentProcessor* Create(GrContext* ctx,
1047 const SkTwoPointConicalGradient& shader,
1048 const SkMatrix& matrix,
1049 SkShader::TileMode tm,
1050 const CircleConicalInfo& info) {
bsalomon4a339522015-10-06 08:40:50 -07001051 return new CircleOutside2PtConicalEffect(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001052 }
1053
1054 virtual ~CircleOutside2PtConicalEffect() {}
1055
mtklein36352bf2015-03-25 18:17:31 -07001056 const char* name() const override { return "Two-Point Conical Gradient Outside"; }
joshualitteb2a6762014-12-04 11:35:33 -08001057
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001058 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
1059 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
1060 SkScalar A() const { return fInfo.fA; }
1061 SkScalar B() const { return fInfo.fB; }
1062 SkScalar C() const { return fInfo.fC; }
1063 SkScalar tLimit() const { return fTLimit; }
1064 bool isFlipped() const { return fIsFlipped; }
1065
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001066private:
egdaniel57d3b032015-11-13 11:57:27 -08001067 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -07001068
egdaniel57d3b032015-11-13 11:57:27 -08001069 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -07001070
mtklein36352bf2015-03-25 18:17:31 -07001071 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -07001072 const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001073 return (INHERITED::onIsEqual(sBase) &&
1074 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
1075 this->fInfo.fA == s.fInfo.fA &&
1076 this->fInfo.fB == s.fInfo.fB &&
1077 this->fInfo.fC == s.fInfo.fC &&
1078 this->fTLimit == s.fTLimit &&
1079 this->fIsFlipped == s.fIsFlipped);
1080 }
1081
1082 CircleOutside2PtConicalEffect(GrContext* ctx,
1083 const SkTwoPointConicalGradient& shader,
1084 const SkMatrix& matrix,
1085 SkShader::TileMode tm,
1086 const CircleConicalInfo& info)
bsalomon4a339522015-10-06 08:40:50 -07001087 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
joshualitteb2a6762014-12-04 11:35:33 -08001088 this->initClassID<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001089 if (shader.getStartRadius() != shader.getEndRadius()) {
reed80ea19c2015-05-12 10:37:34 -07001090 fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001091 } else {
1092 fTLimit = SK_ScalarMin;
1093 }
1094
1095 fIsFlipped = shader.isFlippedGrad();
1096 }
1097
joshualittb0a8a372014-09-23 09:50:21 -07001098 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001099
1100 const CircleConicalInfo fInfo;
1101 SkScalar fTLimit;
1102 bool fIsFlipped;
1103
1104 typedef GrGradientEffect INHERITED;
1105};
1106
1107class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect {
1108public:
joshualitteb2a6762014-12-04 11:35:33 -08001109 GLCircleOutside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001110 virtual ~GLCircleOutside2PtConicalEffect() {}
1111
wangyix7c157a92015-07-22 15:08:53 -07001112 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001113
jvanverthcfc18862015-04-28 08:48:20 -07001114 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001115
1116protected:
egdaniel018fb622015-10-28 07:26:40 -07001117 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -07001118
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001119 UniformHandle fCenterUni;
1120 UniformHandle fParamUni;
1121
1122 const char* fVSVaryingName;
1123 const char* fFSVaryingName;
1124
1125 bool fIsFlipped;
1126
1127 // @{
1128 /// Values last uploaded as uniforms
1129
1130 SkScalar fCachedCenterX;
1131 SkScalar fCachedCenterY;
1132 SkScalar fCachedA;
1133 SkScalar fCachedB;
1134 SkScalar fCachedC;
1135 SkScalar fCachedTLimit;
1136
1137 // @}
1138
1139private:
1140 typedef GrGLGradientEffect INHERITED;
1141
1142};
1143
egdaniel57d3b032015-11-13 11:57:27 -08001144void CircleOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
1145 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -08001146 GLCircleOutside2PtConicalEffect::GenKey(*this, caps, b);
1147}
1148
egdaniel57d3b032015-11-13 11:57:27 -08001149GrGLSLFragmentProcessor* CircleOutside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -07001150 return new GLCircleOutside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001151}
1152
joshualittb0a8a372014-09-23 09:50:21 -07001153GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001154
joshualitt01258472014-09-22 10:29:30 -07001155/*
1156 * All Two point conical gradient test create functions may occasionally create edge case shaders
1157 */
bsalomonc21b09e2015-08-28 18:46:56 -07001158const GrFragmentProcessor* CircleOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -07001159 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
1160 SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001161 SkPoint center2;
1162 SkScalar radius2;
1163 SkScalar diffLen;
1164 do {
joshualitt0067ff52015-07-08 14:26:19 -07001165 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001166 // If the circles share a center than we can't be in the outside case
1167 } while (center1 == center2);
joshualitt01258472014-09-22 10:29:30 -07001168 SkPoint diff = center2 - center1;
1169 diffLen = diff.length();
1170 // Below makes sure that circle one is not contained within circle two
1171 // and have radius2 >= radius to match sorting on cpu side
joshualitt0067ff52015-07-08 14:26:19 -07001172 radius2 = radius1 + d->fRandom->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001173
1174 SkColor colors[kMaxRandomGradientColors];
1175 SkScalar stopsArray[kMaxRandomGradientColors];
1176 SkScalar* stops = stopsArray;
1177 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -07001178 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001179 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
1180 center2, radius2,
1181 colors, stops, colorCount,
1182 tm));
bsalomonc21b09e2015-08-28 18:46:56 -07001183 const GrFragmentProcessor* fp = shader->asFragmentProcessor(
bsalomon4a339522015-10-06 08:40:50 -07001184 d->fContext,GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -07001185 GrAlwaysAssert(fp);
joshualitt8ca93e72015-07-08 06:51:43 -07001186 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001187}
1188
joshualitteb2a6762014-12-04 11:35:33 -08001189GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -07001190 : fVSVaryingName(nullptr)
1191 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001192 , fCachedCenterX(SK_ScalarMax)
1193 , fCachedCenterY(SK_ScalarMax)
1194 , fCachedA(SK_ScalarMax)
1195 , fCachedB(SK_ScalarMax)
1196 , fCachedC(SK_ScalarMax)
1197 , fCachedTLimit(SK_ScalarMax) {
joshualittb0a8a372014-09-23 09:50:21 -07001198 const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001199 fIsFlipped = data.isFlipped();
1200 }
1201
wangyix7c157a92015-07-22 15:08:53 -07001202void GLCircleOutside2PtConicalEffect::emitCode(EmitArgs& args) {
1203 const CircleOutside2PtConicalEffect& ge = args.fFp.cast<CircleOutside2PtConicalEffect>();
1204 this->emitUniforms(args.fBuilder, ge);
egdaniel2d721d32015-11-11 13:06:05 -08001205 fCenterUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
1206 kVec2f_GrSLType, kDefault_GrSLPrecision,
1207 "Conical2FSCenter");
1208 fParamUni = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
1209 kVec4f_GrSLType, kDefault_GrSLPrecision,
1210 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001211 SkString tName("t");
1212
egdaniel0d3f0612015-10-21 10:45:48 -07001213 GrGLSLShaderVar center = args.fBuilder->getUniformVariable(fCenterUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001214 // params.x = A
1215 // params.y = B
1216 // params.z = C
egdaniel0d3f0612015-10-21 10:45:48 -07001217 GrGLSLShaderVar params = args.fBuilder->getUniformVariable(fParamUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001218
1219 // if we have a vec3 from being in perspective, convert it to a vec2 first
egdaniel4ca2e602015-11-18 08:01:26 -08001220 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
1221 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +00001222 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001223
1224 // output will default to transparent black (we simply won't write anything
1225 // else to it if invalid, instead of discarding or returning prematurely)
egdaniel4ca2e602015-11-18 08:01:26 -08001226 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 +00001227
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001228 // p = coords2D
1229 // e = center end
1230 // r = radius end
1231 // A = dot(e, e) - r^2 + 2 * r - 1
1232 // B = (r -1) / A
1233 // C = 1 / A
1234 // d = dot(e, p) + B
1235 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001236
egdaniel4ca2e602015-11-18 08:01:26 -08001237 fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
1238 fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
1239 params.c_str());
1240 fragBuilder->codeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(),
1241 params.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001242
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001243 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
1244 // If so we must also flip sign on sqrt
1245 if (!fIsFlipped) {
egdaniel4ca2e602015-11-18 08:01:26 -08001246 fragBuilder->codeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001247 } else {
egdaniel4ca2e602015-11-18 08:01:26 -08001248 fragBuilder->codeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001249 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001250
egdaniel4ca2e602015-11-18 08:01:26 -08001251 fragBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", tName.c_str(), params.c_str());
1252 fragBuilder->codeAppend("\t\t");
1253 this->emitColor(args.fBuilder,
1254 fragBuilder,
egdaniela2e3e0f2015-11-19 07:23:45 -08001255 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -08001256 ge,
1257 tName.c_str(),
1258 args.fOutputColor,
1259 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -07001260 args.fSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -08001261 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001262}
1263
egdaniel018fb622015-10-28 07:26:40 -07001264void GLCircleOutside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1265 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -07001266 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -07001267 const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +00001268 SkASSERT(data.isFlipped() == fIsFlipped);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001269 SkScalar centerX = data.centerX();
1270 SkScalar centerY = data.centerY();
1271 SkScalar A = data.A();
1272 SkScalar B = data.B();
1273 SkScalar C = data.C();
1274 SkScalar tLimit = data.tLimit();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001275
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001276 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1277 fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001278
kkinnunen7510b222014-07-30 00:04:16 -07001279 pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1280 pdman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001281 SkScalarToFloat(tLimit));
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001282
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001283 fCachedCenterX = centerX;
1284 fCachedCenterY = centerY;
1285 fCachedA = A;
1286 fCachedB = B;
1287 fCachedC = C;
1288 fCachedTLimit = tLimit;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001289 }
1290}
1291
joshualittb0a8a372014-09-23 09:50:21 -07001292void GLCircleOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -07001293 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -07001294 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -07001295 key[0] = GenBaseGradientKey(processor);
1296 key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001297}
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001298
1299//////////////////////////////////////////////////////////////////////////////
1300
joshualittb0a8a372014-09-23 09:50:21 -07001301GrFragmentProcessor* Gr2PtConicalGradientEffect::Create(GrContext* ctx,
1302 const SkTwoPointConicalGradient& shader,
1303 SkShader::TileMode tm,
1304 const SkMatrix* localMatrix) {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001305 SkMatrix matrix;
1306 if (!shader.getLocalMatrix().invert(&matrix)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001307 return nullptr;
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001308 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001309 if (localMatrix) {
1310 SkMatrix inv;
1311 if (!localMatrix->invert(&inv)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001312 return nullptr;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001313 }
1314 matrix.postConcat(inv);
1315 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001316
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001317 if (shader.getStartRadius() < kErrorTol) {
1318 SkScalar focalX;
1319 ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
1320 if (type == kInside_ConicalType) {
bsalomon4a339522015-10-06 08:40:50 -07001321 return FocalInside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001322 } else if(type == kEdge_ConicalType) {
1323 set_matrix_edge_conical(shader, &matrix);
bsalomon4a339522015-10-06 08:40:50 -07001324 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001325 } else {
bsalomon4a339522015-10-06 08:40:50 -07001326 return FocalOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001327 }
1328 }
1329
1330 CircleConicalInfo info;
1331 ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
1332
1333 if (type == kInside_ConicalType) {
bsalomon4a339522015-10-06 08:40:50 -07001334 return CircleInside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001335 } else if (type == kEdge_ConicalType) {
1336 set_matrix_edge_conical(shader, &matrix);
bsalomon4a339522015-10-06 08:40:50 -07001337 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001338 } else {
bsalomon4a339522015-10-06 08:40:50 -07001339 return CircleOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001340 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001341}
1342
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001343#endif