blob: 5b0855ab7c71e0ee0c48299d2b7801e4ac66d8f1 [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);
jvanverthde11ee42016-02-26 13:58:40 -0800230 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
231 kVec3f_GrSLType, kDefault_GrSLPrecision,
232 "Conical2FSParams");
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
jvanverthde11ee42016-02-26 13:58:40 -0800240
241 p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
242 p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
243 p2.appendf("%s.z", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000244
245 // We interpolate the linear component in coords[1].
wangyix7c157a92015-07-22 15:08:53 -0700246 SkASSERT(args.fCoords[0].getType() == args.fCoords[1].getType());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000247 const char* coords2D;
248 SkString bVar;
cdalton85285412016-02-18 12:37:07 -0800249 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
wangyix7c157a92015-07-22 15:08:53 -0700250 if (kVec3f_GrSLType == args.fCoords[0].getType()) {
egdaniel4ca2e602015-11-18 08:01:26 -0800251 fragBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
wangyix7c157a92015-07-22 15:08:53 -0700252 args.fCoords[0].c_str(), args.fCoords[0].c_str(),
253 args.fCoords[1].c_str(), args.fCoords[1].c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000254 coords2D = "interpolants.xy";
255 bVar = "interpolants.z";
256 } else {
wangyix7c157a92015-07-22 15:08:53 -0700257 coords2D = args.fCoords[0].c_str();
258 bVar.printf("%s.x", args.fCoords[1].c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000259 }
260
261 // output will default to transparent black (we simply won't write anything
262 // else to it if invalid, instead of discarding or returning prematurely)
egdaniel4ca2e602015-11-18 08:01:26 -0800263 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 +0000264
265 // c = (x^2)+(y^2) - params[1]
egdaniel4ca2e602015-11-18 08:01:26 -0800266 fragBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000267 cName.c_str(), coords2D, coords2D, p1.c_str());
268
269 // linear case: t = -c/b
egdaniel4ca2e602015-11-18 08:01:26 -0800270 fragBuilder->codeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000271 cName.c_str(), bVar.c_str());
272
273 // if r(t) > 0, then t will be the x coordinate
egdaniel4ca2e602015-11-18 08:01:26 -0800274 fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000275 p2.c_str(), p0.c_str());
egdaniel4ca2e602015-11-18 08:01:26 -0800276 fragBuilder->codeAppend("\t");
egdaniel7ea439b2015-12-03 09:20:44 -0800277 this->emitColor(fragBuilder,
278 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -0800279 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800280 ge,
281 tName.c_str(),
282 args.fOutputColor,
283 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700284 args.fSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -0800285 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000286}
287
egdaniel018fb622015-10-28 07:26:40 -0700288void GLEdge2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
289 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700290 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700291 const Edge2PtConicalEffect& data = processor.cast<Edge2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000292 SkScalar radius0 = data.radius();
293 SkScalar diffRadius = data.diffRadius();
294
295 if (fCachedRadius != radius0 ||
296 fCachedDiffRadius != diffRadius) {
297
jvanverthde11ee42016-02-26 13:58:40 -0800298 pdman.set3f(fParamUni, SkScalarToFloat(radius0),
299 SkScalarToFloat(SkScalarMul(radius0, radius0)), SkScalarToFloat(diffRadius));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000300 fCachedRadius = radius0;
301 fCachedDiffRadius = diffRadius;
302 }
303}
304
joshualittb0a8a372014-09-23 09:50:21 -0700305void GLEdge2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700306 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700307 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000308}
309
310//////////////////////////////////////////////////////////////////////////////
311// Focal Conical Gradients
312//////////////////////////////////////////////////////////////////////////////
313
314static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader,
315 SkMatrix* invLMatrix, SkScalar* focalX) {
316 // Inverse of the current local matrix is passed in then,
317 // translate, scale, and rotate such that endCircle is unit circle on x-axis,
318 // and focal point is at the origin.
319 ConicalType conicalType;
320 const SkPoint& focal = shader.getStartCenter();
321 const SkPoint& centerEnd = shader.getEndCenter();
322 SkScalar radius = shader.getEndRadius();
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000323 SkScalar invRadius = 1.f / radius;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000324
325 SkMatrix matrix;
326
327 matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
328 matrix.postScale(invRadius, invRadius);
329
330 SkPoint focalTrans;
331 matrix.mapPoints(&focalTrans, &focal, 1);
332 *focalX = focalTrans.length();
333
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000334 if (0.f != *focalX) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000335 SkScalar invFocalX = SkScalarInvert(*focalX);
336 SkMatrix rot;
337 rot.setSinCos(-SkScalarMul(invFocalX, focalTrans.fY),
338 SkScalarMul(invFocalX, focalTrans.fX));
339 matrix.postConcat(rot);
340 }
341
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000342 matrix.postTranslate(-(*focalX), 0.f);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000343
344 // If the focal point is touching the edge of the circle it will
345 // cause a degenerate case that must be handled separately
egdaniel8405ef92014-06-09 11:57:28 -0700346 // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
347 // stability trade off versus the linear approx used in the Edge Shader
348 if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000349 return kEdge_ConicalType;
350 }
351
352 // Scale factor 1 / (1 - focalX * focalX)
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000353 SkScalar oneMinusF2 = 1.f - SkScalarMul(*focalX, *focalX);
reed80ea19c2015-05-12 10:37:34 -0700354 SkScalar s = SkScalarInvert(oneMinusF2);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000355
356
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000357 if (s >= 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000358 conicalType = kInside_ConicalType;
359 matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
360 } else {
361 conicalType = kOutside_ConicalType;
362 matrix.postScale(s, s);
363 }
364
365 invLMatrix->postConcat(matrix);
366
367 return conicalType;
368}
369
370//////////////////////////////////////////////////////////////////////////////
371
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000372class FocalOutside2PtConicalEffect : public GrGradientEffect {
373public:
374
joshualittb0a8a372014-09-23 09:50:21 -0700375 static GrFragmentProcessor* Create(GrContext* ctx,
376 const SkTwoPointConicalGradient& shader,
377 const SkMatrix& matrix,
378 SkShader::TileMode tm,
379 SkScalar focalX) {
bsalomon4a339522015-10-06 08:40:50 -0700380 return new FocalOutside2PtConicalEffect(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000381 }
382
383 virtual ~FocalOutside2PtConicalEffect() { }
384
mtklein36352bf2015-03-25 18:17:31 -0700385 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -0800386 return "Two-Point Conical Gradient Focal Outside";
387 }
388
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000389 bool isFlipped() const { return fIsFlipped; }
390 SkScalar focal() const { return fFocalX; }
391
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000392private:
egdaniel57d3b032015-11-13 11:57:27 -0800393 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700394
egdaniel57d3b032015-11-13 11:57:27 -0800395 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700396
mtklein36352bf2015-03-25 18:17:31 -0700397 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700398 const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000399 return (INHERITED::onIsEqual(sBase) &&
400 this->fFocalX == s.fFocalX &&
401 this->fIsFlipped == s.fIsFlipped);
402 }
403
404 FocalOutside2PtConicalEffect(GrContext* ctx,
405 const SkTwoPointConicalGradient& shader,
406 const SkMatrix& matrix,
407 SkShader::TileMode tm,
408 SkScalar focalX)
bsalomon4a339522015-10-06 08:40:50 -0700409 : INHERITED(ctx, shader, matrix, tm)
joshualittb2456052015-07-08 09:36:59 -0700410 , fFocalX(focalX)
411 , fIsFlipped(shader.isFlippedGrad()) {
joshualitteb2a6762014-12-04 11:35:33 -0800412 this->initClassID<FocalOutside2PtConicalEffect>();
413 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000414
joshualittb0a8a372014-09-23 09:50:21 -0700415 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000416
417 SkScalar fFocalX;
418 bool fIsFlipped;
419
420 typedef GrGradientEffect INHERITED;
421};
422
423class GLFocalOutside2PtConicalEffect : public GrGLGradientEffect {
424public:
joshualitteb2a6762014-12-04 11:35:33 -0800425 GLFocalOutside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000426 virtual ~GLFocalOutside2PtConicalEffect() { }
427
wangyix7c157a92015-07-22 15:08:53 -0700428 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000429
jvanverthcfc18862015-04-28 08:48:20 -0700430 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000431
432protected:
egdaniel018fb622015-10-28 07:26:40 -0700433 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700434
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000435 UniformHandle fParamUni;
436
437 const char* fVSVaryingName;
438 const char* fFSVaryingName;
439
440 bool fIsFlipped;
441
442 // @{
443 /// Values last uploaded as uniforms
444
445 SkScalar fCachedFocal;
446
447 // @}
448
449private:
450 typedef GrGLGradientEffect INHERITED;
451
452};
453
egdaniel57d3b032015-11-13 11:57:27 -0800454void FocalOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
455 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800456 GLFocalOutside2PtConicalEffect::GenKey(*this, caps, b);
457}
458
egdaniel57d3b032015-11-13 11:57:27 -0800459GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700460 return new GLFocalOutside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000461}
462
joshualittb0a8a372014-09-23 09:50:21 -0700463GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000464
joshualitt01258472014-09-22 10:29:30 -0700465/*
466 * All Two point conical gradient test create functions may occasionally create edge case shaders
467 */
bsalomonc21b09e2015-08-28 18:46:56 -0700468const GrFragmentProcessor* FocalOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700469 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000470 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000471 SkPoint center2;
472 SkScalar radius2;
473 do {
joshualitt0067ff52015-07-08 14:26:19 -0700474 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000475 // Need to make sure the centers are not the same or else focal point will be inside
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000476 } while (center1 == center2);
477 SkPoint diff = center2 - center1;
478 SkScalar diffLen = diff.length();
479 // Below makes sure that the focal point is not contained within circle two
joshualitt0067ff52015-07-08 14:26:19 -0700480 radius2 = d->fRandom->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000481
482 SkColor colors[kMaxRandomGradientColors];
483 SkScalar stopsArray[kMaxRandomGradientColors];
484 SkScalar* stops = stopsArray;
485 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700486 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000487 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
488 center2, radius2,
489 colors, stops, colorCount,
490 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700491 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700492 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700493 GrAlwaysAssert(fp);
494 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000495}
496
joshualitteb2a6762014-12-04 11:35:33 -0800497GLFocalOutside2PtConicalEffect::GLFocalOutside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -0700498 : fVSVaryingName(nullptr)
499 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000500 , fCachedFocal(SK_ScalarMax) {
joshualittb0a8a372014-09-23 09:50:21 -0700501 const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000502 fIsFlipped = data.isFlipped();
503}
504
wangyix7c157a92015-07-22 15:08:53 -0700505void GLFocalOutside2PtConicalEffect::emitCode(EmitArgs& args) {
506 const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -0800507 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
508 this->emitUniforms(uniformHandler, ge);
jvanverthde11ee42016-02-26 13:58:40 -0800509 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
510 kVec2f_GrSLType, kDefault_GrSLPrecision,
511 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000512 SkString tName("t");
513 SkString p0; // focalX
514 SkString p1; // 1 - focalX * focalX
515
jvanverthde11ee42016-02-26 13:58:40 -0800516 p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
517 p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
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
cdalton85285412016-02-18 12:37:07 -0800520 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
egdaniel4ca2e602015-11-18 08:01:26 -0800521 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");
egdaniel7ea439b2015-12-03 09:20:44 -0800546 this->emitColor(fragBuilder,
547 uniformHandler,
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
jvanverthde11ee42016-02-26 13:58:40 -0800567 pdman.set2f(fParamUni, SkScalarToFloat(focal), SkScalarToFloat(oneMinus2F));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000568 fCachedFocal = focal;
569 }
570}
571
joshualittb0a8a372014-09-23 09:50:21 -0700572void GLFocalOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700573 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -0700574 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -0700575 key[0] = GenBaseGradientKey(processor);
576 key[1] = processor.cast<FocalOutside2PtConicalEffect>().isFlipped();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000577}
578
579//////////////////////////////////////////////////////////////////////////////
580
581class GLFocalInside2PtConicalEffect;
582
583class FocalInside2PtConicalEffect : public GrGradientEffect {
584public:
585
joshualittb0a8a372014-09-23 09:50:21 -0700586 static GrFragmentProcessor* Create(GrContext* ctx,
587 const SkTwoPointConicalGradient& shader,
588 const SkMatrix& matrix,
589 SkShader::TileMode tm,
590 SkScalar focalX) {
bsalomon4a339522015-10-06 08:40:50 -0700591 return new FocalInside2PtConicalEffect(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000592 }
593
594 virtual ~FocalInside2PtConicalEffect() {}
595
mtklein36352bf2015-03-25 18:17:31 -0700596 const char* name() const override {
joshualitteb2a6762014-12-04 11:35:33 -0800597 return "Two-Point Conical Gradient Focal Inside";
598 }
599
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000600 SkScalar focal() const { return fFocalX; }
601
egdaniel57d3b032015-11-13 11:57:27 -0800602 typedef GLFocalInside2PtConicalEffect GLSLProcessor;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000603
604private:
egdaniel57d3b032015-11-13 11:57:27 -0800605 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700606
egdaniel57d3b032015-11-13 11:57:27 -0800607 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700608
mtklein36352bf2015-03-25 18:17:31 -0700609 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700610 const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000611 return (INHERITED::onIsEqual(sBase) &&
612 this->fFocalX == s.fFocalX);
613 }
614
615 FocalInside2PtConicalEffect(GrContext* ctx,
616 const SkTwoPointConicalGradient& shader,
617 const SkMatrix& matrix,
618 SkShader::TileMode tm,
619 SkScalar focalX)
bsalomon4a339522015-10-06 08:40:50 -0700620 : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX) {
joshualitteb2a6762014-12-04 11:35:33 -0800621 this->initClassID<FocalInside2PtConicalEffect>();
622 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000623
joshualittb0a8a372014-09-23 09:50:21 -0700624 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000625
626 SkScalar fFocalX;
627
628 typedef GrGradientEffect INHERITED;
629};
630
631class GLFocalInside2PtConicalEffect : public GrGLGradientEffect {
632public:
joshualitteb2a6762014-12-04 11:35:33 -0800633 GLFocalInside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000634 virtual ~GLFocalInside2PtConicalEffect() {}
635
wangyix7c157a92015-07-22 15:08:53 -0700636 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000637
jvanverthcfc18862015-04-28 08:48:20 -0700638 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000639
640protected:
egdaniel018fb622015-10-28 07:26:40 -0700641 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700642
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000643 UniformHandle fFocalUni;
644
645 const char* fVSVaryingName;
646 const char* fFSVaryingName;
647
648 // @{
649 /// Values last uploaded as uniforms
650
651 SkScalar fCachedFocal;
652
653 // @}
654
655private:
656 typedef GrGLGradientEffect INHERITED;
657
658};
659
egdaniel57d3b032015-11-13 11:57:27 -0800660void FocalInside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
661 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800662 GLFocalInside2PtConicalEffect::GenKey(*this, caps, b);
663}
664
egdaniel57d3b032015-11-13 11:57:27 -0800665GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700666 return new GLFocalInside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000667}
668
joshualittb0a8a372014-09-23 09:50:21 -0700669GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000670
joshualitt01258472014-09-22 10:29:30 -0700671/*
672 * All Two point conical gradient test create functions may occasionally create edge case shaders
673 */
bsalomonc21b09e2015-08-28 18:46:56 -0700674const GrFragmentProcessor* FocalInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700675 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000676 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000677 SkPoint center2;
678 SkScalar radius2;
679 do {
joshualitt0067ff52015-07-08 14:26:19 -0700680 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000681 // Below makes sure radius2 is larger enouch such that the focal point
682 // is inside the end circle
joshualitt0067ff52015-07-08 14:26:19 -0700683 SkScalar increase = d->fRandom->nextUScalar1();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000684 SkPoint diff = center2 - center1;
685 SkScalar diffLen = diff.length();
686 radius2 = diffLen + increase;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000687 // If the circles are identical the factory will give us an empty shader.
688 } while (radius1 == radius2 && center1 == center2);
689
690 SkColor colors[kMaxRandomGradientColors];
691 SkScalar stopsArray[kMaxRandomGradientColors];
692 SkScalar* stops = stopsArray;
693 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700694 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000695 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
696 center2, radius2,
697 colors, stops, colorCount,
698 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700699 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700700 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700701 GrAlwaysAssert(fp);
joshualittb0a8a372014-09-23 09:50:21 -0700702 return fp;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000703}
704
joshualitteb2a6762014-12-04 11:35:33 -0800705GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrProcessor&)
halcanary96fcdcc2015-08-27 07:41:13 -0700706 : fVSVaryingName(nullptr)
707 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000708 , fCachedFocal(SK_ScalarMax) {}
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000709
wangyix7c157a92015-07-22 15:08:53 -0700710void GLFocalInside2PtConicalEffect::emitCode(EmitArgs& args) {
711 const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -0800712 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
713 this->emitUniforms(uniformHandler, ge);
cdalton5e58cee2016-02-11 12:49:47 -0800714 fFocalUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800715 kFloat_GrSLType, kDefault_GrSLPrecision,
716 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000717 SkString tName("t");
718
719 // this is the distance along x-axis from the end center to focal point in
720 // transformed coordinates
egdaniel7ea439b2015-12-03 09:20:44 -0800721 GrGLSLShaderVar focal = uniformHandler->getUniformVariable(fFocalUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000722
723 // if we have a vec3 from being in perspective, convert it to a vec2 first
cdalton85285412016-02-18 12:37:07 -0800724 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
egdaniel4ca2e602015-11-18 08:01:26 -0800725 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000726 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000727
728 // t = p.x * focalX + length(p)
egdaniel4ca2e602015-11-18 08:01:26 -0800729 fragBuilder->codeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(),
egdaniel7ea439b2015-12-03 09:20:44 -0800730 coords2D, focal.c_str(), coords2D);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000731
egdaniel7ea439b2015-12-03 09:20:44 -0800732 this->emitColor(fragBuilder,
733 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -0800734 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -0800735 ge,
736 tName.c_str(),
737 args.fOutputColor,
738 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -0700739 args.fSamplers);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000740}
741
egdaniel018fb622015-10-28 07:26:40 -0700742void GLFocalInside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
743 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -0700744 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -0700745 const FocalInside2PtConicalEffect& data = processor.cast<FocalInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000746 SkScalar focal = data.focal();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000747
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000748 if (fCachedFocal != focal) {
kkinnunen7510b222014-07-30 00:04:16 -0700749 pdman.set1f(fFocalUni, SkScalarToFloat(focal));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000750 fCachedFocal = focal;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000751 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000752}
753
joshualittb0a8a372014-09-23 09:50:21 -0700754void GLFocalInside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -0700755 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700756 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000757}
758
759//////////////////////////////////////////////////////////////////////////////
760// Circle Conical Gradients
761//////////////////////////////////////////////////////////////////////////////
762
763struct CircleConicalInfo {
764 SkPoint fCenterEnd;
765 SkScalar fA;
766 SkScalar fB;
767 SkScalar fC;
768};
769
770// Returns focal distance along x-axis in transformed coords
771static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
772 SkMatrix* invLMatrix, CircleConicalInfo* info) {
773 // Inverse of the current local matrix is passed in then,
774 // translate and scale such that start circle is on the origin and has radius 1
775 const SkPoint& centerStart = shader.getStartCenter();
776 const SkPoint& centerEnd = shader.getEndCenter();
777 SkScalar radiusStart = shader.getStartRadius();
778 SkScalar radiusEnd = shader.getEndRadius();
779
780 SkMatrix matrix;
781
782 matrix.setTranslate(-centerStart.fX, -centerStart.fY);
783
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000784 SkScalar invStartRad = 1.f / radiusStart;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000785 matrix.postScale(invStartRad, invStartRad);
786
787 radiusEnd /= radiusStart;
788
789 SkPoint centerEndTrans;
790 matrix.mapPoints(&centerEndTrans, &centerEnd, 1);
791
792 SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
793 - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
794
795 // Check to see if start circle is inside end circle with edges touching.
796 // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
egdaniel8405ef92014-06-09 11:57:28 -0700797 // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
798 // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
799 // still accurate.
800 if (SkScalarAbs(A) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000801 return kEdge_ConicalType;
802 }
803
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000804 SkScalar C = 1.f / A;
805 SkScalar B = (radiusEnd - 1.f) * C;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000806
807 matrix.postScale(C, C);
808
809 invLMatrix->postConcat(matrix);
810
811 info->fCenterEnd = centerEndTrans;
812 info->fA = A;
813 info->fB = B;
814 info->fC = C;
815
816 // 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 +0000817 if (A < 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000818 return kInside_ConicalType;
819 }
820 return kOutside_ConicalType;
821}
822
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000823class CircleInside2PtConicalEffect : public GrGradientEffect {
824public:
825
joshualittb0a8a372014-09-23 09:50:21 -0700826 static GrFragmentProcessor* Create(GrContext* ctx,
827 const SkTwoPointConicalGradient& shader,
828 const SkMatrix& matrix,
829 SkShader::TileMode tm,
830 const CircleConicalInfo& info) {
bsalomon4a339522015-10-06 08:40:50 -0700831 return new CircleInside2PtConicalEffect(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000832 }
833
834 virtual ~CircleInside2PtConicalEffect() {}
835
mtklein36352bf2015-03-25 18:17:31 -0700836 const char* name() const override { return "Two-Point Conical Gradient Inside"; }
joshualitteb2a6762014-12-04 11:35:33 -0800837
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000838 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
839 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
840 SkScalar A() const { return fInfo.fA; }
841 SkScalar B() const { return fInfo.fB; }
842 SkScalar C() const { return fInfo.fC; }
843
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000844private:
egdaniel57d3b032015-11-13 11:57:27 -0800845 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700846
egdaniel57d3b032015-11-13 11:57:27 -0800847 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps,
848 GrProcessorKeyBuilder* b) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700849
mtklein36352bf2015-03-25 18:17:31 -0700850 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -0700851 const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000852 return (INHERITED::onIsEqual(sBase) &&
853 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
854 this->fInfo.fA == s.fInfo.fA &&
855 this->fInfo.fB == s.fInfo.fB &&
856 this->fInfo.fC == s.fInfo.fC);
857 }
858
859 CircleInside2PtConicalEffect(GrContext* ctx,
860 const SkTwoPointConicalGradient& shader,
861 const SkMatrix& matrix,
862 SkShader::TileMode tm,
863 const CircleConicalInfo& info)
bsalomon4a339522015-10-06 08:40:50 -0700864 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
joshualitteb2a6762014-12-04 11:35:33 -0800865 this->initClassID<CircleInside2PtConicalEffect>();
866 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000867
joshualittb0a8a372014-09-23 09:50:21 -0700868 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000869
870 const CircleConicalInfo fInfo;
871
872 typedef GrGradientEffect INHERITED;
873};
874
875class GLCircleInside2PtConicalEffect : public GrGLGradientEffect {
876public:
joshualitteb2a6762014-12-04 11:35:33 -0800877 GLCircleInside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000878 virtual ~GLCircleInside2PtConicalEffect() {}
879
wangyix7c157a92015-07-22 15:08:53 -0700880 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000881
jvanverthcfc18862015-04-28 08:48:20 -0700882 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000883
884protected:
egdaniel018fb622015-10-28 07:26:40 -0700885 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -0700886
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000887 UniformHandle fCenterUni;
888 UniformHandle fParamUni;
889
890 const char* fVSVaryingName;
891 const char* fFSVaryingName;
892
893 // @{
894 /// Values last uploaded as uniforms
895
896 SkScalar fCachedCenterX;
897 SkScalar fCachedCenterY;
898 SkScalar fCachedA;
899 SkScalar fCachedB;
900 SkScalar fCachedC;
901
902 // @}
903
904private:
905 typedef GrGLGradientEffect INHERITED;
906
907};
908
egdaniel57d3b032015-11-13 11:57:27 -0800909void CircleInside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
910 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800911 GLCircleInside2PtConicalEffect::GenKey(*this, caps, b);
912}
913
egdaniel57d3b032015-11-13 11:57:27 -0800914GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -0700915 return new GLCircleInside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000916}
917
joshualittb0a8a372014-09-23 09:50:21 -0700918GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000919
joshualitt01258472014-09-22 10:29:30 -0700920/*
921 * All Two point conical gradient test create functions may occasionally create edge case shaders
922 */
bsalomonc21b09e2015-08-28 18:46:56 -0700923const GrFragmentProcessor* CircleInside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700924 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
925 SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000926 SkPoint center2;
927 SkScalar radius2;
928 do {
joshualitt0067ff52015-07-08 14:26:19 -0700929 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000930 // Below makes sure that circle one is contained within circle two
joshualitt0067ff52015-07-08 14:26:19 -0700931 SkScalar increase = d->fRandom->nextUScalar1();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000932 SkPoint diff = center2 - center1;
933 SkScalar diffLen = diff.length();
934 radius2 = radius1 + diffLen + increase;
935 // If the circles are identical the factory will give us an empty shader.
936 } while (radius1 == radius2 && center1 == center2);
937
938 SkColor colors[kMaxRandomGradientColors];
939 SkScalar stopsArray[kMaxRandomGradientColors];
940 SkScalar* stops = stopsArray;
941 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -0700942 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000943 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
944 center2, radius2,
945 colors, stops, colorCount,
946 tm));
bsalomonc21b09e2015-08-28 18:46:56 -0700947 const GrFragmentProcessor* fp = shader->asFragmentProcessor(d->fContext,
bsalomon4a339522015-10-06 08:40:50 -0700948 GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -0700949 GrAlwaysAssert(fp);
joshualitt8ca93e72015-07-08 06:51:43 -0700950 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000951}
952
joshualitteb2a6762014-12-04 11:35:33 -0800953GLCircleInside2PtConicalEffect::GLCircleInside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -0700954 : fVSVaryingName(nullptr)
955 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000956 , fCachedCenterX(SK_ScalarMax)
957 , fCachedCenterY(SK_ScalarMax)
958 , fCachedA(SK_ScalarMax)
959 , fCachedB(SK_ScalarMax)
960 , fCachedC(SK_ScalarMax) {}
961
wangyix7c157a92015-07-22 15:08:53 -0700962void GLCircleInside2PtConicalEffect::emitCode(EmitArgs& args) {
963 const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -0800964 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
965 this->emitUniforms(uniformHandler, ge);
cdalton5e58cee2016-02-11 12:49:47 -0800966 fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800967 kVec2f_GrSLType, kDefault_GrSLPrecision,
968 "Conical2FSCenter");
cdalton5e58cee2016-02-11 12:49:47 -0800969 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800970 kVec3f_GrSLType, kDefault_GrSLPrecision,
971 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000972 SkString tName("t");
973
egdaniel7ea439b2015-12-03 09:20:44 -0800974 GrGLSLShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000975 // params.x = A
976 // params.y = B
977 // params.z = C
egdaniel7ea439b2015-12-03 09:20:44 -0800978 GrGLSLShaderVar params = uniformHandler->getUniformVariable(fParamUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000979
980 // if we have a vec3 from being in perspective, convert it to a vec2 first
cdalton85285412016-02-18 12:37:07 -0800981 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
egdaniel4ca2e602015-11-18 08:01:26 -0800982 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000983 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000984
985 // p = coords2D
986 // e = center end
987 // r = radius end
988 // A = dot(e, e) - r^2 + 2 * r - 1
989 // B = (r -1) / A
990 // C = 1 / A
991 // d = dot(e, p) + B
992 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
egdaniel4ca2e602015-11-18 08:01:26 -0800993 fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
994 fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
995 params.c_str());
996 fragBuilder->codeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
997 tName.c_str(), params.c_str(), params.c_str());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000998
egdaniel7ea439b2015-12-03 09:20:44 -0800999 this->emitColor(fragBuilder,
1000 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -08001001 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -08001002 ge,
1003 tName.c_str(),
1004 args.fOutputColor,
1005 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -07001006 args.fSamplers);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001007}
1008
egdaniel018fb622015-10-28 07:26:40 -07001009void GLCircleInside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1010 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -07001011 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -07001012 const CircleInside2PtConicalEffect& data = processor.cast<CircleInside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001013 SkScalar centerX = data.centerX();
1014 SkScalar centerY = data.centerY();
1015 SkScalar A = data.A();
1016 SkScalar B = data.B();
1017 SkScalar C = data.C();
1018
1019 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1020 fCachedA != A || fCachedB != B || fCachedC != C) {
1021
kkinnunen7510b222014-07-30 00:04:16 -07001022 pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1023 pdman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001024
1025 fCachedCenterX = centerX;
1026 fCachedCenterY = centerY;
1027 fCachedA = A;
1028 fCachedB = B;
1029 fCachedC = C;
1030 }
1031}
1032
joshualittb0a8a372014-09-23 09:50:21 -07001033void GLCircleInside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -07001034 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -07001035 b->add32(GenBaseGradientKey(processor));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001036}
1037
1038//////////////////////////////////////////////////////////////////////////////
1039
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001040class CircleOutside2PtConicalEffect : public GrGradientEffect {
1041public:
1042
joshualittb0a8a372014-09-23 09:50:21 -07001043 static GrFragmentProcessor* Create(GrContext* ctx,
1044 const SkTwoPointConicalGradient& shader,
1045 const SkMatrix& matrix,
1046 SkShader::TileMode tm,
1047 const CircleConicalInfo& info) {
bsalomon4a339522015-10-06 08:40:50 -07001048 return new CircleOutside2PtConicalEffect(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001049 }
1050
1051 virtual ~CircleOutside2PtConicalEffect() {}
1052
mtklein36352bf2015-03-25 18:17:31 -07001053 const char* name() const override { return "Two-Point Conical Gradient Outside"; }
joshualitteb2a6762014-12-04 11:35:33 -08001054
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001055 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
1056 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
1057 SkScalar A() const { return fInfo.fA; }
1058 SkScalar B() const { return fInfo.fB; }
1059 SkScalar C() const { return fInfo.fC; }
1060 SkScalar tLimit() const { return fTLimit; }
1061 bool isFlipped() const { return fIsFlipped; }
1062
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001063private:
egdaniel57d3b032015-11-13 11:57:27 -08001064 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -07001065
egdaniel57d3b032015-11-13 11:57:27 -08001066 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
wangyix4b3050b2015-08-04 07:59:37 -07001067
mtklein36352bf2015-03-25 18:17:31 -07001068 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
joshualitt49586be2014-09-16 08:21:41 -07001069 const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001070 return (INHERITED::onIsEqual(sBase) &&
1071 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
1072 this->fInfo.fA == s.fInfo.fA &&
1073 this->fInfo.fB == s.fInfo.fB &&
1074 this->fInfo.fC == s.fInfo.fC &&
1075 this->fTLimit == s.fTLimit &&
1076 this->fIsFlipped == s.fIsFlipped);
1077 }
1078
1079 CircleOutside2PtConicalEffect(GrContext* ctx,
1080 const SkTwoPointConicalGradient& shader,
1081 const SkMatrix& matrix,
1082 SkShader::TileMode tm,
1083 const CircleConicalInfo& info)
bsalomon4a339522015-10-06 08:40:50 -07001084 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
joshualitteb2a6762014-12-04 11:35:33 -08001085 this->initClassID<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001086 if (shader.getStartRadius() != shader.getEndRadius()) {
reed80ea19c2015-05-12 10:37:34 -07001087 fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001088 } else {
1089 fTLimit = SK_ScalarMin;
1090 }
1091
1092 fIsFlipped = shader.isFlippedGrad();
1093 }
1094
joshualittb0a8a372014-09-23 09:50:21 -07001095 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001096
1097 const CircleConicalInfo fInfo;
1098 SkScalar fTLimit;
1099 bool fIsFlipped;
1100
1101 typedef GrGradientEffect INHERITED;
1102};
1103
1104class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect {
1105public:
joshualitteb2a6762014-12-04 11:35:33 -08001106 GLCircleOutside2PtConicalEffect(const GrProcessor&);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001107 virtual ~GLCircleOutside2PtConicalEffect() {}
1108
wangyix7c157a92015-07-22 15:08:53 -07001109 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001110
jvanverthcfc18862015-04-28 08:48:20 -07001111 static void GenKey(const GrProcessor&, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001112
1113protected:
egdaniel018fb622015-10-28 07:26:40 -07001114 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
wangyixb1daa862015-08-18 11:29:31 -07001115
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001116 UniformHandle fCenterUni;
1117 UniformHandle fParamUni;
1118
1119 const char* fVSVaryingName;
1120 const char* fFSVaryingName;
1121
1122 bool fIsFlipped;
1123
1124 // @{
1125 /// Values last uploaded as uniforms
1126
1127 SkScalar fCachedCenterX;
1128 SkScalar fCachedCenterY;
1129 SkScalar fCachedA;
1130 SkScalar fCachedB;
1131 SkScalar fCachedC;
1132 SkScalar fCachedTLimit;
1133
1134 // @}
1135
1136private:
1137 typedef GrGLGradientEffect INHERITED;
1138
1139};
1140
egdaniel57d3b032015-11-13 11:57:27 -08001141void CircleOutside2PtConicalEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
1142 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -08001143 GLCircleOutside2PtConicalEffect::GenKey(*this, caps, b);
1144}
1145
egdaniel57d3b032015-11-13 11:57:27 -08001146GrGLSLFragmentProcessor* CircleOutside2PtConicalEffect::onCreateGLSLInstance() const {
halcanary385fe4d2015-08-26 13:07:48 -07001147 return new GLCircleOutside2PtConicalEffect(*this);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001148}
1149
joshualittb0a8a372014-09-23 09:50:21 -07001150GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001151
joshualitt01258472014-09-22 10:29:30 -07001152/*
1153 * All Two point conical gradient test create functions may occasionally create edge case shaders
1154 */
bsalomonc21b09e2015-08-28 18:46:56 -07001155const GrFragmentProcessor* CircleOutside2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -07001156 SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
1157 SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001158 SkPoint center2;
1159 SkScalar radius2;
1160 SkScalar diffLen;
1161 do {
joshualitt0067ff52015-07-08 14:26:19 -07001162 center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001163 // If the circles share a center than we can't be in the outside case
1164 } while (center1 == center2);
joshualitt01258472014-09-22 10:29:30 -07001165 SkPoint diff = center2 - center1;
1166 diffLen = diff.length();
1167 // Below makes sure that circle one is not contained within circle two
1168 // and have radius2 >= radius to match sorting on cpu side
joshualitt0067ff52015-07-08 14:26:19 -07001169 radius2 = radius1 + d->fRandom->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001170
1171 SkColor colors[kMaxRandomGradientColors];
1172 SkScalar stopsArray[kMaxRandomGradientColors];
1173 SkScalar* stops = stopsArray;
1174 SkShader::TileMode tm;
joshualitt0067ff52015-07-08 14:26:19 -07001175 int colorCount = RandomGradientParams(d->fRandom, colors, &stops, &tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001176 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
1177 center2, radius2,
1178 colors, stops, colorCount,
1179 tm));
bsalomonc21b09e2015-08-28 18:46:56 -07001180 const GrFragmentProcessor* fp = shader->asFragmentProcessor(
bsalomon4a339522015-10-06 08:40:50 -07001181 d->fContext,GrTest::TestMatrix(d->fRandom), NULL, kNone_SkFilterQuality);
bsalomonc21b09e2015-08-28 18:46:56 -07001182 GrAlwaysAssert(fp);
joshualitt8ca93e72015-07-08 06:51:43 -07001183 return fp;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001184}
1185
joshualitteb2a6762014-12-04 11:35:33 -08001186GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrProcessor& processor)
halcanary96fcdcc2015-08-27 07:41:13 -07001187 : fVSVaryingName(nullptr)
1188 , fFSVaryingName(nullptr)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001189 , fCachedCenterX(SK_ScalarMax)
1190 , fCachedCenterY(SK_ScalarMax)
1191 , fCachedA(SK_ScalarMax)
1192 , fCachedB(SK_ScalarMax)
1193 , fCachedC(SK_ScalarMax)
1194 , fCachedTLimit(SK_ScalarMax) {
joshualittb0a8a372014-09-23 09:50:21 -07001195 const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001196 fIsFlipped = data.isFlipped();
1197 }
1198
wangyix7c157a92015-07-22 15:08:53 -07001199void GLCircleOutside2PtConicalEffect::emitCode(EmitArgs& args) {
1200 const CircleOutside2PtConicalEffect& ge = args.fFp.cast<CircleOutside2PtConicalEffect>();
egdaniel7ea439b2015-12-03 09:20:44 -08001201 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
1202 this->emitUniforms(uniformHandler, ge);
cdalton5e58cee2016-02-11 12:49:47 -08001203 fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -08001204 kVec2f_GrSLType, kDefault_GrSLPrecision,
1205 "Conical2FSCenter");
cdalton5e58cee2016-02-11 12:49:47 -08001206 fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -08001207 kVec4f_GrSLType, kDefault_GrSLPrecision,
1208 "Conical2FSParams");
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001209 SkString tName("t");
1210
egdaniel7ea439b2015-12-03 09:20:44 -08001211 GrGLSLShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001212 // params.x = A
1213 // params.y = B
1214 // params.z = C
egdaniel7ea439b2015-12-03 09:20:44 -08001215 GrGLSLShaderVar params = uniformHandler->getUniformVariable(fParamUni);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001216
1217 // if we have a vec3 from being in perspective, convert it to a vec2 first
cdalton85285412016-02-18 12:37:07 -08001218 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
egdaniel4ca2e602015-11-18 08:01:26 -08001219 SkString coords2DString = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +00001220 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001221
1222 // output will default to transparent black (we simply won't write anything
1223 // else to it if invalid, instead of discarding or returning prematurely)
egdaniel4ca2e602015-11-18 08:01:26 -08001224 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 +00001225
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001226 // p = coords2D
1227 // e = center end
1228 // r = radius end
1229 // A = dot(e, e) - r^2 + 2 * r - 1
1230 // B = (r -1) / A
1231 // C = 1 / A
1232 // d = dot(e, p) + B
1233 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001234
egdaniel4ca2e602015-11-18 08:01:26 -08001235 fragBuilder->codeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
1236 fragBuilder->codeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
1237 params.c_str());
1238 fragBuilder->codeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(),
1239 params.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001240
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001241 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
1242 // If so we must also flip sign on sqrt
1243 if (!fIsFlipped) {
egdaniel4ca2e602015-11-18 08:01:26 -08001244 fragBuilder->codeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001245 } else {
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 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001248
egdaniel7ea439b2015-12-03 09:20:44 -08001249 fragBuilder->codeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n",
1250 tName.c_str(), params.c_str());
egdaniel4ca2e602015-11-18 08:01:26 -08001251 fragBuilder->codeAppend("\t\t");
egdaniel7ea439b2015-12-03 09:20:44 -08001252 this->emitColor(fragBuilder,
1253 uniformHandler,
egdaniela2e3e0f2015-11-19 07:23:45 -08001254 args.fGLSLCaps,
egdaniel4ca2e602015-11-18 08:01:26 -08001255 ge,
1256 tName.c_str(),
1257 args.fOutputColor,
1258 args.fInputColor,
wangyix7c157a92015-07-22 15:08:53 -07001259 args.fSamplers);
egdaniel4ca2e602015-11-18 08:01:26 -08001260 fragBuilder->codeAppend("\t}\n");
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001261}
1262
egdaniel018fb622015-10-28 07:26:40 -07001263void GLCircleOutside2PtConicalEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1264 const GrProcessor& processor) {
wangyixb1daa862015-08-18 11:29:31 -07001265 INHERITED::onSetData(pdman, processor);
joshualittb0a8a372014-09-23 09:50:21 -07001266 const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +00001267 SkASSERT(data.isFlipped() == fIsFlipped);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001268 SkScalar centerX = data.centerX();
1269 SkScalar centerY = data.centerY();
1270 SkScalar A = data.A();
1271 SkScalar B = data.B();
1272 SkScalar C = data.C();
1273 SkScalar tLimit = data.tLimit();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001274
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001275 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1276 fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001277
kkinnunen7510b222014-07-30 00:04:16 -07001278 pdman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1279 pdman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001280 SkScalarToFloat(tLimit));
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001281
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001282 fCachedCenterX = centerX;
1283 fCachedCenterY = centerY;
1284 fCachedA = A;
1285 fCachedB = B;
1286 fCachedC = C;
1287 fCachedTLimit = tLimit;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001288 }
1289}
1290
joshualittb0a8a372014-09-23 09:50:21 -07001291void GLCircleOutside2PtConicalEffect::GenKey(const GrProcessor& processor,
jvanverthcfc18862015-04-28 08:48:20 -07001292 const GrGLSLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -07001293 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -07001294 key[0] = GenBaseGradientKey(processor);
1295 key[1] = processor.cast<CircleOutside2PtConicalEffect>().isFlipped();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001296}
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001297
1298//////////////////////////////////////////////////////////////////////////////
1299
joshualittb0a8a372014-09-23 09:50:21 -07001300GrFragmentProcessor* Gr2PtConicalGradientEffect::Create(GrContext* ctx,
1301 const SkTwoPointConicalGradient& shader,
1302 SkShader::TileMode tm,
1303 const SkMatrix* localMatrix) {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001304 SkMatrix matrix;
1305 if (!shader.getLocalMatrix().invert(&matrix)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001306 return nullptr;
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001307 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001308 if (localMatrix) {
1309 SkMatrix inv;
1310 if (!localMatrix->invert(&inv)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001311 return nullptr;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001312 }
1313 matrix.postConcat(inv);
1314 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001315
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001316 if (shader.getStartRadius() < kErrorTol) {
1317 SkScalar focalX;
1318 ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
1319 if (type == kInside_ConicalType) {
bsalomon4a339522015-10-06 08:40:50 -07001320 return FocalInside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001321 } else if(type == kEdge_ConicalType) {
1322 set_matrix_edge_conical(shader, &matrix);
bsalomon4a339522015-10-06 08:40:50 -07001323 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001324 } else {
bsalomon4a339522015-10-06 08:40:50 -07001325 return FocalOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001326 }
1327 }
1328
1329 CircleConicalInfo info;
1330 ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
1331
1332 if (type == kInside_ConicalType) {
bsalomon4a339522015-10-06 08:40:50 -07001333 return CircleInside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001334 } else if (type == kEdge_ConicalType) {
1335 set_matrix_edge_conical(shader, &matrix);
bsalomon4a339522015-10-06 08:40:50 -07001336 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001337 } else {
bsalomon4a339522015-10-06 08:40:50 -07001338 return CircleOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001339 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001340}
1341
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001342#endif