aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rwxr-xr-xsamplecode/SampleShadowUtils.cpp2
-rw-r--r--src/effects/shadows/SkAmbientShadowMaskFilter.cpp107
-rw-r--r--src/effects/shadows/SkSpotShadowMaskFilter.cpp209
-rw-r--r--src/gpu/GrRenderTargetContext.cpp9
-rw-r--r--src/gpu/GrRenderTargetContext.h7
-rw-r--r--src/gpu/ops/GrShadowRRectOp.cpp100
-rw-r--r--src/gpu/ops/GrShadowRRectOp.h2
-rw-r--r--src/utils/SkShadowUtils.cpp3
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);