blob: 375b78378de603fc9b15d14c6842ebb5ba729a32 [file] [log] [blame]
tomhudson@google.com7fab52d2012-05-31 19:40:13 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrGradientEffects.h"
9#include "gl/GrGLProgramStage.h"
10#include "GrProgramStageFactory.h"
rileya@google.com03c1c352012-07-20 20:02:43 +000011#include "SkGr.h"
rileya@google.com91f319c2012-07-25 17:18:31 +000012#include "../core/SkShader.h"
tomhudson@google.com7fab52d2012-05-31 19:40:13 +000013
rileya@google.com22e57f92012-07-19 15:16:19 +000014// Base class for GL gradient custom stages
15class GrGLGradientStage : public GrGLProgramStage {
16public:
17
18 GrGLGradientStage(const GrProgramStageFactory& factory);
19 virtual ~GrGLGradientStage();
20
21 // emit code that gets a fragment's color from an expression for t; for now
22 // this always uses the texture, but for simpler cases we'll be able to lerp
23 void emitColorLookup(GrGLShaderBuilder* builder, const char* t,
24 const char* outputColor, const char* samplerName);
25
26private:
27
28 typedef GrGLProgramStage INHERITED;
29};
30
31GrGLGradientStage::GrGLGradientStage(const GrProgramStageFactory& factory)
32 : INHERITED(factory) { }
33
34GrGLGradientStage::~GrGLGradientStage() { }
35
36void GrGLGradientStage::emitColorLookup(GrGLShaderBuilder* builder,
37 const char* tName,
38 const char* outputColor,
39 const char* samplerName) {
40 // Texture is effectively 1D so the y coordinate is 0.5, if we pack multiple
41 // gradients into a texture, we could instead pick the appropriate row here
42 builder->fSampleCoords.printf("vec2(%s, 0.5)", tName);
43 builder->fComplexCoord = true;
44 builder->emitDefaultFetch(outputColor, samplerName);
45}
46
tomhudson@google.com7fab52d2012-05-31 19:40:13 +000047/////////////////////////////////////////////////////////////////////
48
rileya@google.com22e57f92012-07-19 15:16:19 +000049GrGradientEffect::GrGradientEffect(GrTexture* texture)
50 : fTexture (texture)
51 , fUseTexture(true) {
52 SkSafeRef(fTexture);
53}
54
rileya@google.com91f319c2012-07-25 17:18:31 +000055GrGradientEffect::GrGradientEffect(GrContext* ctx,
56 const SkShader& shader,
57 GrSamplerState* sampler)
rileya@google.com03c1c352012-07-20 20:02:43 +000058 : fTexture (NULL)
59 , fUseTexture (false) {
60 // TODO: check for simple cases where we don't need a texture:
61 //GradientInfo info;
62 //shader.asAGradient(&info);
63 //if (info.fColorCount == 2) { ...
64
65 SkBitmap bitmap;
rileya@google.com91f319c2012-07-25 17:18:31 +000066 shader.asABitmap(&bitmap, NULL, NULL);
rileya@google.com03c1c352012-07-20 20:02:43 +000067
68 GrContext::TextureCacheEntry entry = GrLockCachedBitmapTexture(ctx, bitmap,
rileya@google.com91f319c2012-07-25 17:18:31 +000069 sampler);
rileya@google.com03c1c352012-07-20 20:02:43 +000070 fTexture = entry.texture();
71 SkSafeRef(fTexture);
72 fUseTexture = true;
73
74 // Unlock immediately, this is not great, but we don't have a way of
75 // knowing when else to unlock it currently, so it may get purged from
76 // the cache, but it'll still be ref'd until it's no longer being used.
77 GrUnlockCachedBitmapTexture(ctx, entry);
78}
79
rileya@google.com22e57f92012-07-19 15:16:19 +000080GrGradientEffect::~GrGradientEffect() {
rileya@google.com03c1c352012-07-20 20:02:43 +000081 SkSafeUnref(fTexture);
rileya@google.com22e57f92012-07-19 15:16:19 +000082}
83
84unsigned int GrGradientEffect::numTextures() const {
85 return fUseTexture ? 1 : 0;
86}
87
88GrTexture* GrGradientEffect::texture(unsigned int index)
89 const {
90 GrAssert(fUseTexture && 0 == index);
91 return fTexture;
92}
93
94/////////////////////////////////////////////////////////////////////
95
96class GrGLLinearGradient : public GrGLGradientStage {
97public:
98
99 GrGLLinearGradient(const GrProgramStageFactory& factory,
100 const GrCustomStage&)
101 : INHERITED (factory) { }
102
103 virtual ~GrGLLinearGradient() { }
104
105 virtual void emitVS(GrGLShaderBuilder* builder,
106 const char* vertexCoords) SK_OVERRIDE { }
107 virtual void emitFS(GrGLShaderBuilder* builder,
108 const char* outputColor,
109 const char* inputColor,
110 const char* samplerName) SK_OVERRIDE;
111 static StageKey GenKey(const GrCustomStage& s) { return 0; }
112
113private:
114
115 typedef GrGLGradientStage INHERITED;
116};
117
118void GrGLLinearGradient::emitFS(GrGLShaderBuilder* builder,
119 const char* outputColor,
120 const char* inputColor,
121 const char* samplerName) {
122 SkString t;
123 t.printf("%s.x", builder->fSampleCoords.c_str());
124 this->emitColorLookup(builder, t.c_str(), outputColor, samplerName);
125}
126
127/////////////////////////////////////////////////////////////////////
128
129GrLinearGradient::GrLinearGradient(GrTexture* texture)
130 : INHERITED(texture) {
131}
132
rileya@google.com91f319c2012-07-25 17:18:31 +0000133GrLinearGradient::GrLinearGradient(GrContext* ctx,
134 const SkShader& shader,
135 GrSamplerState* sampler)
136 : INHERITED(ctx, shader, sampler) {
rileya@google.com03c1c352012-07-20 20:02:43 +0000137}
138
rileya@google.com22e57f92012-07-19 15:16:19 +0000139GrLinearGradient::~GrLinearGradient() {
140
141}
142
143const GrProgramStageFactory& GrLinearGradient::getFactory() const {
144 return GrTProgramStageFactory<GrLinearGradient>::getInstance();
145}
146
rileya@google.com22e57f92012-07-19 15:16:19 +0000147/////////////////////////////////////////////////////////////////////
148
149class GrGLRadialGradient : public GrGLGradientStage {
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000150
151public:
152
153 GrGLRadialGradient(const GrProgramStageFactory& factory,
154 const GrCustomStage&) : INHERITED (factory) { }
155 virtual ~GrGLRadialGradient() { }
156
bsalomon@google.com032b2212012-07-16 13:36:18 +0000157 virtual void emitVS(GrGLShaderBuilder* builder,
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000158 const char* vertexCoords) SK_OVERRIDE { }
bsalomon@google.com032b2212012-07-16 13:36:18 +0000159 virtual void emitFS(GrGLShaderBuilder* builder,
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000160 const char* outputColor,
161 const char* inputColor,
162 const char* samplerName) SK_OVERRIDE;
163
164 static StageKey GenKey(const GrCustomStage& s) { return 0; }
165
166private:
167
rileya@google.com22e57f92012-07-19 15:16:19 +0000168 typedef GrGLGradientStage INHERITED;
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000169
170};
171
bsalomon@google.com032b2212012-07-16 13:36:18 +0000172void GrGLRadialGradient::emitFS(GrGLShaderBuilder* builder,
rileya@google.com3e332582012-07-03 13:43:35 +0000173 const char* outputColor,
174 const char* inputColor,
175 const char* samplerName) {
rileya@google.com22e57f92012-07-19 15:16:19 +0000176 SkString t;
177 t.printf("length(%s.xy)", builder->fSampleCoords.c_str());
178 this->emitColorLookup(builder, t.c_str(), outputColor, samplerName);
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000179}
180
181
182/////////////////////////////////////////////////////////////////////
183
184
tomhudson@google.comd0c1a062012-07-12 17:23:52 +0000185GrRadialGradient::GrRadialGradient(GrTexture* texture)
rileya@google.com22e57f92012-07-19 15:16:19 +0000186 : INHERITED(texture) {
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000187
188}
189
rileya@google.com91f319c2012-07-25 17:18:31 +0000190GrRadialGradient::GrRadialGradient(GrContext* ctx, const SkShader& shader,
191 GrSamplerState* sampler)
192 : INHERITED(ctx, shader, sampler) {
rileya@google.com03c1c352012-07-20 20:02:43 +0000193}
194
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000195GrRadialGradient::~GrRadialGradient() {
196
197}
198
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000199const GrProgramStageFactory& GrRadialGradient::getFactory() const {
200 return GrTProgramStageFactory<GrRadialGradient>::getInstance();
201}
202
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000203/////////////////////////////////////////////////////////////////////
204
bsalomon@google.comdbbc4e22012-07-25 17:48:39 +0000205// For brevity
206typedef GrGLUniformManager::UniformHandle UniformHandle;
207static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
bsalomon@google.com032b2212012-07-16 13:36:18 +0000208
rileya@google.com22e57f92012-07-19 15:16:19 +0000209class GrGLRadial2Gradient : public GrGLGradientStage {
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000210
211public:
212
213 GrGLRadial2Gradient(const GrProgramStageFactory& factory,
214 const GrCustomStage&);
215 virtual ~GrGLRadial2Gradient() { }
216
bsalomon@google.com777c3aa2012-07-25 20:58:20 +0000217 virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
bsalomon@google.com032b2212012-07-16 13:36:18 +0000218 virtual void emitVS(GrGLShaderBuilder* builder,
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000219 const char* vertexCoords) SK_OVERRIDE;
bsalomon@google.com032b2212012-07-16 13:36:18 +0000220 virtual void emitFS(GrGLShaderBuilder* builder,
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000221 const char* outputColor,
222 const char* inputColor,
223 const char* samplerName) SK_OVERRIDE;
bsalomon@google.comdbbc4e22012-07-25 17:48:39 +0000224 virtual void setData(const GrGLUniformManager&,
tomhudson@google.comdcdc1fc2012-05-31 19:53:37 +0000225 const GrCustomStage&,
senorblanco@chromium.orgf4770d72012-07-13 18:25:06 +0000226 const GrRenderTarget*,
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000227 int stageNum) SK_OVERRIDE;
228
229 static StageKey GenKey(const GrCustomStage& s) {
230 return (static_cast<const GrRadial2Gradient&>(s).isDegenerate());
231 }
232
233protected:
234
bsalomon@google.com032b2212012-07-16 13:36:18 +0000235 UniformHandle fVSParamUni;
bsalomon@google.com032b2212012-07-16 13:36:18 +0000236 UniformHandle fFSParamUni;
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000237
238 const char* fVSVaryingName;
239 const char* fFSVaryingName;
240
241 bool fIsDegenerate;
242
243 // @{
244 /// Values last uploaded as uniforms
245
246 GrScalar fCachedCenter;
247 GrScalar fCachedRadius;
bsalomon@google.com0b323312012-06-01 18:50:01 +0000248 bool fCachedPosRoot;
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000249
250 // @}
251
252private:
253
rileya@google.com22e57f92012-07-19 15:16:19 +0000254 typedef GrGLGradientStage INHERITED;
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000255
256};
257
258GrGLRadial2Gradient::GrGLRadial2Gradient(
259 const GrProgramStageFactory& factory,
260 const GrCustomStage& baseData)
261 : INHERITED(factory)
bsalomon@google.com032b2212012-07-16 13:36:18 +0000262 , fVSParamUni(kInvalidUniformHandle)
263 , fFSParamUni(kInvalidUniformHandle)
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000264 , fVSVaryingName(NULL)
265 , fFSVaryingName(NULL)
266 , fCachedCenter(GR_ScalarMax)
267 , fCachedRadius(-GR_ScalarMax)
268 , fCachedPosRoot(0) {
269
270 const GrRadial2Gradient& data =
271 static_cast<const GrRadial2Gradient&>(baseData);
272 fIsDegenerate = data.isDegenerate();
273}
274
bsalomon@google.com777c3aa2012-07-25 20:58:20 +0000275void GrGLRadial2Gradient::setupVariables(GrGLShaderBuilder* builder) {
tomhudson@google.com761b37c2012-06-13 15:22:18 +0000276 // 2 copies of uniform array, 1 for each of vertex & fragment shader,
277 // to work around Xoom bug. Doesn't seem to cause performance decrease
278 // in test apps, but need to keep an eye on it.
bsalomon@google.com777c3aa2012-07-25 20:58:20 +0000279 fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType,
280 kFloat_GrSLType, "Radial2VSParams", 6);
281 fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
282 kFloat_GrSLType, "Radial2FSParams", 6);
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000283
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000284 // For radial gradients without perspective we can pass the linear
285 // part of the quadratic as a varying.
bsalomon@google.com032b2212012-07-16 13:36:18 +0000286 if (builder->fVaryingDims == builder->fCoordDims) {
bsalomon@google.com777c3aa2012-07-25 20:58:20 +0000287 builder->addVarying(kFloat_GrSLType, "Radial2BCoeff",
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000288 &fVSVaryingName, &fFSVaryingName);
289 }
290}
291
bsalomon@google.com032b2212012-07-16 13:36:18 +0000292void GrGLRadial2Gradient::emitVS(GrGLShaderBuilder* builder,
rileya@google.com3e332582012-07-03 13:43:35 +0000293 const char* vertexCoords) {
bsalomon@google.com032b2212012-07-16 13:36:18 +0000294 SkString* code = &builder->fVSCode;
bsalomon@google.comf0a104e2012-07-10 17:51:07 +0000295 SkString p2;
296 SkString p3;
bsalomon@google.com032b2212012-07-16 13:36:18 +0000297 builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2);
298 builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3);
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000299
300 // For radial gradients without perspective we can pass the linear
301 // part of the quadratic as a varying.
bsalomon@google.com032b2212012-07-16 13:36:18 +0000302 if (builder->fVaryingDims == builder->fCoordDims) {
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000303 // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
304 code->appendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
305 fVSVaryingName, p2.c_str(),
306 vertexCoords, p3.c_str());
307 }
308}
309
bsalomon@google.com032b2212012-07-16 13:36:18 +0000310void GrGLRadial2Gradient::emitFS(GrGLShaderBuilder* builder,
rileya@google.com3e332582012-07-03 13:43:35 +0000311 const char* outputColor,
312 const char* inputColor,
313 const char* samplerName) {
bsalomon@google.com032b2212012-07-16 13:36:18 +0000314 SkString* code = &builder->fFSCode;
bsalomon@google.comf0a104e2012-07-10 17:51:07 +0000315 SkString cName("c");
316 SkString ac4Name("ac4");
317 SkString rootName("root");
rileya@google.com22e57f92012-07-19 15:16:19 +0000318 SkString t;
bsalomon@google.comf0a104e2012-07-10 17:51:07 +0000319 SkString p0;
320 SkString p1;
321 SkString p2;
322 SkString p3;
323 SkString p4;
324 SkString p5;
bsalomon@google.com032b2212012-07-16 13:36:18 +0000325 builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0);
326 builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1);
327 builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2);
328 builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3);
329 builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4);
330 builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5);
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000331
332 // If we we're able to interpolate the linear component,
333 // bVar is the varying; otherwise compute it
bsalomon@google.comf0a104e2012-07-10 17:51:07 +0000334 SkString bVar;
bsalomon@google.com032b2212012-07-16 13:36:18 +0000335 if (builder->fCoordDims == builder->fVaryingDims) {
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000336 bVar = fFSVaryingName;
bsalomon@google.com032b2212012-07-16 13:36:18 +0000337 GrAssert(2 == builder->fVaryingDims);
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000338 } else {
bsalomon@google.com032b2212012-07-16 13:36:18 +0000339 GrAssert(3 == builder->fVaryingDims);
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000340 bVar = "b";
341 //bVar.appendS32(stageNum);
342 code->appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
343 bVar.c_str(), p2.c_str(),
bsalomon@google.com032b2212012-07-16 13:36:18 +0000344 builder->fSampleCoords.c_str(), p3.c_str());
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000345 }
346
347 // c = (x^2)+(y^2) - params[4]
348 code->appendf("\tfloat %s = dot(%s, %s) - %s;\n",
bsalomon@google.com032b2212012-07-16 13:36:18 +0000349 cName.c_str(), builder->fSampleCoords.c_str(),
350 builder->fSampleCoords.c_str(),
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000351 p4.c_str());
352
353 // If we aren't degenerate, emit some extra code, and accept a slightly
354 // more complex coord.
tomhudson@google.com898e7b52012-06-01 20:42:15 +0000355 if (!fIsDegenerate) {
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000356
357 // ac4 = 4.0 * params[0] * c
358 code->appendf("\tfloat %s = %s * 4.0 * %s;\n",
359 ac4Name.c_str(), p0.c_str(),
360 cName.c_str());
361
362 // root = sqrt(b^2-4ac)
363 // (abs to avoid exception due to fp precision)
364 code->appendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
365 rootName.c_str(), bVar.c_str(), bVar.c_str(),
366 ac4Name.c_str());
367
rileya@google.com22e57f92012-07-19 15:16:19 +0000368 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
369 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
370 rootName.c_str(), p1.c_str());
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000371 } else {
rileya@google.com22e57f92012-07-19 15:16:19 +0000372 // t is: -c/b
373 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000374 }
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000375
rileya@google.com22e57f92012-07-19 15:16:19 +0000376 this->emitColorLookup(builder, t.c_str(), outputColor, samplerName);
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000377}
378
bsalomon@google.comdbbc4e22012-07-25 17:48:39 +0000379void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
rileya@google.com3e332582012-07-03 13:43:35 +0000380 const GrCustomStage& baseData,
senorblanco@chromium.orgf4770d72012-07-13 18:25:06 +0000381 const GrRenderTarget*,
rileya@google.com3e332582012-07-03 13:43:35 +0000382 int stageNum) {
tomhudson@google.comdcdc1fc2012-05-31 19:53:37 +0000383 const GrRadial2Gradient& data =
384 static_cast<const GrRadial2Gradient&>(baseData);
385 GrAssert(data.isDegenerate() == fIsDegenerate);
386 GrScalar centerX1 = data.center();
387 GrScalar radius0 = data.radius();
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000388 if (fCachedCenter != centerX1 ||
389 fCachedRadius != radius0 ||
tomhudson@google.comdcdc1fc2012-05-31 19:53:37 +0000390 fCachedPosRoot != data.isPosRoot()) {
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000391
392 GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1;
393
394 // When we're in the degenerate (linear) case, the second
395 // value will be INF but the program doesn't read it. (We
396 // use the same 6 uniforms even though we don't need them
397 // all in the linear case just to keep the code complexity
398 // down).
399 float values[6] = {
400 GrScalarToFloat(a),
401 1 / (2.f * GrScalarToFloat(a)),
402 GrScalarToFloat(centerX1),
403 GrScalarToFloat(radius0),
404 GrScalarToFloat(GrMul(radius0, radius0)),
tomhudson@google.comdcdc1fc2012-05-31 19:53:37 +0000405 data.isPosRoot() ? 1.f : -1.f
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000406 };
407
bsalomon@google.comdbbc4e22012-07-25 17:48:39 +0000408 uman.set1fv(fVSParamUni, 0, 6, values);
409 uman.set1fv(fFSParamUni, 0, 6, values);
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000410 fCachedCenter = centerX1;
411 fCachedRadius = radius0;
tomhudson@google.comdcdc1fc2012-05-31 19:53:37 +0000412 fCachedPosRoot = data.isPosRoot();
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000413 }
414}
415
416
417/////////////////////////////////////////////////////////////////////
418
tomhudson@google.comd0c1a062012-07-12 17:23:52 +0000419GrRadial2Gradient::GrRadial2Gradient(GrTexture* texture,
420 GrScalar center,
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000421 GrScalar radius,
422 bool posRoot)
rileya@google.com22e57f92012-07-19 15:16:19 +0000423 : INHERITED(texture)
tomhudson@google.comd0c1a062012-07-12 17:23:52 +0000424 , fCenterX1 (center)
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000425 , fRadius0 (radius)
426 , fPosRoot (posRoot) {
427
428}
429
rileya@google.com91f319c2012-07-25 17:18:31 +0000430GrRadial2Gradient::GrRadial2Gradient(GrContext* ctx,
431 const SkShader& shader,
432 GrSamplerState* sampler,
433 SkScalar center,
434 SkScalar startRadius,
435 SkScalar diffRadius)
436 : INHERITED(ctx, shader, sampler)
437 , fCenterX1(center)
438 , fRadius0(startRadius)
439 , fPosRoot(diffRadius < 0) {
rileya@google.com03c1c352012-07-20 20:02:43 +0000440}
441
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000442GrRadial2Gradient::~GrRadial2Gradient() {
443
444}
445
446
447const GrProgramStageFactory& GrRadial2Gradient::getFactory() const {
448 return GrTProgramStageFactory<GrRadial2Gradient>::getInstance();
449}
450
451bool GrRadial2Gradient::isEqual(const GrCustomStage& sBase) const {
452 const GrRadial2Gradient& s = static_cast<const GrRadial2Gradient&>(sBase);
tomhudson@google.comd0c1a062012-07-12 17:23:52 +0000453 return (INHERITED::isEqual(sBase) &&
454 this->fCenterX1 == s.fCenterX1 &&
tomhudson@google.com1dcfa1f2012-07-09 18:21:28 +0000455 this->fRadius0 == s.fRadius0 &&
456 this->fPosRoot == s.fPosRoot);
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000457}
458
459/////////////////////////////////////////////////////////////////////
460
rileya@google.com22e57f92012-07-19 15:16:19 +0000461class GrGLConical2Gradient : public GrGLGradientStage {
rileya@google.com3e332582012-07-03 13:43:35 +0000462
463public:
464
465 GrGLConical2Gradient(const GrProgramStageFactory& factory,
466 const GrCustomStage&);
467 virtual ~GrGLConical2Gradient() { }
468
bsalomon@google.com777c3aa2012-07-25 20:58:20 +0000469 virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
bsalomon@google.com032b2212012-07-16 13:36:18 +0000470 virtual void emitVS(GrGLShaderBuilder* builder,
rileya@google.com3e332582012-07-03 13:43:35 +0000471 const char* vertexCoords) SK_OVERRIDE;
bsalomon@google.com032b2212012-07-16 13:36:18 +0000472 virtual void emitFS(GrGLShaderBuilder* builder,
rileya@google.com3e332582012-07-03 13:43:35 +0000473 const char* outputColor,
474 const char* inputColor,
475 const char* samplerName) SK_OVERRIDE;
bsalomon@google.comdbbc4e22012-07-25 17:48:39 +0000476 virtual void setData(const GrGLUniformManager&,
rileya@google.com3e332582012-07-03 13:43:35 +0000477 const GrCustomStage&,
senorblanco@chromium.orgf4770d72012-07-13 18:25:06 +0000478 const GrRenderTarget*,
rileya@google.com3e332582012-07-03 13:43:35 +0000479 int stageNum) SK_OVERRIDE;
480
481 static StageKey GenKey(const GrCustomStage& s) {
482 return (static_cast<const GrConical2Gradient&>(s).isDegenerate());
483 }
484
485protected:
486
bsalomon@google.com032b2212012-07-16 13:36:18 +0000487 UniformHandle fVSParamUni;
488 GrGLint fVSParamLocation;
489 UniformHandle fFSParamUni;
490 GrGLint fFSParamLocation;
rileya@google.com3e332582012-07-03 13:43:35 +0000491
492 const char* fVSVaryingName;
493 const char* fFSVaryingName;
494
495 bool fIsDegenerate;
496
497 // @{
498 /// Values last uploaded as uniforms
499
500 GrScalar fCachedCenter;
501 GrScalar fCachedRadius;
502 GrScalar fCachedDiffRadius;
503
504 // @}
505
506private:
507
rileya@google.com22e57f92012-07-19 15:16:19 +0000508 typedef GrGLGradientStage INHERITED;
rileya@google.com3e332582012-07-03 13:43:35 +0000509
510};
511
512GrGLConical2Gradient::GrGLConical2Gradient(
513 const GrProgramStageFactory& factory,
514 const GrCustomStage& baseData)
515 : INHERITED(factory)
bsalomon@google.com032b2212012-07-16 13:36:18 +0000516 , fVSParamUni(kInvalidUniformHandle)
517 , fFSParamUni(kInvalidUniformHandle)
rileya@google.com3e332582012-07-03 13:43:35 +0000518 , fVSVaryingName(NULL)
519 , fFSVaryingName(NULL)
520 , fCachedCenter(GR_ScalarMax)
521 , fCachedRadius(-GR_ScalarMax)
522 , fCachedDiffRadius(-GR_ScalarMax) {
523
524 const GrConical2Gradient& data =
525 static_cast<const GrConical2Gradient&>(baseData);
526 fIsDegenerate = data.isDegenerate();
527}
528
bsalomon@google.com777c3aa2012-07-25 20:58:20 +0000529void GrGLConical2Gradient::setupVariables(GrGLShaderBuilder* builder) {
rileya@google.com3e332582012-07-03 13:43:35 +0000530 // 2 copies of uniform array, 1 for each of vertex & fragment shader,
531 // to work around Xoom bug. Doesn't seem to cause performance decrease
532 // in test apps, but need to keep an eye on it.
bsalomon@google.com777c3aa2012-07-25 20:58:20 +0000533 fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType,
534 kFloat_GrSLType, "Conical2VSParams", 6);
535 fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
536 kFloat_GrSLType, "Conical2FSParams", 6);
rileya@google.com3e332582012-07-03 13:43:35 +0000537
538 fVSParamLocation = GrGLProgramStage::kUseUniform;
539 fFSParamLocation = GrGLProgramStage::kUseUniform;
540
541 // For radial gradients without perspective we can pass the linear
542 // part of the quadratic as a varying.
bsalomon@google.com032b2212012-07-16 13:36:18 +0000543 if (builder->fVaryingDims == builder->fCoordDims) {
bsalomon@google.com777c3aa2012-07-25 20:58:20 +0000544 builder->addVarying(kFloat_GrSLType, "Conical2BCoeff",
bsalomon@google.com032b2212012-07-16 13:36:18 +0000545 &fVSVaryingName, &fFSVaryingName);
rileya@google.com3e332582012-07-03 13:43:35 +0000546 }
547}
548
bsalomon@google.com032b2212012-07-16 13:36:18 +0000549void GrGLConical2Gradient::emitVS(GrGLShaderBuilder* builder,
rileya@google.com3e332582012-07-03 13:43:35 +0000550 const char* vertexCoords) {
bsalomon@google.com032b2212012-07-16 13:36:18 +0000551 SkString* code = &builder->fVSCode;
bsalomon@google.comf0a104e2012-07-10 17:51:07 +0000552 SkString p2; // distance between centers
553 SkString p3; // start radius
554 SkString p5; // difference in radii (r1 - r0)
bsalomon@google.com032b2212012-07-16 13:36:18 +0000555 builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2);
556 builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3);
557 builder->getUniformVariable(fVSParamUni).appendArrayAccess(5, &p5);
rileya@google.com3e332582012-07-03 13:43:35 +0000558
559 // For radial gradients without perspective we can pass the linear
560 // part of the quadratic as a varying.
bsalomon@google.com032b2212012-07-16 13:36:18 +0000561 if (builder->fVaryingDims == builder->fCoordDims) {
rileya@google.com3e332582012-07-03 13:43:35 +0000562 // r2Var = -2 * (r2Parm[2] * varCoord.x - r2Param[3] * r2Param[5])
563 code->appendf("\t%s = -2.0 * (%s * %s.x + %s * %s);\n",
564 fVSVaryingName, p2.c_str(),
565 vertexCoords, p3.c_str(), p5.c_str());
566 }
567}
568
bsalomon@google.com032b2212012-07-16 13:36:18 +0000569void GrGLConical2Gradient::emitFS(GrGLShaderBuilder* builder,
rileya@google.com3e332582012-07-03 13:43:35 +0000570 const char* outputColor,
571 const char* inputColor,
572 const char* samplerName) {
bsalomon@google.com032b2212012-07-16 13:36:18 +0000573 SkString* code = &builder->fFSCode;
rileya@google.com3e332582012-07-03 13:43:35 +0000574
bsalomon@google.comf0a104e2012-07-10 17:51:07 +0000575 SkString cName("c");
576 SkString ac4Name("ac4");
577 SkString dName("d");
578 SkString qName("q");
579 SkString r0Name("r0");
580 SkString r1Name("r1");
581 SkString tName("t");
582 SkString p0; // 4a
rileya@google.com62197282012-07-10 20:05:23 +0000583 SkString p1; // 1/a
bsalomon@google.comf0a104e2012-07-10 17:51:07 +0000584 SkString p2; // distance between centers
585 SkString p3; // start radius
586 SkString p4; // start radius squared
587 SkString p5; // difference in radii (r1 - r0)
bsalomon@google.com032b2212012-07-16 13:36:18 +0000588
bsalomon@google.comec4037f2012-07-16 13:46:39 +0000589 builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0);
590 builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1);
591 builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2);
592 builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3);
593 builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4);
594 builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5);
rileya@google.com3e332582012-07-03 13:43:35 +0000595
596 // If we we're able to interpolate the linear component,
597 // bVar is the varying; otherwise compute it
bsalomon@google.comf0a104e2012-07-10 17:51:07 +0000598 SkString bVar;
bsalomon@google.com032b2212012-07-16 13:36:18 +0000599 if (builder->fCoordDims == builder->fVaryingDims) {
rileya@google.com3e332582012-07-03 13:43:35 +0000600 bVar = fFSVaryingName;
bsalomon@google.com032b2212012-07-16 13:36:18 +0000601 GrAssert(2 == builder->fVaryingDims);
rileya@google.com3e332582012-07-03 13:43:35 +0000602 } else {
bsalomon@google.com032b2212012-07-16 13:36:18 +0000603 GrAssert(3 == builder->fVaryingDims);
rileya@google.com3e332582012-07-03 13:43:35 +0000604 bVar = "b";
605 code->appendf("\tfloat %s = -2.0 * (%s * %s.x + %s * %s);\n",
bsalomon@google.com032b2212012-07-16 13:36:18 +0000606 bVar.c_str(), p2.c_str(), builder->fSampleCoords.c_str(),
rileya@google.com3e332582012-07-03 13:43:35 +0000607 p3.c_str(), p5.c_str());
608 }
609
rileya@google.come38160c2012-07-03 18:03:04 +0000610 // output will default to transparent black (we simply won't write anything
611 // else to it if invalid, instead of discarding or returning prematurely)
612 code->appendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
613
rileya@google.com3e332582012-07-03 13:43:35 +0000614 // c = (x^2)+(y^2) - params[4]
615 code->appendf("\tfloat %s = dot(%s, %s) - %s;\n", cName.c_str(),
bsalomon@google.com032b2212012-07-16 13:36:18 +0000616 builder->fSampleCoords.c_str(), builder->fSampleCoords.c_str(),
rileya@google.com3e332582012-07-03 13:43:35 +0000617 p4.c_str());
618
619 // Non-degenerate case (quadratic)
620 if (!fIsDegenerate) {
621
622 // ac4 = params[0] * c
623 code->appendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_str(),
624 cName.c_str());
625
626 // d = b^2 - ac4
627 code->appendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(),
628 bVar.c_str(), bVar.c_str(), ac4Name.c_str());
629
rileya@google.come38160c2012-07-03 18:03:04 +0000630 // only proceed if discriminant is >= 0
631 code->appendf("\tif (%s >= 0.0) {\n", dName.c_str());
rileya@google.com3e332582012-07-03 13:43:35 +0000632
633 // intermediate value we'll use to compute the roots
634 // q = -0.5 * (b +/- sqrt(d))
rileya@google.come38160c2012-07-03 18:03:04 +0000635 code->appendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1.0)"
rileya@google.com3e332582012-07-03 13:43:35 +0000636 " * sqrt(%s));\n", qName.c_str(), bVar.c_str(),
637 bVar.c_str(), dName.c_str());
638
639 // compute both roots
640 // r0 = q * params[1]
rileya@google.come38160c2012-07-03 18:03:04 +0000641 code->appendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(),
642 qName.c_str(), p1.c_str());
rileya@google.com3e332582012-07-03 13:43:35 +0000643 // r1 = c / q
rileya@google.come38160c2012-07-03 18:03:04 +0000644 code->appendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(),
645 cName.c_str(), qName.c_str());
rileya@google.com3e332582012-07-03 13:43:35 +0000646
647 // Note: If there are two roots that both generate radius(t) > 0, the
648 // Canvas spec says to choose the larger t.
649
650 // so we'll look at the larger one first:
rileya@google.come38160c2012-07-03 18:03:04 +0000651 code->appendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(),
rileya@google.com3e332582012-07-03 13:43:35 +0000652 r0Name.c_str(), r1Name.c_str());
653
rileya@google.come38160c2012-07-03 18:03:04 +0000654 // if r(t) > 0, then we're done; t will be our x coordinate
655 code->appendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
rileya@google.com3e332582012-07-03 13:43:35 +0000656 p5.c_str(), p3.c_str());
rileya@google.come38160c2012-07-03 18:03:04 +0000657
rileya@google.come38160c2012-07-03 18:03:04 +0000658 code->appendf("\t\t");
rileya@google.com22e57f92012-07-19 15:16:19 +0000659 this->emitColorLookup(builder, tName.c_str(), outputColor, samplerName);
rileya@google.come38160c2012-07-03 18:03:04 +0000660
661 // otherwise, if r(t) for the larger root was <= 0, try the other root
662 code->appendf("\t\t} else {\n");
663 code->appendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(),
rileya@google.com3e332582012-07-03 13:43:35 +0000664 r0Name.c_str(), r1Name.c_str());
665
rileya@google.come38160c2012-07-03 18:03:04 +0000666 // if r(t) > 0 for the smaller root, then t will be our x coordinate
667 code->appendf("\t\t\tif (%s * %s + %s > 0.0) {\n",
rileya@google.com3e332582012-07-03 13:43:35 +0000668 tName.c_str(), p5.c_str(), p3.c_str());
rileya@google.come38160c2012-07-03 18:03:04 +0000669
rileya@google.come38160c2012-07-03 18:03:04 +0000670 code->appendf("\t\t\t");
rileya@google.com22e57f92012-07-19 15:16:19 +0000671 this->emitColorLookup(builder, tName.c_str(), outputColor, samplerName);
rileya@google.come38160c2012-07-03 18:03:04 +0000672
673 // end if (r(t) > 0) for smaller root
674 code->appendf("\t\t\t}\n");
675 // end if (r(t) > 0), else, for larger root
676 code->appendf("\t\t}\n");
677 // end if (discriminant >= 0)
rileya@google.com3e332582012-07-03 13:43:35 +0000678 code->appendf("\t}\n");
679 } else {
680
681 // linear case: t = -c/b
682 code->appendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
683 cName.c_str(), bVar.c_str());
684
rileya@google.come38160c2012-07-03 18:03:04 +0000685 // if r(t) > 0, then t will be the x coordinate
686 code->appendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
rileya@google.com3e332582012-07-03 13:43:35 +0000687 p5.c_str(), p3.c_str());
rileya@google.come38160c2012-07-03 18:03:04 +0000688 code->appendf("\t");
rileya@google.com22e57f92012-07-19 15:16:19 +0000689 this->emitColorLookup(builder, tName.c_str(), outputColor, samplerName);
rileya@google.come38160c2012-07-03 18:03:04 +0000690 code->appendf("\t}\n");
rileya@google.com3e332582012-07-03 13:43:35 +0000691 }
rileya@google.com3e332582012-07-03 13:43:35 +0000692}
693
bsalomon@google.comdbbc4e22012-07-25 17:48:39 +0000694void GrGLConical2Gradient::setData(const GrGLUniformManager& uman,
rileya@google.com3e332582012-07-03 13:43:35 +0000695 const GrCustomStage& baseData,
senorblanco@chromium.orgf4770d72012-07-13 18:25:06 +0000696 const GrRenderTarget*,
rileya@google.com3e332582012-07-03 13:43:35 +0000697 int stageNum) {
698 const GrConical2Gradient& data =
699 static_cast<const GrConical2Gradient&>(baseData);
700 GrAssert(data.isDegenerate() == fIsDegenerate);
701 GrScalar centerX1 = data.center();
702 GrScalar radius0 = data.radius();
703 GrScalar diffRadius = data.diffRadius();
704
705 if (fCachedCenter != centerX1 ||
706 fCachedRadius != radius0 ||
707 fCachedDiffRadius != diffRadius) {
708
709 GrScalar a = GrMul(centerX1, centerX1) - diffRadius * diffRadius;
710
711 // When we're in the degenerate (linear) case, the second
712 // value will be INF but the program doesn't read it. (We
713 // use the same 6 uniforms even though we don't need them
714 // all in the linear case just to keep the code complexity
715 // down).
716 float values[6] = {
717 GrScalarToFloat(a * 4),
718 1.f / (GrScalarToFloat(a)),
719 GrScalarToFloat(centerX1),
720 GrScalarToFloat(radius0),
721 GrScalarToFloat(SkScalarMul(radius0, radius0)),
722 GrScalarToFloat(diffRadius)
723 };
724
bsalomon@google.comdbbc4e22012-07-25 17:48:39 +0000725 uman.set1fv(fVSParamUni, 0, 6, values);
726 uman.set1fv(fFSParamUni, 0, 6, values);
rileya@google.com3e332582012-07-03 13:43:35 +0000727 fCachedCenter = centerX1;
728 fCachedRadius = radius0;
729 fCachedDiffRadius = diffRadius;
730 }
731}
732
733
734/////////////////////////////////////////////////////////////////////
735
tomhudson@google.comd0c1a062012-07-12 17:23:52 +0000736GrConical2Gradient::GrConical2Gradient(GrTexture* texture,
737 GrScalar center,
rileya@google.com3e332582012-07-03 13:43:35 +0000738 GrScalar radius,
739 GrScalar diffRadius)
rileya@google.com22e57f92012-07-19 15:16:19 +0000740 : INHERITED (texture)
tomhudson@google.comd0c1a062012-07-12 17:23:52 +0000741 , fCenterX1 (center)
rileya@google.com3e332582012-07-03 13:43:35 +0000742 , fRadius0 (radius)
743 , fDiffRadius (diffRadius) {
744
745}
746
rileya@google.com91f319c2012-07-25 17:18:31 +0000747GrConical2Gradient::GrConical2Gradient(GrContext* ctx,
748 const SkShader& shader,
749 GrSamplerState* sampler,
750 SkScalar center,
751 SkScalar startRadius,
752 SkScalar diffRadius)
753 : INHERITED(ctx, shader, sampler)
754 , fCenterX1(center)
755 , fRadius0(startRadius)
756 , fDiffRadius(diffRadius) {
rileya@google.com03c1c352012-07-20 20:02:43 +0000757}
758
rileya@google.com3e332582012-07-03 13:43:35 +0000759GrConical2Gradient::~GrConical2Gradient() {
760
761}
762
763
764const GrProgramStageFactory& GrConical2Gradient::getFactory() const {
765 return GrTProgramStageFactory<GrConical2Gradient>::getInstance();
766}
767
768bool GrConical2Gradient::isEqual(const GrCustomStage& sBase) const {
769 const GrConical2Gradient& s = static_cast<const GrConical2Gradient&>(sBase);
tomhudson@google.comd0c1a062012-07-12 17:23:52 +0000770 return (INHERITED::isEqual(sBase) &&
771 this->fCenterX1 == s.fCenterX1 &&
tomhudson@google.com1dcfa1f2012-07-09 18:21:28 +0000772 this->fRadius0 == s.fRadius0 &&
773 this->fDiffRadius == s.fDiffRadius);
rileya@google.com3e332582012-07-03 13:43:35 +0000774}
775
776/////////////////////////////////////////////////////////////////////
777
778
rileya@google.com22e57f92012-07-19 15:16:19 +0000779class GrGLSweepGradient : public GrGLGradientStage {
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000780
781public:
782
783 GrGLSweepGradient(const GrProgramStageFactory& factory,
784 const GrCustomStage&) : INHERITED (factory) { }
785 virtual ~GrGLSweepGradient() { }
786
bsalomon@google.com032b2212012-07-16 13:36:18 +0000787 virtual void emitVS(GrGLShaderBuilder* builder,
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000788 const char* vertexCoords) SK_OVERRIDE { }
bsalomon@google.com032b2212012-07-16 13:36:18 +0000789 virtual void emitFS(GrGLShaderBuilder* builder,
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000790 const char* outputColor,
791 const char* inputColor,
792 const char* samplerName) SK_OVERRIDE;
793
794 static StageKey GenKey(const GrCustomStage& s) { return 0; }
795
796private:
797
rileya@google.com22e57f92012-07-19 15:16:19 +0000798 typedef GrGLGradientStage INHERITED;
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000799
800};
801
bsalomon@google.com032b2212012-07-16 13:36:18 +0000802void GrGLSweepGradient::emitFS(GrGLShaderBuilder* builder,
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000803 const char* outputColor,
804 const char* inputColor,
805 const char* samplerName) {
rileya@google.com22e57f92012-07-19 15:16:19 +0000806 SkString t;
807 t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5",
bsalomon@google.com032b2212012-07-16 13:36:18 +0000808 builder->fSampleCoords.c_str(), builder->fSampleCoords.c_str());
rileya@google.com22e57f92012-07-19 15:16:19 +0000809 this->emitColorLookup(builder, t.c_str(), outputColor, samplerName);
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000810}
811
812/////////////////////////////////////////////////////////////////////
813
tomhudson@google.comd0c1a062012-07-12 17:23:52 +0000814GrSweepGradient::GrSweepGradient(GrTexture* texture)
rileya@google.com22e57f92012-07-19 15:16:19 +0000815 : INHERITED(texture) {
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000816
817}
818
rileya@google.com91f319c2012-07-25 17:18:31 +0000819GrSweepGradient::GrSweepGradient(GrContext* ctx, const SkShader& shader,
820 GrSamplerState* sampler)
821 : INHERITED(ctx, shader, sampler) {
rileya@google.com03c1c352012-07-20 20:02:43 +0000822}
823
tomhudson@google.com7fab52d2012-05-31 19:40:13 +0000824GrSweepGradient::~GrSweepGradient() {
825
826}
827
828const GrProgramStageFactory& GrSweepGradient::getFactory() const {
829 return GrTProgramStageFactory<GrSweepGradient>::getInstance();
830}
831