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:
parent
a28e263d42
commit
a4f1af8f97
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user