diff options
author | Jim Van Verth <jvanverth@google.com> | 2017-05-22 15:52:21 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-05-22 20:37:57 +0000 |
commit | 8793e3889833a3de18254cd8a147e213ec98b7fc (patch) | |
tree | 40588dde8f49f3a0cb8cf37987f2d0b14b38ab04 | |
parent | 79cdf28c51c8f8ff83575677419f95c0657fa077 (diff) |
Cache ambient and spot shadows at a canonical position
Change-Id: I1f80931513f7d2268b358fb38c86dd331f32f064
Reviewed-on: https://skia-review.googlesource.com/17394
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
-rw-r--r-- | gm/shadowutils.cpp | 5 | ||||
-rwxr-xr-x | samplecode/SampleShadowUtils.cpp | 5 | ||||
-rw-r--r-- | src/utils/SkShadowUtils.cpp | 96 |
3 files changed, 62 insertions, 44 deletions
diff --git a/gm/shadowutils.cpp b/gm/shadowutils.cpp index 974888181e..fbe73c6e31 100644 --- a/gm/shadowutils.cpp +++ b/gm/shadowutils.cpp @@ -30,15 +30,10 @@ void draw_paths(SkCanvas* canvas, bool hideOccluders) { SkRRect oddRRect; oddRRect.setNinePatch(SkRect::MakeWH(50, 50), 9, 13, 6, 16); paths.push_back().addRRect(oddRRect); - paths.back().setIsVolatile(true); paths.push_back().addRect(SkRect::MakeWH(50, 50)); - paths.back().setIsVolatile(true); paths.push_back().addCircle(25, 25, 25); - paths.back().setIsVolatile(true); paths.push_back().cubicTo(100, 50, 20, 100, 0, 0); - paths.back().setIsVolatile(true); paths.push_back().addOval(SkRect::MakeWH(20, 60)); - paths.back().setIsVolatile(true); static constexpr SkScalar kPad = 15.f; static constexpr SkScalar kLightR = 100.f; diff --git a/samplecode/SampleShadowUtils.cpp b/samplecode/SampleShadowUtils.cpp index e592f97b64..8d8a0aeaf6 100755 --- a/samplecode/SampleShadowUtils.cpp +++ b/samplecode/SampleShadowUtils.cpp @@ -154,10 +154,7 @@ protected: static constexpr SkScalar kHeight = 50.f; static constexpr SkScalar kAmbientAlpha = 0.5f; static constexpr SkScalar kSpotAlpha = 0.5f; - - // transform light position relative to canvas to handle tiling - SkPoint lightXY = canvas->getTotalMatrix().mapXY(250, 400); - SkPoint3 lightPos = { lightXY.fX, lightXY.fY, 500 }; + static constexpr SkPoint3 lightPos = { 250, 400, 500 }; canvas->translate(3 * kPad, 3 * kPad); canvas->save(); diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp index 16a1eee9c1..2cbf14a5ca 100644 --- a/src/utils/SkShadowUtils.cpp +++ b/src/utils/SkShadowUtils.cpp @@ -161,28 +161,36 @@ uint64_t resource_cache_shared_id() { struct AmbientVerticesFactory { SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed. bool fTransparent; + SkVector fOffset; bool isCompatible(const AmbientVerticesFactory& that, SkVector* translate) const { if (fOccluderHeight != that.fOccluderHeight || fTransparent != that.fTransparent) { return false; } - translate->set(0, 0); + *translate = that.fOffset; return true; } - sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const { + sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm, + SkVector* translate) const { SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight); - return SkShadowTessellator::MakeAmbient(path, ctm, zParams, fTransparent); + // pick a canonical place to generate shadow + SkMatrix noTrans(ctm); + if (!ctm.hasPerspective()) { + noTrans[SkMatrix::kMTransX] = 0; + noTrans[SkMatrix::kMTransY] = 0; + } + *translate = fOffset; + return SkShadowTessellator::MakeAmbient(path, noTrans, zParams, fTransparent); } }; /** Factory for an spot shadow mesh with particular shadow properties. */ struct SpotVerticesFactory { enum class OccluderType { - // The umbra cannot be dropped out because the occluder is not opaque. + // The umbra cannot be dropped out because either the occluder is not opaque, + // or the center of the umbra is visible. kTransparent, - // The occluder is opaque and the umbra is fully visible - kOpaqueFullUmbra, // The umbra can be dropped where it is occluded. kOpaquePartialUmbra, // It is known that the entire umbra is occluded. @@ -190,6 +198,7 @@ struct SpotVerticesFactory { }; SkVector fOffset; + SkPoint fLocalCenter; SkScalar fOccluderHeight = SK_ScalarNaN; // NaN so that isCompatible will fail until init'ed. SkPoint3 fDevLightPos; SkScalar fLightRadius; @@ -202,11 +211,10 @@ struct SpotVerticesFactory { } switch (fOccluderType) { case OccluderType::kTransparent: - case OccluderType::kOpaqueFullUmbra: case OccluderType::kOpaqueNoUmbra: // 'this' and 'that' will either both have no umbra removed or both have all the // umbra removed. - *translate = that.fOffset - fOffset; + *translate = that.fOffset; return true; case OccluderType::kOpaquePartialUmbra: // In this case we partially remove the umbra differently for 'this' and 'that' @@ -221,11 +229,26 @@ struct SpotVerticesFactory { return false; } - sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const { + sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm, + SkVector* translate) const { bool transparent = OccluderType::kTransparent == fOccluderType; SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight); - return SkShadowTessellator::MakeSpot(path, ctm, zParams, - fDevLightPos, fLightRadius, transparent); + if (ctm.hasPerspective() || OccluderType::kOpaquePartialUmbra == fOccluderType) { + translate->set(0, 0); + return SkShadowTessellator::MakeSpot(path, ctm, zParams, + fDevLightPos, fLightRadius, transparent); + } else { + // pick a canonical place to generate shadow, with light centered over path + SkMatrix noTrans(ctm); + noTrans[SkMatrix::kMTransX] = 0; + noTrans[SkMatrix::kMTransY] = 0; + SkPoint devCenter(fLocalCenter); + noTrans.mapPoints(&devCenter, 1); + SkPoint3 centerLightPos = SkPoint3::Make(devCenter.fX, devCenter.fY, fDevLightPos.fZ); + *translate = fOffset; + return SkShadowTessellator::MakeSpot(path, noTrans, zParams, + centerLightPos, fLightRadius, transparent); + } } }; @@ -245,8 +268,8 @@ public: } sk_sp<SkVertices> add(const SkPath& devPath, const AmbientVerticesFactory& ambient, - const SkMatrix& matrix) { - return fAmbientSet.add(devPath, ambient, matrix); + const SkMatrix& matrix, SkVector* translate) { + return fAmbientSet.add(devPath, ambient, matrix, translate); } sk_sp<SkVertices> find(const SpotVerticesFactory& spot, const SkMatrix& matrix, @@ -255,8 +278,8 @@ public: } sk_sp<SkVertices> add(const SkPath& devPath, const SpotVerticesFactory& spot, - const SkMatrix& matrix) { - return fSpotSet.add(devPath, spot, matrix); + const SkMatrix& matrix, SkVector* translate) { + return fSpotSet.add(devPath, spot, matrix, translate); } private: @@ -280,16 +303,15 @@ private: matrix.getSkewY() != m.getSkewY()) { continue; } - *translate += SkVector{matrix.getTranslateX() - m.getTranslateX(), - matrix.getTranslateY() - m.getTranslateY()}; return fEntries[i].fVertices; } } return nullptr; } - sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix) { - sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix); + sk_sp<SkVertices> add(const SkPath& path, const FACTORY& factory, const SkMatrix& matrix, + SkVector* translate) { + sk_sp<SkVertices> vertices = factory.makeVertices(path, matrix, translate); if (!vertices) { return nullptr; } @@ -461,12 +483,9 @@ template <typename FACTORY> } sk_sp<SkVertices> vertices; - const SkVector* translate; - static constexpr SkVector kZeroTranslate = {0, 0}; bool foundInCache = SkToBool(context.fVertices); if (foundInCache) { vertices = std::move(context.fVertices); - translate = &context.fTranslate; } else { // TODO: handle transforming the path as part of the tessellator if (key) { @@ -477,19 +496,20 @@ template <typename FACTORY> } else { tessellations.reset(new CachedTessellations()); } - vertices = tessellations->add(path.path(), factory, path.viewMatrix()); + vertices = tessellations->add(path.path(), factory, path.viewMatrix(), + &context.fTranslate); if (!vertices) { return; } auto rec = new CachedTessellationsRec(*key, std::move(tessellations)); SkResourceCache::Add(rec); } else { - vertices = factory.makeVertices(path.path(), path.viewMatrix()); + vertices = factory.makeVertices(path.path(), path.viewMatrix(), + &context.fTranslate); if (!vertices) { return; } } - translate = &kZeroTranslate; } SkPaint paint; @@ -499,7 +519,8 @@ template <typename FACTORY> SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate), SkGaussianColorFilter::Make())); - drawProc(vertices.get(), SkBlendMode::kModulate, paint, translate->fX, translate->fY); + drawProc(vertices.get(), SkBlendMode::kModulate, paint, + context.fTranslate.fX, context.fTranslate.fY); } } @@ -583,6 +604,12 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { AmbientVerticesFactory factory; factory.fOccluderHeight = zPlaneParams.fZ; factory.fTransparent = transparent; + if (viewMatrix.hasPerspective()) { + factory.fOffset.set(0, 0); + } else { + factory.fOffset.fX = viewMatrix.getTranslateX(); + factory.fOffset.fY = viewMatrix.getTranslateY(); + } SkColor renderColor = compute_render_color(color, ambientAlpha); draw_shadow(factory, drawVertsProc, shadowedPath, renderColor); @@ -614,6 +641,7 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { // Compute the scale and translation for the spot shadow. SkScalar scale = devLightPos.fZ / (devLightPos.fZ - occluderHeight); SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY()); + factory.fLocalCenter = center; viewMatrix.mapPoints(¢er, 1); factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX), zRatio * (center.fY - devLightPos.fY)); @@ -622,28 +650,26 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { factory.fLightRadius = lightRadius; SkRect devBounds; viewMatrix.mapRect(&devBounds, path.getBounds()); - if (transparent) { - factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent; - } else if (SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() || - SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) { + if (transparent || + SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() || + SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) { // if the translation of the shadow is big enough we're going to end up // filling the entire umbra, so we can treat these as all the same - factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaqueFullUmbra; + factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent; } else if (factory.fOffset.length()*scale + scale < radius) { // if we don't translate more than the blur distance, can assume umbra is covered factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaqueNoUmbra; } else { factory.fOccluderType = SpotVerticesFactory::OccluderType::kOpaquePartialUmbra; } - + // need to add this after we classify the shadow + factory.fOffset.fX += viewMatrix.getTranslateX(); + factory.fOffset.fY += viewMatrix.getTranslateY(); #ifdef DEBUG_SHADOW_CHECKS switch (factory.fOccluderType) { case SpotVerticesFactory::OccluderType::kTransparent: color = 0xFFD2B48C; // tan for transparent break; - case SpotVerticesFactory::OccluderType::kOpaqueFullUmbra: - color = 0xFF614126; // brown for umBra - break; case SpotVerticesFactory::OccluderType::kOpaquePartialUmbra: color = 0xFFFFA500; // orange for opaque break; |