Index: samplecode/SampleAndroidShadows.cpp |
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp |
index f411e5c9b3999301ce7ff9298f30e174c7648828..e14c571ecd18febdbfc6abb470767f2021631e7b 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(¢er, 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(¢er, 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); |
} |