aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar jvanverth <jvanverth@google.com>2016-08-17 07:59:41 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-08-17 07:59:41 -0700
commit6c177a1a49fcfe8bfd5f3ffda3ee50bbe2679463 (patch)
tree754f6f73648a838ed21c50441a0ba22996d11f5e
parent86b6eabeae80c3db69226cd1693d7f1c4591d88e (diff)
Add alternative ambient shadow method to Android shadow sample
TBR=bsalomon@google.com BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2249973003 Review-Url: https://codereview.chromium.org/2249973003
-rw-r--r--gyp/effects.gypi2
-rw-r--r--include/effects/SkGaussianEdgeShader.h24
-rwxr-xr-xsamplecode/SampleAndroidShadows.cpp91
-rw-r--r--src/effects/SkGaussianEdgeShader.cpp152
-rw-r--r--src/gpu/GrOvalRenderer.cpp21
-rw-r--r--src/gpu/batches/GrAnalyticRectBatch.cpp2
-rw-r--r--src/gpu/glsl/GrGLSLProgramBuilder.cpp2
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