aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rwxr-xr-xsamplecode/SampleAndroidShadows.cpp100
-rw-r--r--src/effects/SkGaussianEdgeShader.cpp12
-rw-r--r--src/gpu/GrOvalRenderer.cpp84
3 files changed, 111 insertions, 85 deletions
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp
index c7a2f63db1..f411e5c9b3 100755
--- a/samplecode/SampleAndroidShadows.cpp
+++ b/samplecode/SampleAndroidShadows.cpp
@@ -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);
diff --git a/src/effects/SkGaussianEdgeShader.cpp b/src/effects/SkGaussianEdgeShader.cpp
index 1990fbf67b..d73bfadbd7 100644
--- a/src/effects/SkGaussianEdgeShader.cpp
+++ b/src/effects/SkGaussianEdgeShader.cpp
@@ -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);
diff --git a/src/gpu/GrOvalRenderer.cpp b/src/gpu/GrOvalRenderer.cpp
index e5eb665a3d..b8c3d3eeb8 100644
--- a/src/gpu/GrOvalRenderer.cpp
+++ b/src/gpu/GrOvalRenderer.cpp
@@ -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(0, 0);
- verts->fOuterRadius = -args.fInnerRadius;
- verts->fInnerRadius = innerRadius;
+ verts->fOffset = SkPoint::Make(maxOffset, 0);
+ 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.fLeft + 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.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.fLeft + inset,
- bounds.fBottom - 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.fRight - 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->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++;
}
}