aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Yuqian Li <liyuqian@google.com>2017-12-13 12:35:28 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-12-13 18:58:00 +0000
commit84f18c422ae98085c452d3f721ec32228e92f531 (patch)
treee4745312fa13c3728e052c2b9fb118eabb3a4f85
parent37491d2674a2c71fc8ec451feeb1e7bdb08d6fd1 (diff)
Reimplement TwoPointConicalGradient_gpu
4.24% faster in gradient_conical_clamp_shallow_dither 6.43% faster in gradient_conicalOutZero_clamp_hicolor 10.41% faster in gradient_conicalOut_clamp_hicolor 14.85% faster in gradient_conicalOutZero_clamp_3color 16.72% faster in gradient_conicalOut_clamp 24.30% faster in gradient_conicalOut_clamp_3color CQ_INCLUDE_TRYBOTS=skia.primary:Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-All-Android,Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-All-Android Bug: skia: Change-Id: I6bc1f24c9463fc9c2acbcba7bd4d55b37ade1613 Reviewed-on: https://skia-review.googlesource.com/82161 Reviewed-by: Greg Daniel <egdaniel@google.com> Commit-Queue: Yuqian Li <liyuqian@google.com>
-rw-r--r--gm/gradients_2pt_conical.cpp14
-rw-r--r--src/gpu/GrProcessor.cpp2
-rw-r--r--src/gpu/GrProcessor.h6
-rw-r--r--src/shaders/gradients/SkGradientShaderPriv.h2
-rw-r--r--src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp1455
5 files changed, 303 insertions, 1176 deletions
diff --git a/gm/gradients_2pt_conical.cpp b/gm/gradients_2pt_conical.cpp
index 3cda2c656d..3eb5ab649e 100644
--- a/gm/gradients_2pt_conical.cpp
+++ b/gm/gradients_2pt_conical.cpp
@@ -49,6 +49,17 @@ static sk_sp<SkShader> Make2ConicalOutside(const SkPoint pts[2], const GradData&
data.fPos, data.fCount, tm, 0, &localMatrix);
}
+static sk_sp<SkShader> Make2ConicalOutsideStrip(const SkPoint pts[2], const GradData& data,
+ SkShader::TileMode tm, const SkMatrix& localMatrix) {
+ SkPoint center0, center1;
+ SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
+ SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
+ center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
+ center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
+ return SkGradientShader::MakeTwoPointConical(center0, radius1, center1, radius1, data.fColors,
+ data.fPos, data.fCount, tm, 0, &localMatrix);
+}
+
static sk_sp<SkShader> Make2ConicalOutsideFlip(const SkPoint pts[2], const GradData& data,
SkShader::TileMode tm, const SkMatrix& localMatrix) {
SkPoint center0, center1;
@@ -253,7 +264,8 @@ typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data,
constexpr GradMaker gGradMakersOutside[] = {
Make2ConicalOutside, Make2ConicalOutsideFlip,
- Make2ConicalZeroRadOutside, Make2ConicalZeroRadFlipOutside
+ Make2ConicalZeroRadOutside, Make2ConicalZeroRadFlipOutside,
+ Make2ConicalOutsideStrip
};
constexpr GradMaker gGradMakersInside[] = {
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index 4294c9fae6..8f8e7979bb 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -54,7 +54,7 @@ SkTArray<GrXPFactoryTestFactory*, true>* GrXPFactoryTestFactory::GetFactories()
* we verify the count is as expected. If a new factory is added, then these numbers must be
* manually adjusted.
*/
-static const int kFPFactoryCount = 42;
+static const int kFPFactoryCount = 38;
static const int kGPFactoryCount = 14;
static const int kXPFactoryCount = 4;
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
index 5947f9fe27..0736d036cd 100644
--- a/src/gpu/GrProcessor.h
+++ b/src/gpu/GrProcessor.h
@@ -69,8 +69,6 @@ public:
kBlockInputFragmentProcessor_ClassID,
kCCPRClipProcessor_ClassID,
kCircleGeometryProcessor_ClassID,
- kCircleInside2PtConicalEffect_ClassID,
- kCircleOutside2PtConicalEffect_ClassID,
kCircularRRectEffect_ClassID,
kColorMatrixEffect_ClassID,
kColorTableEffect_ClassID,
@@ -83,11 +81,9 @@ public:
kDefaultGeoProc_ClassID,
kDIEllipseGeometryProcessor_ClassID,
kDisableColorXP_ClassID,
- kEdge2PtConicalEffect_ClassID,
+ kTwoPointConicalEffect_ClassID,
kEllipseGeometryProcessor_ClassID,
kEllipticalRRectEffect_ClassID,
- kFocalInside2PtConicalEffect_ClassID,
- kFocalOutside2PtConicalEffect_ClassID,
kGP_ClassID,
kGrAARectEffect_ClassID,
kGrAlphaThresholdFragmentProcessor_ClassID,
diff --git a/src/shaders/gradients/SkGradientShaderPriv.h b/src/shaders/gradients/SkGradientShaderPriv.h
index 9d82e82ef8..28c4666d4c 100644
--- a/src/shaders/gradients/SkGradientShaderPriv.h
+++ b/src/shaders/gradients/SkGradientShaderPriv.h
@@ -255,7 +255,7 @@ protected:
GrGradientEffect(ClassID classID, const CreateArgs&, bool isOpaque);
explicit GrGradientEffect(const GrGradientEffect&); // facilitates clone() implementations
- void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final;
+ void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
// Helper function used by derived class factories to handle color space transformation and
// modulation by input alpha.
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
index a11a466976..ae8ab9d3b4 100644
--- a/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
+++ b/src/shaders/gradients/SkTwoPointConicalGradient_gpu.cpp
@@ -9,653 +9,152 @@
#include "SkTwoPointConicalGradient.h"
#if SK_SUPPORT_GPU
-#include "GrCoordTransform.h"
-#include "GrPaint.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLProgramDataManager.h"
#include "glsl/GrGLSLUniformHandler.h"
#include "SkTwoPointConicalGradient_gpu.h"
-#include <cmath>
-
// For brevity
typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
-static const SkScalar kErrorTol = 0.00001f;
-static const SkScalar kEdgeErrorTol = 5.f * kErrorTol;
-
-/**
- * We have three general cases for 2pt conical gradients. First we always assume that
- * the start radius <= end radius. Our first case (kInside_) is when the start circle
- * is completely enclosed by the end circle. The second case (kOutside_) is the case
- * when the start circle is either completely outside the end circle or the circles
- * overlap. The final case (kEdge_) is when the start circle is inside the end one,
- * but the two are just barely touching at 1 point along their edges.
- */
-enum ConicalType {
- kInside_ConicalType,
- kOutside_ConicalType,
- kEdge_ConicalType,
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader,
- SkMatrix* invLMatrix) {
- // Inverse of the current local matrix is passed in then,
- // translate to center1, rotate so center2 is on x axis.
- const SkPoint& center1 = shader.getStartCenter();
- const SkPoint& center2 = shader.getEndCenter();
-
- invLMatrix->postTranslate(-center1.fX, -center1.fY);
-
- SkPoint diff = center2 - center1;
- SkScalar diffLen = diff.length();
- if (0 != diffLen) {
- SkScalar invDiffLen = SkScalarInvert(diffLen);
- SkMatrix rot;
- rot.setSinCos(-invDiffLen * diff.fY, invDiffLen * diff.fX);
- invLMatrix->postConcat(rot);
- }
-}
-
-class Edge2PtConicalEffect : public GrGradientEffect {
+class TwoPointConicalEffect : public GrGradientEffect {
public:
- class GLSLEdge2PtConicalProcessor;
-
- static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args) {
- return GrGradientEffect::AdjustFP(std::unique_ptr<Edge2PtConicalEffect>(
- new Edge2PtConicalEffect(args)),
- args);
- }
-
- const char* name() const override {
- return "Two-Point Conical Gradient Edge Touching";
- }
-
- // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
- SkScalar center() const { return fCenterX1; }
- SkScalar diffRadius() const { return fDiffRadius; }
- SkScalar radius() const { return fRadius0; }
-
- std::unique_ptr<GrFragmentProcessor> clone() const override {
- return std::unique_ptr<GrFragmentProcessor>(new Edge2PtConicalEffect(*this));
- }
-
-private:
- GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
- bool onIsEqual(const GrFragmentProcessor& sBase) const override {
- const Edge2PtConicalEffect& s = sBase.cast<Edge2PtConicalEffect>();
- return (INHERITED::onIsEqual(sBase) &&
- this->fCenterX1 == s.fCenterX1 &&
- this->fRadius0 == s.fRadius0 &&
- this->fDiffRadius == s.fDiffRadius);
- }
+ class DegeneratedGLSLProcessor; // radial (center0 == center1) or strip (r0 == r1) case
+ class FocalGLSLProcessor; // all other cases where we can derive a focal point
+
+ enum Type {
+ kRadial_Type,
+ kStrip_Type,
+ kFocal_Type
+ };
+
+ struct Data {
+ SkScalar fRadius0;
+ SkScalar fDiffRadius;
+ Type fType;
+ bool fIsSwapped;
+
+ // Construct from the shader, and set the matrix accordingly
+ Data(const SkTwoPointConicalGradient& shader, SkMatrix& matrix);
+
+ bool operator== (const Data& d) const {
+ return fRadius0 == d.fRadius0 && fDiffRadius == d.fDiffRadius && fType == d.fType &&
+ fIsSwapped == d.fIsSwapped;
+ }
+ };
- explicit Edge2PtConicalEffect(const CreateArgs& args)
- : INHERITED(kEdge2PtConicalEffect_ClassID, args,
- false /* opaque: draws transparent black outside of the cone. */) {
- const SkTwoPointConicalGradient& shader =
- *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
- fCenterX1 = shader.getCenterX1();
- fRadius0 = shader.getStartRadius();
- fDiffRadius = shader.getDiffRadius();
- // We should only be calling this shader if we are degenerate case with touching circles
- // When deciding if we are in edge case, we scaled by the end radius for cases when the
- // start radius was close to zero, otherwise we scaled by the start radius. In addition
- // Our test for the edge case in set_matrix_circle_conical has a higher tolerance so we
- // need the sqrt value below
- SkASSERT(SkScalarAbs(SkScalarAbs(fDiffRadius) - fCenterX1) <
- (fRadius0 < kErrorTol ? shader.getEndRadius() * kEdgeErrorTol :
- fRadius0 * sqrt(kEdgeErrorTol)));
+ static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args, const Data& data);
- // 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 = fRadius0 * fDiffRadius;
- bMatrix[SkMatrix::kMScaleX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMScaleX] +
- r0dr * bMatrix[SkMatrix::kMPersp0]);
- bMatrix[SkMatrix::kMSkewX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMSkewX] +
- r0dr * bMatrix[SkMatrix::kMPersp1]);
- bMatrix[SkMatrix::kMTransX] = -2 * (fCenterX1 * bMatrix[SkMatrix::kMTransX] +
- r0dr * bMatrix[SkMatrix::kMPersp2]);
- this->addCoordTransform(&fBTransform);
- }
+ SkScalar diffRadius() const { return fData.fDiffRadius; }
+ SkScalar r0() const { return fData.fRadius0; }
+ SkScalar r1() const { return fData.fRadius0 + fData.fDiffRadius; }
- explicit Edge2PtConicalEffect(const Edge2PtConicalEffect& that)
- : INHERITED(that)
- , fBTransform(that.fBTransform)
- , fCenterX1(that.fCenterX1)
- , fRadius0(that.fRadius0)
- , fDiffRadius(that.fDiffRadius) {
- this->addCoordTransform(&fBTransform);
- }
+ const char* name() const override { return "Two-Point Conical Gradient"; }
- GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+ // Whether the focal point (0, 0) is on the end circle with center (1, 0) and radius r1. If this
+ // is true, it's as if an aircraft is flying at Mach 1 and all circles (soundwaves) will go
+ // through the focal point (aircraft). In our previous implementations, this was known as the
+ // edge case where the inside circle touches the outside circle (on the focal point). If we were
+ // to solve for t bruteforcely using a quadratic equation, this case implies that the quadratic
+ // equation degenerates to a linear equation.
+ bool isFocalOnCircle() const { return SkScalarNearlyZero(1 - this->r1()); }
+ bool isSwapped() const { return fData.fIsSwapped; }
- // @{
- // Cache of values - these can change arbitrarily, EXCEPT
- // we shouldn't change between degenerate and non-degenerate?!
+ Type getType() const { return fData.fType; }
- GrCoordTransform fBTransform;
- SkScalar fCenterX1;
- SkScalar fRadius0;
- SkScalar fDiffRadius;
+ // Whether the t we solved is always valid (so we don't need to check r(t) > 0).
+ bool isWellBehaved() const { return !this->isFocalOnCircle() && this->r1() > 1; }
- // @}
+ // Whether r0 == 0 so it's focal without any transformation
+ bool isNativelyFocal() const { return SkScalarNearlyZero(fData.fRadius0); }
- typedef GrGradientEffect INHERITED;
-};
-
-class Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor : public GrGradientEffect::GLSLProcessor {
-public:
- GLSLEdge2PtConicalProcessor(const GrProcessor&);
-
- virtual void emitCode(EmitArgs&) override;
+ bool isRadiusIncreasing() const { return fData.fDiffRadius > 0; }
protected:
- void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
-
- UniformHandle fParamUni;
-
- const char* fVSVaryingName;
- const char* fFSVaryingName;
-
- // @{
- /// Values last uploaded as uniforms
-
- SkScalar fCachedRadius;
- SkScalar fCachedDiffRadius;
-
- // @}
-
-private:
- typedef GrGradientEffect::GLSLProcessor INHERITED;
-
-};
-
-GrGLSLFragmentProcessor* Edge2PtConicalEffect::onCreateGLSLInstance() const {
- return new Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor(*this);
-}
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Edge2PtConicalEffect);
-
-/*
- * All Two point conical gradient test create functions may occasionally create edge case shaders
- */
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> Edge2PtConicalEffect::TestCreate(GrProcessorTestData* d) {
- SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
- SkScalar radius1 = d->fRandom->nextUScalar1();
- SkPoint center2;
- SkScalar radius2;
- do {
- center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
- // If the circles are identical the factory will give us an empty shader.
- // This will happen if we pick identical centers
- } while (center1 == center2);
-
- // Below makes sure that circle one is contained within circle two
- // and both circles are touching on an edge
- SkPoint diff = center2 - center1;
- SkScalar diffLen = diff.length();
- radius2 = radius1 + diffLen;
-
- RandomGradientParams params(d->fRandom);
- auto shader = params.fUseColors4f ?
- SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
- params.fColors4f, params.fColorSpace, params.fStops,
- params.fColorCount, params.fTileMode) :
- SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
- params.fColors, params.fStops,
- params.fColorCount, params.fTileMode);
- GrTest::TestAsFPArgs asFPArgs(d);
- std::unique_ptr<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
- GrAlwaysAssert(fp);
- return fp;
-}
-#endif
-
-Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::GLSLEdge2PtConicalProcessor(const GrProcessor&)
- : fVSVaryingName(nullptr)
- , fFSVaryingName(nullptr)
- , fCachedRadius(-SK_ScalarMax)
- , fCachedDiffRadius(-SK_ScalarMax) {}
-
-void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::emitCode(EmitArgs& args) {
- const Edge2PtConicalEffect& ge = args.fFp.cast<Edge2PtConicalEffect>();
- GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
- this->emitUniforms(uniformHandler, ge);
- fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
- "Conical2FSParams");
-
- SkString cName("c");
- SkString tName("t");
- SkString p0; // start radius
- SkString p1; // start radius squared
- SkString p2; // difference in radii (r1 - r0)
-
-
- p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
- p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
- p2.appendf("%s.z", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
-
- // We interpolate the linear component in coords[1].
- SkASSERT(args.fTransformedCoords[0].getType() == args.fTransformedCoords[1].getType());
- const char* coords2D;
- SkString bVar;
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
- if (kHalf3_GrSLType == args.fTransformedCoords[0].getType()) {
- fragBuilder->codeAppendf("\thalf3 interpolants = half3(%s.xy / %s.z, %s.x / %s.z);\n",
- args.fTransformedCoords[0].c_str(),
- args.fTransformedCoords[0].c_str(),
- args.fTransformedCoords[1].c_str(),
- args.fTransformedCoords[1].c_str());
- coords2D = "interpolants.xy";
- bVar = "interpolants.z";
- } else {
- coords2D = args.fTransformedCoords[0].c_str();
- bVar.printf("%s.x", args.fTransformedCoords[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)
- fragBuilder->codeAppendf("\t%s = half4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
-
- // c = (x^2)+(y^2) - params[1]
- fragBuilder->codeAppendf("\thalf %s = dot(%s, %s) - %s;\n",
- cName.c_str(), coords2D, coords2D, p1.c_str());
-
- // linear case: t = -c/b
- fragBuilder->codeAppendf("\thalf %s = -(%s / %s);\n", tName.c_str(),
- cName.c_str(), bVar.c_str());
-
- // if r(t) > 0, then t will be the x coordinate
- fragBuilder->codeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
- p2.c_str(), p0.c_str());
- fragBuilder->codeAppend("\t");
- this->emitColor(fragBuilder,
- uniformHandler,
- args.fShaderCaps,
- ge,
- tName.c_str(),
- args.fOutputColor,
- args.fInputColor,
- args.fTexSamplers);
- fragBuilder->codeAppend("\t}\n");
-}
-
-void Edge2PtConicalEffect::GLSLEdge2PtConicalProcessor::onSetData(
- const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
- INHERITED::onSetData(pdman, processor);
- const Edge2PtConicalEffect& data = processor.cast<Edge2PtConicalEffect>();
- SkScalar radius0 = data.radius();
- SkScalar diffRadius = data.diffRadius();
-
- if (fCachedRadius != radius0 ||
- fCachedDiffRadius != diffRadius) {
-
- pdman.set3f(fParamUni, radius0, radius0 * radius0, diffRadius);
- fCachedRadius = radius0;
- fCachedDiffRadius = diffRadius;
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// Focal Conical Gradients
-//////////////////////////////////////////////////////////////////////////////
-
-static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader,
- SkMatrix* invLMatrix, SkScalar* focalX) {
- // Inverse of the current local matrix is passed in then,
- // translate, scale, and rotate such that endCircle is unit circle on x-axis,
- // and focal point is at the origin.
- ConicalType conicalType;
- const SkPoint& focal = shader.getStartCenter();
- const SkPoint& centerEnd = shader.getEndCenter();
- SkScalar radius = shader.getEndRadius();
- SkScalar invRadius = 1.f / radius;
-
- SkMatrix matrix;
-
- matrix.setTranslate(-centerEnd.fX, -centerEnd.fY);
- matrix.postScale(invRadius, invRadius);
-
- SkPoint focalTrans;
- matrix.mapPoints(&focalTrans, &focal, 1);
- *focalX = focalTrans.length();
-
- if (0.f != *focalX) {
- SkScalar invFocalX = SkScalarInvert(*focalX);
- SkMatrix rot;
- rot.setSinCos(-invFocalX * focalTrans.fY, invFocalX * focalTrans.fX);
- matrix.postConcat(rot);
- }
-
- matrix.postTranslate(-(*focalX), 0.f);
-
- // If the focal point is touching the edge of the circle it will
- // cause a degenerate case that must be handled separately
- // kEdgeErrorTol = 5 * kErrorTol was picked after manual testing the
- // stability trade off versus the linear approx used in the Edge Shader
- if (SkScalarAbs(1.f - (*focalX)) < kEdgeErrorTol) {
- return kEdge_ConicalType;
- }
-
- // Scale factor 1 / (1 - focalX * focalX)
- SkScalar oneMinusF2 = 1.f - *focalX * *focalX;
- SkScalar s = SkScalarInvert(oneMinusF2);
-
-
- if (s >= 0.f) {
- conicalType = kInside_ConicalType;
- matrix.postScale(s, s * SkScalarSqrt(oneMinusF2));
- } else {
- conicalType = kOutside_ConicalType;
- matrix.postScale(s, s);
+ void onGetGLSLProcessorKey(const GrShaderCaps& c, GrProcessorKeyBuilder* b) const override {
+ INHERITED::onGetGLSLProcessorKey(c, b);
+ uint32_t key = 0;
+ key |= fData.fType;
+ SkASSERT(key < (1 << 2));
+ key |= (this->isFocalOnCircle() << 2);
+ key |= (this->isWellBehaved() << 3);
+ key |= (this->isRadiusIncreasing() << 4);
+ key |= (this->isNativelyFocal() << 5);
+ key |= (this->isSwapped() << 6);
+ SkASSERT(key < (1 << 7));
+ b->add32(key);
}
- invLMatrix->postConcat(matrix);
-
- return conicalType;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-class FocalOutside2PtConicalEffect : public GrGradientEffect {
-public:
- class GLSLFocalOutside2PtConicalProcessor;
-
- static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX,
- bool isFlipped) {
- return GrGradientEffect::AdjustFP(std::unique_ptr<FocalOutside2PtConicalEffect>(
- new FocalOutside2PtConicalEffect(args, focalX, isFlipped)),
- args);
- }
-
- const char* name() const override {
- return "Two-Point Conical Gradient Focal Outside";
- }
+ GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
std::unique_ptr<GrFragmentProcessor> clone() const override {
- return std::unique_ptr<GrFragmentProcessor>(new FocalOutside2PtConicalEffect(*this));
+ return std::unique_ptr<GrFragmentProcessor>(new TwoPointConicalEffect(*this));
}
- bool isFlipped() const { return fIsFlipped; }
- SkScalar focal() const { return fFocalX; }
-
-private:
- GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
bool onIsEqual(const GrFragmentProcessor& sBase) const override {
- const FocalOutside2PtConicalEffect& s = sBase.cast<FocalOutside2PtConicalEffect>();
- return (INHERITED::onIsEqual(sBase) &&
- this->fFocalX == s.fFocalX &&
- this->fIsFlipped == s.fIsFlipped);
+ const TwoPointConicalEffect& s = sBase.cast<TwoPointConicalEffect>();
+ return (INHERITED::onIsEqual(sBase) && fData == s.fData);
}
- FocalOutside2PtConicalEffect(const CreateArgs& args, SkScalar focalX, bool isFlipped)
- : INHERITED(kFocalOutside2PtConicalEffect_ClassID, args,
- false /* opaque: draws transparent black outside of the cone. */)
- , fFocalX(focalX)
- , fIsFlipped(isFlipped) {}
+ explicit TwoPointConicalEffect(const CreateArgs& args, const Data data)
+ : INHERITED(kTwoPointConicalEffect_ClassID, args,
+ false /* opaque: draws transparent black outside of the cone. */)
+ , fData(data) {}
- explicit FocalOutside2PtConicalEffect(const FocalOutside2PtConicalEffect& that)
- : INHERITED(that), fFocalX(that.fFocalX), fIsFlipped(that.fIsFlipped) {
- }
+ explicit TwoPointConicalEffect(const TwoPointConicalEffect& that)
+ : INHERITED(that)
+ , fData(that.fData) {}
GR_DECLARE_FRAGMENT_PROCESSOR_TEST
- SkScalar fFocalX;
- bool fIsFlipped;
+ Data fData;
typedef GrGradientEffect INHERITED;
};
-class FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor
- : public GrGradientEffect::GLSLProcessor {
-public:
- GLSLFocalOutside2PtConicalProcessor(const GrProcessor&);
-
- virtual void emitCode(EmitArgs&) override;
-
-protected:
- void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
-
- UniformHandle fParamUni;
-
- const char* fVSVaryingName;
- const char* fFSVaryingName;
-
- // @{
- /// Values last uploaded as uniforms
-
- SkScalar fCachedFocal;
- SkScalar fCachedFlipSign;
-
- // @}
-
-private:
- typedef GrGradientEffect::GLSLProcessor INHERITED;
-
-};
-
-GrGLSLFragmentProcessor* FocalOutside2PtConicalEffect::onCreateGLSLInstance() const {
- return new FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor(*this);
-}
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalOutside2PtConicalEffect);
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(TwoPointConicalEffect);
-/*
- * All Two point conical gradient test create functions may occasionally create edge case shaders
- */
#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> FocalOutside2PtConicalEffect::TestCreate(
+std::unique_ptr<GrFragmentProcessor> TwoPointConicalEffect::TestCreate(
GrProcessorTestData* d) {
SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
- SkScalar radius1 = 0.f;
- SkPoint center2;
- SkScalar radius2;
- do {
- center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
- // Need to make sure the centers are not the same or else focal point will be inside
- } while (center1 == center2);
-
- SkPoint diff = center2 - center1;
- SkScalar diffLen = diff.length();
- // Below makes sure that the focal point is not contained within circle two
- radius2 = d->fRandom->nextRangeF(0.f, diffLen);
-
- RandomGradientParams params(d->fRandom);
- auto shader = params.fUseColors4f ?
- SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
- params.fColors4f, params.fColorSpace, params.fStops,
- params.fColorCount, params.fTileMode) :
- SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
- params.fColors, params.fStops,
- params.fColorCount, params.fTileMode);
- GrTest::TestAsFPArgs asFPArgs(d);
- std::unique_ptr<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
- GrAlwaysAssert(fp);
- return fp;
-}
-#endif
-
-FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor
- ::GLSLFocalOutside2PtConicalProcessor(const GrProcessor& processor)
- : fVSVaryingName(nullptr)
- , fFSVaryingName(nullptr)
- , fCachedFocal(SK_ScalarMax)
- , fCachedFlipSign(SK_ScalarMax) {}
-
-void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::emitCode(EmitArgs& args) {
- const FocalOutside2PtConicalEffect& ge = args.fFp.cast<FocalOutside2PtConicalEffect>();
- GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
- this->emitUniforms(uniformHandler, ge);
- fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType,
- "Conical2FSParams");
- SkString tName("t");
- // TODO: get rid of these locals?
- SkString p0; // focalX
- SkString p1; // 1 - focalX * focalX
- p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
- p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
-
- // params.x = focalX
- // params.y = 1 - focalX * focalX
- // params.z = flipSign
- GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
-
- // if we have a float3 from being in perspective, convert it to a float2 first
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
- SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
- const char* coords2D = coords2DString.c_str();
-
- // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2)
-
- // output will default to transparent black (we simply won't write anything
- // else to it if invalid, instead of discarding or returning prematurely)
- fragBuilder->codeAppendf("\t%s = half4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
-
- fragBuilder->codeAppendf("\thalf xs = %s.x * %s.x;\n", coords2D, coords2D);
- fragBuilder->codeAppendf("\thalf ys = %s.y * %s.y;\n", coords2D, coords2D);
- fragBuilder->codeAppendf("\thalf d = xs + %s * ys;\n", p1.c_str());
- fragBuilder->codeAppendf("\thalf %s = %s.x * %s + sqrt(d);\n",
- tName.c_str(), coords2D, p0.c_str());
-
- fragBuilder->codeAppendf("\tif (%s.z * %s >= 0.0 && d >= 0.0) {\n",
- params.c_str(), tName.c_str());
- fragBuilder->codeAppend("\t\t");
- this->emitColor(fragBuilder,
- uniformHandler,
- args.fShaderCaps,
- ge,
- tName.c_str(),
- args.fOutputColor,
- args.fInputColor,
- args.fTexSamplers);
- fragBuilder->codeAppend("\t}\n");
-}
-
-void FocalOutside2PtConicalEffect::GLSLFocalOutside2PtConicalProcessor::onSetData(
- const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
- INHERITED::onSetData(pdman, processor);
- const FocalOutside2PtConicalEffect& data = processor.cast<FocalOutside2PtConicalEffect>();
- SkScalar focal = data.focal();
- SkScalar flipSign = data.isFlipped() ? -1 : 1;
-
- if (fCachedFocal != focal || fCachedFlipSign != flipSign) {
- SkScalar oneMinus2F = 1.f - focal * focal;
-
- pdman.set3f(fParamUni, focal, oneMinus2F, flipSign);
- fCachedFocal = focal;
- fCachedFlipSign = flipSign;
- }
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-class FocalInside2PtConicalEffect : public GrGradientEffect {
-public:
- class GLSLFocalInside2PtConicalProcessor;
-
- static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args, SkScalar focalX) {
- return GrGradientEffect::AdjustFP(std::unique_ptr<FocalInside2PtConicalEffect>(
- new FocalInside2PtConicalEffect(args, focalX)),
- args);
- }
-
- const char* name() const override {
- return "Two-Point Conical Gradient Focal Inside";
- }
-
- std::unique_ptr<GrFragmentProcessor> clone() const override {
- return std::unique_ptr<GrFragmentProcessor>(new FocalInside2PtConicalEffect(*this));
+ SkPoint center2 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
+ SkScalar radius1 = d->fRandom->nextUScalar1();
+ SkScalar radius2 = d->fRandom->nextUScalar1();
+
+ constexpr int kTestTypeMask = (1 << 2) - 1,
+ kTestNativelyFocalBit = (1 << 2),
+ kTestFocalOnCircleBit = (1 << 3),
+ kTestSwappedBit = (1 << 4);
+ // We won't treat isWellDefined and isRadiusIncreasing specially beacuse they
+ // should have high probability to be turned on and off as we're getting random
+ // radii and centers.
+
+ int mask = d->fRandom->nextU();
+ int type = mask & kTestTypeMask;
+ if (type == TwoPointConicalEffect::kRadial_Type) {
+ center2 = center1;
+ } else if (type == TwoPointConicalEffect::kStrip_Type) {
+ radius1 = SkTMax(radius1, .1f); // Make sure that the radius is non-zero
+ radius2 = radius1;
+ } else { // kFocal_Type
+ if (kTestNativelyFocalBit & mask) {
+ radius1 = 0;
+ }
+ if (kTestFocalOnCircleBit & mask) {
+ radius2 = radius1 + SkPoint::Distance(center1, center2);
+ }
+ if (kTestSwappedBit & mask) {
+ std::swap(radius1, radius2);
+ radius2 = 0;
+ }
}
- SkScalar focal() const { return fFocalX; }
-
- typedef FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor GLSLProcessor;
-
-private:
- GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
- bool onIsEqual(const GrFragmentProcessor& sBase) const override {
- const FocalInside2PtConicalEffect& s = sBase.cast<FocalInside2PtConicalEffect>();
- return (INHERITED::onIsEqual(sBase) &&
- this->fFocalX == s.fFocalX);
+ if (SkScalarNearlyZero(radius1 - radius2) &&
+ SkScalarNearlyZero(SkPoint::Distance(center1, center2))) {
+ radius2 += .1f; // make sure that we're not degenerated
}
-
- FocalInside2PtConicalEffect(const CreateArgs& args, SkScalar focalX)
- : INHERITED(kFocalInside2PtConicalEffect_ClassID, args,
- args.fShader->colorsAreOpaque()), fFocalX(focalX) {}
-
- explicit FocalInside2PtConicalEffect(const FocalInside2PtConicalEffect& that)
- : INHERITED(that), fFocalX(that.fFocalX) {}
-
- GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-
- SkScalar fFocalX;
-
- typedef GrGradientEffect INHERITED;
-};
-
-class FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor
- : public GrGradientEffect::GLSLProcessor {
-public:
- GLSLFocalInside2PtConicalProcessor(const GrProcessor&);
-
- virtual void emitCode(EmitArgs&) override;
-
-protected:
- void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
-
- UniformHandle fFocalUni;
-
- const char* fVSVaryingName;
- const char* fFSVaryingName;
-
- // @{
- /// Values last uploaded as uniforms
-
- SkScalar fCachedFocal;
-
- // @}
-
-private:
- typedef GrGradientEffect::GLSLProcessor INHERITED;
-
-};
-
-GrGLSLFragmentProcessor* FocalInside2PtConicalEffect::onCreateGLSLInstance() const {
- return new FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor(*this);
-}
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(FocalInside2PtConicalEffect);
-
-/*
- * All Two point conical gradient test create functions may occasionally create edge case shaders
- */
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> FocalInside2PtConicalEffect::TestCreate(
- GrProcessorTestData* d) {
- SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
- SkScalar radius1 = 0.f;
- SkPoint center2;
- SkScalar radius2;
- do {
- center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
- // Below makes sure radius2 is larger enouch such that the focal point
- // is inside the end circle
- SkScalar increase = d->fRandom->nextUScalar1();
- SkPoint diff = center2 - center1;
- SkScalar diffLen = diff.length();
- radius2 = diffLen + increase;
- // If the circles are identical the factory will give us an empty shader.
- } while (radius1 == radius2 && center1 == center2);
-
RandomGradientParams params(d->fRandom);
auto shader = params.fUseColors4f ?
SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
@@ -671,579 +170,162 @@ std::unique_ptr<GrFragmentProcessor> FocalInside2PtConicalEffect::TestCreate(
}
#endif
-FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor
- ::GLSLFocalInside2PtConicalProcessor(const GrProcessor&)
- : fVSVaryingName(nullptr)
- , fFSVaryingName(nullptr)
- , fCachedFocal(SK_ScalarMax) {}
-
-void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::emitCode(EmitArgs& args) {
- const FocalInside2PtConicalEffect& ge = args.fFp.cast<FocalInside2PtConicalEffect>();
- GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
- this->emitUniforms(uniformHandler, ge);
- fFocalUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
- "Conical2FSParams");
- SkString tName("t");
-
- // this is the distance along x-axis from the end center to focal point in
- // transformed coordinates
- GrShaderVar focal = uniformHandler->getUniformVariable(fFocalUni);
-
- // if we have a float3 from being in perspective, convert it to a float2 first
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
- SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
- const char* coords2D = coords2DString.c_str();
-
- // t = p.x * focalX + length(p)
- fragBuilder->codeAppendf("\thalf %s = %s.x * %s + length(%s);\n", tName.c_str(),
- coords2D, focal.c_str(), coords2D);
-
- this->emitColor(fragBuilder,
- uniformHandler,
- args.fShaderCaps,
- ge,
- tName.c_str(),
- args.fOutputColor,
- args.fInputColor,
- args.fTexSamplers);
-}
-
-void FocalInside2PtConicalEffect::GLSLFocalInside2PtConicalProcessor::onSetData(
- const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
- INHERITED::onSetData(pdman, processor);
- const FocalInside2PtConicalEffect& data = processor.cast<FocalInside2PtConicalEffect>();
- SkScalar focal = data.focal();
-
- if (fCachedFocal != focal) {
- pdman.set1f(fFocalUni, SkScalarToFloat(focal));
- fCachedFocal = focal;
- }
-}
-
//////////////////////////////////////////////////////////////////////////////
-// Circle Conical Gradients
+// DegeneratedGLSLProcessor
//////////////////////////////////////////////////////////////////////////////
-struct CircleConicalInfo {
- SkPoint fCenterEnd;
- SkScalar fA;
- SkScalar fB;
- SkScalar fC;
-
- bool operator==(const CircleConicalInfo& other) const {
- return fCenterEnd == other.fCenterEnd
- && fA == other.fA
- && fB == other.fB
- && fC == other.fC;
- }
-
- bool operator!=(const CircleConicalInfo& other) const { return !(*this == other); }
-
- // true when endRadius < startRadius
- bool isFlipped() const {
- // B = (endRadius/startRadius - 1) * C
- return std::signbit(fB) != std::signbit(fC);
- }
-};
-
-// Returns focal distance along x-axis in transformed coords
-static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader,
- SkMatrix* invLMatrix, CircleConicalInfo* info) {
- // Inverse of the current local matrix is passed in then,
- // translate and scale such that start circle is on the origin and has radius 1
- const SkPoint& centerStart = shader.getStartCenter();
- const SkPoint& centerEnd = shader.getEndCenter();
- SkScalar radiusStart = shader.getStartRadius();
- SkScalar radiusEnd = shader.getEndRadius();
-
- SkMatrix matrix;
-
- matrix.setTranslate(-centerStart.fX, -centerStart.fY);
-
- SkScalar invStartRad = 1.f / radiusStart;
- matrix.postScale(invStartRad, invStartRad);
-
- radiusEnd /= radiusStart;
-
- SkPoint centerEndTrans;
- matrix.mapPoints(&centerEndTrans, &centerEnd, 1);
-
- SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY
- - radiusEnd * radiusEnd + 2 * radiusEnd - 1;
-
- // Check to see if start circle is inside end circle with edges touching.
- // If touching we return that it is of kEdge_ConicalType, and leave the matrix setting
- // to the edge shader. kEdgeErrorTol = 5 * kErrorTol was picked after manual testing
- // so that C = 1 / A is stable, and the linear approximation used in the Edge shader is
- // still accurate.
- if (SkScalarAbs(A) < kEdgeErrorTol) {
- return kEdge_ConicalType;
- }
-
- SkScalar C = 1.f / A;
- SkScalar B = (radiusEnd - 1.f) * C;
-
- matrix.postScale(C, C);
-
- invLMatrix->postConcat(matrix);
-
- info->fCenterEnd = centerEndTrans;
- info->fA = A;
- info->fB = B;
- info->fC = C;
-
- // if A ends up being negative, the start circle is contained completely inside the end cirlce
- if (A < 0.f) {
- return kInside_ConicalType;
- }
- return kOutside_ConicalType;
-}
-
-class CircleInside2PtConicalEffect : public GrGradientEffect {
-public:
- class GLSLCircleInside2PtConicalProcessor;
-
- static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args,
- const CircleConicalInfo& info) {
- return GrGradientEffect::AdjustFP(std::unique_ptr<CircleInside2PtConicalEffect>(
- new CircleInside2PtConicalEffect(args, info)),
- args);
- }
-
- const char* name() const override { return "Two-Point Conical Gradient Inside"; }
+class TwoPointConicalEffect::DegeneratedGLSLProcessor : public GrGradientEffect::GLSLProcessor {
+protected:
+ void emitCode(EmitArgs& args) override {
+ const TwoPointConicalEffect& ge = args.fFp.cast<TwoPointConicalEffect>();
+ GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+ this->emitUniforms(uniformHandler, ge);
+ fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
+ "Conical2FSParams");
+
+ SkString p0; // r0 for radial case, r0^2 for strip case
+ p0.appendf("%s", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+ const char* tName = "t"; // the gradient
+
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+ SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+ const char* p = coords2D.c_str();
+
+ if (ge.getType() == kRadial_Type) {
+ fragBuilder->codeAppendf("\thalf %s = length(%s) - %s;", tName, p, p0.c_str());
+ } else {
+ // output will default to transparent black (we simply won't write anything
+ // else to it if invalid, instead of discarding or returning prematurely)
+ fragBuilder->codeAppendf("\t%s = half4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
+ fragBuilder->codeAppendf("\thalf temp = %s - %s.y * %s.y;", p0.c_str(), p, p);
+ fragBuilder->codeAppendf("\tif (temp >= 0) {");
+ fragBuilder->codeAppendf("\t\thalf %s = %s.x + sqrt(temp);", tName, p);
+ fragBuilder->codeAppend("\t\t");
+ }
- std::unique_ptr<GrFragmentProcessor> clone() const override {
- return std::unique_ptr<GrFragmentProcessor>(new CircleInside2PtConicalEffect(*this));
+ this->emitColor(fragBuilder,
+ uniformHandler,
+ args.fShaderCaps,
+ ge,
+ tName,
+ args.fOutputColor,
+ args.fInputColor,
+ args.fTexSamplers);
+
+ if (ge.getType() != kRadial_Type) {
+ fragBuilder->codeAppendf("\t}");
+ }
}
- SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
- SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
- SkScalar A() const { return fInfo.fA; }
- SkScalar B() const { return fInfo.fB; }
- SkScalar C() const { return fInfo.fC; }
- bool isFlipped() const { return fInfo.isFlipped(); }
-
-private:
- GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
- bool onIsEqual(const GrFragmentProcessor& sBase) const override {
- const CircleInside2PtConicalEffect& s = sBase.cast<CircleInside2PtConicalEffect>();
- return INHERITED::onIsEqual(sBase) && fInfo == s.fInfo;
+ void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& p) override {
+ INHERITED::onSetData(pdman, p);
+ const TwoPointConicalEffect& data = p.cast<TwoPointConicalEffect>();
+ // kRadialType should imply r1 - r0 = 1 (after our transformation) so r0 = r0 / (r1 - r0)
+ SkASSERT(data.getType() == kStrip_Type || SkScalarNearlyZero(data.r1() - data.r0() - 1));
+ pdman.set1f(fParamUni, data.getType() == kRadial_Type ? data.r0() : data.r0() * data.r0());
}
- CircleInside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info)
- : INHERITED(kCircleInside2PtConicalEffect_ClassID, args,
- args.fShader->colorsAreOpaque()), fInfo(info) {}
-
- explicit CircleInside2PtConicalEffect(const CircleInside2PtConicalEffect& that)
- : INHERITED(that), fInfo(that.fInfo) {}
-
- GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-
- const CircleConicalInfo fInfo;
-
- typedef GrGradientEffect INHERITED;
-};
-
-class CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor
- : public GrGradientEffect::GLSLProcessor {
-public:
- GLSLCircleInside2PtConicalProcessor(const GrProcessor&);
-
- virtual void emitCode(EmitArgs&) override;
-
-protected:
- void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
-
- UniformHandle fCenterUni;
UniformHandle fParamUni;
- const char* fVSVaryingName;
- const char* fFSVaryingName;
-
- // @{
- /// Values last uploaded as uniforms
-
- SkScalar fCachedCenterX;
- SkScalar fCachedCenterY;
- SkScalar fCachedA;
- SkScalar fCachedB;
- SkScalar fCachedC;
- SkScalar fCachedFlipSign;
-
- // @}
-
private:
typedef GrGradientEffect::GLSLProcessor INHERITED;
-
};
-GrGLSLFragmentProcessor* CircleInside2PtConicalEffect::onCreateGLSLInstance() const {
- return new CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor(*this);
-}
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleInside2PtConicalEffect);
-
-/*
- * All Two point conical gradient test create functions may occasionally create edge case shaders
- */
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> CircleInside2PtConicalEffect::TestCreate(
- GrProcessorTestData* d) {
- SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
- SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
- SkPoint center2;
- SkScalar radius2;
- do {
- center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
- // Below makes sure that circle one is contained within circle two
- SkScalar increase = d->fRandom->nextUScalar1();
- SkPoint diff = center2 - center1;
- SkScalar diffLen = diff.length();
- radius2 = radius1 + diffLen + increase;
- // If the circles are identical the factory will give us an empty shader.
- } while (radius1 == radius2 && center1 == center2);
-
- RandomGradientParams params(d->fRandom);
- auto shader = params.fUseColors4f ?
- SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
- params.fColors4f, params.fColorSpace, params.fStops,
- params.fColorCount, params.fTileMode) :
- SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
- params.fColors, params.fStops,
- params.fColorCount, params.fTileMode);
- GrTest::TestAsFPArgs asFPArgs(d);
- std::unique_ptr<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
- GrAlwaysAssert(fp);
- return fp;
-}
-#endif
-
-CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor
- ::GLSLCircleInside2PtConicalProcessor(const GrProcessor& processor)
- : fVSVaryingName(nullptr)
- , fFSVaryingName(nullptr)
- , fCachedCenterX(SK_ScalarMax)
- , fCachedCenterY(SK_ScalarMax)
- , fCachedA(SK_ScalarMax)
- , fCachedB(SK_ScalarMax)
- , fCachedC(SK_ScalarMax)
- , fCachedFlipSign(SK_ScalarMax) {}
-
-void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::emitCode(EmitArgs& args) {
- const CircleInside2PtConicalEffect& ge = args.fFp.cast<CircleInside2PtConicalEffect>();
- GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
- this->emitUniforms(uniformHandler, ge);
- fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType,
- "Conical2FSCenter");
- fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
- "Conical2FSParams");
- SkString tName("t");
-
- GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
- // params.x = A
- // params.y = B
- // params.z = C
- // params.w = flipSign
- GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
-
- // if we have a float3 from being in perspective, convert it to a float2 first
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
- SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
- const char* coords2D = coords2DString.c_str();
-
- // p = coords2D
- // e = center end
- // r = radius end
- // A = dot(e, e) - r^2 + 2 * r - 1
- // B = (r -1) / A
- // C = 1 / A
- // d = dot(e, p) + B
- // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
- fragBuilder->codeAppendf("\thalf pDotp = dot(%s, %s);\n", coords2D, coords2D);
- fragBuilder->codeAppendf("\thalf d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
- params.c_str());
- fragBuilder->codeAppendf("\thalf %s = d + %s.w * sqrt(d * d - %s.x * pDotp + %s.z);\n",
- tName.c_str(), params.c_str(), params.c_str(), params.c_str());
-
- this->emitColor(fragBuilder,
- uniformHandler,
- args.fShaderCaps,
- ge,
- tName.c_str(),
- args.fOutputColor,
- args.fInputColor,
- args.fTexSamplers);
-}
-
-void CircleInside2PtConicalEffect::GLSLCircleInside2PtConicalProcessor::onSetData(
- const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
- INHERITED::onSetData(pdman, processor);
- const CircleInside2PtConicalEffect& data = processor.cast<CircleInside2PtConicalEffect>();
- SkScalar centerX = data.centerX();
- SkScalar centerY = data.centerY();
- SkScalar A = data.A();
- SkScalar B = data.B();
- SkScalar C = data.C();
- SkScalar flipSign = data.isFlipped() ? -1 : 1;
-
- if (fCachedCenterX != centerX || fCachedCenterY != centerY) {
- pdman.set2f(fCenterUni, centerX, centerY);
- fCachedCenterX = centerX;
- fCachedCenterY = centerY;
- }
-
- if (fCachedA != A || fCachedB != B || fCachedC != C || fCachedFlipSign != flipSign) {
- pdman.set4f(fParamUni, A, B, C, flipSign);
- fCachedA = A;
- fCachedB = B;
- fCachedC = C;
- fCachedFlipSign = flipSign;
- }
-}
-
+//////////////////////////////////////////////////////////////////////////////
+// FocalGLSLProcessor
//////////////////////////////////////////////////////////////////////////////
-class CircleOutside2PtConicalEffect : public GrGradientEffect {
-public:
- class GLSLCircleOutside2PtConicalProcessor;
-
- static std::unique_ptr<GrFragmentProcessor> Make(const CreateArgs& args,
- const CircleConicalInfo& info) {
- return GrGradientEffect::AdjustFP(std::unique_ptr<CircleOutside2PtConicalEffect>(
- new CircleOutside2PtConicalEffect(args, info)),
- args);
- }
-
- const char* name() const override { return "Two-Point Conical Gradient Outside"; }
-
- std::unique_ptr<GrFragmentProcessor> clone() const override {
- return std::unique_ptr<GrFragmentProcessor>(new CircleOutside2PtConicalEffect(*this));
- }
-
- SkScalar centerX() const { return fInfo.fCenterEnd.fX; }
- SkScalar centerY() const { return fInfo.fCenterEnd.fY; }
- SkScalar A() const { return fInfo.fA; }
- SkScalar B() const { return fInfo.fB; }
- SkScalar C() const { return fInfo.fC; }
- SkScalar tLimit() const { return fTLimit; }
- bool isFlipped() const { return fInfo.isFlipped(); }
-
-private:
- GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
-
- bool onIsEqual(const GrFragmentProcessor& sBase) const override {
- const CircleOutside2PtConicalEffect& s = sBase.cast<CircleOutside2PtConicalEffect>();
- return INHERITED::onIsEqual(sBase) && fInfo == s.fInfo && fTLimit == s.fTLimit;
- }
-
- CircleOutside2PtConicalEffect(const CreateArgs& args, const CircleConicalInfo& info)
- : INHERITED(kCircleOutside2PtConicalEffect_ClassID, args,
- false /* opaque: draws transparent black outside of the cone. */)
- , fInfo(info) {
- const SkTwoPointConicalGradient& shader =
- *static_cast<const SkTwoPointConicalGradient*>(args.fShader);
- if (shader.getStartRadius() != shader.getEndRadius()) {
- fTLimit = shader.getStartRadius() / (shader.getStartRadius() - shader.getEndRadius());
+class TwoPointConicalEffect::FocalGLSLProcessor : public GrGradientEffect::GLSLProcessor {
+protected:
+ void emitCode(EmitArgs& args) override {
+ const TwoPointConicalEffect& ge = args.fFp.cast<TwoPointConicalEffect>();
+ GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+ this->emitUniforms(uniformHandler, ge);
+ fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType,
+ "Conical2FSParams");
+
+ SkString p0; // 1 / r1
+ SkString p1; // r0 / (r1 - r0)
+ p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+ p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str());
+ const char* tName = "t"; // the gradient
+
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+ SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+ const char* p = coords2D.c_str();
+
+ if (ge.isFocalOnCircle()) {
+ fragBuilder->codeAppendf("\thalf %s_prime = dot(%s, %s) / %s.x;", tName, p, p, p);
+ } else if (ge.isWellBehaved()) {
+ // empty sign is positive
+ char sign = ge.isRadiusIncreasing() ? ' ' : '-';
+ fragBuilder->codeAppendf("\thalf %s_prime = %clength(%s) - %s.x * %s;",
+ tName, sign, p, p, p0.c_str());
} else {
- fTLimit = SK_ScalarMin;
+ char sign = ge.isSwapped() ? '-' : ' ';
+ fragBuilder->codeAppendf("\thalf temp = %s.x * %s.x - %s.y * %s.y;", p, p, p, p);
+ fragBuilder->codeAppendf("\thalf %s_prime = (%csqrt(temp) - %s.x * %s);",
+ tName, sign, p, p0.c_str());
}
- }
-
- explicit CircleOutside2PtConicalEffect(const CircleOutside2PtConicalEffect& that)
- : INHERITED(that)
- , fInfo(that.fInfo)
- , fTLimit(that.fTLimit) {}
-
- GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-
- const CircleConicalInfo fInfo;
- SkScalar fTLimit;
+ // "- 0" is much faster than "- p1" so we specialize the navtively focal case where p1 = 0.
+ fragBuilder->codeAppendf("\thalf %s = %s_prime - %s;", tName, tName,
+ ge.isNativelyFocal() ? "0" : p1.c_str());
- typedef GrGradientEffect INHERITED;
-};
-
-class CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor
- : public GrGradientEffect::GLSLProcessor {
-public:
- GLSLCircleOutside2PtConicalProcessor(const GrProcessor&);
+ if (ge.isSwapped()) {
+ fragBuilder->codeAppendf("\t%s = 1 - %s;", tName, tName);
+ }
- virtual void emitCode(EmitArgs&) override;
+ if (!ge.isWellBehaved()) {
+ // output will default to transparent black (we simply won't write anything
+ // else to it if invalid, instead of discarding or returning prematurely)
+ fragBuilder->codeAppendf("\t%s = half4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
+ // r(t) must be nonnegative
+ char direction = ge.isRadiusIncreasing() ? '>' : '<';
+ fragBuilder->codeAppendf("\tif (%s_prime %c= 0.0) {\n", tName, direction);
+ fragBuilder->codeAppend("\t\t");
+ }
+ this->emitColor(fragBuilder,
+ uniformHandler,
+ args.fShaderCaps,
+ ge,
+ tName,
+ args.fOutputColor,
+ args.fInputColor,
+ args.fTexSamplers);
+ if (!ge.isWellBehaved()) {
+ fragBuilder->codeAppend("\t};");
+ }
+ }
-protected:
- void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+ void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& p) override {
+ INHERITED::onSetData(pdman, p);
+ const TwoPointConicalEffect& data = p.cast<TwoPointConicalEffect>();
+ SkScalar r0 = data.r0();
+ SkScalar r1 = data.r1();
+ pdman.set2f(fParamUni, 1 / r1, r0 / (r1 - r0));
+ }
- UniformHandle fCenterUni;
UniformHandle fParamUni;
- UniformHandle fFlipSignUni;
-
- const char* fVSVaryingName;
- const char* fFSVaryingName;
-
- // @{
- /// Values last uploaded as uniforms
-
- SkScalar fCachedCenterX;
- SkScalar fCachedCenterY;
- SkScalar fCachedA;
- SkScalar fCachedB;
- SkScalar fCachedC;
- SkScalar fCachedTLimit;
- SkScalar fCachedFlipSign;
-
- // @}
private:
typedef GrGradientEffect::GLSLProcessor INHERITED;
-
};
-GrGLSLFragmentProcessor* CircleOutside2PtConicalEffect::onCreateGLSLInstance() const {
- return new CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor(*this);
-}
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircleOutside2PtConicalEffect);
-
-/*
- * All Two point conical gradient test create functions may occasionally create edge case shaders
- */
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> CircleOutside2PtConicalEffect::TestCreate(
- GrProcessorTestData* d) {
- SkPoint center1 = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()};
- SkScalar radius1 = d->fRandom->nextUScalar1() + 0.0001f; // make sure radius1 != 0
- SkPoint center2;
- SkScalar radius2;
- SkScalar diffLen;
- do {
- center2.set(d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1());
- // If the circles share a center than we can't be in the outside case
- } while (center1 == center2);
- SkPoint diff = center2 - center1;
- diffLen = diff.length();
- // Below makes sure that circle one is not contained within circle two
- // and have radius2 >= radius to match sorting on cpu side
- radius2 = radius1 + d->fRandom->nextRangeF(0.f, diffLen);
-
- RandomGradientParams params(d->fRandom);
- auto shader = params.fUseColors4f ?
- SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
- params.fColors4f, params.fColorSpace, params.fStops,
- params.fColorCount, params.fTileMode) :
- SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2,
- params.fColors, params.fStops,
- params.fColorCount, params.fTileMode);
- GrTest::TestAsFPArgs asFPArgs(d);
- std::unique_ptr<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args());
- GrAlwaysAssert(fp);
- return fp;
-}
-#endif
-
-CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor
- ::GLSLCircleOutside2PtConicalProcessor(const GrProcessor& processor)
- : fVSVaryingName(nullptr)
- , fFSVaryingName(nullptr)
- , fCachedCenterX(SK_ScalarMax)
- , fCachedCenterY(SK_ScalarMax)
- , fCachedA(SK_ScalarMax)
- , fCachedB(SK_ScalarMax)
- , fCachedC(SK_ScalarMax)
- , fCachedTLimit(SK_ScalarMax)
- , fCachedFlipSign(SK_ScalarMax) {}
-
-void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::emitCode(EmitArgs& args) {
- const CircleOutside2PtConicalEffect& ge = args.fFp.cast<CircleOutside2PtConicalEffect>();
- GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
- this->emitUniforms(uniformHandler, ge);
- fCenterUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType,
- "Conical2FSCenter");
- fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
- "Conical2FSParams");
- fFlipSignUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
- "Conical2FSFlipSign");
- SkString tName("t");
-
- GrShaderVar center = uniformHandler->getUniformVariable(fCenterUni);
- // params.x = A
- // params.y = B
- // params.z = C
- GrShaderVar params = uniformHandler->getUniformVariable(fParamUni);
- GrShaderVar flipsign = uniformHandler->getUniformVariable(fFlipSignUni);
-
- // if we have a float3 from being in perspective, convert it to a float2 first
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
- SkString coords2DString = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
- const char* coords2D = coords2DString.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)
- fragBuilder->codeAppendf("\t%s = half4(0.0,0.0,0.0,0.0);\n", args.fOutputColor);
-
- // p = coords2D
- // e = center end
- // r = radius end
- // A = dot(e, e) - r^2 + 2 * r - 1
- // B = (r -1) / A
- // C = 1 / A
- // d = dot(e, p) + B
- // t = d +/- sqrt(d^2 - A * dot(p, p) + C)
-
- fragBuilder->codeAppendf("\thalf pDotp = dot(%s, %s);\n", coords2D, coords2D);
- fragBuilder->codeAppendf("\thalf d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(),
- params.c_str());
- fragBuilder->codeAppendf("\thalf deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(),
- params.c_str());
- fragBuilder->codeAppendf("\thalf %s = d + sqrt(deter);\n",
- tName.c_str());
-
- fragBuilder->codeAppendf("\tif (%s * (%s - %s.w) >= 0 && deter >= 0.0) {\n",
- flipsign.c_str(), tName.c_str(), params.c_str());
- fragBuilder->codeAppend("\t\t");
- this->emitColor(fragBuilder,
- uniformHandler,
- args.fShaderCaps,
- ge,
- tName.c_str(),
- args.fOutputColor,
- args.fInputColor,
- args.fTexSamplers);
- fragBuilder->codeAppend("\t}\n");
-}
-
-void CircleOutside2PtConicalEffect::GLSLCircleOutside2PtConicalProcessor::onSetData(
- const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& processor) {
- INHERITED::onSetData(pdman, processor);
- const CircleOutside2PtConicalEffect& data = processor.cast<CircleOutside2PtConicalEffect>();
- SkScalar centerX = data.centerX();
- SkScalar centerY = data.centerY();
- SkScalar A = data.A();
- SkScalar B = data.B();
- SkScalar C = data.C();
- SkScalar tLimit = data.tLimit();
- SkScalar flipSign = data.isFlipped() ? -1 : 1;
-
- if (fCachedCenterX != centerX || fCachedCenterY != centerY) {
- pdman.set2f(fCenterUni, centerX, centerY);
- fCachedCenterX = centerX;
- fCachedCenterY = centerY;
- }
-
- if (fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) {
- pdman.set4f(fParamUni, A, B, C, tLimit);
- fCachedA = A;
- fCachedB = B;
- fCachedC = C;
- fCachedTLimit = tLimit;
- }
+//////////////////////////////////////////////////////////////////////////////
- if (fCachedFlipSign != flipSign) {
- pdman.set1f(fFlipSignUni, flipSign);
- fCachedFlipSign = flipSign;
+GrGLSLFragmentProcessor* TwoPointConicalEffect::onCreateGLSLInstance() const {
+ if (fData.fType == kRadial_Type || fData.fType == kStrip_Type) {
+ return new DegeneratedGLSLProcessor;
}
+ return new FocalGLSLProcessor;
}
-//////////////////////////////////////////////////////////////////////////////
+std::unique_ptr<GrFragmentProcessor> TwoPointConicalEffect::Make(
+ const GrGradientEffect::CreateArgs& args, const Data& data) {
+ return GrGradientEffect::AdjustFP(
+ std::unique_ptr<TwoPointConicalEffect>(new TwoPointConicalEffect(args, data)),
+ args);
+}
std::unique_ptr<GrFragmentProcessor> Gr2PtConicalGradientEffect::Make(
const GrGradientEffect::CreateArgs& args) {
@@ -1263,32 +345,69 @@ std::unique_ptr<GrFragmentProcessor> Gr2PtConicalGradientEffect::Make(
}
GrGradientEffect::CreateArgs newArgs(args.fContext, args.fShader, &matrix, args.fWrapMode,
- args.fDstColorSpace);
-
- if (shader.getStartRadius() < kErrorTol) {
- SkScalar focalX;
- ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX);
- if (type == kInside_ConicalType) {
- return FocalInside2PtConicalEffect::Make(newArgs, focalX);
- } else if(type == kEdge_ConicalType) {
- set_matrix_edge_conical(shader, &matrix);
- return Edge2PtConicalEffect::Make(newArgs);
- } else {
- const bool isFlipped = shader.getStartRadius() > shader.getEndRadius();
- return FocalOutside2PtConicalEffect::Make(newArgs, focalX, isFlipped);
- }
- }
-
- CircleConicalInfo info;
- ConicalType type = set_matrix_circle_conical(shader, &matrix, &info);
+ args.fDstColorSpace);
+ // Data and matrix has to be prepared before constructing TwoPointConicalEffect so its parent
+ // class can have the right matrix to work with during construction.
+ TwoPointConicalEffect::Data data(shader, matrix);
+ return TwoPointConicalEffect::Make(newArgs, data);
+}
- if (type == kInside_ConicalType) {
- return CircleInside2PtConicalEffect::Make(newArgs, info);
- } else if (type == kEdge_ConicalType) {
- set_matrix_edge_conical(shader, &matrix);
- return Edge2PtConicalEffect::Make(newArgs);
+TwoPointConicalEffect::Data::Data(const SkTwoPointConicalGradient& shader, SkMatrix& matrix) {
+ fIsSwapped = false;
+ if (SkScalarNearlyZero(shader.getCenterX1())) {
+ fType = kRadial_Type;
+ SkScalar dr = shader.getDiffRadius();
+ // Map center to (0, 0) and scale dr to 1
+ matrix.postTranslate(-shader.getStartCenter().fX, -shader.getStartCenter().fY);
+ matrix.postScale(1 / dr, 1 / dr);
+ fRadius0 = shader.getStartRadius() / dr;
+ fDiffRadius = 1;
} else {
- return CircleOutside2PtConicalEffect::Make(newArgs, info);
+ // Map centers to (0, 0), (1, 0)
+ const SkPoint centers[2] = { shader.getStartCenter(), shader.getEndCenter() };
+ const SkPoint unitvec[2] = { { 0, 0 },{ 1, 0 } };
+ SkMatrix gradientMatrix;
+ // The radial case is already handled so this must succeed
+ SkAssertResult(gradientMatrix.setPolyToPoly(centers, unitvec, 2));
+ matrix.postConcat(gradientMatrix);
+ fRadius0 = shader.getStartRadius() / shader.getCenterX1();
+ fDiffRadius = shader.getDiffRadius() / shader.getCenterX1();
+
+ if (SkScalarNearlyZero(shader.getDiffRadius())) {
+ fType = kStrip_Type;
+ } else { // focal case
+ fType = kFocal_Type;
+ if (SkScalarNearlyZero(shader.getEndRadius())) {
+ // swap r0, r1
+ matrix.postTranslate(-1, 0);
+ matrix.postScale(-1, 1);
+ fRadius0 = 0;
+ fDiffRadius = -fDiffRadius;
+ fIsSwapped = true;
+ }
+
+ // Map {focal point, (1, 0)} to {(0, 0), (1, 0)}
+ SkScalar focalX = - fRadius0 / fDiffRadius;
+ const SkPoint from[2] = { {focalX, 0}, {1, 0} };
+ const SkPoint to[2] = { {0, 0}, {1, 0} };
+ SkMatrix focalMatrix;
+ focalMatrix.setPolyToPoly(from, to, 2);
+ matrix.postConcat(focalMatrix);
+ fRadius0 /= SkScalarAbs(1 - focalX);
+ fDiffRadius /= SkScalarAbs(1 - focalX);
+
+ SkScalar r0 = fRadius0;
+ SkScalar r1 = fRadius0 + fDiffRadius;
+ // The following transformations are not reflected on data; they're just to accelerate
+ // the shader computation by saving some arithmatic operations.
+ bool isFocalOnCircle = SkScalarNearlyZero(1 - r1);
+ if (isFocalOnCircle) {
+ matrix.postScale(0.5, 0.5); // r1 = 1 so r1 + 1 = 2 and 0.5 = 1 / (r1 + 1)
+ } else {
+ matrix.postScale(r1 / (r1 * r1 - 1), 1 / sqrt(SkScalarAbs(r1 * r1 - 1)));
+ }
+ matrix.postScale(r1 / (r1 - r0), r1 / (r1 - r0));
+ }
}
}