Use stroked rrects for Android shadow sample

Changes the Android shadow sample to use stroked roundrects when
we can (mainly if stroked geometry area < fill geometry area).

Also changes the setup for the overstroke geometry so that it computes
the correct distance to the outer edge.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2283003003

Review-Url: https://codereview.chromium.org/2283003003
This commit is contained in:
jvanverth 2016-08-29 07:17:47 -07:00 committed by Commit bot
parent a28e263d42
commit a4f1af8f97
3 changed files with 113 additions and 87 deletions

View File

@ -143,44 +143,46 @@ protected:
return;
}
SkRect pathRect;
SkRRect pathRRect;
if ((!path.isOval(&pathRect) || pathRect.width() != pathRect.height()) &&
(!path.isRRect(&pathRRect) || !pathRRect.allCornersCircular()) &&
!path.isRect(&pathRect)) {
this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
return;
}
const SkScalar kHeightFactor = 1.f / 128.f;
const SkScalar kGeomFactor = 64;
SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
SkScalar radius = zValue*kHeightFactor*kGeomFactor;
// For all of these, we outset the rect by the radius to get our coverage shape.
SkRect pathRect;
SkRRect pathRRect;
if (radius >= 64 ||
!((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
(path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
path.isRect(&pathRect))) {
this->drawAmbientShadow(canvas, path, zValue, ambientAlpha);
return;
}
// For all of these, we outset the rect by half the radius to get our stroke shape.
SkScalar halfRadius = SK_ScalarHalf*radius;
if (path.isOval(nullptr)) {
pathRect.outset(radius, radius);
pathRect.outset(halfRadius, halfRadius);
pathRRect = SkRRect::MakeOval(pathRect);
} else if (path.isRect(nullptr)) {
pathRect.outset(radius, radius);
pathRRect = SkRRect::MakeRectXY(pathRect, radius, radius);
pathRect.outset(halfRadius, halfRadius);
pathRRect = SkRRect::MakeRectXY(pathRect, halfRadius, halfRadius);
} else {
pathRRect.outset(radius, radius);
pathRRect.outset(halfRadius, halfRadius);
}
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
// we outset the stroke a little to cover up AA on the interior edge
paint.setStrokeWidth(radius + 1);
// handle scale of radius due to CTM
SkScalar maxScale = canvas->getTotalMatrix().getMaxScale();
radius *= maxScale;
unsigned char gray = (unsigned char)(ambientAlpha*umbraAlpha*255.999f);
SkASSERT(radius < 256);
// convert the radius to fixed point 8.8 and
// place it in the G,B components of the color
unsigned char intPart = (unsigned char)radius;
SkScalar fracPart = radius - intPart;
paint.setColor(SkColorSetARGB(1, gray, intPart, (unsigned char)(fracPart*256.f)));
SkASSERT(radius < 64);
// Convert radius to 6.2 fixed point and place in the G component.
paint.setColor(SkColorSetARGB(1, gray, (unsigned char)(4.0f*radius), 0));
sk_sp<SkShader> gaussShader = SkGaussianEdgeShader::Make();
paint.setShader(gaussShader);
@ -265,15 +267,6 @@ protected:
return;
}
SkRect pathRect;
SkRRect pathRRect;
if ((!path.isOval(&pathRect) || pathRect.width() != pathRect.height()) &&
(!path.isRRect(&pathRRect) || !pathRRect.allCornersCircular()) &&
!path.isRect(&pathRect)) {
this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
return;
}
SkScalar zRatio = zValue / (lightPos.fZ - zValue);
if (zRatio < 0.0f) {
zRatio = 0.0f;
@ -282,15 +275,26 @@ protected:
}
SkScalar radius = lightWidth*zRatio;
// For all of these, we outset the rect by the radius to get our coverage shape.
SkRect pathRect;
SkRRect pathRRect;
if (radius >= 64 ||
!((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
(path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
path.isRect(&pathRect))) {
this->drawSpotShadow(canvas, path, zValue, lightPos, lightWidth, spotAlpha);
return;
}
// For all of these, we outset the rect by half the radius to get our stroke shape.
SkScalar halfRadius = SK_ScalarHalf*radius;
if (path.isOval(nullptr)) {
pathRect.outset(radius, radius);
pathRect.outset(halfRadius, halfRadius);
pathRRect = SkRRect::MakeOval(pathRect);
} else if (path.isRect(nullptr)) {
pathRect.outset(radius, radius);
pathRRect = SkRRect::MakeRectXY(pathRect, radius, radius);
pathRect.outset(halfRadius, halfRadius);
pathRRect = SkRRect::MakeRectXY(pathRect, halfRadius, halfRadius);
} else {
pathRRect.outset(radius, radius);
pathRRect.outset(halfRadius, halfRadius);
}
// compute the transformation params
@ -302,18 +306,34 @@ protected:
SkPaint paint;
paint.setAntiAlias(true);
// We outset the stroke by the length of the translation so the shadow extends to
// the edge of the shape. We also add 1/2 to cover up AA on the interior edge.
SkScalar pad = offset.length() + 0.5f;
// compute area
SkScalar strokeWidth = radius + 2.0f*pad;
SkScalar strokedArea = 2.0f*strokeWidth*(pathRRect.width() + pathRRect.height());
SkScalar filledArea = (pathRRect.height() + radius)*(pathRRect.width() + radius);
// If the area of the stroked geometry is larger than the fill geometry, or
// if our pad is too big to convert to 6.2 fixed point, just fill it.
if (strokedArea > filledArea || pad >= 64) {
pad = 0;
paint.setStyle(SkPaint::kStrokeAndFill_Style);
paint.setStrokeWidth(radius);
} else {
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(strokeWidth);
}
sk_sp<SkShader> gaussShader = SkGaussianEdgeShader::Make();
paint.setShader(gaussShader);
// handle scale of radius due to CTM
SkScalar maxScale = canvas->getTotalMatrix().getMaxScale();
radius *= maxScale;
unsigned char gray = (unsigned char)(spotAlpha*255.999f);
SkASSERT(radius < 256);
// convert the radius to fixed point 8.8 and
// place it in the G,B components of the color
unsigned char intPart = (unsigned char)radius;
SkScalar fracPart = radius - intPart;
paint.setColor(SkColorSetARGB(1, gray, intPart, (unsigned char)(fracPart*256.f)));
SkASSERT(radius < 64);
SkASSERT(pad < 64);
// Convert radius and pad to 6.2 fixed point and place in the G & B components.
paint.setColor(SkColorSetARGB(1, gray, (unsigned char)(radius*4.0f),
(unsigned char)(pad*4.0f)));
// apply transformation to shadow
canvas->translate(offset.fX, offset.fY);

View File

@ -12,9 +12,10 @@
/** \class SkGaussianEdgeShaderImpl
This subclass of shader applies a Gaussian to shadow edge
The radius of the Gaussian blur is specified by the g and b values of the color,
where g is the integer component and b is the fractional component. The r value
represents the max final alpha.
The radius of the Gaussian blur is specified by the g value of the color, in 6.2 fixed point.
For spot shadows, we increase the stroke width to set the shadow against the shape. This pad
is specified by b, also in 6.2 fixed point. The r value represents the max final alpha.
The incoming alpha should be 1.
*/
class SkGaussianEdgeShaderImpl : public SkShader {
public:
@ -70,9 +71,10 @@ public:
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
fragBuilder->codeAppend("float radius = color.g*255.0 + color.b;");
fragBuilder->codeAppend("float radius = color.g*64.0;");
fragBuilder->codeAppend("float pad = color.b*64.0;");
fragBuilder->codeAppendf("float factor = 1.0 - clamp(%s.z/radius, 0.0, 1.0);",
fragBuilder->codeAppendf("float factor = 1.0 - clamp((%s.z - pad)/radius, 0.0, 1.0);",
fragBuilder->distanceVectorName());
fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;");
fragBuilder->codeAppendf("%s = factor*vec4(0.0, 0.0, 0.0, color.r);", args.fOutputColor);

View File

@ -1526,7 +1526,7 @@ private:
}
// Setup geometry processor
SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(kStroke_RRectType == fType,
SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(kFill_RRectType != fType,
false, false,
false, localMatrix));
@ -1612,73 +1612,77 @@ private:
verts++;
}
// Add the additional vertices for overstroked rrects.
// Effectively this is an additional rrect, drawn inside out,
// with outerRadius == -innerRadius. This will give us correct AA in the center.
// Effectively this is an additional stroked rrect, with its
// outer radius = outerRadius - innerRadius, and inner radius = 0.
// This will give us correct AA in the center and the correct
// distance to the outer edge.
//
// Note that args.fInnerRadius is negative in this case.
// Also, the offset is a constant vector pointing to the right, which guarantees
// that the distance value along the inner rectangle is constant, which
// is what we want to get nice anti-aliasing.
// Also, the outer offset is a constant vector pointing to the right, which
// guarantees that the distance value along the outer rectangle is constant.
if (kOverstroke_RRectType == fType) {
SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
// this is the normalized distance from the outer rectangle of this
// geometry to the outer edge
SkScalar maxOffset = -args.fInnerRadius/overstrokeOuterRadius;
verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[1]);
verts->fColor = color;
verts->fOffset = SkPoint::Make(0, 0);
verts->fOuterRadius = -args.fInnerRadius;
verts->fInnerRadius = innerRadius;
verts->fOffset = SkPoint::Make(maxOffset, 0);
verts->fOuterRadius = overstrokeOuterRadius;
verts->fInnerRadius = 0;
verts++;
verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[1]);
verts->fColor = color;
verts->fOffset = SkPoint::Make(maxOffset, 0);
verts->fOuterRadius = overstrokeOuterRadius;
verts->fInnerRadius = 0;
verts++;
verts->fPos = SkPoint::Make(bounds.fLeft + overstrokeOuterRadius,
bounds.fTop + overstrokeOuterRadius);
verts->fColor = color;
verts->fOffset = SkPoint::Make(0, 0);
verts->fOuterRadius = -args.fInnerRadius;
verts->fInnerRadius = innerRadius;
verts->fOuterRadius = overstrokeOuterRadius;
verts->fInnerRadius = 0;
verts++;
SkScalar inset = outerRadius - args.fInnerRadius;
verts->fPos = SkPoint::Make(bounds.fLeft + inset,
bounds.fTop + inset);
verts->fPos = SkPoint::Make(bounds.fRight - overstrokeOuterRadius,
bounds.fTop + overstrokeOuterRadius);
verts->fColor = color;
verts->fOffset = SkPoint::Make(1, 0);
verts->fOuterRadius = -args.fInnerRadius;
verts->fInnerRadius = innerRadius;
verts->fOffset = SkPoint::Make(0, 0);
verts->fOuterRadius = overstrokeOuterRadius;
verts->fInnerRadius = 0;
verts++;
verts->fPos = SkPoint::Make(bounds.fRight - inset,
bounds.fTop + inset);
verts->fPos = SkPoint::Make(bounds.fLeft + overstrokeOuterRadius,
bounds.fBottom - overstrokeOuterRadius);
verts->fColor = color;
verts->fOffset = SkPoint::Make(1, 0);
verts->fOuterRadius = -args.fInnerRadius;
verts->fInnerRadius = innerRadius;
verts->fOffset = SkPoint::Make(0, 0);
verts->fOuterRadius = overstrokeOuterRadius;
verts->fInnerRadius = 0;
verts++;
verts->fPos = SkPoint::Make(bounds.fLeft + inset,
bounds.fBottom - inset);
verts->fPos = SkPoint::Make(bounds.fRight - overstrokeOuterRadius,
bounds.fBottom - overstrokeOuterRadius);
verts->fColor = color;
verts->fOffset = SkPoint::Make(1, 0);
verts->fOuterRadius = -args.fInnerRadius;
verts->fInnerRadius = innerRadius;
verts++;
verts->fPos = SkPoint::Make(bounds.fRight - inset,
bounds.fBottom - inset);
verts->fColor = color;
verts->fOffset = SkPoint::Make(1, 0);
verts->fOuterRadius = -args.fInnerRadius;
verts->fInnerRadius = innerRadius;
verts->fOffset = SkPoint::Make(0, 0);
verts->fOuterRadius = overstrokeOuterRadius;
verts->fInnerRadius = 0;
verts++;
verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[2]);
verts->fColor = color;
verts->fOffset = SkPoint::Make(0, 0);
verts->fOuterRadius = -args.fInnerRadius;
verts->fInnerRadius = innerRadius;
verts->fOffset = SkPoint::Make(maxOffset, 0);
verts->fOuterRadius = overstrokeOuterRadius;
verts->fInnerRadius = 0;
verts++;
verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[2]);
verts->fColor = color;
verts->fOffset = SkPoint::Make(0, 0);
verts->fOuterRadius = -args.fInnerRadius;
verts->fInnerRadius = innerRadius;
verts->fOffset = SkPoint::Make(maxOffset, 0);
verts->fOuterRadius = overstrokeOuterRadius;
verts->fInnerRadius = 0;
verts++;
}
}