Split ShadowMaskFilter into separate ambient and spot mask filters
authorJim Van Verth <jvanverth@google.com>
Mon, 16 Jan 2017 18:03:37 +0000 (13:03 -0500)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Mon, 16 Jan 2017 18:40:59 +0000 (18:40 +0000)
This does not change the public API.

BUG=skia:6119

Change-Id: Ibdcd2f8611bc2eec332d8a65e5d51246b89a0a90
Reviewed-on: https://skia-review.googlesource.com/7083
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
gn/core.gni
gn/effects.gni
include/private/SkShadowFlags.h [new file with mode: 0755]
include/utils/SkShadowUtils.h
src/effects/SkShadowMaskFilter.h [deleted file]
src/effects/shadows/SkAmbientShadowMaskFilter.cpp [new file with mode: 0755]
src/effects/shadows/SkAmbientShadowMaskFilter.h [new file with mode: 0755]
src/effects/shadows/SkSpotShadowMaskFilter.cpp [moved from src/effects/SkShadowMaskFilter.cpp with 63% similarity]
src/effects/shadows/SkSpotShadowMaskFilter.h [new file with mode: 0755]
src/utils/SkShadowUtils.cpp

index 4325263..e3ace02 100644 (file)
@@ -457,6 +457,7 @@ skia_core_sources = [
   "$_include/private/SkOnce.h",
   "$_include/private/SkRecords.h",
   "$_include/private/SkSemaphore.h",
+  "$_include/private/SkShadowFlags.h",
   "$_include/private/SkShadowParams.h",
   "$_include/private/SkSpinlock.h",
   "$_include/private/SkTemplates.h",
index b8bc2b1..b1a49c5 100644 (file)
@@ -58,8 +58,6 @@ skia_effects_sources = [
   "$_src/effects/SkPerlinNoiseShader.cpp",
   "$_src/effects/SkPictureImageFilter.cpp",
   "$_src/effects/SkRRectsGaussianEdgeMaskFilter.cpp",
-  "$_src/effects/SkShadowMaskFilter.cpp",
-  "$_src/effects/SkShadowMaskFilter.h",
   "$_src/effects/SkTableColorFilter.cpp",
   "$_src/effects/SkTableMaskFilter.cpp",
   "$_src/effects/SkTileImageFilter.cpp",
@@ -87,6 +85,11 @@ skia_effects_sources = [
   "$_src/effects/gradients/SkSweepGradient.cpp",
   "$_src/effects/gradients/SkSweepGradient.h",
 
+  "$_src/effects/shadows/SkAmbientShadowMaskFilter.cpp",
+  "$_src/effects/shadows/SkAmbientShadowMaskFilter.h",
+  "$_src/effects/shadows/SkSpotShadowMaskFilter.cpp",
+  "$_src/effects/shadows/SkSpotShadowMaskFilter.h",
+
   "$_include/effects/Sk1DPathEffect.h",
   "$_include/effects/Sk2DPathEffect.h",
   "$_include/effects/SkAlphaThresholdFilter.h",
diff --git a/include/private/SkShadowFlags.h b/include/private/SkShadowFlags.h
new file mode 100755 (executable)
index 0000000..0caa010
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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 SkShadowFlags_DEFINED
+#define SkShadowFlags_DEFINED
+
+// A set of flags shared between the SkAmbientShadowMaskFilter and the SkSpotShadowMaskFilter
+enum SkShadowFlags {
+    kNone_ShadowFlag = 0x00,
+    /** The occluding object is not opaque. Knowing that the occluder is opaque allows
+    * us to cull shadow geometry behind it and improve performance. */
+    kTransparentOccluder_ShadowFlag = 0x01,
+    /** Use a larger umbra for a darker shadow */
+    kLargerUmbra_ShadowFlag = 0x02,
+    /** Use a Gaussian for the edge function rather than smoothstep */
+    kGaussianEdge_ShadowFlag = 0x04,
+    /** mask for all shadow flags */
+    kAll_ShadowFlag = 0x07
+};
+
+#endif
index c380929..795c2cf 100755 (executable)
 
 #include "SkColor.h"
 #include "SkScalar.h"
+#include "../private/SkShadowFlags.h"
 
 class SkCanvas;
 class SkPath;
 
 class SkShadowUtils {
 public:
-    enum ShadowFlags {
-        kNone_ShadowFlag = 0x00,
-        /** The occluding object is not opaque. Knowing that the occluder is opaque allows
-        * us to cull shadow geometry behind it and improve performance. */
-        kTransparentOccluder_ShadowFlag = 0x01,
-        /** Use a larger umbra for a darker shadow */
-        kLargerUmbra_ShadowFlag = 0x02,
-        /** Use a Gaussian for the edge function rather than smoothstep */
-        kGaussianEdge_ShadowFlag = 0x04,
-        /** mask for all shadow flags */
-        kAll_ShadowFlag = 0x07
-    };
-
     // Draw an offset spot shadow and outlining ambient shadow for the given path.
     static void DrawShadow(SkCanvas*, const SkPath& path, SkScalar occluderHeight,
                            const SkPoint3& lightPos, SkScalar lightRadius,
                            SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
-                           uint32_t flags = kNone_ShadowFlag);
+                           uint32_t flags = SkShadowFlags::kNone_ShadowFlag);
 };
 
 #endif
diff --git a/src/effects/SkShadowMaskFilter.h b/src/effects/SkShadowMaskFilter.h
deleted file mode 100755 (executable)
index a85da63..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkShadowMaskFilter_DEFINED
-#define SkShadowMaskFilter_DEFINED
-
-#include "SkMaskFilter.h"
-
-
-/*
- * This filter implements a pair of shadows for an occluding object-- one representing
- * ambient occlusion, and one representing a displaced shadow from a point light.
- */
-class SK_API SkShadowMaskFilter {
-public:
-    enum ShadowFlags {
-        kNone_ShadowFlag = 0x00,
-        /** The occluding object is not opaque. Knowing that the occluder is opaque allows
-          * us to cull shadow geometry behind it and improve performance. */
-        kTransparentOccluder_ShadowFlag = 0x01,
-        /** Use a larger umbra for a darker shadow */
-        kLargerUmbra_ShadowFlag = 0x02,
-        /** Use a Gaussian for the edge function rather than smoothstep */
-        kGaussianEdge_ShadowFlag = 0x04,
-        /** mask for all shadow flags */
-        kAll_ShadowFlag = 0x07
-    };
-
-    /** Create a shadow maskfilter.
-     *  @param occluderHeight Height of occluding object off of ground plane.
-     *  @param lightPos       Position of the light applied to this object.
-     *  @param lightRadius    Radius of the light (light is assumed to be spherical).
-     *  @param ambientAlpha   Base opacity of the ambient occlusion shadow.
-     *  @param spotAlpha      Base opacity of the displaced spot shadow.
-     *  @param flags          Flags to use - defaults to none
-     *  @return The new shadow maskfilter
-     */
-    static sk_sp<SkMaskFilter> Make(SkScalar occluderHeight, const SkPoint3& lightPos,
-                                    SkScalar lightRadius, SkScalar ambientAlpha,
-                                    SkScalar spotAlpha, uint32_t flags = kNone_ShadowFlag);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
-
-private:
-    SkShadowMaskFilter(); // can't be instantiated
-};
-#endif
diff --git a/src/effects/shadows/SkAmbientShadowMaskFilter.cpp b/src/effects/shadows/SkAmbientShadowMaskFilter.cpp
new file mode 100755 (executable)
index 0000000..3dfd84e
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * 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 "SkAmbientShadowMaskFilter.h"
+#include "SkReadBuffer.h"
+#include "SkStringUtils.h"
+#include "SkWriteBuffer.h"
+
+#if SK_SUPPORT_GPU
+#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 "SkStrokeRec.h"
+#endif
+
+class SkAmbientShadowMaskFilterImpl : public SkMaskFilter {
+public:
+    SkAmbientShadowMaskFilterImpl(SkScalar occluderHeight, SkScalar ambientAlpha, uint32_t flags);
+
+    // overrides from SkMaskFilter
+    SkMask::Format getFormat() const override;
+    bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
+                    SkIPoint* margin) const override;
+
+#if SK_SUPPORT_GPU
+    bool canFilterMaskGPU(const SkRRect& devRRect,
+                          const SkIRect& clipBounds,
+                          const SkMatrix& ctm,
+                          SkRect* maskRect) const override;
+    bool directFilterMaskGPU(GrTextureProvider* texProvider,
+                             GrRenderTargetContext* drawContext,
+                             GrPaint&&,
+                             const GrClip&,
+                             const SkMatrix& viewMatrix,
+                             const SkStrokeRec& strokeRec,
+                             const SkPath& path) const override;
+    bool directFilterRRectMaskGPU(GrContext*,
+                                  GrRenderTargetContext* drawContext,
+                                  GrPaint&&,
+                                  const GrClip&,
+                                  const SkMatrix& viewMatrix,
+                                  const SkStrokeRec& strokeRec,
+                                  const SkRRect& rrect,
+                                  const SkRRect& devRRect) const override;
+    sk_sp<GrTextureProxy> filterMaskGPU(GrContext*,
+                                        sk_sp<GrTextureProxy> srcProxy,
+                                        const SkMatrix& ctm,
+                                        const SkIRect& maskRect) const override;
+#endif
+
+    void computeFastBounds(const SkRect&, SkRect*) const override;
+
+    SK_TO_STRING_OVERRIDE()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAmbientShadowMaskFilterImpl)
+
+private:
+    SkScalar fOccluderHeight;
+    SkScalar fAmbientAlpha;
+    uint32_t fFlags;
+
+    SkAmbientShadowMaskFilterImpl(SkReadBuffer&);
+    void flatten(SkWriteBuffer&) const override;
+
+    friend class SkAmbientShadowMaskFilter;
+
+    typedef SkMaskFilter INHERITED;
+};
+
+sk_sp<SkMaskFilter> SkAmbientShadowMaskFilter::Make(SkScalar occluderHeight, SkScalar ambientAlpha,
+                                                    uint32_t flags) {
+    // add some param checks here for early exit
+
+    return sk_sp<SkMaskFilter>(new SkAmbientShadowMaskFilterImpl(occluderHeight, ambientAlpha,
+                                                                 flags));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkAmbientShadowMaskFilterImpl::SkAmbientShadowMaskFilterImpl(SkScalar occluderHeight,
+                                                             SkScalar ambientAlpha,
+                                                             uint32_t flags)
+    : fOccluderHeight(occluderHeight)
+    , fAmbientAlpha(ambientAlpha)
+    , fFlags(flags) {
+    SkASSERT(fOccluderHeight > 0);
+    SkASSERT(fAmbientAlpha >= 0);
+}
+
+SkMask::Format SkAmbientShadowMaskFilterImpl::getFormat() const {
+    return SkMask::kA8_Format;
+}
+
+bool SkAmbientShadowMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
+                                               const SkMatrix& matrix,
+                                               SkIPoint* margin) const {
+    // TODO something
+    return false;
+}
+
+void SkAmbientShadowMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) const {
+    // TODO compute based on ambient data
+    dst->set(src.fLeft, src.fTop, src.fRight, src.fBottom);
+}
+
+sk_sp<SkFlattenable> SkAmbientShadowMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
+    const SkScalar occluderHeight = buffer.readScalar();
+    const SkScalar ambientAlpha = buffer.readScalar();
+    const uint32_t flags = buffer.readUInt();
+
+    return SkAmbientShadowMaskFilter::Make(occluderHeight, ambientAlpha, flags);
+}
+
+void SkAmbientShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
+    buffer.writeScalar(fOccluderHeight);
+    buffer.writeScalar(fAmbientAlpha);
+    buffer.writeUInt(fFlags);
+}
+
+#if SK_SUPPORT_GPU
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkAmbientShadowMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
+                                                     const SkIRect& clipBounds,
+                                                     const SkMatrix& ctm,
+                                                     SkRect* maskRect) const {
+    // TODO
+    *maskRect = devRRect.rect();
+    return true;
+}
+
+bool SkAmbientShadowMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvider,
+                                                        GrRenderTargetContext* drawContext,
+                                                        GrPaint&& paint,
+                                                        const GrClip& clip,
+                                                        const SkMatrix& viewMatrix,
+                                                        const SkStrokeRec& strokeRec,
+                                                        const SkPath& path) const {
+    SkASSERT(drawContext);
+    // TODO: this will not handle local coordinates properly
+
+    // if circle
+    // TODO: switch to SkScalarNearlyEqual when either oval renderer is updated or we
+    // have our own GeometryProc.
+    if (path.isOval(nullptr) && path.getBounds().width() == path.getBounds().height()) {
+        SkRRect rrect = SkRRect::MakeOval(path.getBounds());
+        return this->directFilterRRectMaskGPU(nullptr, drawContext, std::move(paint), clip,
+                                              SkMatrix::I(), strokeRec, rrect, rrect);
+    } else if (path.isRect(nullptr)) {
+        SkRRect rrect = SkRRect::MakeRect(path.getBounds());
+        return this->directFilterRRectMaskGPU(nullptr, drawContext, std::move(paint), clip,
+                                              SkMatrix::I(), strokeRec, rrect, rrect);
+    }
+
+    // TODO
+    return false;
+}
+
+bool SkAmbientShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
+                                                             GrRenderTargetContext* rtContext,
+                                                             GrPaint&& paint,
+                                                             const GrClip& clip,
+                                                             const SkMatrix& viewMatrix,
+                                                             const SkStrokeRec& strokeRec,
+                                                             const SkRRect& rrect,
+                                                             const SkRRect& devRRect) const {
+    // It's likely the caller has already done these checks, but we have to be sure.
+    // TODO: support analytic blurring of general rrect
+
+    // Fast path only supports filled rrects for now.
+    // TODO: fill and stroke as well.
+    if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
+        return false;
+    }
+    // Fast path only supports simple rrects with circular corners.
+    SkASSERT(devRRect.allCornersCircular());
+    if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
+        return false;
+    }
+    // Fast path only supports uniform scale.
+    SkScalar scaleFactors[2];
+    if (!viewMatrix.getMinMaxScales(scaleFactors)) {
+        // matrix is degenerate
+        return false;
+    }
+    if (scaleFactors[0] != scaleFactors[1]) {
+        return false;
+    }
+    SkScalar scaleFactor = scaleFactors[0];
+
+    // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
+    const SkScalar minRadius = 0.5f / scaleFactor;
+    bool isRect = rrect.getSimpleRadii().fX <= minRadius;
+
+    // TODO: take flags into account when generating shadow data
+
+    if (fAmbientAlpha > 0.0f) {
+        static const float kHeightFactor = 1.0f / 128.0f;
+        static const float kGeomFactor = 64.0f;
+
+        SkScalar srcSpaceAmbientRadius = fOccluderHeight * kHeightFactor * kGeomFactor;
+        const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f));
+        const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
+
+        // For the ambient rrect, we inset the offset rect by half the srcSpaceAmbientRadius
+        // to get our stroke shape.
+        SkScalar ambientPathOutset = SkTMax(ambientOffset - srcSpaceAmbientRadius * 0.5f,
+                                              minRadius);
+
+        SkRRect ambientRRect;
+        if (isRect) {
+            const SkRect temp = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset);
+            ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
+        } else {
+             rrect.outset(ambientPathOutset, ambientPathOutset, &ambientRRect);
+        }
+
+        const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
+
+        GrPaint newPaint(paint);
+        GrColor4f color = newPaint.getColor4f();
+        color.fRGBA[3] *= fAmbientAlpha;
+        newPaint.setColor4f(color);
+        SkStrokeRec ambientStrokeRec(SkStrokeRec::kHairline_InitStyle);
+        ambientStrokeRec.setStrokeStyle(srcSpaceAmbientRadius, false);
+
+        rtContext->drawShadowRRect(clip, std::move(newPaint), viewMatrix, ambientRRect,
+                                   devSpaceAmbientRadius,
+                                   GrStyle(ambientStrokeRec, nullptr));
+    }
+
+    return true;
+}
+
+sk_sp<GrTextureProxy> SkAmbientShadowMaskFilterImpl::filterMaskGPU(GrContext*,
+                                                                   sk_sp<GrTextureProxy> srcProxy,
+                                                                   const SkMatrix& ctm,
+                                                                   const SkIRect& maskRect) const {
+    // This filter is generative and doesn't operate on pre-existing masks
+    return nullptr;
+}
+
+#endif
+
+#ifndef SK_IGNORE_TO_STRING
+void SkAmbientShadowMaskFilterImpl::toString(SkString* str) const {
+    str->append("SkAmbientShadowMaskFilterImpl: (");
+
+    str->append("occluderHeight: ");
+    str->appendScalar(fOccluderHeight);
+    str->append(" ");
+
+    str->append("ambientAlpha: ");
+    str->appendScalar(fAmbientAlpha);
+    str->append(" ");
+
+    str->append("flags: (");
+    if (fFlags) {
+        bool needSeparator = false;
+        SkAddFlagToString(str,
+                          SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
+                          "TransparentOccluder", &needSeparator);
+        SkAddFlagToString(str,
+                          SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
+                          "GaussianEdge", &needSeparator);
+        SkAddFlagToString(str,
+                          SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
+                          "LargerUmbra", &needSeparator);
+    } else {
+        str->append("None");
+    }
+    str->append("))");
+}
+#endif
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkAmbientShadowMaskFilter)
+SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAmbientShadowMaskFilterImpl)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
diff --git a/src/effects/shadows/SkAmbientShadowMaskFilter.h b/src/effects/shadows/SkAmbientShadowMaskFilter.h
new file mode 100755 (executable)
index 0000000..cd77b10
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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 SkAmbientShadowMaskFilter_DEFINED
+#define SkAmbientShadowMaskFilter_DEFINED
+
+#include "SkMaskFilter.h"
+#include "SkShadowFlags.h"
+
+/*
+ * This filter implements a shadow representing ambient occlusion for an occluding object.
+ */
+class SK_API SkAmbientShadowMaskFilter {
+public:
+    /** Create a shadow maskfilter.
+     *  @param occluderHeight Height of occluding object off of ground plane.
+     *  @param ambientAlpha   Base opacity of the ambient occlusion shadow.
+     *  @param flags          Flags to use - defaults to none
+     *  @return The new shadow maskfilter
+     */
+    static sk_sp<SkMaskFilter> Make(SkScalar occluderHeight, SkScalar ambientAlpha,
+                                    uint32_t flags = SkShadowFlags::kNone_ShadowFlag);
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
+
+private:
+    SkAmbientShadowMaskFilter(); // can't be instantiated
+};
+#endif
similarity index 63%
rename from src/effects/SkShadowMaskFilter.cpp
rename to src/effects/shadows/SkSpotShadowMaskFilter.cpp
index 61a93b8..b214cef 100755 (executable)
@@ -1,11 +1,11 @@
 /*
- * Copyright 2016 Google Inc.
+ * 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 "SkShadowMaskFilter.h"
+#include "SkSpotShadowMaskFilter.h"
 #include "SkReadBuffer.h"
 #include "SkStringUtils.h"
 #include "SkWriteBuffer.h"
 #include "SkStrokeRec.h"
 #endif
 
-class SkShadowMaskFilterImpl : public SkMaskFilter {
+class SkSpotShadowMaskFilterImpl : public SkMaskFilter {
 public:
-    SkShadowMaskFilterImpl(SkScalar occluderHeight, const SkPoint3& lightPos, SkScalar lightRadius,
-                           SkScalar ambientAlpha, SkScalar spotAlpha, uint32_t flags);
+    SkSpotShadowMaskFilterImpl(SkScalar occluderHeight, const SkPoint3& lightPos,
+                               SkScalar lightRadius, SkScalar spotAlpha, uint32_t flags);
 
     // overrides from SkMaskFilter
     SkMask::Format getFormat() const override;
@@ -64,89 +64,86 @@ public:
     void computeFastBounds(const SkRect&, SkRect*) const override;
 
     SK_TO_STRING_OVERRIDE()
-    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkShadowMaskFilterImpl)
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpotShadowMaskFilterImpl)
 
 private:
     SkScalar fOccluderHeight;
     SkPoint3 fLightPos;
     SkScalar fLightRadius;
-    SkScalar fAmbientAlpha;
     SkScalar fSpotAlpha;
     uint32_t fFlags;
 
-    SkShadowMaskFilterImpl(SkReadBuffer&);
+    SkSpotShadowMaskFilterImpl(SkReadBuffer&);
     void flatten(SkWriteBuffer&) const override;
 
-    friend class SkShadowMaskFilter;
+    friend class SkSpotShadowMaskFilter;
 
     typedef SkMaskFilter INHERITED;
 };
 
-sk_sp<SkMaskFilter> SkShadowMaskFilter::Make(SkScalar occluderHeight, const SkPoint3& lightPos,
-                                             SkScalar lightRadius, SkScalar ambientAlpha,
-                                             SkScalar spotAlpha, uint32_t flags) {
+sk_sp<SkMaskFilter> SkSpotShadowMaskFilter::Make(SkScalar occluderHeight, const SkPoint3& lightPos,
+                                                 SkScalar lightRadius, SkScalar spotAlpha,
+                                                 uint32_t flags) {
     // add some param checks here for early exit
 
-    return sk_sp<SkMaskFilter>(new SkShadowMaskFilterImpl(occluderHeight, lightPos, lightRadius,
-                                                          ambientAlpha, spotAlpha, flags));
+    return sk_sp<SkMaskFilter>(new SkSpotShadowMaskFilterImpl(occluderHeight, lightPos,
+                                                              lightRadius, spotAlpha, flags));
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-SkShadowMaskFilterImpl::SkShadowMaskFilterImpl(SkScalar occluderHeight, const SkPoint3& lightPos,
-                                               SkScalar lightRadius, SkScalar ambientAlpha,
-                                               SkScalar spotAlpha, uint32_t flags)
+SkSpotShadowMaskFilterImpl::SkSpotShadowMaskFilterImpl(SkScalar occluderHeight,
+                                                       const SkPoint3& lightPos,
+                                                       SkScalar lightRadius,
+                                                       SkScalar spotAlpha,
+                                                       uint32_t flags)
     : fOccluderHeight(occluderHeight)
     , fLightPos(lightPos)
     , fLightRadius(lightRadius)
-    , fAmbientAlpha(ambientAlpha)
     , fSpotAlpha(spotAlpha)
     , fFlags(flags) {
     SkASSERT(fOccluderHeight > 0);
     SkASSERT(fLightPos.z() > 0 && fLightPos.z() > fOccluderHeight);
     SkASSERT(fLightRadius > 0);
-    SkASSERT(fAmbientAlpha >= 0);
     SkASSERT(fSpotAlpha >= 0);
 }
 
-SkMask::Format SkShadowMaskFilterImpl::getFormat() const {
+SkMask::Format SkSpotShadowMaskFilterImpl::getFormat() const {
     return SkMask::kA8_Format;
 }
 
-bool SkShadowMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
-                                        const SkMatrix& matrix,
-                                        SkIPoint* margin) const {
+bool SkSpotShadowMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
+                                            const SkMatrix& matrix,
+                                            SkIPoint* margin) const {
     // TODO something
     return false;
 }
 
-void SkShadowMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) const {
+void SkSpotShadowMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) const {
     // TODO compute based on ambient + spot data
     dst->set(src.fLeft, src.fTop, src.fRight, src.fBottom);
 }
 
-sk_sp<SkFlattenable> SkShadowMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
+sk_sp<SkFlattenable> SkSpotShadowMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
     const SkScalar occluderHeight = buffer.readScalar();
     const SkScalar lightX = buffer.readScalar();
     const SkScalar lightY = buffer.readScalar();
     const SkScalar lightZ = buffer.readScalar();
     const SkPoint3 lightPos = SkPoint3::Make(lightX, lightY, lightZ);
     const SkScalar lightRadius = buffer.readScalar();
-    const SkScalar ambientAlpha = buffer.readScalar();
     const SkScalar spotAlpha = buffer.readScalar();
     const uint32_t flags = buffer.readUInt();
 
-    return SkShadowMaskFilter::Make(occluderHeight, lightPos, lightRadius,
-                                    ambientAlpha, spotAlpha, flags);
+    return SkSpotShadowMaskFilter::Make(occluderHeight, lightPos, lightRadius,
+                                        spotAlpha, flags);
 }
 
-void SkShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
+void SkSpotShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
     buffer.writeScalar(fOccluderHeight);
     buffer.writeScalar(fLightPos.fX);
     buffer.writeScalar(fLightPos.fY);
     buffer.writeScalar(fLightPos.fZ);
     buffer.writeScalar(fLightRadius);
-    buffer.writeScalar(fAmbientAlpha);
     buffer.writeScalar(fSpotAlpha);
     buffer.writeUInt(fFlags);
 }
@@ -155,22 +152,22 @@ void SkShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-bool SkShadowMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
-                                              const SkIRect& clipBounds,
-                                              const SkMatrix& ctm,
-                                              SkRect* maskRect) const {
+bool SkSpotShadowMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
+                                                  const SkIRect& clipBounds,
+                                                  const SkMatrix& ctm,
+                                                  SkRect* maskRect) const {
     // TODO
     *maskRect = devRRect.rect();
     return true;
 }
 
-bool SkShadowMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvider,
-                                                 GrRenderTargetContext* drawContext,
-                                                 GrPaint&& paint,
-                                                 const GrClip& clip,
-                                                 const SkMatrix& viewMatrix,
-                                                 const SkStrokeRec& strokeRec,
-                                                 const SkPath& path) const {
+bool SkSpotShadowMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvider,
+                                                     GrRenderTargetContext* drawContext,
+                                                     GrPaint&& paint,
+                                                     const GrClip& clip,
+                                                     const SkMatrix& viewMatrix,
+                                                     const SkStrokeRec& strokeRec,
+                                                     const SkPath& path) const {
     SkASSERT(drawContext);
     // TODO: this will not handle local coordinates properly
 
@@ -191,14 +188,14 @@ bool SkShadowMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvider,
     return false;
 }
 
-bool SkShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
-                                                      GrRenderTargetContext* renderTargetContext,
-                                                      GrPaint&& paint,
-                                                      const GrClip& clip,
-                                                      const SkMatrix& viewMatrix,
-                                                      const SkStrokeRec& strokeRec,
-                                                      const SkRRect& rrect,
-                                                      const SkRRect& devRRect) const {
+bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
+                                                          GrRenderTargetContext* rtContext,
+                                                          GrPaint&& paint,
+                                                          const GrClip& clip,
+                                                          const SkMatrix& viewMatrix,
+                                                          const SkStrokeRec& strokeRec,
+                                                          const SkRRect& rrect,
+                                                          const SkRRect& devRRect) const {
     // It's likely the caller has already done these checks, but we have to be sure.
     // TODO: support analytic blurring of general rrect
 
@@ -229,41 +226,6 @@ bool SkShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
 
     // TODO: take flags into account when generating shadow data
 
-    if (fAmbientAlpha > 0.0f) {
-        static const float kHeightFactor = 1.0f / 128.0f;
-        static const float kGeomFactor = 64.0f;
-
-        SkScalar srcSpaceAmbientRadius = fOccluderHeight * kHeightFactor * kGeomFactor;
-        const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f));
-        const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
-
-        // For the ambient rrect, we inset the offset rect by half the srcSpaceAmbientRadius
-        // to get our stroke shape.
-        SkScalar ambientPathOutset = SkTMax(ambientOffset - srcSpaceAmbientRadius * 0.5f,
-                                              minRadius);
-
-        SkRRect ambientRRect;
-        if (isRect) {
-            const SkRect temp = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset);
-            ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
-        } else {
-             rrect.outset(ambientPathOutset, ambientPathOutset, &ambientRRect);
-        }
-
-        const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
-
-        GrPaint newPaint(paint);
-        GrColor4f color = newPaint.getColor4f();
-        color.fRGBA[3] *= fAmbientAlpha;
-        newPaint.setColor4f(color);
-        SkStrokeRec ambientStrokeRec(SkStrokeRec::kHairline_InitStyle);
-        ambientStrokeRec.setStrokeStyle(srcSpaceAmbientRadius, false);
-
-        renderTargetContext->drawShadowRRect(clip, std::move(newPaint), viewMatrix, ambientRRect,
-                                             devSpaceAmbientRadius,
-                                             GrStyle(ambientStrokeRec, nullptr));
-    }
-
     if (fSpotAlpha > 0.0f) {
         float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f);
 
@@ -318,7 +280,7 @@ bool SkShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
         // If the area of the stroked geometry is larger than the fill geometry,
         // or if the caster is transparent, just fill it.
         if (strokedArea > filledArea ||
-            fFlags & SkShadowMaskFilter::kTransparentOccluder_ShadowFlag) {
+            fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag) {
             spotStrokeRec.setStrokeStyle(srcSpaceSpotRadius, true);
         } else {
             // Since we can't have unequal strokes, inset the shadow rect so the inner
@@ -336,17 +298,17 @@ bool SkShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
 
         spotShadowRRect.offset(spotOffset.fX, spotOffset.fY);
 
-        renderTargetContext->drawShadowRRect(clip, std::move(paint), viewMatrix, spotShadowRRect,
-                                             devSpaceSpotRadius, GrStyle(spotStrokeRec, nullptr));
+        rtContext->drawShadowRRect(clip, std::move(paint), viewMatrix, spotShadowRRect,
+                                   devSpaceSpotRadius, GrStyle(spotStrokeRec, nullptr));
     }
 
     return true;
 }
 
-sk_sp<GrTextureProxy> SkShadowMaskFilterImpl::filterMaskGPU(GrContext*,
-                                                            sk_sp<GrTextureProxy> srcProxy,
-                                                            const SkMatrix& ctm,
-                                                            const SkIRect& maskRect) const {
+sk_sp<GrTextureProxy> SkSpotShadowMaskFilterImpl::filterMaskGPU(GrContext*,
+                                                                sk_sp<GrTextureProxy> srcProxy,
+                                                                const SkMatrix& ctm,
+                                                                const SkIRect& maskRect) const {
     // This filter is generative and doesn't operate on pre-existing masks
     return nullptr;
 }
@@ -354,8 +316,8 @@ sk_sp<GrTextureProxy> SkShadowMaskFilterImpl::filterMaskGPU(GrContext*,
 #endif
 
 #ifndef SK_IGNORE_TO_STRING
-void SkShadowMaskFilterImpl::toString(SkString* str) const {
-    str->append("SkShadowMaskFilterImpl: (");
+void SkSpotShadowMaskFilterImpl::toString(SkString* str) const {
+    str->append("SkSpotShadowMaskFilterImpl: (");
 
     str->append("occluderHeight: ");
     str->appendScalar(fOccluderHeight);
@@ -373,10 +335,6 @@ void SkShadowMaskFilterImpl::toString(SkString* str) const {
     str->appendScalar(fLightRadius);
     str->append(" ");
 
-    str->append("ambientAlpha: ");
-    str->appendScalar(fAmbientAlpha);
-    str->append(" ");
-
     str->append("spotAlpha: ");
     str->appendScalar(fSpotAlpha);
     str->append(" ");
@@ -385,13 +343,13 @@ void SkShadowMaskFilterImpl::toString(SkString* str) const {
     if (fFlags) {
         bool needSeparator = false;
         SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowMaskFilter::kTransparentOccluder_ShadowFlag),
+                          SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
                           "TransparentOccluder", &needSeparator);
         SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowMaskFilter::kGaussianEdge_ShadowFlag),
+                          SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
                           "GaussianEdge", &needSeparator);
         SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowMaskFilter::kLargerUmbra_ShadowFlag),
+                          SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
                           "LargerUmbra", &needSeparator);
     } else {
         str->append("None");
@@ -400,6 +358,6 @@ void SkShadowMaskFilterImpl::toString(SkString* str) const {
 }
 #endif
 
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShadowMaskFilter)
-SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkShadowMaskFilterImpl)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkSpotShadowMaskFilter)
+SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSpotShadowMaskFilterImpl)
 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
diff --git a/src/effects/shadows/SkSpotShadowMaskFilter.h b/src/effects/shadows/SkSpotShadowMaskFilter.h
new file mode 100755 (executable)
index 0000000..5e1a4a9
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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 SkSpotShadowMaskFilter_DEFINED
+#define SkSpotShadowMaskFilter_DEFINED
+
+#include "SkMaskFilter.h"
+#include "SkShadowFlags.h"
+
+/*
+ * This filter implements a shadow for an occluding object
+ * representing a displaced shadow from a point light.
+ */
+class SK_API SkSpotShadowMaskFilter {
+public:
+    /** Create a shadow maskfilter.
+     *  @param occluderHeight Height of occluding object off of ground plane.
+     *  @param lightPos       Position of the light applied to this object.
+     *  @param lightRadius    Radius of the light (light is assumed to be spherical).
+     *  @param spotAlpha      Base opacity of the displaced spot shadow.
+     *  @param flags          Flags to use - defaults to none
+     *  @return The new shadow maskfilter
+     */
+    static sk_sp<SkMaskFilter> Make(SkScalar occluderHeight, const SkPoint3& lightPos,
+                                    SkScalar lightRadius, SkScalar spotAlpha,
+                                    uint32_t flags = SkShadowFlags::kNone_ShadowFlag);
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
+
+private:
+    SkSpotShadowMaskFilter(); // can't be instantiated
+};
+#endif
index e8a1f83..e93f778 100755 (executable)
@@ -7,7 +7,8 @@
 
 #include "SkShadowUtils.h"
 #include "SkCanvas.h"
-#include "../effects/SkShadowMaskFilter.h"
+#include "../effects/shadows/SkAmbientShadowMaskFilter.h"
+#include "../effects/shadows/SkSpotShadowMaskFilter.h"
 
 // Draw an offset spot shadow and outlining ambient shadow for the given path.
 void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar occluderHeight,
@@ -16,8 +17,9 @@ void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar oc
                                uint32_t flags) {
     SkPaint newPaint;
     newPaint.setColor(color);
-    newPaint.setMaskFilter(SkShadowMaskFilter::Make(occluderHeight, lightPos, lightRadius,
-                                                    ambientAlpha, spotAlpha, flags));
-
+    newPaint.setMaskFilter(SkAmbientShadowMaskFilter::Make(occluderHeight, ambientAlpha, flags));
+    canvas->drawPath(path, newPaint);
+    newPaint.setMaskFilter(SkSpotShadowMaskFilter::Make(occluderHeight, lightPos, lightRadius,
+                                                        spotAlpha, flags));
     canvas->drawPath(path, newPaint);
 }