From 65f33fcbbadde807667a0c7ce451ad95c31c52ea Mon Sep 17 00:00:00 2001 From: Jim Van Verth Date: Thu, 4 May 2017 09:51:29 -0400 Subject: Fix spot shadow inset. This handles negative values in the spot translation, and produces a tight fit for rrects and circles. Change-Id: Id2536347dca98f06f57b31518390037b86fd8a9e Reviewed-on: https://skia-review.googlesource.com/15248 Commit-Queue: Jim Van Verth Reviewed-by: Robert Phillips --- src/effects/shadows/SkAmbientShadowMaskFilter.cpp | 11 +++-- src/effects/shadows/SkSpotShadowMaskFilter.cpp | 50 ++++++++++++++--------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/effects/shadows/SkAmbientShadowMaskFilter.cpp b/src/effects/shadows/SkAmbientShadowMaskFilter.cpp index 4d5bc7cbd0..584dc747ae 100644 --- a/src/effects/shadows/SkAmbientShadowMaskFilter.cpp +++ b/src/effects/shadows/SkAmbientShadowMaskFilter.cpp @@ -219,11 +219,14 @@ bool SkAmbientShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*, SkScalar ambientPathOutset = 0.5f*srcSpaceStrokeWidth; SkRRect ambientRRect; - if (rrect.isRect()) { - const SkRect temp = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset); - ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset); + SkRect insetRect = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset); + // If the rrect was an oval then its outset will also be one. + // We set it explicitly to avoid errors. + if (rrect.isOval()) { + ambientRRect = SkRRect::MakeOval(insetRect); } else { - rrect.outset(ambientPathOutset, ambientPathOutset, &ambientRRect); + SkScalar insetRad = rrect.getSimpleRadii().fX + ambientPathOutset; + ambientRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad); } GrPaint newPaint(paint); diff --git a/src/effects/shadows/SkSpotShadowMaskFilter.cpp b/src/effects/shadows/SkSpotShadowMaskFilter.cpp index 847150865f..fc73afc9c1 100644 --- a/src/effects/shadows/SkSpotShadowMaskFilter.cpp +++ b/src/effects/shadows/SkSpotShadowMaskFilter.cpp @@ -251,29 +251,38 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*, // 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 - // stroke width but we also have to account for the scaling. - SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(rrect.rect().fLeft), - SkTAbs(rrect.rect().fRight)), - SkTMax(SkTAbs(rrect.rect().fTop), - SkTAbs(rrect.rect().fBottom))); - SkScalar insetAmount = spotOffset.length() - (0.5f * srcSpaceSpotRadius) + scaleOffset; - - // Compute area + // stroke width but we also have to account for the scaling and translation when + // computing a new stroke shape. + // + // We begin by transforming the min and max corners -- inset by the radius -- by the scale + // and translation. The distance from that to the original inset point plus the difference + // between the scaled radius and the original radius gives the distance from the + // transformed shadow shape to the original shape. If the max of the two distances is + // greater than the strokeWidth then we need to inset and increase the strokeWidth to cover + // the hole. If less, then we can outset and decrease the strokeWidth to reduce our + // coverage. + + // This factor handles both the transform scale and subtracting the original value. + SkScalar comboScale = scale - 1; + SkScalar r = rrect.getSimpleRadii().fX; + SkPoint upperLeftOffset = SkPoint::Make(comboScale*(rrect.rect().fLeft + r), + comboScale*(rrect.rect().fTop + r)); + upperLeftOffset += spotOffset; + SkPoint lowerRightOffset = SkPoint::Make(comboScale*(rrect.rect().fRight - r), + comboScale*(rrect.rect().fBottom - r)); + lowerRightOffset += spotOffset; + + SkScalar maxOffset = SkTMax(upperLeftOffset.length(), lowerRightOffset.length()); + maxOffset += comboScale*r; + SkScalar insetAmount = maxOffset - (0.5f * srcSpaceSpotRadius); SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount; - SkScalar strokedArea = 2.0f*strokeWidth * - (spotShadowRRect.width() + spotShadowRRect.height()); - SkScalar filledArea = (spotShadowRRect.height() + srcSpaceSpotRadius) * - (spotShadowRRect.width() + srcSpaceSpotRadius); - GrColor4f color = paint.getColor4f(); - color.fRGBA[3] *= fSpotAlpha; - paint.setColor4f(color); + SkScalar strokedDiff = SkTMin(spotShadowRRect.width(), spotShadowRRect.height()) + - (insetAmount + strokeWidth); SkStrokeRec spotStrokeRec(SkStrokeRec::kFill_InitStyle); - // If the area of the stroked geometry is larger than the fill geometry, - // or if the caster is transparent, just fill it. - if (strokedArea > filledArea || - fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag) { + // If the caster has too large a stroke or is transparent, just fill it. + if (strokedDiff < 0 || fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag) { spotStrokeRec.setStrokeStyle(srcSpaceSpotRadius, true); } else { // Since we can't have unequal strokes, inset the shadow rect so the inner @@ -293,6 +302,9 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*, spotShadowRRect.offset(spotOffset.fX, spotOffset.fY); + GrColor4f color = paint.getColor4f(); + color.fRGBA[3] *= fSpotAlpha; + paint.setColor4f(color); rtContext->drawShadowRRect(clip, std::move(paint), viewMatrix, spotShadowRRect, devSpaceSpotRadius, GrStyle(spotStrokeRec, nullptr)); } -- cgit v1.2.3