Add geometric version of spot shadow
BUG=skia:6119
Change-Id: Ib9770bd88f4eebd68f2d893c5788f966d89f193c
Reviewed-on: https://skia-review.googlesource.com/7585
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index e7e629d..a53daeb 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -42,6 +42,7 @@
"$_include/gpu/GrTypesPriv.h",
"$_include/gpu/GrXferProcessor.h",
+ "$_include/gpu/effects/GrBlurredEdgeFragmentProcessor.h",
"$_include/gpu/effects/GrConstColorProcessor.h",
"$_include/gpu/effects/GrCoverageSetOpXP.h",
"$_include/gpu/effects/GrCustomXfermode.h",
@@ -296,6 +297,7 @@
"$_src/gpu/ops/GrTestMeshDrawOp.h",
"$_src/gpu/effects/Gr1DKernelEffect.h",
+ "$_src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp",
"$_src/gpu/effects/GrConfigConversionEffect.cpp",
"$_src/gpu/effects/GrConfigConversionEffect.h",
"$_src/gpu/effects/GrConstColorProcessor.cpp",
diff --git a/include/gpu/effects/GrBlurredEdgeFragmentProcessor.h b/include/gpu/effects/GrBlurredEdgeFragmentProcessor.h
new file mode 100755
index 0000000..2e52485
--- /dev/null
+++ b/include/gpu/effects/GrBlurredEdgeFragmentProcessor.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrBlurredEdgeFragmentProcessor_DEFINED
+#define GrBlurredEdgeFragmentProcessor_DEFINED
+
+#include "GrFragmentProcessor.h"
+
+/**
+ * Shader for managing a blurred edge for a shadow.
+ *
+ * There are two blurring modes supported: Gaussian blur function and smoothstep function.
+ *
+ * If the primitive supports an implicit distance to the edge, the radius of the blur is specified
+ * by r & g values of the color in 14.2 fixed point. For spot shadows, we increase the stroke width
+ * to set the shadow against the shape. This pad is specified by b, also in 6.2 fixed point.
+ *
+ * When not using implicit distance, then b in the input color represents the input to the
+ * blur function.
+ *
+ * In either case, the a value represents the max final alpha.
+ */
+class GrBlurredEdgeFP : public GrFragmentProcessor {
+public:
+ enum Mode {
+ kGaussian_Mode,
+ kSmoothstep_Mode,
+
+ kLastMode = kSmoothstep_Mode
+ };
+ static const int kModeCnt = kLastMode + 1;
+
+ static sk_sp<GrFragmentProcessor> Make(Mode mode = kGaussian_Mode) {
+ return sk_sp<GrFragmentProcessor>(new GrBlurredEdgeFP(mode));
+ }
+
+ const char* name() const override { return "BlurredEdge"; }
+
+ Mode mode() const { return fMode; }
+
+private:
+ GrBlurredEdgeFP(Mode mode)
+ : INHERITED(kNone_OptimizationFlags)
+ , fMode(mode) {
+ // enable output of distance information for shape
+ this->setWillUseDistanceVectorField();
+
+ this->initClassID<GrBlurredEdgeFP>();
+ }
+
+ GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+
+ void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+
+ bool onIsEqual(const GrFragmentProcessor&) const override;
+
+ void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
+
+ GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
+
+ Mode fMode;
+
+ typedef GrFragmentProcessor INHERITED;
+};
+
+#endif
diff --git a/src/effects/SkGaussianEdgeShader.cpp b/src/effects/SkGaussianEdgeShader.cpp
index 5bd7dca..be05e54 100644
--- a/src/effects/SkGaussianEdgeShader.cpp
+++ b/src/effects/SkGaussianEdgeShader.cpp
@@ -12,18 +12,12 @@
/** \class SkGaussianEdgeShaderImpl
This subclass of shader applies a Gaussian to shadow edge
- If largerBlur is false:
- The radius of the Gaussian blur is specified by the g value of the color, in 6.2 fixed point.
- For spot shadows, we increase the stroke width to set the shadow against the shape. This pad
- is specified by b, also in 6.2 fixed point. The r value represents the max final alpha.
- The incoming alpha should be 1.
+ If the primitive supports an implicit distance to the edge, the radius of the blur is specified
+ by r & g values of the color in 14.2 fixed point. For spot shadows, we increase the stroke width
+ to set the shadow against the shape. This pad is specified by b, also in 6.2 fixed point.
- If largerBlur is true:
- The radius of the Gaussian blur is specified by the r & g values of the color in 14.2 fixed point.
- For spot shadows, we increase the stroke width to set the shadow against the shape. This pad
- is specified by b, also in 6.2 fixed point. The a value represents the max final alpha.
-
- LargerBlur will be removed once Android is migrated to the updated shader.
+ When not using implicit distance, then b in the input color represents the input to the
+ blur function.
*/
class SkGaussianEdgeShaderImpl : public SkShader {
public:
@@ -51,86 +45,12 @@
#if SK_SUPPORT_GPU
-#include "GrCoordTransform.h"
-#include "GrFragmentProcessor.h"
-#include "GrInvariantOutput.h"
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramDataManager.h"
-#include "glsl/GrGLSLUniformHandler.h"
-#include "SkGr.h"
-#include "SkGrPriv.h"
-
-class GaussianEdgeFP : public GrFragmentProcessor {
-public:
- GaussianEdgeFP() : INHERITED(kNone_OptimizationFlags) {
- this->initClassID<GaussianEdgeFP>();
-
- // enable output of distance information for shape
- this->setWillUseDistanceVectorField();
- }
-
- class GLSLGaussianEdgeFP : public GrGLSLFragmentProcessor {
- public:
- GLSLGaussianEdgeFP() {}
-
- void emitCode(EmitArgs& args) override {
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-
- if (!args.fGpImplementsDistanceVector) {
- fragBuilder->codeAppendf("// GP does not implement fsDistanceVector - "
- " using alpha as input to GLSLGaussianEdgeFP\n");
- fragBuilder->codeAppendf("float factor = 1.0 - %s.a;", args.fInputColor);
- fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;");
- fragBuilder->codeAppendf("%s = vec4(0.0, 0.0, 0.0, factor);", args.fOutputColor);
- } else {
- fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
- fragBuilder->codeAppend("float radius = color.r*256.0*64.0 + color.g*64.0;");
- fragBuilder->codeAppend("float pad = color.b*64.0;");
-
- fragBuilder->codeAppendf("float factor = 1.0 - clamp((%s.z - pad)/radius, 0.0, 1.0);",
- fragBuilder->distanceVectorName());
- fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;");
- fragBuilder->codeAppendf("%s = factor*vec4(0.0, 0.0, 0.0, color.a);",
- args.fOutputColor);
- }
- }
-
- static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
- // only one shader generated currently
- b->add32(0x0);
- }
-
- protected:
- void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {}
-
- bool fLargerBlur;
- };
-
- void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
- GLSLGaussianEdgeFP::GenKey(*this, caps, b);
- }
-
- const char* name() const override { return "GaussianEdgeFP"; }
-
- void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
- inout->mulByUnknownFourComponents();
- }
-
-private:
- GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
- return new GLSLGaussianEdgeFP();
- }
-
- bool onIsEqual(const GrFragmentProcessor& proc) const override { return true; }
-
- typedef GrFragmentProcessor INHERITED;
-};
+#include "effects/GrBlurredEdgeFragmentProcessor.h"
////////////////////////////////////////////////////////////////////////////
sk_sp<GrFragmentProcessor> SkGaussianEdgeShaderImpl::asFragmentProcessor(const AsFPArgs&) const {
- return sk_make_sp<GaussianEdgeFP>();
+ return GrBlurredEdgeFP::Make(GrBlurredEdgeFP::kGaussian_Mode);
}
#endif
diff --git a/src/effects/shadows/SkAmbientShadowMaskFilter.cpp b/src/effects/shadows/SkAmbientShadowMaskFilter.cpp
index e7d8d31..0672020 100755
--- a/src/effects/shadows/SkAmbientShadowMaskFilter.cpp
+++ b/src/effects/shadows/SkAmbientShadowMaskFilter.cpp
@@ -14,15 +14,11 @@
#include "GrContext.h"
#include "GrRenderTargetContext.h"
#include "GrFragmentProcessor.h"
-#include "GrInvariantOutput.h"
#include "GrStyle.h"
#include "GrTexture.h"
#include "GrTextureProxy.h"
+#include "effects/GrBlurredEdgeFragmentProcessor.h"
#include "effects/GrShadowTessellator.h"
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramDataManager.h"
-#include "glsl/GrGLSLUniformHandler.h"
#include "SkStrokeRec.h"
#endif
@@ -133,56 +129,6 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// Shader for managing the shadow's edge. a in the input color represents the initial
-// edge color, which is transformed by a Gaussian function. b represents the blend factor,
-// which is multiplied by this transformed value.
-//
-class ShadowEdgeFP : public GrFragmentProcessor {
-public:
- ShadowEdgeFP() : INHERITED(kNone_OptimizationFlags) { this->initClassID<ShadowEdgeFP>(); }
-
- class GLSLShadowEdgeFP : public GrGLSLFragmentProcessor {
- public:
- GLSLShadowEdgeFP() {}
-
- void emitCode(EmitArgs& args) override {
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-
- fragBuilder->codeAppendf("float factor = 1.0 - %s.a;", args.fInputColor);
- fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;");
- fragBuilder->codeAppendf("%s = vec4(0.0, 0.0, 0.0, %s.b*factor);", args.fOutputColor,
- args.fInputColor);
- }
-
- static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*) {}
-
- protected:
- void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {}
- };
-
- void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
- GLSLShadowEdgeFP::GenKey(*this, caps, b);
- }
-
- const char* name() const override { return "ShadowEdgeFP"; }
-
- void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
- inout->mulByUnknownFourComponents();
- }
-
-private:
- GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
- return new GLSLShadowEdgeFP();
- }
-
- bool onIsEqual(const GrFragmentProcessor& proc) const override { return true; }
-
- typedef GrFragmentProcessor INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
bool SkAmbientShadowMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
const SkIRect& clipBounds,
const SkMatrix& ctm,
@@ -200,7 +146,7 @@
GrPaint&& paint,
const GrClip& clip,
const SkMatrix& viewMatrix,
- const SkStrokeRec&,
+ const SkStrokeRec& strokeRec,
const SkPath& path) const {
SkASSERT(rtContext);
// TODO: this will not handle local coordinates properly
@@ -214,6 +160,10 @@
return false;
}
+ if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) {
+ return false;
+ }
+
#ifdef SUPPORT_FAST_PATH
// if circle
// TODO: switch to SkScalarNearlyEqual when either oval renderer is updated or we
@@ -233,16 +183,16 @@
SkScalar umbraAlpha = SkScalarInvert((1.0f+SkTMax(fOccluderHeight * kHeightFactor, 0.0f)));
// umbraColor is the interior value, penumbraColor the exterior value.
// umbraAlpha is the factor that is linearly interpolated from outside to inside, and
- // then "blurred" by the ShadowEdgeFP. It is then multiplied by fAmbientAlpha to get
+ // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
// the final alpha.
- GrColor umbraColor = GrColorPackRGBA(0, 0, fAmbientAlpha*255.9999f, umbraAlpha*255.9999f);
- GrColor penumbraColor = GrColorPackRGBA(0, 0, fAmbientAlpha*255.9999f, 0);
+ GrColor umbraColor = GrColorPackRGBA(0, 0, umbraAlpha*255.9999f, fAmbientAlpha*255.9999f);
+ GrColor penumbraColor = GrColorPackRGBA(0, 0, 0, fAmbientAlpha*255.9999f);
- GrAmbientShadowTessellator tess(SkMatrix::I(), path, radius, umbraColor, penumbraColor,
+ GrAmbientShadowTessellator tess(path, radius, umbraColor, penumbraColor,
SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag));
- sk_sp<ShadowEdgeFP> edgeFP(new ShadowEdgeFP);
- paint.addColorFragmentProcessor(edgeFP);
+ sk_sp<GrFragmentProcessor> edgeFP = GrBlurredEdgeFP::Make(GrBlurredEdgeFP::kGaussian_Mode);
+ paint.addColorFragmentProcessor(std::move(edgeFP));
rtContext->drawVertices(clip, std::move(paint), SkMatrix::I(), kTriangles_GrPrimitiveType,
tess.vertexCount(), tess.positions(), nullptr,
diff --git a/src/effects/shadows/SkSpotShadowMaskFilter.cpp b/src/effects/shadows/SkSpotShadowMaskFilter.cpp
index b214cef..7a1f311 100755
--- a/src/effects/shadows/SkSpotShadowMaskFilter.cpp
+++ b/src/effects/shadows/SkSpotShadowMaskFilter.cpp
@@ -14,14 +14,11 @@
#include "GrContext.h"
#include "GrRenderTargetContext.h"
#include "GrFragmentProcessor.h"
-#include "GrInvariantOutput.h"
#include "GrStyle.h"
#include "GrTexture.h"
#include "GrTextureProxy.h"
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramDataManager.h"
-#include "glsl/GrGLSLUniformHandler.h"
+#include "effects/GrBlurredEdgeFragmentProcessor.h"
+#include "effects/GrShadowTessellator.h"
#include "SkStrokeRec.h"
#endif
@@ -162,15 +159,29 @@
}
bool SkSpotShadowMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvider,
- GrRenderTargetContext* drawContext,
+ GrRenderTargetContext* rtContext,
GrPaint&& paint,
const GrClip& clip,
const SkMatrix& viewMatrix,
const SkStrokeRec& strokeRec,
const SkPath& path) const {
- SkASSERT(drawContext);
+ SkASSERT(rtContext);
// TODO: this will not handle local coordinates properly
+ if (fSpotAlpha <= 0.0f) {
+ return true;
+ }
+
+ // only convex paths for now
+ if (!path.isConvex()) {
+ return false;
+ }
+
+ if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) {
+ return false;
+ }
+
+#ifdef SUPPORT_FAST_PATH
// if circle
// TODO: switch to SkScalarNearlyEqual when either oval renderer is updated or we
// have our own GeometryProc.
@@ -183,9 +194,32 @@
return this->directFilterRRectMaskGPU(nullptr, drawContext, std::move(paint), clip,
SkMatrix::I(), strokeRec, rrect, rrect);
}
+#endif
- // TODO
- return false;
+ float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f);
+
+ SkScalar radius = fLightRadius * zRatio;
+
+ // Compute the scale and translation for the spot shadow.
+ const SkScalar scale = fLightPos.fZ / (fLightPos.fZ - fOccluderHeight);
+
+ SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
+ const SkVector spotOffset = SkVector::Make(zRatio*(center.fX - fLightPos.fX),
+ zRatio*(center.fY - fLightPos.fY));
+
+ GrColor umbraColor = GrColorPackRGBA(0, 0, 255, fSpotAlpha*255.9999f);
+ GrColor penumbraColor = GrColorPackRGBA(0, 0, 0, fSpotAlpha*255.9999f);
+ GrSpotShadowTessellator tess(path, scale, spotOffset, radius, umbraColor, penumbraColor,
+ SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag));
+
+ sk_sp<GrFragmentProcessor> edgeFP = GrBlurredEdgeFP::Make(GrBlurredEdgeFP::kGaussian_Mode);
+ paint.addColorFragmentProcessor(std::move(edgeFP));
+
+ rtContext->drawVertices(clip, std::move(paint), SkMatrix::I(), kTriangles_GrPrimitiveType,
+ tess.vertexCount(), tess.positions(), nullptr,
+ tess.colors(), tess.indices(), tess.indexCount());
+
+ return true;
}
bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
@@ -196,6 +230,10 @@
const SkStrokeRec& strokeRec,
const SkRRect& rrect,
const SkRRect& devRRect) const {
+#ifndef SUPPORT_FAST_PATH
+ return false;
+#endif
+
// It's likely the caller has already done these checks, but we have to be sure.
// TODO: support analytic blurring of general rrect
diff --git a/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp b/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp
new file mode 100755
index 0000000..1441a75
--- /dev/null
+++ b/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "effects/GrBlurredEdgeFragmentProcessor.h"
+#include "GrInvariantOutput.h"
+
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+
+class GLSLBlurredEdgeFP : public GrGLSLFragmentProcessor {
+public:
+ GLSLBlurredEdgeFP() {}
+
+ void emitCode(EmitArgs& args) override {
+
+ GrBlurredEdgeFP::Mode mode = args.fFp.cast<GrBlurredEdgeFP>().mode();
+
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+
+ fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
+ if (!args.fGpImplementsDistanceVector) {
+ fragBuilder->codeAppendf("// assuming interpolant is set in vertex colors\n");
+ fragBuilder->codeAppendf("float factor = 1.0 - color.b;");
+ } else {
+ fragBuilder->codeAppendf("// using distance to edge to compute interpolant\n");
+ fragBuilder->codeAppend("float radius = color.r*256.0*64.0 + color.g*64.0;");
+ fragBuilder->codeAppend("float pad = color.b*64.0;");
+
+ fragBuilder->codeAppendf("float factor = 1.0 - clamp((%s.z - pad)/radius, 0.0, 1.0);",
+ fragBuilder->distanceVectorName());
+ }
+ switch (mode) {
+ case GrBlurredEdgeFP::kGaussian_Mode:
+ fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;");
+ break;
+ case GrBlurredEdgeFP::kSmoothstep_Mode:
+ fragBuilder->codeAppend("factor = smoothstep(factor, 0.0, 1.0);");
+ break;
+ }
+ fragBuilder->codeAppendf("%s = factor*vec4(0.0, 0.0, 0.0, color.a);",
+ args.fOutputColor);
+ }
+
+protected:
+ void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {}
+
+ GrBlurredEdgeFP::Mode fMode;
+};
+
+GrGLSLFragmentProcessor* GrBlurredEdgeFP::onCreateGLSLInstance() const {
+ return new GLSLBlurredEdgeFP();
+}
+
+void GrBlurredEdgeFP::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+ GrProcessorKeyBuilder* b) const {
+ b->add32(fMode);
+}
+
+bool GrBlurredEdgeFP::onIsEqual(const GrFragmentProcessor& other) const {
+ const GrBlurredEdgeFP& that = other.cast<GrBlurredEdgeFP>();
+ return that.fMode == fMode;
+}
+
+void GrBlurredEdgeFP::onComputeInvariantOutput(GrInvariantOutput* inout) const {
+ inout->mulByUnknownFourComponents();
+}
+
+
diff --git a/src/gpu/effects/GrShadowTessellator.cpp b/src/gpu/effects/GrShadowTessellator.cpp
index 8aa91e2..a51a66a 100755
--- a/src/gpu/effects/GrShadowTessellator.cpp
+++ b/src/gpu/effects/GrShadowTessellator.cpp
@@ -39,8 +39,7 @@
*n = SkScalarFloorToInt(steps);
}
-GrAmbientShadowTessellator::GrAmbientShadowTessellator(const SkMatrix& viewMatrix,
- const SkPath& path,
+GrAmbientShadowTessellator::GrAmbientShadowTessellator(const SkPath& path,
SkScalar radius,
GrColor umbraColor,
GrColor penumbraColor,
@@ -74,16 +73,16 @@
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kLine_Verb:
- this->handleLine(viewMatrix, pts[1]);
+ this->handleLine(pts[1]);
break;
case SkPath::kQuad_Verb:
- this->handleQuad(viewMatrix, pts);
+ this->handleQuad(pts);
break;
case SkPath::kCubic_Verb:
- this->handleCubic(viewMatrix, pts);
+ this->handleCubic(pts);
break;
case SkPath::kConic_Verb:
- this->handleConic(viewMatrix, pts, iter.conicWeight());
+ this->handleConic(pts, iter.conicWeight());
break;
case SkPath::kMove_Verb:
case SkPath::kClose_Verb:
@@ -192,15 +191,10 @@
SkVector normal;
if (compute_normal(fPositions[fPrevInnerIndex], p, fRadius, fDirection, &normal)) {
this->addArc(normal);
- this->addEdge(p, normal);
+ this->finishArcAndAddEdge(p, normal);
}
}
-void GrAmbientShadowTessellator::handleLine(const SkMatrix& m, SkPoint p) {
- m.mapPoints(&p, 1);
- this->handleLine(p);
-}
-
void GrAmbientShadowTessellator::handleQuad(const SkPoint pts[3]) {
int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
fPointBuffer.setReserve(maxCount);
@@ -213,13 +207,7 @@
}
}
-void GrAmbientShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
- m.mapPoints(pts, 3);
- this->handleQuad(pts);
-}
-
-void GrAmbientShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
- m.mapPoints(pts, 4);
+void GrAmbientShadowTessellator::handleCubic(SkPoint pts[4]) {
int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
fPointBuffer.setReserve(maxCount);
SkPoint* target = fPointBuffer.begin();
@@ -231,8 +219,7 @@
}
}
-void GrAmbientShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
- m.mapPoints(pts, 3);
+void GrAmbientShadowTessellator::handleConic(SkPoint pts[3], SkScalar w) {
SkAutoConicToQuads quadder;
const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
SkPoint lastPoint = *(quads++);
@@ -268,7 +255,6 @@
}
}
-
void GrAmbientShadowTessellator::finishArcAndAddEdge(const SkPoint& nextPoint,
const SkVector& nextNormal) {
// close out previous arc
@@ -309,3 +295,338 @@
fPrevInnerIndex = fPositions.count() - 2;
fPrevNormal = nextNormal;
}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+GrSpotShadowTessellator::GrSpotShadowTessellator(const SkPath& path,
+ SkScalar scale, const SkVector& translate,
+ SkScalar radius,
+ GrColor umbraColor, GrColor penumbraColor,
+ bool /* transparent */)
+ : fRadius(radius)
+ , fUmbraColor(umbraColor)
+ , fPenumbraColor(penumbraColor)
+ , fPrevInnerIndex(-1) {
+
+ // TODO: calculate these better
+ // Outer ring: 3*numPts
+ // Inner ring: numPts
+ fPositions.setReserve(4 * path.countPoints());
+ fColors.setReserve(4 * path.countPoints());
+ // Outer ring: 12*numPts
+ // Inner ring: 0
+ fIndices.setReserve(12 * path.countPoints());
+
+ fInitPoints.setReserve(3);
+
+ fClipPolygon.setReserve(path.countPoints());
+ this->computeClipBounds(path);
+ fCentroid *= scale;
+ fCentroid += translate;
+
+ // walk around the path, tessellate and generate inner and outer rings
+ SkPath::Iter iter(path, true);
+ SkPoint pts[4];
+ SkPath::Verb verb;
+ *fPositions.push() = fCentroid;
+ *fColors.push() = fUmbraColor;
+ while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+ switch (verb) {
+ case SkPath::kLine_Verb:
+ this->handleLine(scale, translate, pts[1]);
+ break;
+ case SkPath::kQuad_Verb:
+ this->handleQuad(scale, translate, pts);
+ break;
+ case SkPath::kCubic_Verb:
+ this->handleCubic(scale, translate, pts);
+ break;
+ case SkPath::kConic_Verb:
+ this->handleConic(scale, translate, pts, iter.conicWeight());
+ break;
+ case SkPath::kMove_Verb:
+ case SkPath::kClose_Verb:
+ case SkPath::kDone_Verb:
+ break;
+ }
+ }
+
+ SkVector normal;
+ if (compute_normal(fPrevPoint, fFirstPoint, fRadius, fDirection,
+ &normal)) {
+ this->addArc(normal);
+
+ // close out previous arc
+ *fPositions.push() = fPrevPoint + normal;
+ *fColors.push() = fPenumbraColor;
+ *fIndices.push() = fPrevInnerIndex;
+ *fIndices.push() = fPositions.count() - 2;
+ *fIndices.push() = fPositions.count() - 1;
+
+ // add final edge
+ *fPositions.push() = fFirstPoint + normal;
+ *fColors.push() = fPenumbraColor;
+
+ *fIndices.push() = fPrevInnerIndex;
+ *fIndices.push() = fPositions.count() - 2;
+ *fIndices.push() = fFirstVertex;
+
+ *fIndices.push() = fPositions.count() - 2;
+ *fIndices.push() = fPositions.count() - 1;
+ *fIndices.push() = fFirstVertex;
+
+ // add to center fan
+ *fIndices.push() = 0;
+ *fIndices.push() = fPrevInnerIndex;
+ *fIndices.push() = fFirstVertex;
+ }
+
+ // final fan
+ if (fPositions.count() >= 3) {
+ fPrevInnerIndex = fFirstVertex;
+ fPrevPoint = fFirstPoint;
+ fPrevNormal = normal;
+ this->addArc(fFirstNormal);
+
+ *fIndices.push() = fFirstVertex;
+ *fIndices.push() = fPositions.count() - 1;
+ *fIndices.push() = fFirstVertex + 1;
+ }
+}
+
+void GrSpotShadowTessellator::computeClipBounds(const SkPath& path) {
+ // walk around the path and compute clip polygon
+ // if original path is transparent, will accumulate sum of points for centroid
+ SkPath::Iter iter(path, true);
+ SkPoint pts[4];
+ SkPath::Verb verb;
+
+ fCentroid = SkPoint::Make(0, 0);
+ int centroidCount = 0;
+ fClipPolygon.reset();
+
+ while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ break;
+ case SkPath::kLine_Verb:
+ fCentroid += pts[1];
+ centroidCount++;
+ *fClipPolygon.push() = pts[1];
+ break;
+ case SkPath::kQuad_Verb:
+ fCentroid += pts[1];
+ fCentroid += pts[2];
+ centroidCount += 2;
+ *fClipPolygon.push() = pts[2];
+ break;
+ case SkPath::kConic_Verb:
+ fCentroid += pts[1];
+ fCentroid += pts[2];
+ centroidCount += 2;
+ *fClipPolygon.push() = pts[2];
+ break;
+ case SkPath::kCubic_Verb:
+ fCentroid += pts[1];
+ fCentroid += pts[2];
+ fCentroid += pts[3];
+ centroidCount += 3;
+ *fClipPolygon.push() = pts[3];
+ break;
+ case SkPath::kClose_Verb:
+ break;
+ default:
+ SkDEBUGFAIL("unknown verb");
+ }
+ }
+
+ fCentroid *= SkScalarInvert(centroidCount);
+}
+
+void GrSpotShadowTessellator::mapPoints(SkScalar scale, const SkVector& xlate,
+ SkPoint* pts, int count) {
+ // TODO: vectorize
+ for (int i = 0; i < count; ++i) {
+ pts[i] *= scale;
+ pts[i] += xlate;
+ }
+}
+
+void GrSpotShadowTessellator::handleLine(const SkPoint& p) {
+ if (fInitPoints.count() < 2) {
+ *fInitPoints.push() = p;
+ return;
+ }
+
+ if (fInitPoints.count() == 2) {
+ // determine if cw or ccw
+ SkVector v0 = fInitPoints[1] - fInitPoints[0];
+ SkVector v1 = p - fInitPoints[0];
+ SkScalar perpDot = v0.fX*v1.fY - v0.fY*v1.fX;
+ if (SkScalarNearlyZero(perpDot)) {
+ // nearly parallel, just treat as straight line and continue
+ fInitPoints[1] = p;
+ return;
+ }
+
+ // if perpDot > 0, winding is ccw
+ fDirection = (perpDot > 0) ? -1 : 1;
+
+ // add first quad
+ if (!compute_normal(fInitPoints[0], fInitPoints[1], fRadius, fDirection,
+ &fFirstNormal)) {
+ // first two points are incident, make the third point the second and continue
+ fInitPoints[1] = p;
+ return;
+ }
+
+ fFirstPoint = fInitPoints[0];
+ fFirstVertex = fPositions.count();
+ fPrevNormal = fFirstNormal;
+ fPrevPoint = fFirstPoint;
+ fPrevInnerIndex = fFirstVertex;
+
+ this->addInnerPoint(fFirstPoint, fUmbraColor, fRadius);
+ SkPoint newPoint = fFirstPoint + fFirstNormal;
+ *fPositions.push() = newPoint;
+ *fColors.push() = fPenumbraColor;
+ this->addEdge(fInitPoints[1], fFirstNormal);
+
+ // to ensure we skip this block next time
+ *fInitPoints.push() = p;
+ }
+
+ SkVector normal;
+ if (compute_normal(fPrevPoint, p, fRadius, fDirection, &normal)) {
+ this->addArc(normal);
+ this->finishArcAndAddEdge(p, normal);
+ }
+}
+
+void GrSpotShadowTessellator::handleLine(SkScalar scale, const SkVector& xlate, SkPoint p) {
+ this->mapPoints(scale, xlate, &p, 1);
+ this->handleLine(p);
+}
+
+void GrSpotShadowTessellator::handleQuad(const SkPoint pts[3]) {
+ int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
+ fPointBuffer.setReserve(maxCount);
+ SkPoint* target = fPointBuffer.begin();
+ int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
+ kQuadTolerance, &target, maxCount);
+ fPointBuffer.setCount(count);
+ for (int i = 0; i < count; i++) {
+ this->handleLine(fPointBuffer[i]);
+ }
+}
+
+void GrSpotShadowTessellator::handleQuad(SkScalar scale, const SkVector& xlate, SkPoint pts[3]) {
+ this->mapPoints(scale, xlate, pts, 3);
+ this->handleQuad(pts);
+}
+
+void GrSpotShadowTessellator::handleCubic(SkScalar scale, const SkVector& xlate, SkPoint pts[4]) {
+ this->mapPoints(scale, xlate, pts, 4);
+ int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
+ fPointBuffer.setReserve(maxCount);
+ SkPoint* target = fPointBuffer.begin();
+ int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
+ kCubicTolerance, &target, maxCount);
+ fPointBuffer.setCount(count);
+ for (int i = 0; i < count; i++) {
+ this->handleLine(fPointBuffer[i]);
+ }
+}
+
+void GrSpotShadowTessellator::handleConic(SkScalar scale, const SkVector& xlate,
+ SkPoint pts[3], SkScalar w) {
+ this->mapPoints(scale, xlate, pts, 3);
+ SkAutoConicToQuads quadder;
+ const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
+ SkPoint lastPoint = *(quads++);
+ int count = quadder.countQuads();
+ for (int i = 0; i < count; ++i) {
+ SkPoint quadPts[3];
+ quadPts[0] = lastPoint;
+ quadPts[1] = quads[0];
+ quadPts[2] = i == count - 1 ? pts[2] : quads[1];
+ this->handleQuad(quadPts);
+ lastPoint = quadPts[2];
+ quads += 2;
+ }
+}
+
+void GrSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint, GrColor umbraColor,
+ SkScalar radius) {
+ SkVector v = fCentroid - pathPoint;
+ SkScalar distance = v.length();
+ if (distance < radius) {
+ *fPositions.push() = fCentroid;
+ *fColors.push() = umbraColor; // fix this
+ // TODO: deal with fanning from centroid
+ } else {
+ SkScalar t = radius / distance;
+ v *= t;
+ SkPoint innerPoint = pathPoint + v;
+ *fPositions.push() = innerPoint;
+ *fColors.push() = umbraColor;
+ }
+ fPrevPoint = pathPoint;
+}
+
+void GrSpotShadowTessellator::addArc(const SkVector& nextNormal) {
+ // fill in fan from previous quad
+ SkScalar rotSin, rotCos;
+ int numSteps;
+ compute_radial_steps(fPrevNormal, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
+ SkVector prevNormal = fPrevNormal;
+ for (int i = 0; i < numSteps; ++i) {
+ SkVector nextNormal;
+ nextNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
+ nextNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
+ *fPositions.push() = fPrevPoint + nextNormal;
+ *fColors.push() = fPenumbraColor;
+ *fIndices.push() = fPrevInnerIndex;
+ *fIndices.push() = fPositions.count() - 2;
+ *fIndices.push() = fPositions.count() - 1;
+
+ prevNormal = nextNormal;
+ }
+}
+
+void GrSpotShadowTessellator::finishArcAndAddEdge(const SkPoint& nextPoint,
+ const SkVector& nextNormal) {
+ // close out previous arc
+ SkPoint newPoint = fPrevPoint + nextNormal;
+ *fPositions.push() = newPoint;
+ *fColors.push() = fPenumbraColor;
+ *fIndices.push() = fPrevInnerIndex;
+ *fIndices.push() = fPositions.count() - 2;
+ *fIndices.push() = fPositions.count() - 1;
+
+ this->addEdge(nextPoint, nextNormal);
+}
+
+void GrSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
+ // add next quad
+ this->addInnerPoint(nextPoint, fUmbraColor, fRadius);
+ SkPoint newPoint = nextPoint + nextNormal;
+ *fPositions.push() = newPoint;
+ *fColors.push() = fPenumbraColor;
+
+ *fIndices.push() = fPrevInnerIndex;
+ *fIndices.push() = fPositions.count() - 3;
+ *fIndices.push() = fPositions.count() - 2;
+
+ *fIndices.push() = fPositions.count() - 3;
+ *fIndices.push() = fPositions.count() - 1;
+ *fIndices.push() = fPositions.count() - 2;
+
+ // add to center fan
+ *fIndices.push() = 0;
+ *fIndices.push() = fPrevInnerIndex;
+ *fIndices.push() = fPositions.count() - 2;
+
+ fPrevInnerIndex = fPositions.count() - 2;
+ fPrevNormal = nextNormal;
+}
diff --git a/src/gpu/effects/GrShadowTessellator.h b/src/gpu/effects/GrShadowTessellator.h
index 6d42f49..c2acde7 100755
--- a/src/gpu/effects/GrShadowTessellator.h
+++ b/src/gpu/effects/GrShadowTessellator.h
@@ -16,6 +16,8 @@
class SkMatrix;
class SkPath;
+// TODO: derive these two classes from a base class containing common elements
+
/**
* This class generates an ambient shadow for a path by walking the path, outsetting by the
* radius, and setting inner and outer colors to umbraColor and penumbraColor, respectively.
@@ -23,8 +25,8 @@
*/
class GrAmbientShadowTessellator {
public:
- GrAmbientShadowTessellator(const SkMatrix& viewMatrix, const SkPath& path, SkScalar radius,
- GrColor umbraColor, GrColor penumbraColor, bool transparent);
+ GrAmbientShadowTessellator(const SkPath& path, SkScalar radius, GrColor umbraColor,
+ GrColor penumbraColor, bool transparent);
int vertexCount() { return fPositions.count(); }
SkPoint* positions() { return fPositions.begin(); }
@@ -34,14 +36,12 @@
private:
void handleLine(const SkPoint& p);
- void handleLine(const SkMatrix& m, SkPoint p);
void handleQuad(const SkPoint pts[3]);
- void handleQuad(const SkMatrix& m, SkPoint pts[3]);
- void handleCubic(const SkMatrix& m, SkPoint pts[4]);
+ void handleCubic(SkPoint pts[4]);
- void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
+ void handleConic(SkPoint pts[3], SkScalar w);
void addArc(const SkVector& nextNormal);
void finishArcAndAddEdge(const SkVector& nextPoint, const SkVector& nextNormal);
@@ -69,4 +69,65 @@
SkTDArray<SkPoint> fPointBuffer;
};
+/**
+ * This class generates an spot shadow for a path by walking the transformed path, further
+ * transforming by the scale and translation, and outsetting and insetting by a radius.
+ * The center will be clipped against the original path unless transparent is true.
+ */
+class GrSpotShadowTessellator {
+public:
+ GrSpotShadowTessellator(const SkPath& path, SkScalar scale, const SkVector& translate,
+ SkScalar radius, GrColor umbraColor, GrColor penumbraColor,
+ bool transparent);
+
+ int vertexCount() { return fPositions.count(); }
+ SkPoint* positions() { return fPositions.begin(); }
+ GrColor* colors() { return fColors.begin(); }
+ int indexCount() { return fIndices.count(); }
+ uint16_t* indices() { return fIndices.begin(); }
+
+private:
+ void computeClipBounds(const SkPath& path);
+
+ void handleLine(const SkPoint& p);
+ void handleLine(SkScalar scale, const SkVector& xlate, SkPoint p);
+
+ void handleQuad(const SkPoint pts[3]);
+ void handleQuad(SkScalar scale, const SkVector& xlate, SkPoint pts[3]);
+
+ void handleCubic(SkScalar scale, const SkVector& xlate, SkPoint pts[4]);
+
+ void handleConic(SkScalar scale, const SkVector& xlate, SkPoint pts[3], SkScalar w);
+
+ void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
+ void addInnerPoint(const SkPoint& pathPoint, GrColor umbraColor, SkScalar radiusSqd);
+ void addArc(const SkVector& nextNormal);
+ void finishArcAndAddEdge(const SkVector& nextPoint, const SkVector& nextNormal);
+ void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
+
+ SkScalar fRadius;
+ GrColor fUmbraColor;
+ GrColor fPenumbraColor;
+
+ SkTDArray<SkPoint> fPositions;
+ SkTDArray<GrColor> fColors;
+ SkTDArray<uint16_t> fIndices;
+
+ int fPrevInnerIndex;
+ SkPoint fPrevPoint;
+ SkVector fPrevNormal;
+ int fFirstVertex;
+ SkPoint fFirstPoint;
+ SkVector fFirstNormal;
+ SkScalar fDirection;
+
+ SkPoint fCentroid;
+ SkTDArray<SkPoint> fClipPolygon;
+
+ // first three points
+ SkTDArray<SkPoint> fInitPoints;
+ // temporary buffer
+ SkTDArray<SkPoint> fPointBuffer;
+};
+
#endif