diff options
-rw-r--r-- | gyp/effects.gypi | 2 | ||||
-rw-r--r-- | include/effects/SkGaussianEdgeShader.h | 24 | ||||
-rwxr-xr-x | samplecode/SampleAndroidShadows.cpp | 91 | ||||
-rw-r--r-- | src/effects/SkGaussianEdgeShader.cpp | 152 | ||||
-rw-r--r-- | src/gpu/GrOvalRenderer.cpp | 21 | ||||
-rw-r--r-- | src/gpu/batches/GrAnalyticRectBatch.cpp | 2 | ||||
-rw-r--r-- | src/gpu/glsl/GrGLSLProgramBuilder.cpp | 2 |
7 files changed, 285 insertions, 9 deletions
diff --git a/gyp/effects.gypi b/gyp/effects.gypi index c755ac5bff..45eced88e2 100644 --- a/gyp/effects.gypi +++ b/gyp/effects.gypi @@ -44,6 +44,7 @@ '<(skia_src_path)/effects/SkEmbossMaskFilter.cpp', '<(skia_src_path)/effects/SkImageSource.cpp', '<(skia_src_path)/effects/SkGammaColorFilter.cpp', + '<(skia_src_path)/effects/SkGaussianEdgeShader.cpp', '<(skia_src_path)/effects/SkGpuBlurUtils.h', '<(skia_src_path)/effects/SkGpuBlurUtils.cpp', '<(skia_src_path)/effects/SkLayerDrawLooper.cpp', @@ -106,6 +107,7 @@ '<(skia_include_path)/effects/SkDropShadowImageFilter.h', '<(skia_include_path)/effects/SkEmbossMaskFilter.h', '<(skia_include_path)/effects/SkGammaColorFilter.h', + '<(skia_include_path)/effects/SkGaussianEdgeShader.h', '<(skia_include_path)/effects/SkGradientShader.h', '<(skia_include_path)/effects/SkImageSource.h', '<(skia_include_path)/effects/SkLayerDrawLooper.h', diff --git a/include/effects/SkGaussianEdgeShader.h b/include/effects/SkGaussianEdgeShader.h new file mode 100644 index 0000000000..59a2889e94 --- /dev/null +++ b/include/effects/SkGaussianEdgeShader.h @@ -0,0 +1,24 @@ +/* + * 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 SkGaussianEdgeShader_DEFINED +#define SkGaussianEdgeShader_DEFINED + +#include "SkShader.h" + +class SK_API SkGaussianEdgeShader { +public: + /** Returns a shader that applies a Gaussian blur depending on distance to the edge + * Currently this is only useable with Circle and RRect shapes on the GPU backend. + * Raster will draw nothing. + */ + static sk_sp<SkShader> Make(); + + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() +}; + +#endif diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp index 9ab69a670c..38fcd204d9 100755 --- a/samplecode/SampleAndroidShadows.cpp +++ b/samplecode/SampleAndroidShadows.cpp @@ -9,12 +9,15 @@ #include "SkBlurMask.h" #include "SkBlurMaskFilter.h" #include "SkCanvas.h" +#include "SkGaussianEdgeShader.h" #include "SkPath.h" #include "SkPoint3.h" #include "SkUtils.h" #include "SkView.h" #include "sk_tool_utils.h" +//////////////////////////////////////////////////////////////////////////// + class ShadowsView : public SampleView { SkPath fRectPath; SkPath fRRPath; @@ -22,12 +25,14 @@ class ShadowsView : public SampleView { SkPoint3 fLightPos; bool fShowAmbient; + bool fUseAltAmbient; bool fShowSpot; bool fShowObject; public: ShadowsView() : fShowAmbient(true) + , fUseAltAmbient(true) , fShowSpot(true) , fShowObject(true) {} @@ -52,6 +57,9 @@ protected: case 'B': fShowAmbient = !fShowAmbient; break; + case 'T': + fUseAltAmbient = !fUseAltAmbient; + break; case 'S': fShowSpot = !fShowSpot; break; @@ -120,10 +128,79 @@ protected: canvas->drawPath(path, paint); // draw occlusion rect +#if DRAW_OCCL_RECT SkPaint stroke; stroke.setStyle(SkPaint::kStroke_Style); stroke.setColor(SK_ColorBLUE); canvas->drawRect(occlRect, stroke); +#endif + } + + void drawAmbientShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue, + SkScalar ambientAlpha) { + + if (ambientAlpha <= 0) { + return; + } + + const SkScalar kHeightFactor = 1.f / 128.f; + const SkScalar kGeomFactor = 64; + + SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0)); + SkScalar radius = zValue*kHeightFactor*kGeomFactor; + + // fast path + SkRect pathRect; + SkRRect pathRRect; + if ((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) || + (path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) || + path.isRect(&pathRect)) { + + // For all of these, we outset the rect by half the radius to get our stroke shape. + if (path.isOval(nullptr)) { + pathRect.outset(0.5f*radius, 0.5f*radius); + pathRRect = SkRRect::MakeOval(pathRect); + } else if (path.isRect(nullptr)) { + pathRect.outset(0.5f*radius, 0.5f*radius); + pathRRect = SkRRect::MakeRectXY(pathRect, 0.5f*radius, 0.5f*radius); + } else { + pathRRect.outset(0.5f*radius, 0.5f*radius); + } + + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(SkColorSetARGB((unsigned char)(ambientAlpha*umbraAlpha*255.999f), + 0, 0, 0)); + paint.setStrokeWidth(radius); + paint.setStyle(SkPaint::kStroke_Style); + + paint.setShader(SkGaussianEdgeShader::Make()); + canvas->drawRRect(pathRRect, paint); + } else { + // occlude blur + SkRect occlRect; + GetOcclRect(path, &occlRect); + sk_sp<SkMaskFilter> f = SkBlurMaskFilter::Make(kNormal_SkBlurStyle, + SkBlurMask::ConvertRadiusToSigma(radius), + occlRect, + SkBlurMaskFilter::kNone_BlurFlag); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setMaskFilter(std::move(f)); + paint.setColor(SkColorSetARGB((unsigned char)(ambientAlpha*umbraAlpha*255.999f), + 0, 0, 0)); + canvas->drawPath(path, paint); + + // draw occlusion rect +#if DRAW_OCCL_RECT + SkPaint stroke; + stroke.setStyle(SkPaint::kStroke_Style); + stroke.setColor(SK_ColorBLUE); + canvas->drawRect(occlRect, stroke); +#endif + } + } void drawSpotShadow(SkCanvas* canvas, const SkPath& path, SkScalar zValue, @@ -178,10 +255,12 @@ protected: canvas->drawPath(path, paint); // draw occlusion rect +#if DRAW_OCCL_RECT SkPaint stroke; stroke.setStyle(SkPaint::kStroke_Style); stroke.setColor(SK_ColorRED); - canvas->drawRect(occlRect, stroke); + canvas->drawRect(occlRect, stroke) +#endif } void drawShadowedPath(SkCanvas* canvas, const SkPath& path, SkScalar zValue, @@ -191,7 +270,11 @@ protected: const SkScalar kSpotAlpha = 0.25f; if (fShowAmbient) { - this->drawAmbientShadow(canvas, path, zValue, kAmbientAlpha); + if (fUseAltAmbient) { + this->drawAmbientShadowAlt(canvas, path, zValue, kAmbientAlpha); + } else { + this->drawAmbientShadow(canvas, path, zValue, kAmbientAlpha); + } } if (fShowSpot) { this->drawSpotShadow(canvas, path, zValue, fLightPos, kLightWidth, kSpotAlpha); @@ -218,6 +301,10 @@ protected: paint.setColor(SK_ColorBLUE); canvas->translate(-250, 110); this->drawShadowedPath(canvas, fCirclePath, 5, paint); + + paint.setColor(SK_ColorGREEN); + canvas->translate(250, 0); + this->drawShadowedPath(canvas, fRRPath, 5, paint); } protected: diff --git a/src/effects/SkGaussianEdgeShader.cpp b/src/effects/SkGaussianEdgeShader.cpp new file mode 100644 index 0000000000..e519598f1b --- /dev/null +++ b/src/effects/SkGaussianEdgeShader.cpp @@ -0,0 +1,152 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkGaussianEdgeShader.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" + + /** \class SkGaussianEdgeShaderImpl + This subclass of shader applies a Gaussian to shadow edge + */ +class SkGaussianEdgeShaderImpl : public SkShader { +public: + SkGaussianEdgeShaderImpl() {} + + bool isOpaque() const override; + +#if SK_SUPPORT_GPU + sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; +#endif + + SK_TO_STRING_OVERRIDE() + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkGaussianEdgeShaderImpl) + +protected: + void flatten(SkWriteBuffer&) const override; + +private: + friend class SkGaussianEdgeShader; + + typedef SkShader INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////// + +#if SK_SUPPORT_GPU + +#include "GrCoordTransform.h" +#include "GrFragmentProcessor.h" +#include "GrInvariantOutput.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" +#include "SkGr.h" +#include "SkGrPriv.h" + +class GaussianEdgeFP : public GrFragmentProcessor { +public: + GaussianEdgeFP() { + this->initClassID<GaussianEdgeFP>(); + + // enable output of distance information for shape + fUsesDistanceVectorField = true; + } + + class GLSLGaussianEdgeFP : public GrGLSLFragmentProcessor { + public: + GLSLGaussianEdgeFP() {} + + void emitCode(EmitArgs& args) override { + + GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; + + fragBuilder->codeAppendf("vec4 output = %s;", args.fInputColor); + // outside the outer edge + fragBuilder->codeAppendf("if (%s.z <= 0) {", fragBuilder->distanceVectorName()); + fragBuilder->codeAppend("output *= 0.0;"); + // inside the stroke + fragBuilder->codeAppendf("} else if (%s.w > 0) {", fragBuilder->distanceVectorName()); + fragBuilder->codeAppendf("float factor = %s.w/(%s.z + %s.w);", + fragBuilder->distanceVectorName(), + fragBuilder->distanceVectorName(), + fragBuilder->distanceVectorName()); + fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;"); + fragBuilder->codeAppend("output *= factor;"); + fragBuilder->codeAppend("}"); + fragBuilder->codeAppendf("%s = output;", args.fOutputColor); + } + + static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, + GrProcessorKeyBuilder* b) { + // only one shader generated currently + b->add32(0x0); + } + + protected: + void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {} + }; + + void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { + GLSLGaussianEdgeFP::GenKey(*this, caps, b); + } + + const char* name() const override { return "GaussianEdgeFP"; } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + inout->mulByUnknownFourComponents(); + } + +private: + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLGaussianEdgeFP; } + + bool onIsEqual(const GrFragmentProcessor& proc) const override { return true; } +}; + +//////////////////////////////////////////////////////////////////////////// + +sk_sp<GrFragmentProcessor> SkGaussianEdgeShaderImpl::asFragmentProcessor(const AsFPArgs& args) const { + return sk_make_sp<GaussianEdgeFP>(); +} + +#endif + +//////////////////////////////////////////////////////////////////////////// + +bool SkGaussianEdgeShaderImpl::isOpaque() const { + return false; +} + +//////////////////////////////////////////////////////////////////////////// + +#ifndef SK_IGNORE_TO_STRING +void SkGaussianEdgeShaderImpl::toString(SkString* str) const { + str->appendf("GaussianEdgeShader: ()"); +} +#endif + +sk_sp<SkFlattenable> SkGaussianEdgeShaderImpl::CreateProc(SkReadBuffer& buf) { + return sk_make_sp<SkGaussianEdgeShaderImpl>(); +} + +void SkGaussianEdgeShaderImpl::flatten(SkWriteBuffer& buf) const { + this->INHERITED::flatten(buf); +} + +/////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkShader> SkGaussianEdgeShader::Make() { + return sk_make_sp<SkGaussianEdgeShaderImpl>(); +} + +/////////////////////////////////////////////////////////////////////////////// + +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGaussianEdgeShader) +SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkGaussianEdgeShaderImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp index 89e35a1d70..77ed6c9854 100644 --- a/src/gpu/GrOvalRenderer.cpp +++ b/src/gpu/GrOvalRenderer.cpp @@ -69,6 +69,12 @@ inline bool circle_stays_circle(const SkMatrix& m) { * p is the position in the normalized space. * outerRad is the outerRadius in device space. * innerRad is the innerRadius in normalized space (ignored if not stroking). + * If fUsesDistanceVectorField is set in fragment processors in the same program, then + * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName(): + * vec4f : (v.xy, outerDistance, innerDistance) + * v is a normalized vector pointing to the outer edge + * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape + * if stroking, innerDistance is the distance to the inner edge, < 0 if outside */ class CircleGeometryProcessor : public GrGeometryProcessor { @@ -129,20 +135,25 @@ public: args.fTransformsOut); fragBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn()); - fragBuilder->codeAppendf("float distanceToEdge = %s.z * (1.0 - d);", v.fsIn()); - fragBuilder->codeAppendf("float edgeAlpha = clamp(distanceToEdge, 0.0, 1.0);"); + fragBuilder->codeAppendf("float distanceToOuterEdge = %s.z * (1.0 - d);", v.fsIn()); + fragBuilder->codeAppendf("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);"); if (cgp.fStroke) { - fragBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);", + fragBuilder->codeAppendf("float distanceToInnerEdge = %s.z * (d - %s.w);", v.fsIn(), v.fsIn()); + fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);"); fragBuilder->codeAppend("edgeAlpha *= innerAlpha;"); + } else { + fragBuilder->codeAppend("float distanceToInnerEdge = 0.0;"); } if (args.fDistanceVectorName) { fragBuilder->codeAppend ("if (d == 0.0) {"); // if on the center of the circle - fragBuilder->codeAppendf(" %s = vec3(1.0, 0.0, distanceToEdge);", // no normalize + fragBuilder->codeAppendf(" %s = vec4(1.0, 0.0, distanceToOuterEdge, " + "distanceToInnerEdge);", // no normalize args.fDistanceVectorName); fragBuilder->codeAppend ("} else {"); - fragBuilder->codeAppendf(" %s = vec3(normalize(%s.xy), distanceToEdge);", + fragBuilder->codeAppendf(" %s = vec4(normalize(%s.xy), distanceToOuterEdge, " + "distanceToInnerEdge);", args.fDistanceVectorName, v.fsIn()); fragBuilder->codeAppend ("}"); } diff --git a/src/gpu/batches/GrAnalyticRectBatch.cpp b/src/gpu/batches/GrAnalyticRectBatch.cpp index b41aa7f719..b13ea77a66 100644 --- a/src/gpu/batches/GrAnalyticRectBatch.cpp +++ b/src/gpu/batches/GrAnalyticRectBatch.cpp @@ -183,7 +183,7 @@ public: fragBuilder->codeAppend( "}"); fragBuilder->codeAppend( "float dvSign = sign(dot(offset, dvAxis));"); - fragBuilder->codeAppendf("%s = vec3(dvSign * dvAxis, dvLength);", + fragBuilder->codeAppendf("%s = vec4(dvSign * dvAxis, dvLength, 0.0);", args.fDistanceVectorName); } diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.cpp b/src/gpu/glsl/GrGLSLProgramBuilder.cpp index 37c20deeb9..a6bff8a85a 100644 --- a/src/gpu/glsl/GrGLSLProgramBuilder.cpp +++ b/src/gpu/glsl/GrGLSLProgramBuilder.cpp @@ -92,7 +92,7 @@ void GrGLSLProgramBuilder::emitAndInstallPrimProc(const GrPrimitiveProcessor& pr distanceVectorName = fFS.distanceVectorName(); fFS.codeAppend( "// Normalized vector to the closest geometric edge (in device space)\n"); fFS.codeAppend( "// Distance to the edge encoded in the z-component\n"); - fFS.codeAppendf("vec3 %s;", distanceVectorName); + fFS.codeAppendf("vec4 %s;", distanceVectorName); } // Enclose custom code in a block to avoid namespace conflicts |