aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar jvanverth <jvanverth@google.com>2016-09-12 07:51:04 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-09-12 07:51:05 -0700
commitd99858ad46ca2c3b5c38061be8b006b25d24c6e0 (patch)
tree28272af51aae2efa6e8b825a71903b3bdbf8ed2f
parent2a78fca05416a13bad1b680b52d0ed4fc7f84762 (diff)
Update SampleAndroidShadows to use algorithm closer to Android OpenGL
Includes: * Update light position to be at a similar distance to Android OS * Scale spot shadows correctly * Compute stroke shapes and radii correctly * Allow for larger blur radius for shadows GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2319003003 TBR=reed@google.com NOTRY=true NOTREECHECKS=true Review-Url: https://codereview.chromium.org/2319003003
-rw-r--r--include/effects/SkGaussianEdgeShader.h2
-rwxr-xr-xsamplecode/SampleAndroidShadows.cpp188
-rw-r--r--src/effects/SkGaussianEdgeShader.cpp58
3 files changed, 132 insertions, 116 deletions
diff --git a/include/effects/SkGaussianEdgeShader.h b/include/effects/SkGaussianEdgeShader.h
index ef54ece56e..9e07450482 100644
--- a/include/effects/SkGaussianEdgeShader.h
+++ b/include/effects/SkGaussianEdgeShader.h
@@ -16,7 +16,7 @@ public:
* Currently this is only useable with Circle and RRect shapes on the GPU backend.
* Raster will draw nothing.
*/
- static sk_sp<SkShader> Make();
+ static sk_sp<SkShader> Make(bool largerBlur = false);
SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp
index f411e5c9b3..e14c571ecd 100755
--- a/samplecode/SampleAndroidShadows.cpp
+++ b/samplecode/SampleAndroidShadows.cpp
@@ -41,7 +41,7 @@ protected:
fCirclePath.addCircle(0, 0, 50);
fRectPath.addRect(SkRect::MakeXYWH(-100, -50, 200, 100));
fRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -50, 200, 100), 4, 4));
- fLightPos = SkPoint3::Make(-2, -2, 6);
+ fLightPos = SkPoint3::Make(-700, -700, 2800);
}
// overrides from SkEventSink
@@ -148,10 +148,16 @@ protected:
SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
SkScalar radius = zValue*kHeightFactor*kGeomFactor;
+ // distance to outer of edge of geometry from original shape edge
+ SkScalar offset = radius*umbraAlpha;
SkRect pathRect;
SkRRect pathRRect;
- if (radius >= 64 ||
+ SkScalar scaleFactors[2];
+ if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
+ return;
+ }
+ if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 64 ||
!((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
(path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
path.isRect(&pathRect))) {
@@ -159,33 +165,41 @@ protected:
return;
}
- // For all of these, we outset the rect by half the radius to get our stroke shape.
- SkScalar halfRadius = SK_ScalarHalf*radius;
+ // For all of these, we inset the offset rect by half the radius to get our stroke shape.
+ SkScalar strokeOutset = offset - SK_ScalarHalf*radius;
+ // Make sure we'll have a radius of at least 0.5 after xform
+ if (strokeOutset*scaleFactors[0] < 0.5f) {
+ strokeOutset = 0.5f / scaleFactors[0];
+ }
if (path.isOval(nullptr)) {
- pathRect.outset(halfRadius, halfRadius);
+ pathRect.outset(strokeOutset, strokeOutset);
pathRRect = SkRRect::MakeOval(pathRect);
} else if (path.isRect(nullptr)) {
- pathRect.outset(halfRadius, halfRadius);
- pathRRect = SkRRect::MakeRectXY(pathRect, halfRadius, halfRadius);
+ pathRect.outset(strokeOutset, strokeOutset);
+ pathRRect = SkRRect::MakeRectXY(pathRect, strokeOutset, strokeOutset);
} else {
- pathRRect.outset(halfRadius, halfRadius);
+ pathRRect.outset(strokeOutset, strokeOutset);
}
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
// we outset the stroke a little to cover up AA on the interior edge
- paint.setStrokeWidth(radius + 1);
- // handle scale of radius due to CTM
- SkScalar maxScale = canvas->getTotalMatrix().getMaxScale();
- radius *= maxScale;
- unsigned char gray = (unsigned char)(ambientAlpha*umbraAlpha*255.999f);
- SkASSERT(radius < 64);
- // Convert radius to 6.2 fixed point and place in the G component.
- paint.setColor(SkColorSetARGB(1, gray, (unsigned char)(4.0f*radius), 0));
-
- sk_sp<SkShader> gaussShader = SkGaussianEdgeShader::Make();
- paint.setShader(gaussShader);
+ SkScalar pad = 0.5f;
+ paint.setStrokeWidth(radius + 2*pad);
+ // handle scale of radius and pad due to CTM
+ radius *= scaleFactors[0];
+ pad *= scaleFactors[0];
+ SkASSERT(radius < 16384);
+ SkASSERT(pad < 64);
+ // Convert radius to 14.2 fixed point and place in the R & G components.
+ // Convert pad to 6.2 fixed point and place in the B component.
+ uint16_t iRadius = (uint16_t)(radius*4.0f);
+ unsigned char alpha = (unsigned char)(ambientAlpha*255.999f);
+ paint.setColor(SkColorSetARGB(alpha, iRadius >> 8, iRadius & 0xff,
+ (unsigned char)(4.0f*pad)));
+
+ paint.setShader(SkGaussianEdgeShader::Make(true));
canvas->drawRRect(pathRRect, paint);
}
@@ -201,42 +215,24 @@ protected:
} else if (zRatio > 0.95f) {
zRatio = 0.95f;
}
- SkScalar radius = lightWidth*zRatio;
+ SkScalar blurRadius = lightWidth*zRatio;
// compute the transformation params
SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
- canvas->getTotalMatrix().mapPoints(&center, 1);
- SkPoint offset = SkPoint::Make(-zRatio*(lightPos.fX - center.fX),
- -zRatio*(lightPos.fY - center.fY));
- SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
- if (scale < 1.0f) {
- scale = 1.0f;
- } else if (scale > 1024.f) {
- scale = 1024.f;
+ SkMatrix ctmInverse;
+ if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+ return;
}
+ SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
+ ctmInverse.mapPoints(&lightPos2D, 1);
+ SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+ zRatio*(center.fY - lightPos2D.fY));
+ SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
SkAutoCanvasRestore acr(canvas, true);
- SkRect occlRect;
- GetOcclRect(path, &occlRect);
- // apply inverse transform
- occlRect.offset(-offset);
-#if 0
- // It looks like the scale may be invalid
- SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
- if (scale < 1.0f) {
- scale = 1.0f;
- } else if (scale > 1024.f) {
- scale = 1024.f;
- }
- occlRect.fLeft /= scale;
- occlRect.fRight /= scale;
- occlRect.fTop /= scale;
- occlRect.fBottom /= scale;
-#endif
sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
- SkBlurMask::ConvertRadiusToSigma(radius),
- occlRect,
+ SkBlurMask::ConvertRadiusToSigma(blurRadius),
SkBlurMaskFilter::kNone_BlurFlag);
SkPaint paint;
@@ -245,20 +241,9 @@ protected:
paint.setColor(SkColorSetARGB((unsigned char)(spotAlpha*255.999f), 0, 0, 0));
// apply transformation to shadow
- canvas->translate(offset.fX, offset.fY);
-#if 0
- // It looks like the scale may be invalid
canvas->scale(scale, scale);
-#endif
+ canvas->translate(offset.fX, offset.fY);
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)
-#endif
}
void drawSpotShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
@@ -273,11 +258,15 @@ protected:
} else if (zRatio > 0.95f) {
zRatio = 0.95f;
}
- SkScalar radius = lightWidth*zRatio;
+ SkScalar radius = 2.0f*lightWidth*zRatio;
SkRect pathRect;
SkRRect pathRRect;
- if (radius >= 64 ||
+ SkScalar scaleFactors[2];
+ if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
+ return;
+ }
+ if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 16384 ||
!((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
(path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
path.isRect(&pathRect))) {
@@ -285,23 +274,31 @@ protected:
return;
}
- // For all of these, we outset the rect by half the radius to get our stroke shape.
- SkScalar halfRadius = SK_ScalarHalf*radius;
+ // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
+ SkScalar minRadius = SK_ScalarHalf/scaleFactors[0];
if (path.isOval(nullptr)) {
- pathRect.outset(halfRadius, halfRadius);
pathRRect = SkRRect::MakeOval(pathRect);
} else if (path.isRect(nullptr)) {
- pathRect.outset(halfRadius, halfRadius);
- pathRRect = SkRRect::MakeRectXY(pathRect, halfRadius, halfRadius);
+ pathRRect = SkRRect::MakeRectXY(pathRect, minRadius, minRadius);
} else {
- pathRRect.outset(halfRadius, halfRadius);
+ if (pathRRect.getSimpleRadii().fX < minRadius) {
+ pathRRect.setRectXY(pathRRect.rect(), minRadius, minRadius);
+ }
}
- // compute the transformation params
- SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
- canvas->getTotalMatrix().mapPoints(&center, 1);
- SkPoint offset = SkPoint::Make(-zRatio*(lightPos.fX - center.fX),
- -zRatio*(lightPos.fY - center.fY));
+ // compute the scale and translation for the shadow
+ SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
+ SkRRect shadowRRect;
+ pathRRect.transform(SkMatrix::MakeScale(scale, scale), &shadowRRect);
+ SkPoint center = SkPoint::Make(shadowRRect.rect().centerX(), shadowRRect.rect().centerY());
+ SkMatrix ctmInverse;
+ if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+ return;
+ }
+ SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
+ ctmInverse.mapPoints(&lightPos2D, 1);
+ SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+ zRatio*(center.fY - lightPos2D.fY));
SkAutoCanvasRestore acr(canvas, true);
SkPaint paint;
@@ -310,9 +307,9 @@ protected:
// the edge of the shape. We also add 1/2 to cover up AA on the interior edge.
SkScalar pad = offset.length() + 0.5f;
// compute area
- SkScalar strokeWidth = radius + 2.0f*pad;
- SkScalar strokedArea = 2.0f*strokeWidth*(pathRRect.width() + pathRRect.height());
- SkScalar filledArea = (pathRRect.height() + radius)*(pathRRect.width() + radius);
+ SkScalar strokeWidth = radius + 2.0f*pad/scaleFactors[0];
+ SkScalar strokedArea = 2.0f*strokeWidth*(shadowRRect.width() + shadowRRect.height());
+ SkScalar filledArea = (shadowRRect.height() + radius)*(shadowRRect.width() + radius);
// If the area of the stroked geometry is larger than the fill geometry, or
// if our pad is too big to convert to 6.2 fixed point, just fill it.
if (strokedArea > filledArea || pad >= 64) {
@@ -323,31 +320,22 @@ protected:
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(strokeWidth);
}
- sk_sp<SkShader> gaussShader = SkGaussianEdgeShader::Make();
- paint.setShader(gaussShader);
+ paint.setShader(SkGaussianEdgeShader::Make(true));
// handle scale of radius due to CTM
- SkScalar maxScale = canvas->getTotalMatrix().getMaxScale();
- radius *= maxScale;
- unsigned char gray = (unsigned char)(spotAlpha*255.999f);
- SkASSERT(radius < 64);
+ radius *= scaleFactors[0];
+ // don't need to scale pad as it was computed from the transformed offset
+ SkASSERT(radius < 16384);
SkASSERT(pad < 64);
- // Convert radius and pad to 6.2 fixed point and place in the G & B components.
- paint.setColor(SkColorSetARGB(1, gray, (unsigned char)(radius*4.0f),
- (unsigned char)(pad*4.0f)));
+ // Convert radius to 14.2 fixed point and place in the R & G components.
+ // Convert pad to 6.2 fixed point and place in the B component.
+ uint16_t iRadius = (uint16_t)(radius*4.0f);
+ unsigned char alpha = (unsigned char)(spotAlpha*255.999f);
+ paint.setColor(SkColorSetARGB(alpha, iRadius >> 8, iRadius & 0xff,
+ (unsigned char)(4.0f*pad)));
// apply transformation to shadow
canvas->translate(offset.fX, offset.fY);
-#if 0
- // It looks like the scale may be invalid
- SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
- if (scale < 1.0f) {
- scale = 1.0f;
- } else if (scale > 1024.f) {
- scale = 1024.f;
- }
- canvas->scale(scale, scale);
-#endif
- canvas->drawRRect(pathRRect, paint);
+ canvas->drawRRect(shadowRRect, paint);
}
void drawShadowedPath(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
@@ -374,7 +362,7 @@ protected:
void onDrawContent(SkCanvas* canvas) override {
this->drawBG(canvas);
- const SkScalar kLightWidth = 3;
+ const SkScalar kLightWidth = 2800;
const SkScalar kAmbientAlpha = 0.25f;
const SkScalar kSpotAlpha = 0.25f;
@@ -387,26 +375,26 @@ protected:
canvas->translate(200, 90);
lightPos.fX += 200;
lightPos.fY += 90;
- this->drawShadowedPath(canvas, fRectPath, 5, paint, kAmbientAlpha,
+ this->drawShadowedPath(canvas, fRectPath, 2, paint, kAmbientAlpha,
lightPos, kLightWidth, kSpotAlpha);
paint.setColor(SK_ColorRED);
canvas->translate(250, 0);
lightPos.fX += 250;
- this->drawShadowedPath(canvas, fRRPath, 5, paint, kAmbientAlpha,
+ this->drawShadowedPath(canvas, fRRPath, 4, paint, kAmbientAlpha,
lightPos, kLightWidth, kSpotAlpha);
paint.setColor(SK_ColorBLUE);
canvas->translate(-250, 110);
lightPos.fX -= 250;
lightPos.fY += 110;
- this->drawShadowedPath(canvas, fCirclePath, 5, paint, 0.0f,
+ this->drawShadowedPath(canvas, fCirclePath, 8, paint, 0.0f,
lightPos, kLightWidth, 0.5f);
paint.setColor(SK_ColorGREEN);
canvas->translate(250, 0);
lightPos.fX += 250;
- this->drawShadowedPath(canvas, fRRPath, 5, paint, kAmbientAlpha,
+ this->drawShadowedPath(canvas, fRRPath, 64, paint, kAmbientAlpha,
lightPos, kLightWidth, kSpotAlpha);
}
diff --git a/src/effects/SkGaussianEdgeShader.cpp b/src/effects/SkGaussianEdgeShader.cpp
index 01b0606c01..a7c27004be 100644
--- a/src/effects/SkGaussianEdgeShader.cpp
+++ b/src/effects/SkGaussianEdgeShader.cpp
@@ -12,14 +12,26 @@
/** \class SkGaussianEdgeShaderImpl
This subclass of shader applies a Gaussian to shadow edge
+ If largerBlur is false:
The radius of the Gaussian blur is specified by the g value of the color, in 6.2 fixed point.
For spot shadows, we increase the stroke width to set the shadow against the shape. This pad
is specified by b, also in 6.2 fixed point. The r value represents the max final alpha.
The incoming alpha should be 1.
+
+ If largerBlur is true:
+ The radius of the Gaussian blur is specified by the r & g values of the color in 14.2 fixed point.
+ For spot shadows, we increase the stroke width to set the shadow against the shape. This pad
+ is specified by b, also in 6.2 fixed point. The a value represents the max final alpha.
+
+ LargerBlur will be removed once Android is migrated to the updated shader.
*/
class SkGaussianEdgeShaderImpl : public SkShader {
public:
- SkGaussianEdgeShaderImpl() {}
+ SkGaussianEdgeShaderImpl()
+ : fLargerBlur(false) {}
+
+ SkGaussianEdgeShaderImpl(bool largerBlur)
+ : fLargerBlur(largerBlur) {}
bool isOpaque() const override;
@@ -35,6 +47,7 @@ protected:
private:
friend class SkGaussianEdgeShader;
+ bool fLargerBlur;
typedef SkShader INHERITED;
};
@@ -55,7 +68,7 @@ private:
class GaussianEdgeFP : public GrFragmentProcessor {
public:
- GaussianEdgeFP() {
+ GaussianEdgeFP(bool largerBlur) : fLargerBlur(largerBlur) {
this->initClassID<GaussianEdgeFP>();
// enable output of distance information for shape
@@ -64,7 +77,7 @@ public:
class GLSLGaussianEdgeFP : public GrGLSLFragmentProcessor {
public:
- GLSLGaussianEdgeFP() {}
+ GLSLGaussianEdgeFP(bool largerBlur) : fLargerBlur(largerBlur) {}
void emitCode(EmitArgs& args) override {
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
@@ -74,28 +87,39 @@ public:
" returning grey in GLSLGaussianEdgeFP\n");
fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
fragBuilder->codeAppendf("%s = vec4(0, 0, 0, color.r);", args.fOutputColor);
+ } else if (fLargerBlur) {
+ fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
+ fragBuilder->codeAppend("float radius = color.r*256*64 + color.g*64;");
+ fragBuilder->codeAppend("float pad = color.b*64;");
+
+ fragBuilder->codeAppendf("float factor = 1 - clamp((%s.z - pad)/radius, 0, 1);",
+ fragBuilder->distanceVectorName());
+ fragBuilder->codeAppend("factor = exp(-factor * factor * 4) - 0.018;");
+ fragBuilder->codeAppendf("%s = factor*vec4(0, 0, 0, color.a);",
+ args.fOutputColor);
} else {
fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
- fragBuilder->codeAppend("float radius = color.g*64.0;");
- fragBuilder->codeAppend("float pad = color.b*64.0;");
+ fragBuilder->codeAppend("float radius = color.g*64;");
+ fragBuilder->codeAppend("float pad = color.b*64;");
- fragBuilder->codeAppendf("float factor = 1.0 - clamp((%s.z - pad)/radius,"
- "0.0, 1.0);",
+ fragBuilder->codeAppendf("float factor = 1 - clamp((%s.z - pad)/radius, 0, 1);",
fragBuilder->distanceVectorName());
- fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;");
- fragBuilder->codeAppendf("%s = factor*vec4(0.0, 0.0, 0.0, color.r);",
+ fragBuilder->codeAppend("factor = exp(-factor * factor * 4) - 0.018;");
+ fragBuilder->codeAppendf("%s = factor*vec4(0, 0, 0, color.r);",
args.fOutputColor);
}
}
static void GenKey(const GrProcessor& proc, const GrGLSLCaps&,
GrProcessorKeyBuilder* b) {
- // only one shader generated currently
- b->add32(0x0);
+ const GaussianEdgeFP& gefp = proc.cast<GaussianEdgeFP>();
+ b->add32(gefp.fLargerBlur ? 0x1 : 0x0);
}
protected:
void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {}
+
+ bool fLargerBlur;
};
void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
@@ -109,15 +133,19 @@ public:
}
private:
- GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLGaussianEdgeFP; }
+ GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
+ return new GLSLGaussianEdgeFP(fLargerBlur);
+ }
bool onIsEqual(const GrFragmentProcessor& proc) const override { return true; }
+
+ bool fLargerBlur;
};
////////////////////////////////////////////////////////////////////////////
sk_sp<GrFragmentProcessor> SkGaussianEdgeShaderImpl::asFragmentProcessor(const AsFPArgs& args) const {
- return sk_make_sp<GaussianEdgeFP>();
+ return sk_make_sp<GaussianEdgeFP>(fLargerBlur);
}
#endif
@@ -145,8 +173,8 @@ void SkGaussianEdgeShaderImpl::flatten(SkWriteBuffer& buf) const {
///////////////////////////////////////////////////////////////////////////////
-sk_sp<SkShader> SkGaussianEdgeShader::Make() {
- return sk_make_sp<SkGaussianEdgeShaderImpl>();
+sk_sp<SkShader> SkGaussianEdgeShader::Make(bool largerBlur) {
+ return sk_make_sp<SkGaussianEdgeShaderImpl>(largerBlur);
}
///////////////////////////////////////////////////////////////////////////////