blob: 37c524db891a3baf4afab2cf93795f51cd020f7e [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"
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000015// For brevity
16typedef GrGLUniformManager::UniformHandle UniformHandle;
17
commit-bot@chromium.org80894672014-04-22 21:24:22 +000018static const SkScalar kErrorTol = 0.00001f;
egdaniel8405ef92014-06-09 11:57:28 -070019static const SkScalar kEdgeErrorTol = 5.f * kErrorTol;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000020
21/**
22 * We have three general cases for 2pt conical gradients. First we always assume that
23 * the start radius <= end radius. Our first case (kInside_) is when the start circle
24 * is completely enclosed by the end circle. The second case (kOutside_) is the case
25 * when the start circle is either completely outside the end circle or the circles
26 * overlap. The final case (kEdge_) is when the start circle is inside the end one,
27 * but the two are just barely touching at 1 point along their edges.
28 */
29enum ConicalType {
30 kInside_ConicalType,
31 kOutside_ConicalType,
32 kEdge_ConicalType,
33};
34
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000035//////////////////////////////////////////////////////////////////////////////
36
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000037static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader,
38 SkMatrix* invLMatrix) {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000039 // Inverse of the current local matrix is passed in then,
40 // translate to center1, rotate so center2 is on x axis.
41 const SkPoint& center1 = shader.getStartCenter();
42 const SkPoint& center2 = shader.getEndCenter();
43
44 invLMatrix->postTranslate(-center1.fX, -center1.fY);
45
46 SkPoint diff = center2 - center1;
47 SkScalar diffLen = diff.length();
48 if (0 != diffLen) {
49 SkScalar invDiffLen = SkScalarInvert(diffLen);
50 SkMatrix rot;
51 rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY),
52 SkScalarMul(invDiffLen, diff.fX));
53 invLMatrix->postConcat(rot);
54 }
55}
56
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000057class GLEdge2PtConicalEffect;
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000058
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000059class Edge2PtConicalEffect : public GrGradientEffect {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +000060public:
61
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000062 static GrEffectRef* Create(GrContext* ctx,
63 const SkTwoPointConicalGradient& shader,
64 const SkMatrix& matrix,
65 SkShader::TileMode tm) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +000066 AutoEffectUnref effect(SkNEW_ARGS(Edge2PtConicalEffect, (ctx, shader, matrix, tm)));
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +000067 return CreateEffectRef(effect);
68 }
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&,
142 EffectKey,
143 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
149 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
150
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
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000176GrEffectRef* 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;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000206 return shader->asNewEffect(context, paint, NULL);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000207}
208
209GLEdge2PtConicalEffect::GLEdge2PtConicalEffect(const GrBackendEffectFactory& factory,
210 const GrDrawEffect& drawEffect)
211 : INHERITED(factory)
212 , fVSVaryingName(NULL)
213 , fFSVaryingName(NULL)
214 , fCachedRadius(-SK_ScalarMax)
215 , fCachedDiffRadius(-SK_ScalarMax) {}
216
217void GLEdge2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
218 const GrDrawEffect&,
219 EffectKey key,
220 const char* outputColor,
221 const char* inputColor,
222 const TransformedCoordsArray& coords,
223 const TextureSamplerArray& samplers) {
224 this->emitUniforms(builder, key);
225 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
226 kFloat_GrSLType, "Conical2FSParams", 3);
227
228 SkString cName("c");
229 SkString tName("t");
230 SkString p0; // start radius
231 SkString p1; // start radius squared
232 SkString p2; // difference in radii (r1 - r0)
233
234 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
235 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
236 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
237
238 // We interpolate the linear component in coords[1].
239 SkASSERT(coords[0].type() == coords[1].type());
240 const char* coords2D;
241 SkString bVar;
242 if (kVec3f_GrSLType == coords[0].type()) {
243 builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n",
244 coords[0].c_str(), coords[0].c_str(), coords[1].c_str(), coords[1].c_str());
245 coords2D = "interpolants.xy";
246 bVar = "interpolants.z";
247 } else {
248 coords2D = coords[0].c_str();
249 bVar.printf("%s.x", coords[1].c_str());
250 }
251
252 // output will default to transparent black (we simply won't write anything
253 // else to it if invalid, instead of discarding or returning prematurely)
254 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
255
256 // c = (x^2)+(y^2) - params[1]
257 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
258 cName.c_str(), coords2D, coords2D, p1.c_str());
259
260 // linear case: t = -c/b
261 builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
262 cName.c_str(), bVar.c_str());
263
264 // if r(t) > 0, then t will be the x coordinate
265 builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
266 p2.c_str(), p0.c_str());
267 builder->fsCodeAppend("\t");
268 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
269 builder->fsCodeAppend("\t}\n");
270}
271
272void GLEdge2PtConicalEffect::setData(const GrGLUniformManager& uman,
273 const GrDrawEffect& drawEffect) {
274 INHERITED::setData(uman, drawEffect);
275 const Edge2PtConicalEffect& data = drawEffect.castEffect<Edge2PtConicalEffect>();
276 SkScalar radius0 = data.radius();
277 SkScalar diffRadius = data.diffRadius();
278
279 if (fCachedRadius != radius0 ||
280 fCachedDiffRadius != diffRadius) {
281
282 float values[3] = {
283 SkScalarToFloat(radius0),
284 SkScalarToFloat(SkScalarMul(radius0, radius0)),
285 SkScalarToFloat(diffRadius)
286 };
287
288 uman.set1fv(fParamUni, 3, values);
289 fCachedRadius = radius0;
290 fCachedDiffRadius = diffRadius;
291 }
292}
293
294GrGLEffect::EffectKey GLEdge2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
295 const GrGLCaps&) {
296 return GenBaseGradientKey(drawEffect);
297}
298
299//////////////////////////////////////////////////////////////////////////////
300// Focal Conical Gradients
301//////////////////////////////////////////////////////////////////////////////
302
303static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader,
304 SkMatrix* invLMatrix, SkScalar* focalX) {
305 // Inverse of the current local matrix is passed in then,
306 // translate, scale, and rotate such that endCircle is unit circle on x-axis,
307 // and focal point is at the origin.
308 ConicalType conicalType;
309 const SkPoint& focal = shader.getStartCenter();
310 const SkPoint& centerEnd = shader.getEndCenter();
311 SkScalar radius = shader.getEndRadius();
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000312 SkScalar invRadius = 1.f / radius;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000313
314 SkMatrix matrix;
315
316 matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
317 matrix.postScale(invRadius, invRadius);
318
319 SkPoint focalTrans;
320 matrix.mapPoints(&focalTrans, &focal, 1);
321 *focalX = focalTrans.length();
322
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000323 if (0.f != *focalX) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000324 SkScalar invFocalX = SkScalarInvert(*focalX);
325 SkMatrix rot;
326 rot.setSinCos(-SkScalarMul(invFocalX, focalTrans.fY),
327 SkScalarMul(invFocalX, focalTrans.fX));
328 matrix.postConcat(rot);
329 }
330
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000331 matrix.postTranslate(-(*focalX), 0.f);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000332
333 // If the focal point is touching the edge of the circle it will
334 // cause a degenerate case that must be handled separately
egdaniel8405ef92014-06-09 11:57:28 -0700335 // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
336 // stability trade off versus the linear approx used in the Edge Shader
337 if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000338 return kEdge_ConicalType;
339 }
340
341 // Scale factor 1 / (1 - focalX * focalX)
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000342 SkScalar oneMinusF2 = 1.f - SkScalarMul(*focalX, *focalX);
343 SkScalar s = SkScalarDiv(1.f, oneMinusF2);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000344
345
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000346 if (s >= 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000347 conicalType = kInside_ConicalType;
348 matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
349 } else {
350 conicalType = kOutside_ConicalType;
351 matrix.postScale(s, s);
352 }
353
354 invLMatrix->postConcat(matrix);
355
356 return conicalType;
357}
358
359//////////////////////////////////////////////////////////////////////////////
360
361class GLFocalOutside2PtConicalEffect;
362
363class FocalOutside2PtConicalEffect : public GrGradientEffect {
364public:
365
366 static GrEffectRef* Create(GrContext* ctx,
367 const SkTwoPointConicalGradient& shader,
368 const SkMatrix& matrix,
369 SkShader::TileMode tm,
370 SkScalar focalX) {
371 AutoEffectUnref effect(SkNEW_ARGS(FocalOutside2PtConicalEffect, (ctx, shader, matrix, tm, focalX)));
372 return CreateEffectRef(effect);
373 }
374
375 virtual ~FocalOutside2PtConicalEffect() { }
376
377 static const char* Name() { return "Two-Point Conical Gradient Focal Outside"; }
378 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
379
380 bool isFlipped() const { return fIsFlipped; }
381 SkScalar focal() const { return fFocalX; }
382
383 typedef GLFocalOutside2PtConicalEffect GLEffect;
384
385private:
386 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
387 const FocalOutside2PtConicalEffect& s = CastEffect<FocalOutside2PtConicalEffect>(sBase);
388 return (INHERITED::onIsEqual(sBase) &&
389 this->fFocalX == s.fFocalX &&
390 this->fIsFlipped == s.fIsFlipped);
391 }
392
393 FocalOutside2PtConicalEffect(GrContext* ctx,
394 const SkTwoPointConicalGradient& shader,
395 const SkMatrix& matrix,
396 SkShader::TileMode tm,
397 SkScalar focalX)
398 : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX), fIsFlipped(shader.isFlippedGrad()) {}
399
400 GR_DECLARE_EFFECT_TEST;
401
402 SkScalar fFocalX;
403 bool fIsFlipped;
404
405 typedef GrGradientEffect INHERITED;
406};
407
408class GLFocalOutside2PtConicalEffect : public GrGLGradientEffect {
409public:
410 GLFocalOutside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
411 virtual ~GLFocalOutside2PtConicalEffect() { }
412
413 virtual void emitCode(GrGLShaderBuilder*,
414 const GrDrawEffect&,
415 EffectKey,
416 const char* outputColor,
417 const char* inputColor,
418 const TransformedCoordsArray&,
419 const TextureSamplerArray&) SK_OVERRIDE;
420 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
421
422 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
423
424protected:
425 UniformHandle fParamUni;
426
427 const char* fVSVaryingName;
428 const char* fFSVaryingName;
429
430 bool fIsFlipped;
431
432 // @{
433 /// Values last uploaded as uniforms
434
435 SkScalar fCachedFocal;
436
437 // @}
438
439private:
440 typedef GrGLGradientEffect INHERITED;
441
442};
443
444const GrBackendEffectFactory& FocalOutside2PtConicalEffect::getFactory() const {
445 return GrTBackendEffectFactory<FocalOutside2PtConicalEffect>::getInstance();
446}
447
448GR_DEFINE_EFFECT_TEST(FocalOutside2PtConicalEffect);
449
450GrEffectRef* FocalOutside2PtConicalEffect::TestCreate(SkRandom* random,
451 GrContext* context,
452 const GrDrawTargetCaps&,
453 GrTexture**) {
454 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000455 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000456 SkPoint center2;
457 SkScalar radius2;
458 do {
459 center2.set(random->nextUScalar1(), random->nextUScalar1());
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000460 // 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 +0000461 } while (center1 == center2);
462 SkPoint diff = center2 - center1;
463 SkScalar diffLen = diff.length();
464 // Below makes sure that the focal point is not contained within circle two
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000465 radius2 = random->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000466
467 SkColor colors[kMaxRandomGradientColors];
468 SkScalar stopsArray[kMaxRandomGradientColors];
469 SkScalar* stops = stopsArray;
470 SkShader::TileMode tm;
471 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
472 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
473 center2, radius2,
474 colors, stops, colorCount,
475 tm));
476 SkPaint paint;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000477 return shader->asNewEffect(context, paint, NULL);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000478}
479
480GLFocalOutside2PtConicalEffect::GLFocalOutside2PtConicalEffect(const GrBackendEffectFactory& factory,
481 const GrDrawEffect& drawEffect)
482 : INHERITED(factory)
483 , fVSVaryingName(NULL)
484 , fFSVaryingName(NULL)
485 , fCachedFocal(SK_ScalarMax) {
486 const FocalOutside2PtConicalEffect& data = drawEffect.castEffect<FocalOutside2PtConicalEffect>();
487 fIsFlipped = data.isFlipped();
488}
489
490void GLFocalOutside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
491 const GrDrawEffect&,
492 EffectKey key,
493 const char* outputColor,
494 const char* inputColor,
495 const TransformedCoordsArray& coords,
496 const TextureSamplerArray& samplers) {
497 this->emitUniforms(builder, key);
498 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
499 kFloat_GrSLType, "Conical2FSParams", 2);
500 SkString tName("t");
501 SkString p0; // focalX
502 SkString p1; // 1 - focalX * focalX
503
504 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
505 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
506
507 // if we have a vec3 from being in perspective, convert it to a vec2 first
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000508 SkString coords2DString = builder->ensureFSCoords2D(coords, 0);
509 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000510
511 // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
512
513 // output will default to transparent black (we simply won't write anything
514 // else to it if invalid, instead of discarding or returning prematurely)
515 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
516
517 builder->fsCodeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D);
518 builder->fsCodeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D);
519 builder->fsCodeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str());
520
521 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
522 // If so we must also flip sign on sqrt
523 if (!fIsFlipped) {
524 builder->fsCodeAppendf("\tfloat %s = %s.x * %s + sqrt(d);\n", tName.c_str(),
525 coords2D, p0.c_str());
526 } else {
527 builder->fsCodeAppendf("\tfloat %s = %s.x * %s - sqrt(d);\n", tName.c_str(),
528 coords2D, p0.c_str());
529 }
530
531 builder->fsCodeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str());
532 builder->fsCodeAppend("\t\t");
533 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
534 builder->fsCodeAppend("\t}\n");
535}
536
537void GLFocalOutside2PtConicalEffect::setData(const GrGLUniformManager& uman,
538 const GrDrawEffect& drawEffect) {
539 INHERITED::setData(uman, drawEffect);
540 const FocalOutside2PtConicalEffect& data = drawEffect.castEffect<FocalOutside2PtConicalEffect>();
541 SkASSERT(data.isFlipped() == fIsFlipped);
542 SkScalar focal = data.focal();
543
544 if (fCachedFocal != focal) {
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000545 SkScalar oneMinus2F = 1.f - SkScalarMul(focal, focal);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000546
547 float values[2] = {
548 SkScalarToFloat(focal),
549 SkScalarToFloat(oneMinus2F),
550 };
551
552 uman.set1fv(fParamUni, 2, values);
553 fCachedFocal = focal;
554 }
555}
556
557GrGLEffect::EffectKey GLFocalOutside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
558 const GrGLCaps&) {
559 enum {
560 kIsFlipped = 1 << kBaseKeyBitCnt,
561 };
562
563 EffectKey key = GenBaseGradientKey(drawEffect);
564
565 if (drawEffect.castEffect<FocalOutside2PtConicalEffect>().isFlipped()) {
566 key |= kIsFlipped;
567 }
568 return key;
569}
570
571//////////////////////////////////////////////////////////////////////////////
572
573class GLFocalInside2PtConicalEffect;
574
575class FocalInside2PtConicalEffect : public GrGradientEffect {
576public:
577
578 static GrEffectRef* Create(GrContext* ctx,
579 const SkTwoPointConicalGradient& shader,
580 const SkMatrix& matrix,
581 SkShader::TileMode tm,
582 SkScalar focalX) {
583 AutoEffectUnref effect(SkNEW_ARGS(FocalInside2PtConicalEffect, (ctx, shader, matrix, tm, focalX)));
584 return CreateEffectRef(effect);
585 }
586
587 virtual ~FocalInside2PtConicalEffect() {}
588
589 static const char* Name() { return "Two-Point Conical Gradient Focal Inside"; }
590 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
591
592 SkScalar focal() const { return fFocalX; }
593
594 typedef GLFocalInside2PtConicalEffect GLEffect;
595
596private:
597 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
598 const FocalInside2PtConicalEffect& s = CastEffect<FocalInside2PtConicalEffect>(sBase);
599 return (INHERITED::onIsEqual(sBase) &&
600 this->fFocalX == s.fFocalX);
601 }
602
603 FocalInside2PtConicalEffect(GrContext* ctx,
604 const SkTwoPointConicalGradient& shader,
605 const SkMatrix& matrix,
606 SkShader::TileMode tm,
607 SkScalar focalX)
608 : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX) {}
609
610 GR_DECLARE_EFFECT_TEST;
611
612 SkScalar fFocalX;
613
614 typedef GrGradientEffect INHERITED;
615};
616
617class GLFocalInside2PtConicalEffect : public GrGLGradientEffect {
618public:
619 GLFocalInside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
620 virtual ~GLFocalInside2PtConicalEffect() {}
621
622 virtual void emitCode(GrGLShaderBuilder*,
623 const GrDrawEffect&,
624 EffectKey,
625 const char* outputColor,
626 const char* inputColor,
627 const TransformedCoordsArray&,
628 const TextureSamplerArray&) SK_OVERRIDE;
629 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
630
631 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
632
633protected:
634 UniformHandle fFocalUni;
635
636 const char* fVSVaryingName;
637 const char* fFSVaryingName;
638
639 // @{
640 /// Values last uploaded as uniforms
641
642 SkScalar fCachedFocal;
643
644 // @}
645
646private:
647 typedef GrGLGradientEffect INHERITED;
648
649};
650
651const GrBackendEffectFactory& FocalInside2PtConicalEffect::getFactory() const {
652 return GrTBackendEffectFactory<FocalInside2PtConicalEffect>::getInstance();
653}
654
655GR_DEFINE_EFFECT_TEST(FocalInside2PtConicalEffect);
656
657GrEffectRef* FocalInside2PtConicalEffect::TestCreate(SkRandom* random,
658 GrContext* context,
659 const GrDrawTargetCaps&,
660 GrTexture**) {
661 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000662 SkScalar radius1 = 0.f;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000663 SkPoint center2;
664 SkScalar radius2;
665 do {
666 center2.set(random->nextUScalar1(), random->nextUScalar1());
667 // Below makes sure radius2 is larger enouch such that the focal point
668 // is inside the end circle
669 SkScalar increase = random->nextUScalar1();
670 SkPoint diff = center2 - center1;
671 SkScalar diffLen = diff.length();
672 radius2 = diffLen + increase;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000673 // If the circles are identical the factory will give us an empty shader.
674 } while (radius1 == radius2 && center1 == center2);
675
676 SkColor colors[kMaxRandomGradientColors];
677 SkScalar stopsArray[kMaxRandomGradientColors];
678 SkScalar* stops = stopsArray;
679 SkShader::TileMode tm;
680 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
681 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
682 center2, radius2,
683 colors, stops, colorCount,
684 tm));
685 SkPaint paint;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000686 return shader->asNewEffect(context, paint, NULL);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000687}
688
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000689GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrBackendEffectFactory& factory,
690 const GrDrawEffect& drawEffect)
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000691 : INHERITED(factory)
692 , fVSVaryingName(NULL)
693 , fFSVaryingName(NULL)
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000694 , fCachedFocal(SK_ScalarMax) {}
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000695
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000696void GLFocalInside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
697 const GrDrawEffect&,
698 EffectKey key,
699 const char* outputColor,
700 const char* inputColor,
701 const TransformedCoordsArray& coords,
702 const TextureSamplerArray& samplers) {
703 this->emitUniforms(builder, key);
704 fFocalUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
705 kFloat_GrSLType, "Conical2FSParams");
706 SkString tName("t");
707
708 // this is the distance along x-axis from the end center to focal point in
709 // transformed coordinates
710 GrGLShaderVar focal = builder->getUniformVariable(fFocalUni);
711
712 // if we have a vec3 from being in perspective, convert it to a vec2 first
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000713 SkString coords2DString = builder->ensureFSCoords2D(coords, 0);
714 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000715
716 // t = p.x * focalX + length(p)
717 builder->fsCodeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(),
718 coords2D, focal.c_str(), coords2D);
719
720 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000721}
722
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000723void GLFocalInside2PtConicalEffect::setData(const GrGLUniformManager& uman,
724 const GrDrawEffect& drawEffect) {
725 INHERITED::setData(uman, drawEffect);
726 const FocalInside2PtConicalEffect& data = drawEffect.castEffect<FocalInside2PtConicalEffect>();
727 SkScalar focal = data.focal();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000728
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000729 if (fCachedFocal != focal) {
730 uman.set1f(fFocalUni, SkScalarToFloat(focal));
731 fCachedFocal = focal;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +0000732 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000733}
734
735GrGLEffect::EffectKey GLFocalInside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
736 const GrGLCaps&) {
737 return GenBaseGradientKey(drawEffect);
738}
739
740//////////////////////////////////////////////////////////////////////////////
741// Circle Conical Gradients
742//////////////////////////////////////////////////////////////////////////////
743
744struct CircleConicalInfo {
745 SkPoint fCenterEnd;
746 SkScalar fA;
747 SkScalar fB;
748 SkScalar fC;
749};
750
751// Returns focal distance along x-axis in transformed coords
752static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
753 SkMatrix* invLMatrix, CircleConicalInfo* info) {
754 // Inverse of the current local matrix is passed in then,
755 // translate and scale such that start circle is on the origin and has radius 1
756 const SkPoint& centerStart = shader.getStartCenter();
757 const SkPoint& centerEnd = shader.getEndCenter();
758 SkScalar radiusStart = shader.getStartRadius();
759 SkScalar radiusEnd = shader.getEndRadius();
760
761 SkMatrix matrix;
762
763 matrix.setTranslate(-centerStart.fX, -centerStart.fY);
764
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000765 SkScalar invStartRad = 1.f / radiusStart;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000766 matrix.postScale(invStartRad, invStartRad);
767
768 radiusEnd /= radiusStart;
769
770 SkPoint centerEndTrans;
771 matrix.mapPoints(&centerEndTrans, &centerEnd, 1);
772
773 SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
774 - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
775
776 // Check to see if start circle is inside end circle with edges touching.
777 // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
egdaniel8405ef92014-06-09 11:57:28 -0700778 // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
779 // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
780 // still accurate.
781 if (SkScalarAbs(A) < kEdgeErrorTol) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000782 return kEdge_ConicalType;
783 }
784
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000785 SkScalar C = 1.f / A;
786 SkScalar B = (radiusEnd - 1.f) * C;
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000787
788 matrix.postScale(C, C);
789
790 invLMatrix->postConcat(matrix);
791
792 info->fCenterEnd = centerEndTrans;
793 info->fA = A;
794 info->fB = B;
795 info->fC = C;
796
797 // 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 +0000798 if (A < 0.f) {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000799 return kInside_ConicalType;
800 }
801 return kOutside_ConicalType;
802}
803
804class GLCircleInside2PtConicalEffect;
805
806class CircleInside2PtConicalEffect : public GrGradientEffect {
807public:
808
809 static GrEffectRef* Create(GrContext* ctx,
810 const SkTwoPointConicalGradient& shader,
811 const SkMatrix& matrix,
812 SkShader::TileMode tm,
813 const CircleConicalInfo& info) {
814 AutoEffectUnref effect(SkNEW_ARGS(CircleInside2PtConicalEffect, (ctx, shader, matrix, tm, info)));
815 return CreateEffectRef(effect);
816 }
817
818 virtual ~CircleInside2PtConicalEffect() {}
819
820 static const char* Name() { return "Two-Point Conical Gradient Inside"; }
821 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
822
823 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
824 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
825 SkScalar A() const { return fInfo.fA; }
826 SkScalar B() const { return fInfo.fB; }
827 SkScalar C() const { return fInfo.fC; }
828
829 typedef GLCircleInside2PtConicalEffect GLEffect;
830
831private:
832 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
833 const CircleInside2PtConicalEffect& s = CastEffect<CircleInside2PtConicalEffect>(sBase);
834 return (INHERITED::onIsEqual(sBase) &&
835 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
836 this->fInfo.fA == s.fInfo.fA &&
837 this->fInfo.fB == s.fInfo.fB &&
838 this->fInfo.fC == s.fInfo.fC);
839 }
840
841 CircleInside2PtConicalEffect(GrContext* ctx,
842 const SkTwoPointConicalGradient& shader,
843 const SkMatrix& matrix,
844 SkShader::TileMode tm,
845 const CircleConicalInfo& info)
846 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {}
847
848 GR_DECLARE_EFFECT_TEST;
849
850 const CircleConicalInfo fInfo;
851
852 typedef GrGradientEffect INHERITED;
853};
854
855class GLCircleInside2PtConicalEffect : public GrGLGradientEffect {
856public:
857 GLCircleInside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
858 virtual ~GLCircleInside2PtConicalEffect() {}
859
860 virtual void emitCode(GrGLShaderBuilder*,
861 const GrDrawEffect&,
862 EffectKey,
863 const char* outputColor,
864 const char* inputColor,
865 const TransformedCoordsArray&,
866 const TextureSamplerArray&) SK_OVERRIDE;
867 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
868
869 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
870
871protected:
872 UniformHandle fCenterUni;
873 UniformHandle fParamUni;
874
875 const char* fVSVaryingName;
876 const char* fFSVaryingName;
877
878 // @{
879 /// Values last uploaded as uniforms
880
881 SkScalar fCachedCenterX;
882 SkScalar fCachedCenterY;
883 SkScalar fCachedA;
884 SkScalar fCachedB;
885 SkScalar fCachedC;
886
887 // @}
888
889private:
890 typedef GrGLGradientEffect INHERITED;
891
892};
893
894const GrBackendEffectFactory& CircleInside2PtConicalEffect::getFactory() const {
895 return GrTBackendEffectFactory<CircleInside2PtConicalEffect>::getInstance();
896}
897
898GR_DEFINE_EFFECT_TEST(CircleInside2PtConicalEffect);
899
900GrEffectRef* CircleInside2PtConicalEffect::TestCreate(SkRandom* random,
901 GrContext* context,
902 const GrDrawTargetCaps&,
903 GrTexture**) {
904 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +0000905 SkScalar radius1 = random->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000906 SkPoint center2;
907 SkScalar radius2;
908 do {
909 center2.set(random->nextUScalar1(), random->nextUScalar1());
910 // Below makes sure that circle one is contained within circle two
911 SkScalar increase = random->nextUScalar1();
912 SkPoint diff = center2 - center1;
913 SkScalar diffLen = diff.length();
914 radius2 = radius1 + diffLen + increase;
915 // If the circles are identical the factory will give us an empty shader.
916 } while (radius1 == radius2 && center1 == center2);
917
918 SkColor colors[kMaxRandomGradientColors];
919 SkScalar stopsArray[kMaxRandomGradientColors];
920 SkScalar* stops = stopsArray;
921 SkShader::TileMode tm;
922 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
923 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
924 center2, radius2,
925 colors, stops, colorCount,
926 tm));
927 SkPaint paint;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000928 return shader->asNewEffect(context, paint, NULL);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000929}
930
931GLCircleInside2PtConicalEffect::GLCircleInside2PtConicalEffect(const GrBackendEffectFactory& factory,
932 const GrDrawEffect& drawEffect)
933 : INHERITED(factory)
934 , fVSVaryingName(NULL)
935 , fFSVaryingName(NULL)
936 , fCachedCenterX(SK_ScalarMax)
937 , fCachedCenterY(SK_ScalarMax)
938 , fCachedA(SK_ScalarMax)
939 , fCachedB(SK_ScalarMax)
940 , fCachedC(SK_ScalarMax) {}
941
942void GLCircleInside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
943 const GrDrawEffect&,
944 EffectKey key,
945 const char* outputColor,
946 const char* inputColor,
947 const TransformedCoordsArray& coords,
948 const TextureSamplerArray& samplers) {
949 this->emitUniforms(builder, key);
950 fCenterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
951 kVec2f_GrSLType, "Conical2FSCenter");
952 fParamUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
953 kVec3f_GrSLType, "Conical2FSParams");
954 SkString tName("t");
955
956 GrGLShaderVar center = builder->getUniformVariable(fCenterUni);
957 // params.x = A
958 // params.y = B
959 // params.z = C
960 GrGLShaderVar params = builder->getUniformVariable(fParamUni);
961
962 // if we have a vec3 from being in perspective, convert it to a vec2 first
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +0000963 SkString coords2DString = builder->ensureFSCoords2D(coords, 0);
964 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +0000965
966 // p = coords2D
967 // e = center end
968 // r = radius end
969 // A = dot(e, e) - r^2 + 2 * r - 1
970 // B = (r -1) / A
971 // C = 1 / A
972 // d = dot(e, p) + B
973 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
974 builder->fsCodeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
975 builder->fsCodeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(), params.c_str());
976 builder->fsCodeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n",
977 tName.c_str(), params.c_str(), params.c_str());
978
979 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
980}
981
982void GLCircleInside2PtConicalEffect::setData(const GrGLUniformManager& uman,
983 const GrDrawEffect& drawEffect) {
984 INHERITED::setData(uman, drawEffect);
985 const CircleInside2PtConicalEffect& data = drawEffect.castEffect<CircleInside2PtConicalEffect>();
986 SkScalar centerX = data.centerX();
987 SkScalar centerY = data.centerY();
988 SkScalar A = data.A();
989 SkScalar B = data.B();
990 SkScalar C = data.C();
991
992 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
993 fCachedA != A || fCachedB != B || fCachedC != C) {
994
995 uman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
996 uman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C));
997
998 fCachedCenterX = centerX;
999 fCachedCenterY = centerY;
1000 fCachedA = A;
1001 fCachedB = B;
1002 fCachedC = C;
1003 }
1004}
1005
1006GrGLEffect::EffectKey GLCircleInside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
1007 const GrGLCaps&) {
1008 EffectKey key = GenBaseGradientKey(drawEffect);
1009 return key;
1010}
1011
1012//////////////////////////////////////////////////////////////////////////////
1013
1014class GLCircleOutside2PtConicalEffect;
1015
1016class CircleOutside2PtConicalEffect : public GrGradientEffect {
1017public:
1018
1019 static GrEffectRef* Create(GrContext* ctx,
1020 const SkTwoPointConicalGradient& shader,
1021 const SkMatrix& matrix,
1022 SkShader::TileMode tm,
1023 const CircleConicalInfo& info) {
1024 AutoEffectUnref effect(SkNEW_ARGS(CircleOutside2PtConicalEffect, (ctx, shader, matrix, tm, info)));
1025 return CreateEffectRef(effect);
1026 }
1027
1028 virtual ~CircleOutside2PtConicalEffect() {}
1029
1030 static const char* Name() { return "Two-Point Conical Gradient Outside"; }
1031 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
1032
1033 SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
1034 SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
1035 SkScalar A() const { return fInfo.fA; }
1036 SkScalar B() const { return fInfo.fB; }
1037 SkScalar C() const { return fInfo.fC; }
1038 SkScalar tLimit() const { return fTLimit; }
1039 bool isFlipped() const { return fIsFlipped; }
1040
1041 typedef GLCircleOutside2PtConicalEffect GLEffect;
1042
1043private:
1044 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
1045 const CircleOutside2PtConicalEffect& s = CastEffect<CircleOutside2PtConicalEffect>(sBase);
1046 return (INHERITED::onIsEqual(sBase) &&
1047 this->fInfo.fCenterEnd == s.fInfo.fCenterEnd &&
1048 this->fInfo.fA == s.fInfo.fA &&
1049 this->fInfo.fB == s.fInfo.fB &&
1050 this->fInfo.fC == s.fInfo.fC &&
1051 this->fTLimit == s.fTLimit &&
1052 this->fIsFlipped == s.fIsFlipped);
1053 }
1054
1055 CircleOutside2PtConicalEffect(GrContext* ctx,
1056 const SkTwoPointConicalGradient& shader,
1057 const SkMatrix& matrix,
1058 SkShader::TileMode tm,
1059 const CircleConicalInfo& info)
1060 : INHERITED(ctx, shader, matrix, tm), fInfo(info) {
1061 if (shader.getStartRadius() != shader.getEndRadius()) {
1062 fTLimit = SkScalarDiv(shader.getStartRadius(), (shader.getStartRadius() - shader.getEndRadius()));
1063 } else {
1064 fTLimit = SK_ScalarMin;
1065 }
1066
1067 fIsFlipped = shader.isFlippedGrad();
1068 }
1069
1070 GR_DECLARE_EFFECT_TEST;
1071
1072 const CircleConicalInfo fInfo;
1073 SkScalar fTLimit;
1074 bool fIsFlipped;
1075
1076 typedef GrGradientEffect INHERITED;
1077};
1078
1079class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect {
1080public:
1081 GLCircleOutside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
1082 virtual ~GLCircleOutside2PtConicalEffect() {}
1083
1084 virtual void emitCode(GrGLShaderBuilder*,
1085 const GrDrawEffect&,
1086 EffectKey,
1087 const char* outputColor,
1088 const char* inputColor,
1089 const TransformedCoordsArray&,
1090 const TextureSamplerArray&) SK_OVERRIDE;
1091 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
1092
1093 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
1094
1095protected:
1096 UniformHandle fCenterUni;
1097 UniformHandle fParamUni;
1098
1099 const char* fVSVaryingName;
1100 const char* fFSVaryingName;
1101
1102 bool fIsFlipped;
1103
1104 // @{
1105 /// Values last uploaded as uniforms
1106
1107 SkScalar fCachedCenterX;
1108 SkScalar fCachedCenterY;
1109 SkScalar fCachedA;
1110 SkScalar fCachedB;
1111 SkScalar fCachedC;
1112 SkScalar fCachedTLimit;
1113
1114 // @}
1115
1116private:
1117 typedef GrGLGradientEffect INHERITED;
1118
1119};
1120
1121const GrBackendEffectFactory& CircleOutside2PtConicalEffect::getFactory() const {
1122 return GrTBackendEffectFactory<CircleOutside2PtConicalEffect>::getInstance();
1123}
1124
1125GR_DEFINE_EFFECT_TEST(CircleOutside2PtConicalEffect);
1126
1127GrEffectRef* CircleOutside2PtConicalEffect::TestCreate(SkRandom* random,
1128 GrContext* context,
1129 const GrDrawTargetCaps&,
1130 GrTexture**) {
1131 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
commit-bot@chromium.org80894672014-04-22 21:24:22 +00001132 SkScalar radius1 = random->nextUScalar1() + 0.0001f; // make sure radius1 != 0
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001133 SkPoint center2;
1134 SkScalar radius2;
1135 SkScalar diffLen;
1136 do {
1137 center2.set(random->nextUScalar1(), random->nextUScalar1());
1138 // If the circles share a center than we can't be in the outside case
1139 } while (center1 == center2);
1140 SkPoint diff = center2 - center1;
1141 diffLen = diff.length();
1142 // Below makes sure that circle one is not contained within circle two
1143 // and have radius2 >= radius to match sorting on cpu side
commit-bot@chromium.org80894672014-04-22 21:24:22 +00001144 radius2 = radius1 + random->nextRangeF(0.f, diffLen);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001145
1146 SkColor colors[kMaxRandomGradientColors];
1147 SkScalar stopsArray[kMaxRandomGradientColors];
1148 SkScalar* stops = stopsArray;
1149 SkShader::TileMode tm;
1150 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
1151 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
1152 center2, radius2,
1153 colors, stops, colorCount,
1154 tm));
1155 SkPaint paint;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001156 return shader->asNewEffect(context, paint, NULL);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001157}
1158
1159GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrBackendEffectFactory& factory,
1160 const GrDrawEffect& drawEffect)
1161 : INHERITED(factory)
1162 , fVSVaryingName(NULL)
1163 , fFSVaryingName(NULL)
1164 , fCachedCenterX(SK_ScalarMax)
1165 , fCachedCenterY(SK_ScalarMax)
1166 , fCachedA(SK_ScalarMax)
1167 , fCachedB(SK_ScalarMax)
1168 , fCachedC(SK_ScalarMax)
1169 , fCachedTLimit(SK_ScalarMax) {
1170 const CircleOutside2PtConicalEffect& data = drawEffect.castEffect<CircleOutside2PtConicalEffect>();
1171 fIsFlipped = data.isFlipped();
1172 }
1173
1174void GLCircleOutside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder,
1175 const GrDrawEffect&,
1176 EffectKey key,
1177 const char* outputColor,
1178 const char* inputColor,
1179 const TransformedCoordsArray& coords,
1180 const TextureSamplerArray& samplers) {
1181 this->emitUniforms(builder, key);
1182 fCenterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
1183 kVec2f_GrSLType, "Conical2FSCenter");
1184 fParamUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
1185 kVec4f_GrSLType, "Conical2FSParams");
1186 SkString tName("t");
1187
1188 GrGLShaderVar center = builder->getUniformVariable(fCenterUni);
1189 // params.x = A
1190 // params.y = B
1191 // params.z = C
1192 GrGLShaderVar params = builder->getUniformVariable(fParamUni);
1193
1194 // if we have a vec3 from being in perspective, convert it to a vec2 first
skia.committer@gmail.comede0c5c2014-04-23 03:04:11 +00001195 SkString coords2DString = builder->ensureFSCoords2D(coords, 0);
1196 const char* coords2D = coords2DString.c_str();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001197
1198 // output will default to transparent black (we simply won't write anything
1199 // else to it if invalid, instead of discarding or returning prematurely)
1200 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
1201
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001202 // p = coords2D
1203 // e = center end
1204 // r = radius end
1205 // A = dot(e, e) - r^2 + 2 * r - 1
1206 // B = (r -1) / A
1207 // C = 1 / A
1208 // d = dot(e, p) + B
1209 // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001210
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001211 builder->fsCodeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D);
1212 builder->fsCodeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(), params.c_str());
1213 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 +00001214
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001215 // Must check to see if we flipped the circle order (to make sure start radius < end radius)
1216 // If so we must also flip sign on sqrt
1217 if (!fIsFlipped) {
1218 builder->fsCodeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001219 } else {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001220 builder->fsCodeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str());
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001221 }
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001222
1223 builder->fsCodeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", tName.c_str(), params.c_str());
1224 builder->fsCodeAppend("\t\t");
1225 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
1226 builder->fsCodeAppend("\t}\n");
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001227}
1228
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001229void GLCircleOutside2PtConicalEffect::setData(const GrGLUniformManager& uman,
1230 const GrDrawEffect& drawEffect) {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001231 INHERITED::setData(uman, drawEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001232 const CircleOutside2PtConicalEffect& data = drawEffect.castEffect<CircleOutside2PtConicalEffect>();
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +00001233 SkASSERT(data.isFlipped() == fIsFlipped);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001234 SkScalar centerX = data.centerX();
1235 SkScalar centerY = data.centerY();
1236 SkScalar A = data.A();
1237 SkScalar B = data.B();
1238 SkScalar C = data.C();
1239 SkScalar tLimit = data.tLimit();
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001240
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001241 if (fCachedCenterX != centerX || fCachedCenterY != centerY ||
1242 fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001243
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001244 uman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY));
1245 uman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C),
1246 SkScalarToFloat(tLimit));
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001247
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001248 fCachedCenterX = centerX;
1249 fCachedCenterY = centerY;
1250 fCachedA = A;
1251 fCachedB = B;
1252 fCachedC = C;
1253 fCachedTLimit = tLimit;
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001254 }
1255}
1256
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001257GrGLEffect::EffectKey GLCircleOutside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect,
1258 const GrGLCaps&) {
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001259 enum {
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001260 kIsFlipped = 1 << kBaseKeyBitCnt,
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001261 };
1262
1263 EffectKey key = GenBaseGradientKey(drawEffect);
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001264
1265 if (drawEffect.castEffect<CircleOutside2PtConicalEffect>().isFlipped()) {
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +00001266 key |= kIsFlipped;
1267 }
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001268 return key;
1269}
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001270
1271//////////////////////////////////////////////////////////////////////////////
1272
1273GrEffectRef* Gr2PtConicalGradientEffect::Create(GrContext* ctx,
1274 const SkTwoPointConicalGradient& shader,
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001275 SkShader::TileMode tm,
1276 const SkMatrix* localMatrix) {
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001277 SkMatrix matrix;
1278 if (!shader.getLocalMatrix().invert(&matrix)) {
1279 return NULL;
1280 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +00001281 if (localMatrix) {
1282 SkMatrix inv;
1283 if (!localMatrix->invert(&inv)) {
1284 return NULL;
1285 }
1286 matrix.postConcat(inv);
1287 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001288
commit-bot@chromium.orgc8379d72014-04-22 20:56:37 +00001289 if (shader.getStartRadius() < kErrorTol) {
1290 SkScalar focalX;
1291 ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
1292 if (type == kInside_ConicalType) {
1293 return FocalInside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
1294 } else if(type == kEdge_ConicalType) {
1295 set_matrix_edge_conical(shader, &matrix);
1296 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
1297 } else {
1298 return FocalOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX);
1299 }
1300 }
1301
1302 CircleConicalInfo info;
1303 ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
1304
1305 if (type == kInside_ConicalType) {
1306 return CircleInside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
1307 } else if (type == kEdge_ConicalType) {
1308 set_matrix_edge_conical(shader, &matrix);
1309 return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm);
1310 } else {
1311 return CircleOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, info);
1312 }
commit-bot@chromium.org2af1a2d2014-04-04 13:50:50 +00001313}
1314
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00001315#endif