Fix spot shadow inset.
authorJim Van Verth <jvanverth@google.com>
Thu, 4 May 2017 13:51:29 +0000 (09:51 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Thu, 4 May 2017 14:21:08 +0000 (14:21 +0000)
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 <jvanverth@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
src/effects/shadows/SkAmbientShadowMaskFilter.cpp
src/effects/shadows/SkSpotShadowMaskFilter.cpp

index 4d5bc7c..584dc74 100644 (file)
@@ -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);
index 8471508..fc73afc 100644 (file)
@@ -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));
     }