aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Jim Van Verth <jvanverth@google.com>2017-05-22 15:52:21 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-05-22 20:37:57 +0000
commit8793e3889833a3de18254cd8a147e213ec98b7fc (patch)
tree40588dde8f49f3a0cb8cf37987f2d0b14b38ab04
parent79cdf28c51c8f8ff83575677419f95c0657fa077 (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.cpp5
-rwxr-xr-xsamplecode/SampleShadowUtils.cpp5
-rw-r--r--src/utils/SkShadowUtils.cpp96
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(&center, 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;