From a4f1af8f9778462184c2bef91de1b1827568c08b Mon Sep 17 00:00:00 2001 From: jvanverth Date: Mon, 29 Aug 2016 07:17:47 -0700 Subject: [PATCH] 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 --- samplecode/SampleAndroidShadows.cpp | 100 +++++++++++++++++++++-------------- src/effects/SkGaussianEdgeShader.cpp | 12 +++-- src/gpu/GrOvalRenderer.cpp | 84 +++++++++++++++-------------- 3 files changed, 111 insertions(+), 85 deletions(-) diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp index c7a2f63..f411e5c 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 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 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 1990fbf..d73bfad 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 e5eb665..b8c3d3e 100644 --- a/src/gpu/GrOvalRenderer.cpp +++ b/src/gpu/GrOvalRenderer.cpp @@ -1526,7 +1526,7 @@ private: } // Setup geometry processor - SkAutoTUnref gp(new CircleGeometryProcessor(kStroke_RRectType == fType, + SkAutoTUnref 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++; } } -- 2.7.4