diff options
-rwxr-xr-x | samplecode/SampleShadowUtils.cpp | 2 | ||||
-rw-r--r-- | src/effects/shadows/SkAmbientShadowMaskFilter.cpp | 107 | ||||
-rw-r--r-- | src/effects/shadows/SkSpotShadowMaskFilter.cpp | 209 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetContext.cpp | 9 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetContext.h | 7 | ||||
-rw-r--r-- | src/gpu/ops/GrShadowRRectOp.cpp | 100 | ||||
-rw-r--r-- | src/gpu/ops/GrShadowRRectOp.h | 2 | ||||
-rw-r--r-- | src/utils/SkShadowUtils.cpp | 3 |
8 files changed, 194 insertions, 245 deletions
diff --git a/samplecode/SampleShadowUtils.cpp b/samplecode/SampleShadowUtils.cpp index 858d212b85..f5b6635886 100755 --- a/samplecode/SampleShadowUtils.cpp +++ b/samplecode/SampleShadowUtils.cpp @@ -105,7 +105,7 @@ protected: } void drawBG(SkCanvas* canvas) { - canvas->drawColor(0xFFDDDDDD); + canvas->drawColor(0xFFFFFFFF); } void drawShadowedPath(SkCanvas* canvas, const SkPath& path, diff --git a/src/effects/shadows/SkAmbientShadowMaskFilter.cpp b/src/effects/shadows/SkAmbientShadowMaskFilter.cpp index 584dc747ae..7578ae3899 100644 --- a/src/effects/shadows/SkAmbientShadowMaskFilter.cpp +++ b/src/effects/shadows/SkAmbientShadowMaskFilter.cpp @@ -149,32 +149,22 @@ bool SkAmbientShadowMaskFilterImpl::directFilterMaskGPU(GrContext* context, SkASSERT(rtContext); // TODO: this will not handle local coordinates properly - if (fAmbientAlpha <= 0.0f) { - return true; - } - - // only convex paths for now - if (!path.isConvex()) { - return false; - } - - if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) { - return false; - } - - // if circle - if (path.isOval(nullptr) && SkScalarNearlyEqual(path.getBounds().width(), - path.getBounds().height())) { - SkRRect rrect = SkRRect::MakeOval(path.getBounds()); + SkRect rect; + SkRRect rrect; + if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), + rect.height())) { + rrect = SkRRect::MakeOval(rect); return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip, SkMatrix::I(), strokeRec, rrect, rrect); - } else if (path.isRect(nullptr)) { - SkRRect rrect = SkRRect::MakeRect(path.getBounds()); + } else if (path.isRect(&rect)) { + rrect = SkRRect::MakeRect(rect); + return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip, + SkMatrix::I(), strokeRec, rrect, rrect); + } else if (path.isRRect(&rrect) && rrect.isSimpleCircular()) { return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip, SkMatrix::I(), strokeRec, rrect, rrect); } - // TODO return false; } @@ -186,62 +176,55 @@ bool SkAmbientShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*, const SkStrokeRec& strokeRec, const SkRRect& rrect, const SkRRect& devRRect) const { - // It's likely the caller has already done these checks, but we have to be sure. - // Fast path only supports filled rrects for now. // TODO: fill and stroke as well. if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) { return false; } + // These should have been checked by the caller. // Fast path only supports simple rrects with circular corners. - if (!devRRect.isRect() && !devRRect.isCircle() && - (!devRRect.isSimple() || !devRRect.allCornersCircular())) { - return false; - } + SkASSERT(devRRect.isRect() || devRRect.isCircle() || + (devRRect.isSimple() && devRRect.allCornersCircular())); // Fast path only supports uniform scale. - if (!viewMatrix.isSimilarity()) { - return false; - } + SkASSERT(viewMatrix.isSimilarity()); + // Assume we have positive alpha + SkASSERT(fAmbientAlpha > 0); + // 1/scale - SkScalar scaleFactor = viewMatrix.isScaleTranslate() ? + SkScalar devToSrcScale = viewMatrix.isScaleTranslate() ? SkScalarInvert(viewMatrix[SkMatrix::kMScaleX]) : sk_float_rsqrt(viewMatrix[SkMatrix::kMScaleX] * viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX] * viewMatrix[SkMatrix::kMSkewX]); - if (fAmbientAlpha > 0.0f) { - SkScalar devSpaceStrokeWidth = fOccluderHeight * kHeightFactor * kGeomFactor; - const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f)); - const SkScalar devSpaceAmbientBlur = devSpaceStrokeWidth * umbraAlpha; - - // For the ambient rrect, we outset the offset rect - // by half the strokeWidth to get our stroke shape. - SkScalar srcSpaceStrokeWidth = devSpaceStrokeWidth * scaleFactor; - SkScalar ambientPathOutset = 0.5f*srcSpaceStrokeWidth; - - SkRRect ambientRRect; - 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 { - SkScalar insetRad = rrect.getSimpleRadii().fX + ambientPathOutset; - ambientRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad); - } - - GrPaint newPaint(paint); - GrColor4f color = newPaint.getColor4f(); - color.fRGBA[3] *= fAmbientAlpha; - newPaint.setColor4f(color); - SkStrokeRec ambientStrokeRec(SkStrokeRec::kFill_InitStyle); - bool transparent = SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag); - ambientStrokeRec.setStrokeStyle(srcSpaceStrokeWidth, transparent); - - rtContext->drawShadowRRect(clip, std::move(newPaint), viewMatrix, ambientRRect, - devSpaceAmbientBlur, - GrStyle(ambientStrokeRec, nullptr)); + SkScalar devSpaceInsetWidth = fOccluderHeight * kHeightFactor * kGeomFactor; + const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f)); + const SkScalar devSpaceAmbientBlur = devSpaceInsetWidth * umbraAlpha; + + // Outset the shadow rrect to the border of the penumbra + SkScalar ambientPathOutset = devSpaceInsetWidth * devToSrcScale; + SkRRect ambientRRect; + SkRect outsetRect = 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(outsetRect); + } else { + SkScalar outsetRad = rrect.getSimpleRadii().fX + ambientPathOutset; + ambientRRect = SkRRect::MakeRectXY(outsetRect, outsetRad, outsetRad); } + GrPaint newPaint(paint); + GrColor4f color = newPaint.getColor4f(); + color.fRGBA[3] *= fAmbientAlpha; + newPaint.setColor4f(color); + if (SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag)) { + // set a large inset to force a fill + devSpaceInsetWidth = ambientRRect.width(); + } + + rtContext->drawShadowRRect(clip, std::move(newPaint), viewMatrix, ambientRRect, + devSpaceAmbientBlur, devSpaceInsetWidth); + return true; } diff --git a/src/effects/shadows/SkSpotShadowMaskFilter.cpp b/src/effects/shadows/SkSpotShadowMaskFilter.cpp index fc73afc9c1..0716d9b870 100644 --- a/src/effects/shadows/SkSpotShadowMaskFilter.cpp +++ b/src/effects/shadows/SkSpotShadowMaskFilter.cpp @@ -166,27 +166,18 @@ bool SkSpotShadowMaskFilterImpl::directFilterMaskGPU(GrContext* context, SkASSERT(rtContext); // TODO: this will not handle local coordinates properly - if (fSpotAlpha <= 0.0f) { - return true; - } - - // only convex paths for now - if (!path.isConvex()) { - return false; - } - - if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) { - return false; - } - - // if circle - if (path.isOval(nullptr) && SkScalarNearlyEqual(path.getBounds().width(), - path.getBounds().height())) { - SkRRect rrect = SkRRect::MakeOval(path.getBounds()); + SkRect rect; + SkRRect rrect; + if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), + rect.height())) { + rrect = SkRRect::MakeOval(rect); + return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip, + SkMatrix::I(), strokeRec, rrect, rrect); + } else if (path.isRect(&rect)) { + rrect = SkRRect::MakeRect(rect); return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip, SkMatrix::I(), strokeRec, rrect, rrect); - } else if (path.isRect(nullptr)) { - SkRRect rrect = SkRRect::MakeRect(path.getBounds()); + } else if (path.isRRect(&rrect) && rrect.isSimpleCircular()) { return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip, SkMatrix::I(), strokeRec, rrect, rrect); } @@ -202,113 +193,115 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*, const SkStrokeRec& strokeRec, const SkRRect& rrect, const SkRRect& devRRect) const { - // It's likely the caller has already done these checks, but we have to be sure. - // Fast path only supports filled rrects for now. // TODO: fill and stroke as well. if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) { return false; } + // These should have been checked by the caller. // Fast path only supports simple rrects with circular corners. - if (!devRRect.isRect() && !devRRect.isCircle() && - (!devRRect.isSimple() || !devRRect.allCornersCircular())) { - return false; - } + SkASSERT(devRRect.isRect() || devRRect.isCircle() || + (devRRect.isSimple() && devRRect.allCornersCircular())); // Fast path only supports uniform scale. - if (!viewMatrix.isSimilarity()) { - return false; - } + SkASSERT(viewMatrix.isSimilarity()); + // Assume we have positive alpha + SkASSERT(fSpotAlpha > 0); + // 1/scale - SkScalar scaleFactor = viewMatrix.isScaleTranslate() ? + SkScalar devToSrcScale = viewMatrix.isScaleTranslate() ? SkScalarInvert(viewMatrix[SkMatrix::kMScaleX]) : sk_float_rsqrt(viewMatrix[SkMatrix::kMScaleX] * viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX] * viewMatrix[SkMatrix::kMSkewX]); - if (fSpotAlpha > 0.0f) { - float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f); - - SkScalar devSpaceSpotRadius = 2.0f * fLightRadius * zRatio; - // handle scale of radius and pad due to CTM - const SkScalar srcSpaceSpotRadius = devSpaceSpotRadius * scaleFactor; - - SkRRect spotShadowRRect; - // Compute the scale and translation for the spot shadow. - const SkScalar scale = fLightPos.fZ / (fLightPos.fZ - fOccluderHeight); - rrect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect); - - 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)) { - // Since the matrix is a similarity, this should never happen, but just in case... - SkDebugf("Matrix is degenerate. Will not render spot shadow!\n"); - return true; - } - 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 - // stroke width but we also have to account for the scaling and translation when - // computing a new stroke shape. + float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f); + + SkScalar devSpaceSpotBlur = 2.0f * fLightRadius * zRatio; + // handle scale of radius and pad due to CTM + const SkScalar srcSpaceSpotBlur = devSpaceSpotBlur * devToSrcScale; + + // Compute the scale and translation for the spot shadow. + const SkScalar spotScale = fLightPos.fZ / (fLightPos.fZ - fOccluderHeight); + SkPoint spotOffset = SkPoint::Make(zRatio*(-fLightPos.fX), zRatio*(-fLightPos.fY)); + // Adjust translate for the effect of the scale. + spotOffset.fX += spotScale*viewMatrix[SkMatrix::kMTransX]; + spotOffset.fY += spotScale*viewMatrix[SkMatrix::kMTransY]; + // This offset is in dev space, need to transform it into source space. + SkMatrix ctmInverse; + if (!viewMatrix.invert(&ctmInverse)) { + // Since the matrix is a similarity, this should never happen, but just in case... + SkDebugf("Matrix is degenerate. Will not render spot shadow!\n"); + return true; + } + ctmInverse.mapPoints(&spotOffset, 1); + + // Compute the transformed shadow rrect + SkRRect spotShadowRRect; + SkMatrix shadowTransform; + shadowTransform.setScaleTranslate(spotScale, spotScale, spotOffset.fX, spotOffset.fY); + rrect.transform(shadowTransform, &spotShadowRRect); + SkScalar spotRadius = spotShadowRRect.getSimpleRadii().fX; + + // Compute the insetWidth + SkScalar blurOutset = 0.5f*srcSpaceSpotBlur; + SkScalar insetWidth = blurOutset; + if (fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag) { + // If transparent, just do a fill + insetWidth += spotShadowRRect.width(); + } else { + // For shadows, instead of using a stroke we specify an inset from the penumbra + // border. We want to extend this inset area so that it meets up with the caster + // geometry. The inset geometry will by default already be inset by the blur width. + // + // We compare the min and max corners inset by the radius between the original + // rrect and the shadow rrect. The distance between the two plus the difference + // between the scaled radius and the original radius gives the distance from the + // transformed shadow shape to the original shape in that corner. The max + // of these gives the maximum distance we need to cover. // - // 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 strokedDiff = SkTMin(spotShadowRRect.width(), spotShadowRRect.height()) - - (insetAmount + strokeWidth); - - SkStrokeRec spotStrokeRec(SkStrokeRec::kFill_InitStyle); - // 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); + // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to + // that to get the full insetWidth. + SkScalar maxOffset; + if (rrect.isRect()) { + // Manhattan distance works better for rects + maxOffset = SkTMax(SkTMax(SkTAbs(spotShadowRRect.rect().fLeft - + rrect.rect().fLeft), + SkTAbs(spotShadowRRect.rect().fTop - + rrect.rect().fTop)), + SkTMax(SkTAbs(spotShadowRRect.rect().fRight - + rrect.rect().fRight), + SkTAbs(spotShadowRRect.rect().fBottom - + rrect.rect().fBottom))); } 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. - 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 = spotShadowRRect.getSimpleRadii().fX - insetAmount; - spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad); - } - spotStrokeRec.setStrokeStyle(strokeWidth, false); + SkScalar dr = spotRadius - rrect.getSimpleRadii().fX; + SkPoint upperLeftOffset = SkPoint::Make(spotShadowRRect.rect().fLeft - + rrect.rect().fLeft + dr, + spotShadowRRect.rect().fTop - + rrect.rect().fTop + dr); + SkPoint lowerRightOffset = SkPoint::Make(spotShadowRRect.rect().fRight - + rrect.rect().fRight - dr, + spotShadowRRect.rect().fBottom - + rrect.rect().fBottom - dr); + maxOffset = SkScalarSqrt(SkTMax(upperLeftOffset.lengthSqd(), + lowerRightOffset.lengthSqd())) + dr; } + insetWidth += maxOffset; + } - 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)); + // Outset the shadow rrect to the border of the penumbra + SkRect outsetRect = spotShadowRRect.rect().makeOutset(blurOutset, blurOutset); + if (spotShadowRRect.isOval()) { + spotShadowRRect = SkRRect::MakeOval(outsetRect); + } else { + SkScalar outsetRad = spotRadius + blurOutset; + spotShadowRRect = SkRRect::MakeRectXY(outsetRect, outsetRad, outsetRad); } + GrColor4f color = paint.getColor4f(); + color.fRGBA[3] *= fSpotAlpha; + paint.setColor4f(color); + rtContext->drawShadowRRect(clip, std::move(paint), viewMatrix, spotShadowRRect, + devSpaceSpotBlur, insetWidth); + return true; } diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp index e21522fc65..f81dc46255 100644 --- a/src/gpu/GrRenderTargetContext.cpp +++ b/src/gpu/GrRenderTargetContext.cpp @@ -985,8 +985,8 @@ void GrRenderTargetContext::drawShadowRRect(const GrClip& clip, GrPaint&& paint, const SkMatrix& viewMatrix, const SkRRect& rrect, - SkScalar blurRadius, - const GrStyle& style) { + SkScalar blurWidth, + SkScalar insetWidth) { ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) @@ -995,14 +995,11 @@ void GrRenderTargetContext::drawShadowRRect(const GrClip& clip, return; } - SkASSERT(!style.pathEffect()); // this should've been devolved to a path in SkGpuDevice - AutoCheckFlush acf(this->drawingManager()); - const SkStrokeRec stroke = style.strokeRec(); // TODO: add instancing support? std::unique_ptr<GrLegacyMeshDrawOp> op = GrShadowRRectOp::Make(paint.getColor(), viewMatrix, - rrect, blurRadius, stroke); + rrect, blurWidth, insetWidth); if (op) { GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone); this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op)); diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h index dd42db6156..b3ff137919 100644 --- a/src/gpu/GrRenderTargetContext.h +++ b/src/gpu/GrRenderTargetContext.h @@ -158,15 +158,16 @@ public: * @param paint describes how to color pixels. * @param viewMatrix transformation matrix * @param rrect the roundrect to draw - * @param blurRadius amount of shadow blur to apply (in device space) - * @param style style to apply to the rrect. Currently path effects are not allowed. + * @param blurWidth amount of shadow blur to apply (in device space) + * @param insetWidth minimum amount to inset from the rrect edge (in local space). + * We may inset more depending on the blur radius and geometry. */ void drawShadowRRect(const GrClip&, GrPaint&&, const SkMatrix& viewMatrix, const SkRRect& rrect, SkScalar blurRadius, - const GrStyle& style); + SkScalar insetWidth); /** * Shortcut for filling a SkPath consisting of nested rrects using a paint. The result is diff --git a/src/gpu/ops/GrShadowRRectOp.cpp b/src/gpu/ops/GrShadowRRectOp.cpp index 7803449346..fc99b6d6ae 100644 --- a/src/gpu/ops/GrShadowRRectOp.cpp +++ b/src/gpu/ops/GrShadowRRectOp.cpp @@ -188,49 +188,38 @@ class ShadowCircularRRectOp final : public GrLegacyMeshDrawOp { public: DEFINE_OP_CLASS_ID - // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates - // whether the rrect is only stroked or stroked and filled. + // An insetWidth > 1/2 rect width or height indicates a simple fill. ShadowCircularRRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect, - float devRadius, bool isCircle, float blurRadius, - float devStrokeWidth, bool strokeOnly) + float devRadius, bool isCircle, float blurRadius, float insetWidth) : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) { SkRect bounds = devRect; - SkASSERT(devStrokeWidth > 0 || !strokeOnly); + SkASSERT(insetWidth > 0); SkScalar innerRadius = 0.0f; SkScalar outerRadius = devRadius; SkScalar umbraInset; + + RRectType type = kFill_RRectType; if (isCircle) { umbraInset = 0; + } else if (insetWidth > 0 && insetWidth <= outerRadius) { + // If the client has requested a stroke smaller than the outer radius, + // we will assume they want no special umbra inset (this is for ambient shadows). + umbraInset = outerRadius; } else { umbraInset = SkTMax(outerRadius, blurRadius); } - RRectType type = kFill_RRectType; - if (devStrokeWidth > 0) { - SkScalar halfWidth = SkScalarHalf(devStrokeWidth); - outerRadius += halfWidth; - bounds.outset(halfWidth, halfWidth); - - // If the client has requested a stroke smaller than the outer radius, - // we will assume they want no special umbra inset (this is for ambient shadows). - if (devStrokeWidth <= outerRadius) { - umbraInset = outerRadius; - } - - if (strokeOnly) { - // If stroke is greater than width or height, this is still a fill, - // otherwise we compute stroke params. - if (isCircle) { - innerRadius = devRadius - halfWidth; - type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType; - } else { - if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) { - // We don't worry about a real inner radius, we just need to know if we - // need to create overstroke vertices. - innerRadius = SkTMax(devStrokeWidth - umbraInset, 0.0f); - type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType; - } - } + // If stroke is greater than width or height, this is still a fill, + // otherwise we compute stroke params. + if (isCircle) { + innerRadius = devRadius - insetWidth; + type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType; + } else { + if (insetWidth <= 0.5f*SkTMin(devRect.width(), devRect.height())) { + // We don't worry about a real inner radius, we just need to know if we + // need to create overstroke vertices. + innerRadius = SkTMax(insetWidth - umbraInset, 0.0f); + type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType; } } @@ -650,45 +639,30 @@ namespace GrShadowRRectOp { std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix, const SkRRect& rrect, - const SkScalar blurRadius, - const SkStrokeRec& stroke) { + SkScalar blurWidth, + SkScalar insetWidth) { // Shadow rrect ops only handle simple circular rrects. SkASSERT(viewMatrix.isSimilarity() && (rrect.isSimple() || rrect.isRect() || rrect.isOval())); + SkASSERT(rrect.getSimpleRadii().fX > SK_ScalarNearlyZero && + SkScalarNearlyEqual(rrect.getSimpleRadii().fX, rrect.getSimpleRadii().fY)); // Do any matrix crunching before we reset the draw state for device coords. const SkRect& rrectBounds = rrect.getBounds(); SkRect bounds; viewMatrix.mapRect(&bounds, rrectBounds); - SkVector radii = rrect.getSimpleRadii(); - SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX + - viewMatrix[SkMatrix::kMSkewX] * radii.fY); - SkDEBUGCODE(SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * radii.fX + - viewMatrix[SkMatrix::kMScaleY] * radii.fY)); - SkASSERT(SkScalarNearlyEqual(xRadius, yRadius)); - - // Hairline style is unexpected and meaningless with this DrawOp. - // It will be treated as a fill, which will make it visibly obvious that something's wrong. - SkStrokeRec::Style style = stroke.getStyle(); - SkASSERT(SkStrokeRec::kHairline_Style != style); - - // Do mapping of stroke. Use -1 to indicate fill-only draws. - SkScalar scaledStrokeWidth = -1; - bool isStrokeOnly = SkStrokeRec::kStroke_Style == style; - bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; - if (hasStroke) { - SkScalar strokeWidth = stroke.getWidth(); - // As the matrix is a similarity matrix, this should be isotropic. - scaledStrokeWidth = SkScalarAbs( - strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX])); - } + // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic. + SkScalar radius = rrect.getSimpleRadii().fX; + SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX]; + SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor); + SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor); return std::unique_ptr<GrLegacyMeshDrawOp>(new ShadowCircularRRectOp(color, viewMatrix, bounds, - xRadius, rrect.isOval(), - blurRadius, - scaledStrokeWidth, - isStrokeOnly)); + scaledRadius, + rrect.isOval(), + blurWidth, + scaledInsetWidth)); } } @@ -707,16 +681,16 @@ DRAW_OP_TEST_DEFINE(ShadowRRectOp) { viewMatrix.postTranslate(translateX, translateY); viewMatrix.postScale(scale, scale); GrColor color = GrRandomColor(random); - SkStrokeRec stroke = GrTest::TestStrokeRec(random); - SkScalar blurRadius = random->nextSScalar1() * 72.f; + SkScalar insetWidth = random->nextSScalar1() * 72.f; + SkScalar blurWidth = random->nextSScalar1() * 72.f; bool isCircle = random->nextBool(); if (isCircle) { SkRect circle = GrTest::TestSquare(random); SkRRect rrect = SkRRect::MakeOval(circle); - return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurRadius, stroke); + return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth); } else { const SkRRect& rrect = GrTest::TestRRectSimple(random); - return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurRadius, stroke); + return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth); } } diff --git a/src/gpu/ops/GrShadowRRectOp.h b/src/gpu/ops/GrShadowRRectOp.h index dec63cab5d..f9727883e8 100644 --- a/src/gpu/ops/GrShadowRRectOp.h +++ b/src/gpu/ops/GrShadowRRectOp.h @@ -20,7 +20,7 @@ class SkStrokeRec; namespace GrShadowRRectOp { std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor, const SkMatrix& viewMatrix, const SkRRect& rrect, - const SkScalar blurRadius, const SkStrokeRec& stroke); + SkScalar blurWidth, SkScalar insetWidth); } #endif diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp index c6102a908d..19b2c2d403 100644 --- a/src/utils/SkShadowUtils.cpp +++ b/src/utils/SkShadowUtils.cpp @@ -526,7 +526,8 @@ static bool draw_analytic_shadows(SkCanvas* canvas, const SkPath& path, SkScalar SkRect rect; SkRRect rrect; - if (canvas->getTotalMatrix().isSimilarity()) { + const SkMatrix& ctm = canvas->getTotalMatrix(); + if (ctm.rectStaysRect() && ctm.isSimilarity()) { if (path.isRect(&rect)) { SkPaint newPaint; newPaint.setColor(color); |