From 8f7dc9f6caabe798723d9f17aff371121369b846 Mon Sep 17 00:00:00 2001 From: Jim Van Verth Date: Thu, 20 Apr 2017 15:48:37 -0400 Subject: Circular shadow fixes for Flutter. * Fix spot shadow placement for SkSpotShadowMaskFilter. * Make sure we don't try to render an oval as a plain RRect due to floating point error. * Use fast path for uncached circles. * Make sure ShadowMaskFilters can handle near-circles. Change-Id: Ia9967a00a6e1c980a1c0a7ba8248f09fde61a3b7 Reviewed-on: https://skia-review.googlesource.com/13969 Reviewed-by: Jim Van Verth Reviewed-by: Robert Phillips Commit-Queue: Jim Van Verth --- src/effects/shadows/SkAmbientShadowMaskFilter.cpp | 5 ++- src/effects/shadows/SkSpotShadowMaskFilter.cpp | 42 +++++++++++++---------- 2 files changed, 25 insertions(+), 22 deletions(-) (limited to 'src/effects') diff --git a/src/effects/shadows/SkAmbientShadowMaskFilter.cpp b/src/effects/shadows/SkAmbientShadowMaskFilter.cpp index e6a8de4141..84f9836b52 100644 --- a/src/effects/shadows/SkAmbientShadowMaskFilter.cpp +++ b/src/effects/shadows/SkAmbientShadowMaskFilter.cpp @@ -163,9 +163,8 @@ bool SkAmbientShadowMaskFilterImpl::directFilterMaskGPU(GrContext* context, } // if circle - // TODO: switch to SkScalarNearlyEqual when either oval renderer is updated or we - // have our own GeometryProc. - if (path.isOval(nullptr) && path.getBounds().width() == path.getBounds().height()) { + if (path.isOval(nullptr) && SkScalarNearlyEqual(path.getBounds().width(), + path.getBounds().height())) { SkRRect rrect = SkRRect::MakeOval(path.getBounds()); return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip, SkMatrix::I(), strokeRec, rrect, rrect); diff --git a/src/effects/shadows/SkSpotShadowMaskFilter.cpp b/src/effects/shadows/SkSpotShadowMaskFilter.cpp index ee82342ecc..93c7e9c0fa 100644 --- a/src/effects/shadows/SkSpotShadowMaskFilter.cpp +++ b/src/effects/shadows/SkSpotShadowMaskFilter.cpp @@ -180,9 +180,8 @@ bool SkSpotShadowMaskFilterImpl::directFilterMaskGPU(GrContext* context, } // if circle - // TODO: switch to SkScalarNearlyEqual when either oval renderer is updated or we - // have our own GeometryProc. - if (path.isOval(nullptr) && path.getBounds().width() == path.getBounds().height()) { + if (path.isOval(nullptr) && SkScalarNearlyEqual(path.getBounds().width(), + path.getBounds().height())) { SkRRect rrect = SkRRect::MakeOval(path.getBounds()); return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip, SkMatrix::I(), strokeRec, rrect, rrect); @@ -211,7 +210,7 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*, if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) { return false; } - // Fast path only supports simple rrects with circular corners. + // Fast path only supports simple rrects with near-circular corners. SkASSERT(devRRect.allCornersCircular()); if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) { return false; @@ -236,7 +235,9 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*, if (fSpotAlpha > 0.0f) { float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f); - SkScalar srcSpaceSpotRadius = 2.0f * fLightRadius * zRatio; + SkScalar devSpaceSpotRadius = 2.0f * fLightRadius * zRatio; + // handle scale of radius and pad due to CTM + const SkScalar srcSpaceSpotRadius = devSpaceSpotRadius / scaleFactor; SkRRect spotRRect; if (isRect) { @@ -250,18 +251,18 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*, const SkScalar scale = fLightPos.fZ / (fLightPos.fZ - fOccluderHeight); spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect); - SkPoint center = SkPoint::Make(spotShadowRRect.rect().centerX(), - spotShadowRRect.rect().centerY()); + SkPoint spotOffset = SkPoint::Make(zRatio*(-fLightPos.fX), zRatio*(-fLightPos.fY)); + // Adjust for the effect of the scale. + spotOffset.fX += scale*viewMatrix[SkMatrix::kMTransX]; + spotOffset.fY += scale*viewMatrix[SkMatrix::kMTransY]; + // This offset is in dev space, need to transform it into source space. SkMatrix ctmInverse; if (!viewMatrix.invert(&ctmInverse)) { SkDebugf("Matrix is degenerate. Will not render spot shadow!\n"); //**** TODO: this is not good return true; } - SkPoint lightPos2D = SkPoint::Make(fLightPos.fX, fLightPos.fY); - ctmInverse.mapPoints(&lightPos2D, 1); - const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX), - zRatio*(center.fY - lightPos2D.fY)); + ctmInverse.mapPoints(&spotOffset, 1); // We want to extend the stroked area in so that it meets up with the caster // geometry. The stroked geometry will, by definition already be inset half the @@ -292,17 +293,20 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*, } else { // Since we can't have unequal strokes, inset the shadow rect so the inner // and outer edges of the stroke will land where we want. - SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount / 2.0f, - insetAmount / 2.0f); - SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount / 2.0f, - minRadius); - spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad); + insetAmount *= 0.5f; + SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount, insetAmount); + // If the shadowRRect was an oval then its inset will also be one. + // We set it explicitly to avoid errors. + if (spotShadowRRect.isOval()) { + spotShadowRRect = SkRRect::MakeOval(insetRect); + } else { + SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount, + minRadius); + spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad); + } spotStrokeRec.setStrokeStyle(strokeWidth, false); } - // handle scale of radius and pad due to CTM - const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor; - spotShadowRRect.offset(spotOffset.fX, spotOffset.fY); rtContext->drawShadowRRect(clip, std::move(paint), viewMatrix, spotShadowRRect, -- cgit v1.2.3