Add ShadowFlag to use only blurs for concave shapes.

The geometric method for concave shadows seems to be failing in certain
cases. See: https://github.com/flutter/flutter/issues/84262. I've never
been satisfied with this solution and I've been thinking of removing it. This flag allows for Flutter to disable it for the time being until I
can determine if anyone else is using it.

Change-Id: Ia0a3f57002d94928f2baa655c88dc4d10b9edef8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/533881
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
Jim Van Verth 2022-04-27 11:06:19 -04:00 committed by SkCQ
parent c35e492a19
commit e8199af1d9
3 changed files with 84 additions and 66 deletions

View File

@ -18,8 +18,10 @@ enum SkShadowFlags {
kGeometricOnly_ShadowFlag = 0x02,
/** Light position represents a direction, light radius is blur radius at elevation 1 */
kDirectionalLight_ShadowFlag = 0x04,
/** Concave paths will only use blur to generate the shadow */
kConcaveBlurOnly_ShadowFlag = 0x08,
/** mask for all shadow flags */
kAll_ShadowFlag = 0x07
kAll_ShadowFlag = 0x0F
};
#endif

View File

@ -42,6 +42,7 @@ class ShadowsView : public Sample {
bool fShowAmbient = true;
bool fShowSpot = true;
bool fUseAlt = false;
bool fUseBlur = true;
bool fShowObject = true;
bool fIgnoreShadowAlpha = false;
bool fDoAlphaAnimation = false;
@ -108,6 +109,10 @@ class ShadowsView : public Sample {
fUseAlt = !fUseAlt;
handled = true;
break;
case 'B':
fUseBlur = !fUseBlur;
handled = true;
break;
case 'O':
fShowObject = !fShowObject;
handled = true;
@ -158,6 +163,9 @@ class ShadowsView : public Sample {
if (fUseAlt) {
flags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
}
if (fUseBlur) {
flags |= SkShadowFlags::kConcaveBlurOnly_ShadowFlag;
}
SkColor ambientColor = SkColorSetARGB(ambientAlpha * 255, 0, 0, 0);
SkColor spotColor = SkColorSetARGB(spotAlpha * 255, 0, 0, 0);

View File

@ -668,6 +668,8 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
bool tiltZPlane = tilted(rec.fZPlaneParams);
bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
bool directional = SkToBool(rec.fFlags & SkShadowFlags::kDirectionalLight_ShadowFlag);
bool useBlur = SkToBool(rec.fFlags & SkShadowFlags::kConcaveBlurOnly_ShadowFlag) &&
!path.isConvex();
bool uncached = tiltZPlane || path.isVolatile();
SkPoint3 zPlaneParams = rec.fZPlaneParams;
@ -679,7 +681,7 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
if (SkColorGetA(rec.fAmbientColor) > 0) {
bool success = false;
if (uncached) {
if (uncached && !useBlur) {
sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
zPlaneParams,
transparent);
@ -702,7 +704,7 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
}
}
if (!success) {
if (!success && !useBlur) {
AmbientVerticesFactory factory;
factory.fOccluderHeight = zPlaneParams.fZ;
factory.fTransparent = transparent;
@ -713,59 +715,62 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
factory.fOffset.fY = viewMatrix.getTranslateY();
}
if (!draw_shadow(factory, drawVertsProc, shadowedPath, rec.fAmbientColor)) {
// Pretransform the path to avoid transforming the stroke, below.
SkPath devSpacePath;
path.transform(viewMatrix, &devSpacePath);
devSpacePath.setIsVolatile(true);
success = draw_shadow(factory, drawVertsProc, shadowedPath, rec.fAmbientColor);
}
// The tesselator outsets by AmbientBlurRadius (or 'r') to get the outer ring of
// the tesselation, and sets the alpha on the path to 1/AmbientRecipAlpha (or 'a').
//
// We want to emulate this with a blur. The full blur width (2*blurRadius or 'f')
// can be calculated by interpolating:
//
// original edge outer edge
// | |<---------- r ------>|
// |<------|--- f -------------->|
// | | |
// alpha = 1 alpha = a alpha = 0
//
// Taking ratios, f/1 = r/a, so f = r/a and blurRadius = f/2.
//
// We now need to outset the path to place the new edge in the center of the
// blur region:
//
// original new
// | |<------|--- r ------>|
// |<------|--- f -|------------>|
// | |<- o ->|<--- f/2 --->|
//
// r = o + f/2, so o = r - f/2
//
// We outset by using the stroker, so the strokeWidth is o/2.
//
SkScalar devSpaceOutset = SkDrawShadowMetrics::AmbientBlurRadius(zPlaneParams.fZ);
SkScalar oneOverA = SkDrawShadowMetrics::AmbientRecipAlpha(zPlaneParams.fZ);
SkScalar blurRadius = 0.5f*devSpaceOutset*oneOverA;
SkScalar strokeWidth = 0.5f*(devSpaceOutset - blurRadius);
// All else has failed, draw with blur
if (!success) {
// Pretransform the path to avoid transforming the stroke, below.
SkPath devSpacePath;
path.transform(viewMatrix, &devSpacePath);
devSpacePath.setIsVolatile(true);
// Now draw with blur
SkPaint paint;
paint.setColor(rec.fAmbientColor);
paint.setStrokeWidth(strokeWidth);
paint.setStyle(SkPaint::kStrokeAndFill_Style);
SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(blurRadius);
bool respectCTM = false;
paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
this->drawPath(devSpacePath, paint);
}
// The tesselator outsets by AmbientBlurRadius (or 'r') to get the outer ring of
// the tesselation, and sets the alpha on the path to 1/AmbientRecipAlpha (or 'a').
//
// We want to emulate this with a blur. The full blur width (2*blurRadius or 'f')
// can be calculated by interpolating:
//
// original edge outer edge
// | |<---------- r ------>|
// |<------|--- f -------------->|
// | | |
// alpha = 1 alpha = a alpha = 0
//
// Taking ratios, f/1 = r/a, so f = r/a and blurRadius = f/2.
//
// We now need to outset the path to place the new edge in the center of the
// blur region:
//
// original new
// | |<------|--- r ------>|
// |<------|--- f -|------------>|
// | |<- o ->|<--- f/2 --->|
//
// r = o + f/2, so o = r - f/2
//
// We outset by using the stroker, so the strokeWidth is o/2.
//
SkScalar devSpaceOutset = SkDrawShadowMetrics::AmbientBlurRadius(zPlaneParams.fZ);
SkScalar oneOverA = SkDrawShadowMetrics::AmbientRecipAlpha(zPlaneParams.fZ);
SkScalar blurRadius = 0.5f*devSpaceOutset*oneOverA;
SkScalar strokeWidth = 0.5f*(devSpaceOutset - blurRadius);
// Now draw with blur
SkPaint paint;
paint.setColor(rec.fAmbientColor);
paint.setStrokeWidth(strokeWidth);
paint.setStyle(SkPaint::kStrokeAndFill_Style);
SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(blurRadius);
bool respectCTM = false;
paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
this->drawPath(devSpacePath, paint);
}
}
if (SkColorGetA(rec.fSpotColor) > 0) {
bool success = false;
if (uncached) {
if (uncached && !useBlur) {
sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix,
zPlaneParams,
devLightPos, lightRadius,
@ -790,7 +795,7 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
}
}
if (!success) {
if (!success && !useBlur) {
SpotVerticesFactory factory;
factory.fOccluderHeight = zPlaneParams.fZ;
factory.fDevLightPos = devLightPos;
@ -856,24 +861,27 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
break;
}
#endif
if (!draw_shadow(factory, drawVertsProc, shadowedPath, color)) {
// draw with blur
SkMatrix shadowMatrix;
if (!SkDrawShadowMetrics::GetSpotShadowTransform(devLightPos, lightRadius,
viewMatrix, zPlaneParams,
path.getBounds(), directional,
&shadowMatrix, &radius)) {
return;
}
SkAutoDeviceTransformRestore adr2(this, shadowMatrix);
success = draw_shadow(factory, drawVertsProc, shadowedPath, color);
}
SkPaint paint;
paint.setColor(rec.fSpotColor);
SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
bool respectCTM = false;
paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
this->drawPath(path, paint);
// All else has failed, draw with blur
if (!success) {
SkMatrix shadowMatrix;
SkScalar radius;
if (!SkDrawShadowMetrics::GetSpotShadowTransform(devLightPos, lightRadius,
viewMatrix, zPlaneParams,
path.getBounds(), directional,
&shadowMatrix, &radius)) {
return;
}
SkAutoDeviceTransformRestore adr2(this, shadowMatrix);
SkPaint paint;
paint.setColor(rec.fSpotColor);
SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
bool respectCTM = false;
paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM));
this->drawPath(path, paint);
}
}
}