Pull Gpu shader out of SkTwoPointConicalGradient into own file

BUG=skia:
R=bsalomon@google.com

Author: egdaniel@google.com

Review URL: https://codereview.chromium.org/222943002

git-svn-id: http://skia.googlecode.com/svn/trunk@14044 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2014-04-03 14:59:19 +00:00
parent 6ce77fa778
commit aa64fbfd34
5 changed files with 386 additions and 351 deletions

View File

@ -77,6 +77,8 @@
'<(skia_src_path)/effects/gradients/SkTwoPointRadialGradient.h',
'<(skia_src_path)/effects/gradients/SkTwoPointConicalGradient.cpp',
'<(skia_src_path)/effects/gradients/SkTwoPointConicalGradient.h',
'<(skia_src_path)/effects/gradients/SkTwoPointConicalGradient_gpu.cpp',
'<(skia_src_path)/effects/gradients/SkTwoPointConicalGradient_gpu.h',
'<(skia_src_path)/effects/gradients/SkSweepGradient.cpp',
'<(skia_src_path)/effects/gradients/SkSweepGradient.h',

View File

@ -7,6 +7,8 @@
#include "SkTwoPointConicalGradient.h"
#include "SkTwoPointConicalGradient_gpu.h"
static int valid_divide(float numer, float denom, float* ratio) {
SkASSERT(ratio);
if (0 == denom) {
@ -328,356 +330,8 @@ void SkTwoPointConicalGradient::flatten(
buffer.writeScalar(fRadius2);
}
/////////////////////////////////////////////////////////////////////
#if SK_SUPPORT_GPU
#include "GrTBackendEffectFactory.h"
// For brevity
typedef GrGLUniformManager::UniformHandle UniformHandle;
class GrGLConical2Gradient : public GrGLGradientEffect {
public:
GrGLConical2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
virtual ~GrGLConical2Gradient() { }
virtual void emitCode(GrGLShaderBuilder*,
const GrDrawEffect&,
EffectKey,
const char* outputColor,
const char* inputColor,
const TransformedCoordsArray&,
const TextureSamplerArray&) SK_OVERRIDE;
virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
protected:
UniformHandle fParamUni;
const char* fVSVaryingName;
const char* fFSVaryingName;
bool fIsDegenerate;
// @{
/// Values last uploaded as uniforms
SkScalar fCachedCenter;
SkScalar fCachedRadius;
SkScalar fCachedDiffRadius;
// @}
private:
typedef GrGLGradientEffect INHERITED;
};
/////////////////////////////////////////////////////////////////////
class GrConical2Gradient : public GrGradientEffect {
public:
static GrEffectRef* Create(GrContext* ctx,
const SkTwoPointConicalGradient& shader,
const SkMatrix& matrix,
SkShader::TileMode tm) {
AutoEffectUnref effect(SkNEW_ARGS(GrConical2Gradient, (ctx, shader, matrix, tm)));
return CreateEffectRef(effect);
}
virtual ~GrConical2Gradient() { }
static const char* Name() { return "Two-Point Conical Gradient"; }
virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
return GrTBackendEffectFactory<GrConical2Gradient>::getInstance();
}
// The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); }
SkScalar center() const { return fCenterX1; }
SkScalar diffRadius() const { return fDiffRadius; }
SkScalar radius() const { return fRadius0; }
typedef GrGLConical2Gradient GLEffect;
private:
virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
const GrConical2Gradient& s = CastEffect<GrConical2Gradient>(sBase);
return (INHERITED::onIsEqual(sBase) &&
this->fCenterX1 == s.fCenterX1 &&
this->fRadius0 == s.fRadius0 &&
this->fDiffRadius == s.fDiffRadius);
}
GrConical2Gradient(GrContext* ctx,
const SkTwoPointConicalGradient& shader,
const SkMatrix& matrix,
SkShader::TileMode tm)
: INHERITED(ctx, shader, matrix, tm)
, fCenterX1(shader.getCenterX1())
, fRadius0(shader.getStartRadius())
, fDiffRadius(shader.getDiffRadius()) {
// We pass the linear part of the quadratic as a varying.
// float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
fBTransform = this->getCoordTransform();
SkMatrix& bMatrix = *fBTransform.accessMatrix();
SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius);
bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) +
SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0]));
bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) +
SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1]));
bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) +
SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2]));
this->addCoordTransform(&fBTransform);
}
GR_DECLARE_EFFECT_TEST;
// @{
// Cache of values - these can change arbitrarily, EXCEPT
// we shouldn't change between degenerate and non-degenerate?!
GrCoordTransform fBTransform;
SkScalar fCenterX1;
SkScalar fRadius0;
SkScalar fDiffRadius;
// @}
typedef GrGradientEffect INHERITED;
};
GR_DEFINE_EFFECT_TEST(GrConical2Gradient);
GrEffectRef* GrConical2Gradient::TestCreate(SkRandom* random,
GrContext* context,
const GrDrawTargetCaps&,
GrTexture**) {
SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
SkScalar radius1 = random->nextUScalar1();
SkPoint center2;
SkScalar radius2;
do {
center2.set(random->nextUScalar1(), random->nextUScalar1());
radius2 = random->nextUScalar1 ();
// If the circles are identical the factory will give us an empty shader.
} while (radius1 == radius2 && center1 == center2);
SkColor colors[kMaxRandomGradientColors];
SkScalar stopsArray[kMaxRandomGradientColors];
SkScalar* stops = stopsArray;
SkShader::TileMode tm;
int colorCount = RandomGradientParams(random, colors, &stops, &tm);
SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
center2, radius2,
colors, stops, colorCount,
tm));
SkPaint paint;
return shader->asNewEffect(context, paint);
}
/////////////////////////////////////////////////////////////////////
GrGLConical2Gradient::GrGLConical2Gradient(const GrBackendEffectFactory& factory,
const GrDrawEffect& drawEffect)
: INHERITED(factory)
, fVSVaryingName(NULL)
, fFSVaryingName(NULL)
, fCachedCenter(SK_ScalarMax)
, fCachedRadius(-SK_ScalarMax)
, fCachedDiffRadius(-SK_ScalarMax) {
const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>();
fIsDegenerate = data.isDegenerate();
}
void GrGLConical2Gradient::emitCode(GrGLShaderBuilder* builder,
const GrDrawEffect&,
EffectKey key,
const char* outputColor,
const char* inputColor,
const TransformedCoordsArray& coords,
const TextureSamplerArray& samplers) {
this->emitUniforms(builder, key);
fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
kFloat_GrSLType, "Conical2FSParams", 6);
SkString cName("c");
SkString ac4Name("ac4");
SkString dName("d");
SkString qName("q");
SkString r0Name("r0");
SkString r1Name("r1");
SkString tName("t");
SkString p0; // 4a
SkString p1; // 1/a
SkString p2; // distance between centers
SkString p3; // start radius
SkString p4; // start radius squared
SkString p5; // difference in radii (r1 - r0)
builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
// We interpolate the linear component in coords[1].
SkASSERT(coords[0].type() == coords[1].type());
const char* coords2D;
SkString bVar;
if (kVec3f_GrSLType == coords[0].type()) {
builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
coords2D = "interpolants.xy";
bVar = "interpolants.z";
} else {
coords2D = coords[0].c_str();
bVar.printf("%s.x", coords[1].c_str());
}
// output will default to transparent black (we simply won't write anything
// else to it if invalid, instead of discarding or returning prematurely)
builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
// c = (x^2)+(y^2) - params[4]
builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
cName.c_str(), coords2D, coords2D, p4.c_str());
// Non-degenerate case (quadratic)
if (!fIsDegenerate) {
// ac4 = params[0] * c
builder->fsCodeAppendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_str(),
cName.c_str());
// d = b^2 - ac4
builder->fsCodeAppendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(),
bVar.c_str(), bVar.c_str(), ac4Name.c_str());
// only proceed if discriminant is >= 0
builder->fsCodeAppendf("\tif (%s >= 0.0) {\n", dName.c_str());
// intermediate value we'll use to compute the roots
// q = -0.5 * (b +/- sqrt(d))
builder->fsCodeAppendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1.0)"
" * sqrt(%s));\n", qName.c_str(), bVar.c_str(),
bVar.c_str(), dName.c_str());
// compute both roots
// r0 = q * params[1]
builder->fsCodeAppendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(),
qName.c_str(), p1.c_str());
// r1 = c / q
builder->fsCodeAppendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(),
cName.c_str(), qName.c_str());
// Note: If there are two roots that both generate radius(t) > 0, the
// Canvas spec says to choose the larger t.
// so we'll look at the larger one first:
builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(),
r0Name.c_str(), r1Name.c_str());
// if r(t) > 0, then we're done; t will be our x coordinate
builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
p5.c_str(), p3.c_str());
builder->fsCodeAppend("\t\t");
this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
// otherwise, if r(t) for the larger root was <= 0, try the other root
builder->fsCodeAppend("\t\t} else {\n");
builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(),
r0Name.c_str(), r1Name.c_str());
// if r(t) > 0 for the smaller root, then t will be our x coordinate
builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n",
tName.c_str(), p5.c_str(), p3.c_str());
builder->fsCodeAppend("\t\t\t");
this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
// end if (r(t) > 0) for smaller root
builder->fsCodeAppend("\t\t\t}\n");
// end if (r(t) > 0), else, for larger root
builder->fsCodeAppend("\t\t}\n");
// end if (discriminant >= 0)
builder->fsCodeAppend("\t}\n");
} else {
// linear case: t = -c/b
builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
cName.c_str(), bVar.c_str());
// if r(t) > 0, then t will be the x coordinate
builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
p5.c_str(), p3.c_str());
builder->fsCodeAppend("\t");
this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
builder->fsCodeAppend("\t}\n");
}
}
void GrGLConical2Gradient::setData(const GrGLUniformManager& uman,
const GrDrawEffect& drawEffect) {
INHERITED::setData(uman, drawEffect);
const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>();
SkASSERT(data.isDegenerate() == fIsDegenerate);
SkScalar centerX1 = data.center();
SkScalar radius0 = data.radius();
SkScalar diffRadius = data.diffRadius();
if (fCachedCenter != centerX1 ||
fCachedRadius != radius0 ||
fCachedDiffRadius != diffRadius) {
SkScalar a = SkScalarMul(centerX1, centerX1) - diffRadius * diffRadius;
// When we're in the degenerate (linear) case, the second
// value will be INF but the program doesn't read it. (We
// use the same 6 uniforms even though we don't need them
// all in the linear case just to keep the code complexity
// down).
float values[6] = {
SkScalarToFloat(a * 4),
1.f / (SkScalarToFloat(a)),
SkScalarToFloat(centerX1),
SkScalarToFloat(radius0),
SkScalarToFloat(SkScalarMul(radius0, radius0)),
SkScalarToFloat(diffRadius)
};
uman.set1fv(fParamUni, 6, values);
fCachedCenter = centerX1;
fCachedRadius = radius0;
fCachedDiffRadius = diffRadius;
}
}
GrGLEffect::EffectKey GrGLConical2Gradient::GenKey(const GrDrawEffect& drawEffect,
const GrGLCaps&) {
enum {
kIsDegenerate = 1 << kBaseKeyBitCnt,
};
EffectKey key = GenBaseGradientKey(drawEffect);
if (drawEffect.castEffect<GrConical2Gradient>().isDegenerate()) {
key |= kIsDegenerate;
}
return key;
}
/////////////////////////////////////////////////////////////////////
GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint&) const {
SkASSERT(NULL != context);
SkASSERT(fPtsToUnit.isIdentity());
@ -698,7 +352,7 @@ GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const Sk
matrix.postConcat(rot);
}
return GrConical2Gradient::Create(context, *this, matrix, fTileMode);
return Gr2PtConicalGradientEffect::Create(context, *this, matrix, fTileMode);
}
#else

View File

@ -6,8 +6,8 @@
* found in the LICENSE file.
*/
#ifndef SkTwoPointConicalGradient_DEFINED
#define SkTwoPointConicalGradient_DEFINED
#ifndef SkTwoPointConicalGradient_DEFINED
#define SkTwoPointConicalGradient_DEFINED
#include "SkGradientShaderPriv.h"

View File

@ -0,0 +1,306 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#if SK_SUPPORT_GPU
#include "SkTwoPointConicalGradient_gpu.h"
#include "GrTBackendEffectFactory.h"
#include "SkTwoPointConicalGradient.h"
// For brevity
typedef GrGLUniformManager::UniformHandle UniformHandle;
class GrGL2PtConicalGradientEffect : public GrGLGradientEffect {
public:
GrGL2PtConicalGradientEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&);
virtual ~GrGL2PtConicalGradientEffect() { }
virtual void emitCode(GrGLShaderBuilder*,
const GrDrawEffect&,
EffectKey,
const char* outputColor,
const char* inputColor,
const TransformedCoordsArray&,
const TextureSamplerArray&) SK_OVERRIDE;
virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
protected:
UniformHandle fParamUni;
const char* fVSVaryingName;
const char* fFSVaryingName;
bool fIsDegenerate;
// @{
/// Values last uploaded as uniforms
SkScalar fCachedCenter;
SkScalar fCachedRadius;
SkScalar fCachedDiffRadius;
// @}
private:
typedef GrGLGradientEffect INHERITED;
};
const GrBackendEffectFactory& Gr2PtConicalGradientEffect::getFactory() const {
return GrTBackendEffectFactory<Gr2PtConicalGradientEffect>::getInstance();
}
Gr2PtConicalGradientEffect::Gr2PtConicalGradientEffect(GrContext* ctx,
const SkTwoPointConicalGradient& shader,
const SkMatrix& matrix,
SkShader::TileMode tm) :
INHERITED(ctx, shader, matrix, tm),
fCenterX1(shader.getCenterX1()),
fRadius0(shader.getStartRadius()),
fDiffRadius(shader.getDiffRadius()) {
// We pass the linear part of the quadratic as a varying.
// float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
fBTransform = this->getCoordTransform();
SkMatrix& bMatrix = *fBTransform.accessMatrix();
SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius);
bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) +
SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0]));
bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) +
SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1]));
bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) +
SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2]));
this->addCoordTransform(&fBTransform);
}
GR_DEFINE_EFFECT_TEST(Gr2PtConicalGradientEffect);
GrEffectRef* Gr2PtConicalGradientEffect::TestCreate(SkRandom* random,
GrContext* context,
const GrDrawTargetCaps&,
GrTexture**) {
SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
SkScalar radius1 = random->nextUScalar1();
SkPoint center2;
SkScalar radius2;
do {
center2.set(random->nextUScalar1(), random->nextUScalar1());
radius2 = random->nextUScalar1 ();
// If the circles are identical the factory will give us an empty shader.
} while (radius1 == radius2 && center1 == center2);
SkColor colors[kMaxRandomGradientColors];
SkScalar stopsArray[kMaxRandomGradientColors];
SkScalar* stops = stopsArray;
SkShader::TileMode tm;
int colorCount = RandomGradientParams(random, colors, &stops, &tm);
SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
center2, radius2,
colors, stops, colorCount,
tm));
SkPaint paint;
return shader->asNewEffect(context, paint);
}
/////////////////////////////////////////////////////////////////////
GrGL2PtConicalGradientEffect::GrGL2PtConicalGradientEffect(const GrBackendEffectFactory& factory,
const GrDrawEffect& drawEffect)
: INHERITED(factory)
, fVSVaryingName(NULL)
, fFSVaryingName(NULL)
, fCachedCenter(SK_ScalarMax)
, fCachedRadius(-SK_ScalarMax)
, fCachedDiffRadius(-SK_ScalarMax) {
const Gr2PtConicalGradientEffect& data = drawEffect.castEffect<Gr2PtConicalGradientEffect>();
fIsDegenerate = data.isDegenerate();
}
void GrGL2PtConicalGradientEffect::emitCode(GrGLShaderBuilder* builder,
const GrDrawEffect&,
EffectKey key,
const char* outputColor,
const char* inputColor,
const TransformedCoordsArray& coords,
const TextureSamplerArray& samplers) {
this->emitUniforms(builder, key);
fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
kFloat_GrSLType, "Conical2FSParams", 6);
SkString cName("c");
SkString ac4Name("ac4");
SkString dName("d");
SkString qName("q");
SkString r0Name("r0");
SkString r1Name("r1");
SkString tName("t");
SkString p0; // 4a
SkString p1; // 1/a
SkString p2; // distance between centers
SkString p3; // start radius
SkString p4; // start radius squared
SkString p5; // difference in radii (r1 - r0)
builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
// We interpolate the linear component in coords[1].
SkASSERT(coords[0].type() == coords[1].type());
const char* coords2D;
SkString bVar;
if (kVec3f_GrSLType == coords[0].type()) {
builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
coords2D = "interpolants.xy";
bVar = "interpolants.z";
} else {
coords2D = coords[0].c_str();
bVar.printf("%s.x", coords[1].c_str());
}
// output will default to transparent black (we simply won't write anything
// else to it if invalid, instead of discarding or returning prematurely)
builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
// c = (x^2)+(y^2) - params[4]
builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
cName.c_str(), coords2D, coords2D, p4.c_str());
// Non-degenerate case (quadratic)
if (!fIsDegenerate) {
// ac4 = params[0] * c
builder->fsCodeAppendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_str(),
cName.c_str());
// d = b^2 - ac4
builder->fsCodeAppendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(),
bVar.c_str(), bVar.c_str(), ac4Name.c_str());
// only proceed if discriminant is >= 0
builder->fsCodeAppendf("\tif (%s >= 0.0) {\n", dName.c_str());
// intermediate value we'll use to compute the roots
// q = -0.5 * (b +/- sqrt(d))
builder->fsCodeAppendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1.0)"
" * sqrt(%s));\n", qName.c_str(), bVar.c_str(),
bVar.c_str(), dName.c_str());
// compute both roots
// r0 = q * params[1]
builder->fsCodeAppendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(),
qName.c_str(), p1.c_str());
// r1 = c / q
builder->fsCodeAppendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(),
cName.c_str(), qName.c_str());
// Note: If there are two roots that both generate radius(t) > 0, the
// Canvas spec says to choose the larger t.
// so we'll look at the larger one first:
builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(),
r0Name.c_str(), r1Name.c_str());
// if r(t) > 0, then we're done; t will be our x coordinate
builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
p5.c_str(), p3.c_str());
builder->fsCodeAppend("\t\t");
this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
// otherwise, if r(t) for the larger root was <= 0, try the other root
builder->fsCodeAppend("\t\t} else {\n");
builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(),
r0Name.c_str(), r1Name.c_str());
// if r(t) > 0 for the smaller root, then t will be our x coordinate
builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n",
tName.c_str(), p5.c_str(), p3.c_str());
builder->fsCodeAppend("\t\t\t");
this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
// end if (r(t) > 0) for smaller root
builder->fsCodeAppend("\t\t\t}\n");
// end if (r(t) > 0), else, for larger root
builder->fsCodeAppend("\t\t}\n");
// end if (discriminant >= 0)
builder->fsCodeAppend("\t}\n");
} else {
// linear case: t = -c/b
builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
cName.c_str(), bVar.c_str());
// if r(t) > 0, then t will be the x coordinate
builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
p5.c_str(), p3.c_str());
builder->fsCodeAppend("\t");
this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
builder->fsCodeAppend("\t}\n");
}
}
void GrGL2PtConicalGradientEffect::setData(const GrGLUniformManager& uman,
const GrDrawEffect& drawEffect) {
INHERITED::setData(uman, drawEffect);
const Gr2PtConicalGradientEffect& data = drawEffect.castEffect<Gr2PtConicalGradientEffect>();
SkASSERT(data.isDegenerate() == fIsDegenerate);
SkScalar centerX1 = data.center();
SkScalar radius0 = data.radius();
SkScalar diffRadius = data.diffRadius();
if (fCachedCenter != centerX1 ||
fCachedRadius != radius0 ||
fCachedDiffRadius != diffRadius) {
SkScalar a = SkScalarMul(centerX1, centerX1) - diffRadius * diffRadius;
// When we're in the degenerate (linear) case, the second
// value will be INF but the program doesn't read it. (We
// use the same 6 uniforms even though we don't need them
// all in the linear case just to keep the code complexity
// down).
float values[6] = {
SkScalarToFloat(a * 4),
1.f / (SkScalarToFloat(a)),
SkScalarToFloat(centerX1),
SkScalarToFloat(radius0),
SkScalarToFloat(SkScalarMul(radius0, radius0)),
SkScalarToFloat(diffRadius)
};
uman.set1fv(fParamUni, 6, values);
fCachedCenter = centerX1;
fCachedRadius = radius0;
fCachedDiffRadius = diffRadius;
}
}
GrGLEffect::EffectKey GrGL2PtConicalGradientEffect::GenKey(const GrDrawEffect& drawEffect,
const GrGLCaps&) {
enum {
kIsDegenerate = 1 << kBaseKeyBitCnt,
};
EffectKey key = GenBaseGradientKey(drawEffect);
if (drawEffect.castEffect<Gr2PtConicalGradientEffect>().isDegenerate()) {
key |= kIsDegenerate;
}
return key;
}
#endif

View File

@ -0,0 +1,73 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkTwoPointConicalGradient_gpu_DEFINED
#define SkTwoPointconicalGradient_gpu_DEFINED
#if SK_SUPPORT_GPU
#include "SkGradientShaderPriv.h"
class GrEffectRef;
class SkTwoPointConicalGradient;
class GrGL2PtConicalGradientEffect;
class Gr2PtConicalGradientEffect : public GrGradientEffect {
public:
static GrEffectRef* Create(GrContext* ctx,
const SkTwoPointConicalGradient& shader,
const SkMatrix& matrix,
SkShader::TileMode tm) {
AutoEffectUnref effect(SkNEW_ARGS(Gr2PtConicalGradientEffect, (ctx, shader, matrix, tm)));
return CreateEffectRef(effect);
}
virtual ~Gr2PtConicalGradientEffect() { }
static const char* Name() { return "Two-Point Conical Gradient"; }
virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
// The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); }
SkScalar center() const { return fCenterX1; }
SkScalar diffRadius() const { return fDiffRadius; }
SkScalar radius() const { return fRadius0; }
typedef GrGL2PtConicalGradientEffect GLEffect;
private:
virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
const Gr2PtConicalGradientEffect& s = CastEffect<Gr2PtConicalGradientEffect>(sBase);
return (INHERITED::onIsEqual(sBase) &&
this->fCenterX1 == s.fCenterX1 &&
this->fRadius0 == s.fRadius0 &&
this->fDiffRadius == s.fDiffRadius);
}
Gr2PtConicalGradientEffect(GrContext* ctx,
const SkTwoPointConicalGradient& shader,
const SkMatrix& matrix,
SkShader::TileMode tm);
GR_DECLARE_EFFECT_TEST;
// @{
// Cache of values - these can change arbitrarily, EXCEPT
// we shouldn't change between degenerate and non-degenerate?!
GrCoordTransform fBTransform;
SkScalar fCenterX1;
SkScalar fRadius0;
SkScalar fDiffRadius;
// @}
typedef GrGradientEffect INHERITED;
};
#endif
#endif