From d99858ad46ca2c3b5c38061be8b006b25d24c6e0 Mon Sep 17 00:00:00 2001 From: jvanverth Date: Mon, 12 Sep 2016 07:51:04 -0700 Subject: 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 --- samplecode/SampleAndroidShadows.cpp | 188 +++++++++++++++++------------------- 1 file changed, 88 insertions(+), 100 deletions(-) (limited to 'samplecode/SampleAndroidShadows.cpp') 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 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 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 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); } -- cgit v1.2.3