aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/utils/SkShadowUtils.cpp
diff options
context:
space:
mode:
authorGravatar Brian Salomon <bsalomon@google.com>2017-02-01 12:07:17 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-02-01 18:01:17 +0000
commit5e6895295a88fb702f49309542149d73f709bb10 (patch)
tree27870dc65a454a55fa114efffcf31f148d5242eb /src/utils/SkShadowUtils.cpp
parentc86b1456f73eaf5a43fac8c920c7140993b2f0ba (diff)
Add caching of ambient and spot shadow meshes.
Change-Id: If882186225621af4af4b4ddae0c786ec33ff40f3 Reviewed-on: https://skia-review.googlesource.com/7643 Commit-Queue: Brian Salomon <bsalomon@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com>
Diffstat (limited to 'src/utils/SkShadowUtils.cpp')
-rwxr-xr-xsrc/utils/SkShadowUtils.cpp270
1 files changed, 238 insertions, 32 deletions
diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp
index 6a08965e1a..6ad87e5e24 100755
--- a/src/utils/SkShadowUtils.cpp
+++ b/src/utils/SkShadowUtils.cpp
@@ -9,7 +9,13 @@
#include "SkCanvas.h"
#include "SkColorFilter.h"
#include "SkPath.h"
+#include "SkResourceCache.h"
#include "SkShadowTessellator.h"
+#include "SkTLazy.h"
+#if SK_SUPPORT_GPU
+#include "GrShape.h"
+#include "effects/GrBlurredEdgeFragmentProcessor.h"
+#endif
/**
* Gaussian color filter -- produces a Gaussian ramp based on the color's B value,
@@ -63,7 +69,6 @@ void SkGaussianColorFilter::toString(SkString* str) const {
#endif
#if SK_SUPPORT_GPU
-#include "effects/GrBlurredEdgeFragmentProcessor.h"
sk_sp<GrFragmentProcessor> SkGaussianColorFilter::asFragmentProcessor(GrContext*,
SkColorSpace*) const {
@@ -72,6 +77,218 @@ sk_sp<GrFragmentProcessor> SkGaussianColorFilter::asFragmentProcessor(GrContext*
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+struct AmbientVerticesFactory {
+ SkScalar fRadius;
+ SkColor fUmbraColor;
+ SkColor fPenumbraColor;
+ bool fTransparent;
+
+ bool operator==(const AmbientVerticesFactory& that) const {
+ return fRadius == that.fRadius && fUmbraColor == that.fUmbraColor &&
+ fPenumbraColor == that.fPenumbraColor && fTransparent == that.fTransparent;
+ }
+ bool operator!=(const AmbientVerticesFactory& that) const { return !(*this == that); }
+
+ sk_sp<SkShadowVertices> makeVertices(const SkPath& devPath) const {
+ return SkShadowVertices::MakeAmbient(devPath, fRadius, fUmbraColor, fPenumbraColor,
+ fTransparent);
+ }
+};
+
+struct SpotVerticesFactory {
+ SkScalar fRadius;
+ SkColor fUmbraColor;
+ SkColor fPenumbraColor;
+ SkScalar fScale;
+ SkVector fOffset;
+ bool fTransparent;
+
+ bool operator==(const SpotVerticesFactory& that) const {
+ return fRadius == that.fRadius && fUmbraColor == that.fUmbraColor &&
+ fPenumbraColor == that.fPenumbraColor && fTransparent == that.fTransparent &&
+ fScale == that.fScale && fOffset == that.fOffset;
+ }
+ bool operator!=(const SpotVerticesFactory& that) const { return !(*this == that); }
+
+ sk_sp<SkShadowVertices> makeVertices(const SkPath& devPath) const {
+ return SkShadowVertices::MakeSpot(devPath, fScale, fOffset, fRadius, fUmbraColor,
+ fPenumbraColor, fTransparent);
+ }
+};
+
+/**
+ * A record of shadow vertices stored in SkResourceCache. Each shape may have one record for a given
+ * FACTORY type.
+ */
+template <typename FACTORY>
+class TessPathRec : public SkResourceCache::Rec {
+public:
+ TessPathRec(const SkResourceCache::Key& key, const SkMatrix& viewMatrix, const FACTORY& factory,
+ sk_sp<SkShadowVertices> vertices)
+ : fVertices(std::move(vertices)), fFactory(factory), fOriginalMatrix(viewMatrix) {
+ fKey.reset(new uint8_t[key.size()]);
+ memcpy(fKey.get(), &key, key.size());
+ }
+
+ const Key& getKey() const override {
+ return *reinterpret_cast<SkResourceCache::Key*>(fKey.get());
+ }
+ size_t bytesUsed() const override { return fVertices->size(); }
+
+ const char* getCategory() const override { return "tessellated shadow mask"; }
+
+ sk_sp<SkShadowVertices> refVertices() const { return fVertices; }
+
+ const FACTORY& factory() const { return fFactory; }
+
+ const SkMatrix& originalViewMatrix() const { return fOriginalMatrix; }
+
+private:
+ std::unique_ptr<uint8_t[]> fKey;
+ sk_sp<SkShadowVertices> fVertices;
+ FACTORY fFactory;
+ SkMatrix fOriginalMatrix;
+};
+
+/**
+ * Used by FindVisitor to determine whether a cache entry can be reused and if so returns the
+ * vertices and translation vector.
+ */
+template <typename FACTORY>
+struct FindContext {
+ FindContext(const SkMatrix* viewMatrix, const FACTORY* factory)
+ : fViewMatrix(viewMatrix), fFactory(factory) {}
+ const SkMatrix* fViewMatrix;
+ SkVector fTranslate = {0, 0};
+ sk_sp<SkShadowVertices> fVertices;
+ const FACTORY* fFactory;
+};
+
+/**
+ * Function called by SkResourceCache when a matching cache key is found. The FACTORY and matrix of
+ * the FindContext are used to determine if the vertices are reusable. If so the vertices and
+ * necessary translation vector are set on the FindContext.
+ */
+template <typename FACTORY>
+bool FindVisitor(const SkResourceCache::Rec& baseRec, void* ctx) {
+ FindContext<FACTORY>* findContext = (FindContext<FACTORY>*)ctx;
+ const TessPathRec<FACTORY>& rec = static_cast<const TessPathRec<FACTORY>&>(baseRec);
+
+ const SkMatrix& viewMatrix = *findContext->fViewMatrix;
+ const SkMatrix& recMatrix = rec.originalViewMatrix();
+ if (findContext->fViewMatrix->hasPerspective() || recMatrix.hasPerspective()) {
+ if (recMatrix != viewMatrix) {
+ return false;
+ }
+ } else if (recMatrix.getScaleX() != viewMatrix.getScaleX() ||
+ recMatrix.getSkewX() != viewMatrix.getSkewX() ||
+ recMatrix.getScaleY() != viewMatrix.getScaleY() ||
+ recMatrix.getSkewY() != viewMatrix.getSkewY()) {
+ return false;
+ }
+ if (*findContext->fFactory != rec.factory()) {
+ return false;
+ }
+ findContext->fTranslate.fX = viewMatrix.getTranslateX() - recMatrix.getTranslateX();
+ findContext->fTranslate.fY = viewMatrix.getTranslateY() - recMatrix.getTranslateY();
+ findContext->fVertices = rec.refVertices();
+ return true;
+}
+
+class ShadowedPath {
+public:
+ ShadowedPath(const SkPath* path, const SkMatrix* viewMatrix)
+ : fOriginalPath(path)
+ , fViewMatrix(viewMatrix)
+#if SK_SUPPORT_GPU
+ , fShapeForKey(*path, GrStyle::SimpleFill())
+#endif
+ {}
+
+ const SkPath& transformedPath() {
+ if (!fTransformedPath.isValid()) {
+ fOriginalPath->transform(*fViewMatrix, fTransformedPath.init());
+ }
+ return *fTransformedPath.get();
+ }
+
+ const SkMatrix& viewMatrix() const { return *fViewMatrix; }
+#if SK_SUPPORT_GPU
+ /** Negative means the vertices should not be cached for this path. */
+ int keyBytes() const { return fShapeForKey.unstyledKeySize() * sizeof(uint32_t); }
+ void writeKey(void* key) const {
+ fShapeForKey.writeUnstyledKey(reinterpret_cast<uint32_t*>(key));
+ }
+#else
+ int keyBytes() const { return -1; }
+ void writeKey(void* key) const { SkFAIL("Should never be called"); }
+#endif
+
+private:
+ const SkPath* fOriginalPath;
+ const SkMatrix* fViewMatrix;
+#if SK_SUPPORT_GPU
+ GrShape fShapeForKey;
+#endif
+ SkTLazy<SkPath> fTransformedPath;
+};
+
+/**
+ * Draws a shadow to 'canvas'. The vertices used to draw the shadow are created by 'factory' unless
+ * they are first found in SkResourceCache.
+ */
+template <typename FACTORY>
+void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, SkColor color) {
+ FindContext<FACTORY> context(&path.viewMatrix(), &factory);
+ static void* kNamespace;
+
+ SkResourceCache::Key* key = nullptr;
+ SkAutoSTArray<32 * 4, uint8_t> keyStorage;
+ int keyDataBytes = path.keyBytes();
+ if (keyDataBytes >= 0) {
+ keyStorage.reset(keyDataBytes + sizeof(SkResourceCache::Key));
+ key = new (keyStorage.begin()) SkResourceCache::Key();
+ path.writeKey((uint32_t*)(keyStorage.begin() + sizeof(*key)));
+ key->init(&kNamespace, 0, keyDataBytes);
+ SkResourceCache::Find(*key, FindVisitor<FACTORY>, &context);
+ }
+
+ sk_sp<SkShadowVertices> 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
+ vertices = factory.makeVertices(path.transformedPath());
+ translate = &kZeroTranslate;
+ }
+
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setColorFilter(SkGaussianColorFilter::Make());
+ if (translate->fX || translate->fY) {
+ canvas->save();
+ canvas->translate(translate->fX, translate->fY);
+ }
+ canvas->drawVertices(SkCanvas::kTriangles_VertexMode, vertices->vertexCount(),
+ vertices->positions(), nullptr, vertices->colors(), vertices->indices(),
+ vertices->indexCount(), paint);
+ if (translate->fX || translate->fY) {
+ canvas->restore();
+ }
+ if (!foundInCache && key) {
+ SkResourceCache::Add(
+ new TessPathRec<FACTORY>(*key, path.viewMatrix(), factory, std::move(vertices)));
+ }
+}
+}
+
static const float kHeightFactor = 1.0f / 128.0f;
static const float kGeomFactor = 64.0f;
@@ -80,58 +297,47 @@ void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar oc
const SkPoint3& lightPos, SkScalar lightRadius,
SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
uint32_t flags) {
+ SkMatrix viewMatrix = canvas->getTotalMatrix();
- SkPath xformedPath;
- // TODO: handle transforming the path as part of the tessellator
- path.transform(canvas->getTotalMatrix(), &xformedPath);
canvas->save();
canvas->resetMatrix();
+ ShadowedPath shadowedPath(&path, &viewMatrix);
+
bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
if (ambientAlpha > 0) {
- SkScalar radius = occluderHeight * kHeightFactor * kGeomFactor;
+ AmbientVerticesFactory factory;
+ factory.fRadius = occluderHeight * kHeightFactor * kGeomFactor;
SkScalar umbraAlpha = SkScalarInvert((1.0f + SkTMax(occluderHeight*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 GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get
// the final alpha.
- SkColor umbraColor = SkColorSetARGB(255, 0, ambientAlpha*255.9999f, umbraAlpha*255.9999f);
- SkColor penumbraColor = SkColorSetARGB(255, 0, ambientAlpha*255.9999f, 0);
+ factory.fUmbraColor =
+ SkColorSetARGB(255, 0, ambientAlpha * 255.9999f, umbraAlpha * 255.9999f);
+ factory.fPenumbraColor = SkColorSetARGB(255, 0, ambientAlpha * 255.9999f, 0);
+ factory.fTransparent = transparent;
- sk_sp<SkShadowVertices> vertices =
- SkShadowVertices::MakeAmbient(xformedPath, radius, umbraColor, penumbraColor,
- transparent);
- SkPaint paint;
- paint.setColor(color);
- paint.setColorFilter(SkGaussianColorFilter::Make());
- canvas->drawVertices(SkCanvas::kTriangles_VertexMode, vertices->vertexCount(),
- vertices->positions(), nullptr, vertices->colors(),
- vertices->indices(), vertices->indexCount(), paint);
+ draw_shadow(factory, canvas, shadowedPath, color);
}
if (spotAlpha > 0) {
+ SpotVerticesFactory factory;
float zRatio = SkTPin(occluderHeight / (lightPos.fZ - occluderHeight), 0.0f, 0.95f);
- SkScalar radius = lightRadius * zRatio;
+ factory.fRadius = lightRadius * zRatio;
// Compute the scale and translation for the spot shadow.
- const SkScalar scale = lightPos.fZ / (lightPos.fZ - occluderHeight);
+ factory.fScale = lightPos.fZ / (lightPos.fZ - occluderHeight);
SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
- const SkVector spotOffset = SkVector::Make(zRatio*(center.fX - lightPos.fX),
- zRatio*(center.fY - lightPos.fY));
-
- SkColor umbraColor = SkColorSetARGB(255, 0, spotAlpha*255.9999f, 255);
- SkColor penumbraColor = SkColorSetARGB(255, 0, spotAlpha*255.9999f, 0);
- sk_sp<SkShadowVertices> vertices =
- SkShadowVertices::MakeSpot(xformedPath, scale, spotOffset, radius, umbraColor,
- penumbraColor, transparent);
- SkPaint paint;
- paint.setColor(color);
- paint.setColorFilter(SkGaussianColorFilter::Make());
- canvas->drawVertices(SkCanvas::kTriangles_VertexMode, vertices->vertexCount(),
- vertices->positions(), nullptr, vertices->colors(),
- vertices->indices(), vertices->indexCount(), paint);
+ factory.fOffset = SkVector::Make(zRatio * (center.fX - lightPos.fX),
+ zRatio * (center.fY - lightPos.fY));
+ factory.fUmbraColor = SkColorSetARGB(255, 0, spotAlpha * 255.9999f, 255);
+ factory.fPenumbraColor = SkColorSetARGB(255, 0, spotAlpha * 255.9999f, 0);
+ factory.fTransparent = transparent;
+
+ draw_shadow(factory, canvas, shadowedPath, color);
}
canvas->restore();