diff options
author | Jim Van Verth <jvanverth@google.com> | 2017-01-27 14:15:54 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-01-27 19:53:18 +0000 |
commit | 91af72703830f3946c538b47c6c7c96afc0adde2 (patch) | |
tree | f478faca5f38782c9178ca51068ff4fc827f35d3 /src/gpu/effects | |
parent | fe8225802d2ab0dbc6f0d40f9666ebca1b4e2a4a (diff) |
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>
Diffstat (limited to 'src/gpu/effects')
-rwxr-xr-x | src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp | 72 | ||||
-rwxr-xr-x | src/gpu/effects/GrShadowTessellator.cpp | 365 | ||||
-rwxr-xr-x | src/gpu/effects/GrShadowTessellator.h | 73 |
3 files changed, 482 insertions, 28 deletions
diff --git a/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp b/src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp new file mode 100755 index 0000000000..1441a752c8 --- /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 8aa91e2f40..a51a66addd 100755 --- a/src/gpu/effects/GrShadowTessellator.cpp +++ b/src/gpu/effects/GrShadowTessellator.cpp @@ -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; +} diff --git a/src/gpu/effects/GrShadowTessellator.h b/src/gpu/effects/GrShadowTessellator.h index 6d42f496f7..c2acde72f4 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 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 |