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>
This commit is contained in:
Jim Van Verth 2017-01-27 14:15:54 -05:00 committed by Skia Commit-Bot
parent fe8225802d
commit 91af727038
8 changed files with 620 additions and 186 deletions

View File

@ -42,6 +42,7 @@ skia_gpu_sources = [
"$_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 @@ skia_gpu_sources = [
"$_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",

View File

@ -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

View File

@ -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 @@ private:
#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

View File

@ -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 @@ void SkAmbientShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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 @@ bool SkAmbientShadowMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texPr
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 @@ bool SkAmbientShadowMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texPr
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 @@ bool SkAmbientShadowMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texPr
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,

View File

@ -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::canFilterMaskGPU(const SkRRect& devRRect,
}
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 @@ bool SkSpotShadowMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvi
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 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
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

View File

@ -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();
}

View File

@ -39,8 +39,7 @@ static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScala
*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 @@ GrAmbientShadowTessellator::GrAmbientShadowTessellator(const SkMatrix& viewMatri
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 @@ void GrAmbientShadowTessellator::handleLine(const SkPoint& p) {
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 SkPoint pts[3]) {
}
}
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::handleCubic(const SkMatrix& m, SkPoint pts[4])
}
}
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::addArc(const SkVector& nextNormal) {
}
}
void GrAmbientShadowTessellator::finishArcAndAddEdge(const SkPoint& nextPoint,
const SkVector& nextNormal) {
// close out previous arc
@ -309,3 +295,338 @@ void GrAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVecto
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;
}

View File

@ -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 SkPath;
*/
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 @@ public:
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 @@ private:
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