blob: 76f9a492b8ee5f75c1dfb60b58d9d4b1eed44f24 [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
commit-bot@chromium.org7f434932014-04-10 16:03:06 +000014#include "GrTBackendEffectFactory.h"
bsalomon848faf02014-07-11 10:01:02 -070015#include "gl/GrGLShaderBuilder.h"
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000016// For brevity
17typedef GrGLUniformManager::UniformHandle UniformHandle;
18
commit-bot@chromium.org80894672014-04-22 21:24:22 +000019static const SkScalar kErrorTol = 0.00001f;
egdaniel8405ef92014-06-09 11:57:28 -070020static const SkScalar kEdgeErrorTol = 5.f * kErrorTol;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000021
22/**
23 * We have three general cases for 2pt conical gradients. First we always assume that
24 * the start radius <= end radius. Our first case (kInside_) is when the start circle
25 * is completely enclosed by the end circle. The second case (kOutside_) is the case
26 * when the start circle is either completely outside the end circle or the circles
27 * overlap. The final case (kEdge_) is when the start circle is inside the end one,
28 * but the two are just barely touching at 1 point along their edges.
29 */
30enum ConicalType {
31 kInside_ConicalType,
32 kOutside_ConicalType,
33 kEdge_ConicalType,
34};
35
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000036//////////////////////////////////////////////////////////////////////////////
37
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000038static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader,
39 SkMatrix* invLMatrix) {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000040 // Inverse of the current local matrix is passed in then,
41 // translate to center1, rotate so center2 is on x axis.
42 const SkPoint& center1 = shader.getStartCenter();
43 const SkPoint& center2 = shader.getEndCenter();
44
45 invLMatrix->postTranslate(-center1.fX, -center1.fY);
46
47 SkPoint diff = center2 - center1;
48 SkScalar diffLen = diff.length();
49 if (0 != diffLen) {
50 SkScalar invDiffLen = SkScalarInvert(diffLen);
51 SkMatrix rot;
52 rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY),
53 SkScalarMul(invDiffLen, diff.fX));
54 invLMatrix->postConcat(rot);
55 }
56}
57
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000058class GLEdge2PtConicalEffect;
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000059
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000060class Edge2PtConicalEffect : public GrGradientEffect {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000061public:
62
bsalomon83d081a2014-07-08 09:56:10 -070063 static GrEffect* Create(GrContext* ctx,
64 const SkTwoPointConicalGradient& shader,
65 const SkMatrix& matrix,
66 SkShader::TileMode tm) {
bsalomon55fad7a2014-07-08 07:34:20 -070067 return SkNEW_ARGS(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
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000072 static const char* Name() { return "Two-Point Conical Gradient Edge Touching"; }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000073 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
74
75 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000076 SkScalar center() const { return fCenterX1; }
77 SkScalar diffRadius() const { return fDiffRadius; }
78 SkScalar radius() const { return fRadius0; }
79
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000080 typedef GLEdge2PtConicalEffect GLEffect;
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000081
82private:
83 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000084 const Edge2PtConicalEffect& s = CastEffect<Edge2PtConicalEffect>(sBase);
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000085 return (INHERITED::onIsEqual(sBase) &&
86 this->fCenterX1 == s.fCenterX1 &&
87 this->fRadius0 == s.fRadius0 &&
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000088 this->fDiffRadius == s.fDiffRadius);
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000089 }
90
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000091 Edge2PtConicalEffect(GrContext* ctx,
92 const SkTwoPointConicalGradient& shader,
93 const SkMatrix& matrix,
94 SkShader::TileMode tm)
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000095 : INHERITED(ctx, shader, matrix, tm),
96 fCenterX1(shader.getCenterX1()),
97 fRadius0(shader.getStartRadius()),
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000098 fDiffRadius(shader.getDiffRadius()){
99 // We should only be calling this shader if we are degenerate case with touching circles
egdaniel8405ef92014-06-09 11:57:28 -0700100 // When deciding if we are in edge case, we scaled by the end radius for cases when the
101 // start radius was close to zero, otherwise we scaled by the start radius
102 SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - SkScalarAbs(fCenterX1)) <
103 kEdgeErrorTol * (fRadius0 < kErrorTol ? shader.getEndRadius() : fRadius0));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000104
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000105 // We pass the linear part of the quadratic as a varying.
106 // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
107 fBTransform = this->getCoordTransform();
108 SkMatrix& bMatrix = *fBTransform.accessMatrix();
109 SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius);
110 bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) +
111 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0]));
112 bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) +
113 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1]));
114 bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) +
115 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2]));
116 this->addCoordTransform(&fBTransform);
117 }
118
119 GR_DECLARE_EFFECT_TEST;
120
121 // @{
122 // Cache of values - these can change arbitrarily, EXCEPT
123 // we shouldn't change between degenerate and non-degenerate?!
124
125 GrCoordTransform fBTransform;
126 SkScalar fCenterX1;
127 SkScalar fRadius0;
128 SkScalar fDiffRadius;
129
130 // @}
131
132 typedef GrGradientEffect INHERITED;
133};
134
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000135class GLEdge2PtConicalEffect : public GrGLGradientEffect {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +0000136public:
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000137 GLEdge2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
138 virtual ~GLEdge2PtConicalEffect() { }
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000139
140 virtual void emitCode(GrGLShaderBuilder*,
141 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700142 const GrEffectKey&,
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000143 const char* outputColor,
144 const char* inputColor,
145 const TransformedCoordsArray&,
146 const TextureSamplerArray&) SK_OVERRIDE;
147 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
148
bsalomon63e99f72014-07-21 08:03:14 -0700149 static void GenKey(const GrDrawEffect&, const GrGLCaps& caps, GrEffectKeyBuilder* b);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000150
151protected:
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000152 UniformHandle fParamUni;
153
154 const char* fVSVaryingName;
155 const char* fFSVaryingName;
156
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000157 // @{
158 /// Values last uploaded as uniforms
159
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000160 SkScalar fCachedRadius;
161 SkScalar fCachedDiffRadius;
162
163 // @}
164
165private:
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000166 typedef GrGLGradientEffect INHERITED;
167
168};
169
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000170const GrBackendEffectFactory& Edge2PtConicalEffect::getFactory() const {
171 return GrTBackendEffectFactory<Edge2PtConicalEffect>::getInstance();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000172}
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000173
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000174GR_DEFINE_EFFECT_TEST(Edge2PtConicalEffect);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000175
bsalomon83d081a2014-07-08 09:56:10 -0700176GrEffect* Edge2PtConicalEffect::TestCreate(SkRandom* random,
177 GrContext* context,
178 const GrDrawTargetCaps&,
179 GrTexture**) {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000180 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
181 SkScalar radius1 = random->nextUScalar1();
182 SkPoint center2;
183 SkScalar radius2;
184 do {
185 center2.set(random->nextUScalar1(), random->nextUScalar1());
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000186 // If the circles are identical the factory will give us an empty shader.
187 // This will happen if we pick identical centers
188 } while (center1 == center2);
189
190 // Below makes sure that circle one is contained within circle two
191 // and both circles are touching on an edge
192 SkPoint diff = center2 - center1;
193 SkScalar diffLen = diff.length();
194 radius2 = radius1 + diffLen;
195
196 SkColor colors[kMaxRandomGradientColors];
197 SkScalar stopsArray[kMaxRandomGradientColors];
198 SkScalar* stops = stopsArray;
199 SkShader::TileMode tm;
200 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
201 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
202 center2, radius2,
203 colors, stops, colorCount,
204 tm));
205 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700206 GrEffect* effect;
207 GrColor paintColor;
208 SkAssertResult(shader->asNewEffect(context, paint, NULL, &paintColor, &effect));
dandov9de5b512014-06-10 14:38:28 -0700209 return effect;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000210}
211
212GLEdge2PtConicalEffect::GLEdge2PtConicalEffect(const GrBackendEffectFactory& factory,
213 const GrDrawEffect& drawEffect)
214 : INHERITED(factory)
215 , fVSVaryingName(NULL)
216 , fFSVaryingName(NULL)
217 , fCachedRadius(-SK_ScalarMax)
218 , fCachedDiffRadius(-SK_ScalarMax) {}
219
220void GLEdge2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
221 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700222 const GrEffectKey& key,
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000223 const char* outputColor,
224 const char* inputColor,
225 const TransformedCoordsArray& coords,
226 const TextureSamplerArray& samplers) {
bsalomon63e99f72014-07-21 08:03:14 -0700227 uint32_t baseKey = key.get32(0);
228 this->emitUniforms(builder, baseKey);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000229 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
230 kFloat_GrSLType, "Conical2FSParams", 3);
231
232 SkString cName("c");
233 SkString tName("t");
234 SkString p0; // start radius
235 SkString p1; // start radius squared
236 SkString p2; // difference in radii (r1 - r0)
237
238 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
239 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
240 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
241
242 // We interpolate the linear component in coords[1].
243 SkASSERT(coords[0].type() == coords[1].type());
244 const char* coords2D;
245 SkString bVar;
246 if (kVec3f_GrSLType == coords[0].type()) {
247 builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
248 coords[0].c_str(), coords[0].c_str(), coords[1].c_str(), coords[1].c_str());
249 coords2D = "interpolants.xy";
250 bVar = "interpolants.z";
251 } else {
252 coords2D = coords[0].c_str();
253 bVar.printf("%s.x", coords[1].c_str());
254 }
255
256 // output will default to transparent black (we simply won't write anything
257 // else to it if invalid, instead of discarding or returning prematurely)
258 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
259
260 // c = (x^2)+(y^2) - params[1]
261 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
262 cName.c_str(), coords2D, coords2D, p1.c_str());
263
264 // linear case: t = -c/b
265 builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
266 cName.c_str(), bVar.c_str());
267
268 // if r(t) > 0, then t will be the x coordinate
269 builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
270 p2.c_str(), p0.c_str());
271 builder->fsCodeAppend("\t");
bsalomon63e99f72014-07-21 08:03:14 -0700272 this->emitColor(builder, tName.c_str(), baseKey, outputColor, inputColor, samplers);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000273 builder->fsCodeAppend("\t}\n");
274}
275
276void GLEdge2PtConicalEffect::setData(const GrGLUniformManager& uman,
277 const GrDrawEffect& drawEffect) {
278 INHERITED::setData(uman, drawEffect);
279 const Edge2PtConicalEffect& data = drawEffect.castEffect<Edge2PtConicalEffect>();
280 SkScalar radius0 = data.radius();
281 SkScalar diffRadius = data.diffRadius();
282
283 if (fCachedRadius != radius0 ||
284 fCachedDiffRadius != diffRadius) {
285
286 float values[3] = {
287 SkScalarToFloat(radius0),
288 SkScalarToFloat(SkScalarMul(radius0, radius0)),
289 SkScalarToFloat(diffRadius)
290 };
291
292 uman.set1fv(fParamUni, 3, values);
293 fCachedRadius = radius0;
294 fCachedDiffRadius = diffRadius;
295 }
296}
297
bsalomon63e99f72014-07-21 08:03:14 -0700298void GLEdge2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
299 const GrGLCaps&, GrEffectKeyBuilder* b) {
300 b->add32(GenBaseGradientKey(drawEffect));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000301}
302
303//////////////////////////////////////////////////////////////////////////////
304// Focal Conical Gradients
305//////////////////////////////////////////////////////////////////////////////
306
307static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader,
308 SkMatrix* invLMatrix, SkScalar* focalX) {
309 // Inverse of the current local matrix is passed in then,
310 // translate, scale, and rotate such that endCircle is unit circle on x-axis,
311 // and focal point is at the origin.
312 ConicalType conicalType;
313 const SkPoint& focal = shader.getStartCenter();
314 const SkPoint& centerEnd = shader.getEndCenter();
315 SkScalar radius = shader.getEndRadius();
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000316 SkScalar invRadius = 1.f / radius;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000317
318 SkMatrix matrix;
319
320 matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
321 matrix.postScale(invRadius, invRadius);
322
323 SkPoint focalTrans;
324 matrix.mapPoints(&focalTrans, &focal, 1);
325 *focalX = focalTrans.length();
326
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000327 if (0.f != *focalX) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000328 SkScalar invFocalX = SkScalarInvert(*focalX);
329 SkMatrix rot;
330 rot.setSinCos(-SkScalarMul(invFocalX, focalTrans.fY),
331 SkScalarMul(invFocalX, focalTrans.fX));
332 matrix.postConcat(rot);
333 }
334
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000335 matrix.postTranslate(-(*focalX), 0.f);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000336
337 // If the focal point is touching the edge of the circle it will
338 // cause a degenerate case that must be handled separately
egdaniel8405ef92014-06-09 11:57:28 -0700339 // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
340 // stability trade off versus the linear approx used in the Edge Shader
341 if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000342 return kEdge_ConicalType;
343 }
344
345 // Scale factor 1 / (1 - focalX * focalX)
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000346 SkScalar oneMinusF2 = 1.f - SkScalarMul(*focalX, *focalX);
347 SkScalar s = SkScalarDiv(1.f, oneMinusF2);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000348
349
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000350 if (s >= 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000351 conicalType = kInside_ConicalType;
352 matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
353 } else {
354 conicalType = kOutside_ConicalType;
355 matrix.postScale(s, s);
356 }
357
358 invLMatrix->postConcat(matrix);
359
360 return conicalType;
361}
362
363//////////////////////////////////////////////////////////////////////////////
364
365class GLFocalOutside2PtConicalEffect;
366
367class FocalOutside2PtConicalEffect : public GrGradientEffect {
368public:
369
bsalomon83d081a2014-07-08 09:56:10 -0700370 static GrEffect* Create(GrContext* ctx,
371 const SkTwoPointConicalGradient& shader,
372 const SkMatrix& matrix,
373 SkShader::TileMode tm,
374 SkScalar focalX) {
bsalomon55fad7a2014-07-08 07:34:20 -0700375 return SkNEW_ARGS(FocalOutside2PtConicalEffect, (ctx, shader, matrix, tm, focalX));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000376 }
377
378 virtual ~FocalOutside2PtConicalEffect() { }
379
380 static const char* Name() { return "Two-Point Conical Gradient Focal Outside"; }
381 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
382
383 bool isFlipped() const { return fIsFlipped; }
384 SkScalar focal() const { return fFocalX; }
385
386 typedef GLFocalOutside2PtConicalEffect GLEffect;
387
388private:
389 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
390 const FocalOutside2PtConicalEffect& s = CastEffect<FocalOutside2PtConicalEffect>(sBase);
391 return (INHERITED::onIsEqual(sBase) &&
392 this->fFocalX == s.fFocalX &&
393 this->fIsFlipped == s.fIsFlipped);
394 }
395
396 FocalOutside2PtConicalEffect(GrContext* ctx,
397 const SkTwoPointConicalGradient& shader,
398 const SkMatrix& matrix,
399 SkShader::TileMode tm,
400 SkScalar focalX)
401 : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX), fIsFlipped(shader.isFlippedGrad()) {}
402
403 GR_DECLARE_EFFECT_TEST;
404
405 SkScalar fFocalX;
406 bool fIsFlipped;
407
408 typedef GrGradientEffect INHERITED;
409};
410
411class GLFocalOutside2PtConicalEffect : public GrGLGradientEffect {
412public:
413 GLFocalOutside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
414 virtual ~GLFocalOutside2PtConicalEffect() { }
415
416 virtual void emitCode(GrGLShaderBuilder*,
417 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700418 const GrEffectKey&,
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000419 const char* outputColor,
420 const char* inputColor,
421 const TransformedCoordsArray&,
422 const TextureSamplerArray&) SK_OVERRIDE;
423 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
424
bsalomon63e99f72014-07-21 08:03:14 -0700425 static void GenKey(const GrDrawEffect&, const GrGLCaps& caps, GrEffectKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000426
427protected:
428 UniformHandle fParamUni;
429
430 const char* fVSVaryingName;
431 const char* fFSVaryingName;
432
433 bool fIsFlipped;
434
435 // @{
436 /// Values last uploaded as uniforms
437
438 SkScalar fCachedFocal;
439
440 // @}
441
442private:
443 typedef GrGLGradientEffect INHERITED;
444
445};
446
447const GrBackendEffectFactory& FocalOutside2PtConicalEffect::getFactory() const {
448 return GrTBackendEffectFactory<FocalOutside2PtConicalEffect>::getInstance();
449}
450
451GR_DEFINE_EFFECT_TEST(FocalOutside2PtConicalEffect);
452
bsalomon83d081a2014-07-08 09:56:10 -0700453GrEffect* FocalOutside2PtConicalEffect::TestCreate(SkRandom* random,
454 GrContext* context,
455 const GrDrawTargetCaps&,
456 GrTexture**) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000457 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000458 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000459 SkPoint center2;
460 SkScalar radius2;
461 do {
462 center2.set(random->nextUScalar1(), random->nextUScalar1());
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000463 // 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 +0000464 } while (center1 == center2);
465 SkPoint diff = center2 - center1;
466 SkScalar diffLen = diff.length();
467 // Below makes sure that the focal point is not contained within circle two
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000468 radius2 = random->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000469
470 SkColor colors[kMaxRandomGradientColors];
471 SkScalar stopsArray[kMaxRandomGradientColors];
472 SkScalar* stops = stopsArray;
473 SkShader::TileMode tm;
474 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
475 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
476 center2, radius2,
477 colors, stops, colorCount,
478 tm));
479 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700480 GrEffect* effect;
481 GrColor paintColor;
482 SkAssertResult(shader->asNewEffect(context, paint, NULL, &paintColor, &effect));
dandov9de5b512014-06-10 14:38:28 -0700483 return effect;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000484}
485
486GLFocalOutside2PtConicalEffect::GLFocalOutside2PtConicalEffect(const GrBackendEffectFactory& factory,
487 const GrDrawEffect& drawEffect)
488 : INHERITED(factory)
489 , fVSVaryingName(NULL)
490 , fFSVaryingName(NULL)
491 , fCachedFocal(SK_ScalarMax) {
492 const FocalOutside2PtConicalEffect& data = drawEffect.castEffect<FocalOutside2PtConicalEffect>();
493 fIsFlipped = data.isFlipped();
494}
495
496void GLFocalOutside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
497 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700498 const GrEffectKey& key,
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000499 const char* outputColor,
500 const char* inputColor,
501 const TransformedCoordsArray& coords,
502 const TextureSamplerArray& samplers) {
bsalomon63e99f72014-07-21 08:03:14 -0700503 uint32_t baseKey = key.get32(0);
504 this->emitUniforms(builder, baseKey);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000505 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
506 kFloat_GrSLType, "Conical2FSParams", 2);
507 SkString tName("t");
508 SkString p0; // focalX
509 SkString p1; // 1 - focalX * focalX
510
511 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
512 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
513
514 // if we have a vec3 from being in perspective, convert it to a vec2 first
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000515 SkString coords2DString = builder->ensureFSCoords2D(coords, 0);
516 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000517
518 // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
519
520 // output will default to transparent black (we simply won't write anything
521 // else to it if invalid, instead of discarding or returning prematurely)
522 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
523
524 builder->fsCodeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D);
525 builder->fsCodeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D);
526 builder->fsCodeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str());
527
528 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
529 // If so we must also flip sign on sqrt
530 if (!fIsFlipped) {
531 builder->fsCodeAppendf("\tfloat %s = %s.x * %s + sqrt(d);\n", tName.c_str(),
532 coords2D, p0.c_str());
533 } else {
534 builder->fsCodeAppendf("\tfloat %s = %s.x * %s - sqrt(d);\n", tName.c_str(),
535 coords2D, p0.c_str());
536 }
537
538 builder->fsCodeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str());
539 builder->fsCodeAppend("\t\t");
bsalomon63e99f72014-07-21 08:03:14 -0700540 this->emitColor(builder, tName.c_str(), baseKey, outputColor, inputColor, samplers);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000541 builder->fsCodeAppend("\t}\n");
542}
543
544void GLFocalOutside2PtConicalEffect::setData(const GrGLUniformManager& uman,
545 const GrDrawEffect& drawEffect) {
546 INHERITED::setData(uman, drawEffect);
547 const FocalOutside2PtConicalEffect& data = drawEffect.castEffect<FocalOutside2PtConicalEffect>();
548 SkASSERT(data.isFlipped() == fIsFlipped);
549 SkScalar focal = data.focal();
550
551 if (fCachedFocal != focal) {
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000552 SkScalar oneMinus2F = 1.f - SkScalarMul(focal, focal);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000553
554 float values[2] = {
555 SkScalarToFloat(focal),
556 SkScalarToFloat(oneMinus2F),
557 };
558
559 uman.set1fv(fParamUni, 2, values);
560 fCachedFocal = focal;
561 }
562}
563
bsalomon63e99f72014-07-21 08:03:14 -0700564void GLFocalOutside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
565 const GrGLCaps&, GrEffectKeyBuilder* b) {
566 uint32_t* key = b->add32n(2);
567 key[0] = GenBaseGradientKey(drawEffect);
568 key[1] = drawEffect.castEffect<FocalOutside2PtConicalEffect>().isFlipped();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000569}
570
571//////////////////////////////////////////////////////////////////////////////
572
573class GLFocalInside2PtConicalEffect;
574
575class FocalInside2PtConicalEffect : public GrGradientEffect {
576public:
577
bsalomon83d081a2014-07-08 09:56:10 -0700578 static GrEffect* Create(GrContext* ctx,
579 const SkTwoPointConicalGradient& shader,
580 const SkMatrix& matrix,
581 SkShader::TileMode tm,
582 SkScalar focalX) {
bsalomon55fad7a2014-07-08 07:34:20 -0700583 return SkNEW_ARGS(FocalInside2PtConicalEffect, (ctx, shader, matrix, tm, focalX));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000584 }
585
586 virtual ~FocalInside2PtConicalEffect() {}
587
588 static const char* Name() { return "Two-Point Conical Gradient Focal Inside"; }
589 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
590
591 SkScalar focal() const { return fFocalX; }
592
593 typedef GLFocalInside2PtConicalEffect GLEffect;
594
595private:
596 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
597 const FocalInside2PtConicalEffect& s = CastEffect<FocalInside2PtConicalEffect>(sBase);
598 return (INHERITED::onIsEqual(sBase) &&
599 this->fFocalX == s.fFocalX);
600 }
601
602 FocalInside2PtConicalEffect(GrContext* ctx,
603 const SkTwoPointConicalGradient& shader,
604 const SkMatrix& matrix,
605 SkShader::TileMode tm,
606 SkScalar focalX)
607 : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX) {}
608
609 GR_DECLARE_EFFECT_TEST;
610
611 SkScalar fFocalX;
612
613 typedef GrGradientEffect INHERITED;
614};
615
616class GLFocalInside2PtConicalEffect : public GrGLGradientEffect {
617public:
618 GLFocalInside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
619 virtual ~GLFocalInside2PtConicalEffect() {}
620
621 virtual void emitCode(GrGLShaderBuilder*,
622 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700623 const GrEffectKey&,
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000624 const char* outputColor,
625 const char* inputColor,
626 const TransformedCoordsArray&,
627 const TextureSamplerArray&) SK_OVERRIDE;
628 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
629
bsalomon63e99f72014-07-21 08:03:14 -0700630 static void GenKey(const GrDrawEffect&, const GrGLCaps& caps, GrEffectKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000631
632protected:
633 UniformHandle fFocalUni;
634
635 const char* fVSVaryingName;
636 const char* fFSVaryingName;
637
638 // @{
639 /// Values last uploaded as uniforms
640
641 SkScalar fCachedFocal;
642
643 // @}
644
645private:
646 typedef GrGLGradientEffect INHERITED;
647
648};
649
650const GrBackendEffectFactory& FocalInside2PtConicalEffect::getFactory() const {
651 return GrTBackendEffectFactory<FocalInside2PtConicalEffect>::getInstance();
652}
653
654GR_DEFINE_EFFECT_TEST(FocalInside2PtConicalEffect);
655
bsalomon83d081a2014-07-08 09:56:10 -0700656GrEffect* FocalInside2PtConicalEffect::TestCreate(SkRandom* random,
657 GrContext* context,
658 const GrDrawTargetCaps&,
659 GrTexture**) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000660 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000661 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000662 SkPoint center2;
663 SkScalar radius2;
664 do {
665 center2.set(random->nextUScalar1(), random->nextUScalar1());
666 // Below makes sure radius2 is larger enouch such that the focal point
667 // is inside the end circle
668 SkScalar increase = random->nextUScalar1();
669 SkPoint diff = center2 - center1;
670 SkScalar diffLen = diff.length();
671 radius2 = diffLen + increase;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000672 // If the circles are identical the factory will give us an empty shader.
673 } while (radius1 == radius2 && center1 == center2);
674
675 SkColor colors[kMaxRandomGradientColors];
676 SkScalar stopsArray[kMaxRandomGradientColors];
677 SkScalar* stops = stopsArray;
678 SkShader::TileMode tm;
679 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
680 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
681 center2, radius2,
682 colors, stops, colorCount,
683 tm));
684 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700685 GrColor paintColor;
686 GrEffect* effect;
687 SkAssertResult(shader->asNewEffect(context, paint, NULL, &paintColor, &effect));
688 return effect;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000689}
690
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000691GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrBackendEffectFactory& factory,
692 const GrDrawEffect& drawEffect)
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000693 : INHERITED(factory)
694 , fVSVaryingName(NULL)
695 , fFSVaryingName(NULL)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000696 , fCachedFocal(SK_ScalarMax) {}
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000697
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000698void GLFocalInside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
699 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700700 const GrEffectKey& key,
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000701 const char* outputColor,
702 const char* inputColor,
703 const TransformedCoordsArray& coords,
704 const TextureSamplerArray& samplers) {
bsalomon63e99f72014-07-21 08:03:14 -0700705 uint32_t baseKey = key.get32(0);
706 this->emitUniforms(builder, baseKey);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000707 fFocalUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
708 kFloat_GrSLType, "Conical2FSParams");
709 SkString tName("t");
710
711 // this is the distance along x-axis from the end center to focal point in
712 // transformed coordinates
713 GrGLShaderVar focal = builder->getUniformVariable(fFocalUni);
714
715 // if we have a vec3 from being in perspective, convert it to a vec2 first
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000716 SkString coords2DString = builder->ensureFSCoords2D(coords, 0);
717 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000718
719 // t = p.x * focalX + length(p)
720 builder->fsCodeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(),
721 coords2D, focal.c_str(), coords2D);
722
bsalomon63e99f72014-07-21 08:03:14 -0700723 this->emitColor(builder, tName.c_str(), baseKey, outputColor, inputColor, samplers);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000724}
725
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000726void GLFocalInside2PtConicalEffect::setData(const GrGLUniformManager& uman,
727 const GrDrawEffect& drawEffect) {
728 INHERITED::setData(uman, drawEffect);
729 const FocalInside2PtConicalEffect& data = drawEffect.castEffect<FocalInside2PtConicalEffect>();
730 SkScalar focal = data.focal();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000731
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000732 if (fCachedFocal != focal) {
733 uman.set1f(fFocalUni, SkScalarToFloat(focal));
734 fCachedFocal = focal;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000735 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000736}
737
bsalomon63e99f72014-07-21 08:03:14 -0700738void GLFocalInside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
739 const GrGLCaps&, GrEffectKeyBuilder* b) {
740 b->add32(GenBaseGradientKey(drawEffect));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000741}
742
743//////////////////////////////////////////////////////////////////////////////
744// Circle Conical Gradients
745//////////////////////////////////////////////////////////////////////////////
746
747struct CircleConicalInfo {
748 SkPoint fCenterEnd;
749 SkScalar fA;
750 SkScalar fB;
751 SkScalar fC;
752};
753
754// Returns focal distance along x-axis in transformed coords
755static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
756 SkMatrix* invLMatrix, CircleConicalInfo* info) {
757 // Inverse of the current local matrix is passed in then,
758 // translate and scale such that start circle is on the origin and has radius 1
759 const SkPoint& centerStart = shader.getStartCenter();
760 const SkPoint& centerEnd = shader.getEndCenter();
761 SkScalar radiusStart = shader.getStartRadius();
762 SkScalar radiusEnd = shader.getEndRadius();
763
764 SkMatrix matrix;
765
766 matrix.setTranslate(-centerStart.fX, -centerStart.fY);
767
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000768 SkScalar invStartRad = 1.f / radiusStart;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000769 matrix.postScale(invStartRad, invStartRad);
770
771 radiusEnd /= radiusStart;
772
773 SkPoint centerEndTrans;
774 matrix.mapPoints(&centerEndTrans, &centerEnd, 1);
775
776 SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
777 - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
778
779 // Check to see if start circle is inside end circle with edges touching.
780 // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
egdaniel8405ef92014-06-09 11:57:28 -0700781 // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
782 // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
783 // still accurate.
784 if (SkScalarAbs(A) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000785 return kEdge_ConicalType;
786 }
787
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000788 SkScalar C = 1.f / A;
789 SkScalar B = (radiusEnd - 1.f) * C;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000790
791 matrix.postScale(C, C);
792
793 invLMatrix->postConcat(matrix);
794
795 info->fCenterEnd = centerEndTrans;
796 info->fA = A;
797 info->fB = B;
798 info->fC = C;
799
800 // 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 +0000801 if (A < 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000802 return kInside_ConicalType;
803 }
804 return kOutside_ConicalType;
805}
806
807class GLCircleInside2PtConicalEffect;
808
809class CircleInside2PtConicalEffect : public GrGradientEffect {
810public:
811
bsalomon83d081a2014-07-08 09:56:10 -0700812 static GrEffect* Create(GrContext* ctx,
813 const SkTwoPointConicalGradient& shader,
814 const SkMatrix& matrix,
815 SkShader::TileMode tm,
816 const CircleConicalInfo& info) {
bsalomon55fad7a2014-07-08 07:34:20 -0700817 return SkNEW_ARGS(CircleInside2PtConicalEffect, (ctx, shader, matrix, tm, info));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000818 }
819
820 virtual ~CircleInside2PtConicalEffect() {}
821
822 static const char* Name() { return "Two-Point Conical Gradient Inside"; }
823 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
824
825 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
826 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
827 SkScalar A() const { return fInfo.fA; }
828 SkScalar B() const { return fInfo.fB; }
829 SkScalar C() const { return fInfo.fC; }
830
831 typedef GLCircleInside2PtConicalEffect GLEffect;
832
833private:
834 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
835 const CircleInside2PtConicalEffect& s = CastEffect<CircleInside2PtConicalEffect>(sBase);
836 return (INHERITED::onIsEqual(sBase) &&
837 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
838 this->fInfo.fA == s.fInfo.fA &&
839 this->fInfo.fB == s.fInfo.fB &&
840 this->fInfo.fC == s.fInfo.fC);
841 }
842
843 CircleInside2PtConicalEffect(GrContext* ctx,
844 const SkTwoPointConicalGradient& shader,
845 const SkMatrix& matrix,
846 SkShader::TileMode tm,
847 const CircleConicalInfo& info)
848 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {}
849
850 GR_DECLARE_EFFECT_TEST;
851
852 const CircleConicalInfo fInfo;
853
854 typedef GrGradientEffect INHERITED;
855};
856
857class GLCircleInside2PtConicalEffect : public GrGLGradientEffect {
858public:
859 GLCircleInside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
860 virtual ~GLCircleInside2PtConicalEffect() {}
861
862 virtual void emitCode(GrGLShaderBuilder*,
863 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700864 const GrEffectKey&,
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000865 const char* outputColor,
866 const char* inputColor,
867 const TransformedCoordsArray&,
868 const TextureSamplerArray&) SK_OVERRIDE;
869 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
870
bsalomon63e99f72014-07-21 08:03:14 -0700871 static void GenKey(const GrDrawEffect&, const GrGLCaps& caps, GrEffectKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000872
873protected:
874 UniformHandle fCenterUni;
875 UniformHandle fParamUni;
876
877 const char* fVSVaryingName;
878 const char* fFSVaryingName;
879
880 // @{
881 /// Values last uploaded as uniforms
882
883 SkScalar fCachedCenterX;
884 SkScalar fCachedCenterY;
885 SkScalar fCachedA;
886 SkScalar fCachedB;
887 SkScalar fCachedC;
888
889 // @}
890
891private:
892 typedef GrGLGradientEffect INHERITED;
893
894};
895
896const GrBackendEffectFactory& CircleInside2PtConicalEffect::getFactory() const {
897 return GrTBackendEffectFactory<CircleInside2PtConicalEffect>::getInstance();
898}
899
900GR_DEFINE_EFFECT_TEST(CircleInside2PtConicalEffect);
901
bsalomon83d081a2014-07-08 09:56:10 -0700902GrEffect* CircleInside2PtConicalEffect::TestCreate(SkRandom* random,
903 GrContext* context,
904 const GrDrawTargetCaps&,
905 GrTexture**) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000906 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000907 SkScalar radius1 = random->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000908 SkPoint center2;
909 SkScalar radius2;
910 do {
911 center2.set(random->nextUScalar1(), random->nextUScalar1());
912 // Below makes sure that circle one is contained within circle two
913 SkScalar increase = random->nextUScalar1();
914 SkPoint diff = center2 - center1;
915 SkScalar diffLen = diff.length();
916 radius2 = radius1 + diffLen + increase;
917 // If the circles are identical the factory will give us an empty shader.
918 } while (radius1 == radius2 && center1 == center2);
919
920 SkColor colors[kMaxRandomGradientColors];
921 SkScalar stopsArray[kMaxRandomGradientColors];
922 SkScalar* stops = stopsArray;
923 SkShader::TileMode tm;
924 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
925 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
926 center2, radius2,
927 colors, stops, colorCount,
928 tm));
929 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700930 GrColor paintColor;
931 GrEffect* effect;
932 SkAssertResult(shader->asNewEffect(context, paint, NULL, &paintColor, &effect));
933 return effect;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000934}
935
936GLCircleInside2PtConicalEffect::GLCircleInside2PtConicalEffect(const GrBackendEffectFactory& factory,
937 const GrDrawEffect& drawEffect)
938 : INHERITED(factory)
939 , fVSVaryingName(NULL)
940 , fFSVaryingName(NULL)
941 , fCachedCenterX(SK_ScalarMax)
942 , fCachedCenterY(SK_ScalarMax)
943 , fCachedA(SK_ScalarMax)
944 , fCachedB(SK_ScalarMax)
945 , fCachedC(SK_ScalarMax) {}
946
947void GLCircleInside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
948 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700949 const GrEffectKey& key,
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000950 const char* outputColor,
951 const char* inputColor,
952 const TransformedCoordsArray& coords,
953 const TextureSamplerArray& samplers) {
bsalomon63e99f72014-07-21 08:03:14 -0700954 uint32_t baseKey = key.get32(0);
955 this->emitUniforms(builder, baseKey);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000956 fCenterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
957 kVec2f_GrSLType, "Conical2FSCenter");
958 fParamUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
959 kVec3f_GrSLType, "Conical2FSParams");
960 SkString tName("t");
961
962 GrGLShaderVar center = builder->getUniformVariable(fCenterUni);
963 // params.x = A
964 // params.y = B
965 // params.z = C
966 GrGLShaderVar params = builder->getUniformVariable(fParamUni);
967
968 // if we have a vec3 from being in perspective, convert it to a vec2 first
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000969 SkString coords2DString = builder->ensureFSCoords2D(coords, 0);
970 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000971
972 // p = coords2D
973 // e = center end
974 // r = radius end
975 // A = dot(e, e) - r^2 + 2 * r - 1
976 // B = (r -1) / A
977 // C = 1 / A
978 // d = dot(e, p) + B
979 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
980 builder->fsCodeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
981 builder->fsCodeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(), params.c_str());
982 builder->fsCodeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
983 tName.c_str(), params.c_str(), params.c_str());
984
bsalomon63e99f72014-07-21 08:03:14 -0700985 this->emitColor(builder, tName.c_str(), baseKey, outputColor, inputColor, samplers);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000986}
987
988void GLCircleInside2PtConicalEffect::setData(const GrGLUniformManager& uman,
989 const GrDrawEffect& drawEffect) {
990 INHERITED::setData(uman, drawEffect);
991 const CircleInside2PtConicalEffect& data = drawEffect.castEffect<CircleInside2PtConicalEffect>();
992 SkScalar centerX = data.centerX();
993 SkScalar centerY = data.centerY();
994 SkScalar A = data.A();
995 SkScalar B = data.B();
996 SkScalar C = data.C();
997
998 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
999 fCachedA != A || fCachedB != B || fCachedC != C) {
1000
1001 uman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1002 uman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C));
1003
1004 fCachedCenterX = centerX;
1005 fCachedCenterY = centerY;
1006 fCachedA = A;
1007 fCachedB = B;
1008 fCachedC = C;
1009 }
1010}
1011
bsalomon63e99f72014-07-21 08:03:14 -07001012void GLCircleInside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
1013 const GrGLCaps&, GrEffectKeyBuilder* b) {
1014 b->add32(GenBaseGradientKey(drawEffect));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001015}
1016
1017//////////////////////////////////////////////////////////////////////////////
1018
1019class GLCircleOutside2PtConicalEffect;
1020
1021class CircleOutside2PtConicalEffect : public GrGradientEffect {
1022public:
1023
bsalomon83d081a2014-07-08 09:56:10 -07001024 static GrEffect* Create(GrContext* ctx,
1025 const SkTwoPointConicalGradient& shader,
1026 const SkMatrix& matrix,
1027 SkShader::TileMode tm,
1028 const CircleConicalInfo& info) {
bsalomon55fad7a2014-07-08 07:34:20 -07001029 return SkNEW_ARGS(CircleOutside2PtConicalEffect, (ctx, shader, matrix, tm, info));
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001030 }
1031
1032 virtual ~CircleOutside2PtConicalEffect() {}
1033
1034 static const char* Name() { return "Two-Point Conical Gradient Outside"; }
1035 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
1036
1037 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
1038 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
1039 SkScalar A() const { return fInfo.fA; }
1040 SkScalar B() const { return fInfo.fB; }
1041 SkScalar C() const { return fInfo.fC; }
1042 SkScalar tLimit() const { return fTLimit; }
1043 bool isFlipped() const { return fIsFlipped; }
1044
1045 typedef GLCircleOutside2PtConicalEffect GLEffect;
1046
1047private:
1048 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
1049 const CircleOutside2PtConicalEffect& s = CastEffect<CircleOutside2PtConicalEffect>(sBase);
1050 return (INHERITED::onIsEqual(sBase) &&
1051 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
1052 this->fInfo.fA == s.fInfo.fA &&
1053 this->fInfo.fB == s.fInfo.fB &&
1054 this->fInfo.fC == s.fInfo.fC &&
1055 this->fTLimit == s.fTLimit &&
1056 this->fIsFlipped == s.fIsFlipped);
1057 }
1058
1059 CircleOutside2PtConicalEffect(GrContext* ctx,
1060 const SkTwoPointConicalGradient& shader,
1061 const SkMatrix& matrix,
1062 SkShader::TileMode tm,
1063 const CircleConicalInfo& info)
1064 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
1065 if (shader.getStartRadius() != shader.getEndRadius()) {
1066 fTLimit = SkScalarDiv(shader.getStartRadius(), (shader.getStartRadius() - shader.getEndRadius()));
1067 } else {
1068 fTLimit = SK_ScalarMin;
1069 }
1070
1071 fIsFlipped = shader.isFlippedGrad();
1072 }
1073
1074 GR_DECLARE_EFFECT_TEST;
1075
1076 const CircleConicalInfo fInfo;
1077 SkScalar fTLimit;
1078 bool fIsFlipped;
1079
1080 typedef GrGradientEffect INHERITED;
1081};
1082
1083class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect {
1084public:
1085 GLCircleOutside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
1086 virtual ~GLCircleOutside2PtConicalEffect() {}
1087
1088 virtual void emitCode(GrGLShaderBuilder*,
1089 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -07001090 const GrEffectKey&,
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001091 const char* outputColor,
1092 const char* inputColor,
1093 const TransformedCoordsArray&,
1094 const TextureSamplerArray&) SK_OVERRIDE;
1095 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
1096
bsalomon63e99f72014-07-21 08:03:14 -07001097 static void GenKey(const GrDrawEffect&, const GrGLCaps& caps, GrEffectKeyBuilder* b);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001098
1099protected:
1100 UniformHandle fCenterUni;
1101 UniformHandle fParamUni;
1102
1103 const char* fVSVaryingName;
1104 const char* fFSVaryingName;
1105
1106 bool fIsFlipped;
1107
1108 // @{
1109 /// Values last uploaded as uniforms
1110
1111 SkScalar fCachedCenterX;
1112 SkScalar fCachedCenterY;
1113 SkScalar fCachedA;
1114 SkScalar fCachedB;
1115 SkScalar fCachedC;
1116 SkScalar fCachedTLimit;
1117
1118 // @}
1119
1120private:
1121 typedef GrGLGradientEffect INHERITED;
1122
1123};
1124
1125const GrBackendEffectFactory& CircleOutside2PtConicalEffect::getFactory() const {
1126 return GrTBackendEffectFactory<CircleOutside2PtConicalEffect>::getInstance();
1127}
1128
1129GR_DEFINE_EFFECT_TEST(CircleOutside2PtConicalEffect);
1130
bsalomon83d081a2014-07-08 09:56:10 -07001131GrEffect* CircleOutside2PtConicalEffect::TestCreate(SkRandom* random,
1132 GrContext* context,
1133 const GrDrawTargetCaps&,
1134 GrTexture**) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001135 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +00001136 SkScalar radius1 = random->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001137 SkPoint center2;
1138 SkScalar radius2;
1139 SkScalar diffLen;
1140 do {
1141 center2.set(random->nextUScalar1(), random->nextUScalar1());
1142 // If the circles share a center than we can't be in the outside case
1143 } while (center1 == center2);
1144 SkPoint diff = center2 - center1;
1145 diffLen = diff.length();
1146 // Below makes sure that circle one is not contained within circle two
1147 // and have radius2 >= radius to match sorting on cpu side
commit-bot@chromium.org80894672014-04-22 21:24:22 +00001148 radius2 = radius1 + random->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001149
1150 SkColor colors[kMaxRandomGradientColors];
1151 SkScalar stopsArray[kMaxRandomGradientColors];
1152 SkScalar* stops = stopsArray;
1153 SkShader::TileMode tm;
1154 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
1155 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
1156 center2, radius2,
1157 colors, stops, colorCount,
1158 tm));
1159 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -07001160 GrColor paintColor;
1161 GrEffect* effect;
1162 SkAssertResult(shader->asNewEffect(context, paint, NULL, &paintColor, &effect));
1163 return effect;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001164}
1165
1166GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrBackendEffectFactory& factory,
1167 const GrDrawEffect& drawEffect)
1168 : INHERITED(factory)
1169 , fVSVaryingName(NULL)
1170 , fFSVaryingName(NULL)
1171 , fCachedCenterX(SK_ScalarMax)
1172 , fCachedCenterY(SK_ScalarMax)
1173 , fCachedA(SK_ScalarMax)
1174 , fCachedB(SK_ScalarMax)
1175 , fCachedC(SK_ScalarMax)
1176 , fCachedTLimit(SK_ScalarMax) {
1177 const CircleOutside2PtConicalEffect& data = drawEffect.castEffect<CircleOutside2PtConicalEffect>();
1178 fIsFlipped = data.isFlipped();
1179 }
1180
1181void GLCircleOutside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
1182 const GrDrawEffect&,
bsalomon63e99f72014-07-21 08:03:14 -07001183 const GrEffectKey& key,
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001184 const char* outputColor,
1185 const char* inputColor,
1186 const TransformedCoordsArray& coords,
1187 const TextureSamplerArray& samplers) {
bsalomon63e99f72014-07-21 08:03:14 -07001188 uint32_t baseKey = key.get32(0);
1189 this->emitUniforms(builder, baseKey);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001190 fCenterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
1191 kVec2f_GrSLType, "Conical2FSCenter");
1192 fParamUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
1193 kVec4f_GrSLType, "Conical2FSParams");
1194 SkString tName("t");
1195
1196 GrGLShaderVar center = builder->getUniformVariable(fCenterUni);
1197 // params.x = A
1198 // params.y = B
1199 // params.z = C
1200 GrGLShaderVar params = builder->getUniformVariable(fParamUni);
1201
1202 // if we have a vec3 from being in perspective, convert it to a vec2 first
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +00001203 SkString coords2DString = builder->ensureFSCoords2D(coords, 0);
1204 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001205
1206 // output will default to transparent black (we simply won't write anything
1207 // else to it if invalid, instead of discarding or returning prematurely)
1208 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
1209
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001210 // p = coords2D
1211 // e = center end
1212 // r = radius end
1213 // A = dot(e, e) - r^2 + 2 * r - 1
1214 // B = (r -1) / A
1215 // C = 1 / A
1216 // d = dot(e, p) + B
1217 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001218
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001219 builder->fsCodeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
1220 builder->fsCodeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(), params.c_str());
1221 builder->fsCodeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(), params.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001222
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001223 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
1224 // If so we must also flip sign on sqrt
1225 if (!fIsFlipped) {
1226 builder->fsCodeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001227 } else {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001228 builder->fsCodeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001229 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001230
1231 builder->fsCodeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", tName.c_str(), params.c_str());
1232 builder->fsCodeAppend("\t\t");
bsalomon63e99f72014-07-21 08:03:14 -07001233 this->emitColor(builder, tName.c_str(), baseKey, outputColor, inputColor, samplers);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001234 builder->fsCodeAppend("\t}\n");
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001235}
1236
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001237void GLCircleOutside2PtConicalEffect::setData(const GrGLUniformManager& uman,
1238 const GrDrawEffect& drawEffect) {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001239 INHERITED::setData(uman, drawEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001240 const CircleOutside2PtConicalEffect& data = drawEffect.castEffect<CircleOutside2PtConicalEffect>();
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +00001241 SkASSERT(data.isFlipped() == fIsFlipped);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001242 SkScalar centerX = data.centerX();
1243 SkScalar centerY = data.centerY();
1244 SkScalar A = data.A();
1245 SkScalar B = data.B();
1246 SkScalar C = data.C();
1247 SkScalar tLimit = data.tLimit();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001248
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001249 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1250 fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001251
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001252 uman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1253 uman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
1254 SkScalarToFloat(tLimit));
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001255
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001256 fCachedCenterX = centerX;
1257 fCachedCenterY = centerY;
1258 fCachedA = A;
1259 fCachedB = B;
1260 fCachedC = C;
1261 fCachedTLimit = tLimit;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001262 }
1263}
1264
bsalomon63e99f72014-07-21 08:03:14 -07001265void GLCircleOutside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
1266 const GrGLCaps&, GrEffectKeyBuilder* b) {
1267 uint32_t* key = b->add32n(2);
1268 key[0] = GenBaseGradientKey(drawEffect);
1269 key[1] = drawEffect.castEffect<CircleOutside2PtConicalEffect>().isFlipped();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001270}
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001271
1272//////////////////////////////////////////////////////////////////////////////
1273
bsalomon83d081a2014-07-08 09:56:10 -07001274GrEffect* Gr2PtConicalGradientEffect::Create(GrContext* ctx,
1275 const SkTwoPointConicalGradient& shader,
1276 SkShader::TileMode tm,
1277 const SkMatrix* localMatrix) {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001278 SkMatrix matrix;
1279 if (!shader.getLocalMatrix().invert(&matrix)) {
1280 return NULL;
1281 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001282 if (localMatrix) {
1283 SkMatrix inv;
1284 if (!localMatrix->invert(&inv)) {
1285 return NULL;
1286 }
1287 matrix.postConcat(inv);
1288 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001289
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001290 if (shader.getStartRadius() < kErrorTol) {
1291 SkScalar focalX;
1292 ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
1293 if (type == kInside_ConicalType) {
1294 return FocalInside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
1295 } else if(type == kEdge_ConicalType) {
1296 set_matrix_edge_conical(shader, &matrix);
1297 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
1298 } else {
1299 return FocalOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
1300 }
1301 }
1302
1303 CircleConicalInfo info;
1304 ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
1305
1306 if (type == kInside_ConicalType) {
1307 return CircleInside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
1308 } else if (type == kEdge_ConicalType) {
1309 set_matrix_edge_conical(shader, &matrix);
1310 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
1311 } else {
1312 return CircleOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
1313 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001314}
1315
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001316#endif