From 2103cf0ff09763aeaa35508734f765aec9b75665 Mon Sep 17 00:00:00 2001 From: Jim Van Verth Date: Mon, 16 Jan 2017 13:03:37 -0500 Subject: [PATCH] Split ShadowMaskFilter into separate ambient and spot mask filters 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 Reviewed-by: Jim Van Verth Reviewed-by: Robert Phillips --- gn/core.gni | 1 + gn/effects.gni | 7 +- include/private/SkShadowFlags.h | 25 ++ include/utils/SkShadowUtils.h | 16 +- src/effects/SkShadowMaskFilter.h | 51 ---- src/effects/shadows/SkAmbientShadowMaskFilter.cpp | 290 +++++++++++++++++++++ src/effects/shadows/SkAmbientShadowMaskFilter.h | 33 +++ .../SkSpotShadowMaskFilter.cpp} | 162 +++++------- src/effects/shadows/SkSpotShadowMaskFilter.h | 37 +++ src/utils/SkShadowUtils.cpp | 10 +- 10 files changed, 459 insertions(+), 173 deletions(-) create mode 100755 include/private/SkShadowFlags.h delete mode 100755 src/effects/SkShadowMaskFilter.h create mode 100755 src/effects/shadows/SkAmbientShadowMaskFilter.cpp create mode 100755 src/effects/shadows/SkAmbientShadowMaskFilter.h rename src/effects/{SkShadowMaskFilter.cpp => shadows/SkSpotShadowMaskFilter.cpp} (63%) create mode 100755 src/effects/shadows/SkSpotShadowMaskFilter.h diff --git a/gn/core.gni b/gn/core.gni index 4325263..e3ace02 100644 --- a/gn/core.gni +++ b/gn/core.gni @@ -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", diff --git a/gn/effects.gni b/gn/effects.gni index b8bc2b1..b1a49c5 100644 --- a/gn/effects.gni +++ b/gn/effects.gni @@ -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 index 0000000..0caa010 --- /dev/null +++ b/include/private/SkShadowFlags.h @@ -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 diff --git a/include/utils/SkShadowUtils.h b/include/utils/SkShadowUtils.h index c380929..795c2cf 100755 --- a/include/utils/SkShadowUtils.h +++ b/include/utils/SkShadowUtils.h @@ -10,30 +10,18 @@ #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 index a85da63..0000000 --- a/src/effects/SkShadowMaskFilter.h +++ /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 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 index 0000000..3dfd84e --- /dev/null +++ b/src/effects/shadows/SkAmbientShadowMaskFilter.cpp @@ -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 filterMaskGPU(GrContext*, + sk_sp 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 SkAmbientShadowMaskFilter::Make(SkScalar occluderHeight, SkScalar ambientAlpha, + uint32_t flags) { + // add some param checks here for early exit + + return sk_sp(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 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 SkAmbientShadowMaskFilterImpl::filterMaskGPU(GrContext*, + sk_sp 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 index 0000000..cd77b10 --- /dev/null +++ b/src/effects/shadows/SkAmbientShadowMaskFilter.h @@ -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 Make(SkScalar occluderHeight, SkScalar ambientAlpha, + uint32_t flags = SkShadowFlags::kNone_ShadowFlag); + + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() + +private: + SkAmbientShadowMaskFilter(); // can't be instantiated +}; +#endif diff --git a/src/effects/SkShadowMaskFilter.cpp b/src/effects/shadows/SkSpotShadowMaskFilter.cpp similarity index 63% rename from src/effects/SkShadowMaskFilter.cpp rename to src/effects/shadows/SkSpotShadowMaskFilter.cpp index 61a93b8..b214cef 100755 --- a/src/effects/SkShadowMaskFilter.cpp +++ b/src/effects/shadows/SkSpotShadowMaskFilter.cpp @@ -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" @@ -25,10 +25,10 @@ #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 SkShadowMaskFilter::Make(SkScalar occluderHeight, const SkPoint3& lightPos, - SkScalar lightRadius, SkScalar ambientAlpha, - SkScalar spotAlpha, uint32_t flags) { +sk_sp 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(new SkShadowMaskFilterImpl(occluderHeight, lightPos, lightRadius, - ambientAlpha, spotAlpha, flags)); + return sk_sp(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 SkShadowMaskFilterImpl::CreateProc(SkReadBuffer& buffer) { +sk_sp 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 SkShadowMaskFilterImpl::filterMaskGPU(GrContext*, - sk_sp srcProxy, - const SkMatrix& ctm, - const SkIRect& maskRect) const { +sk_sp SkSpotShadowMaskFilterImpl::filterMaskGPU(GrContext*, + sk_sp 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 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 index 0000000..5e1a4a9 --- /dev/null +++ b/src/effects/shadows/SkSpotShadowMaskFilter.h @@ -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 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 diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp index e8a1f83..e93f778 100755 --- a/src/utils/SkShadowUtils.cpp +++ b/src/utils/SkShadowUtils.cpp @@ -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); } -- 2.7.4