fCirclePath.addCircle(0, 0, 50);
fRectPath.addRect(SkRect::MakeXYWH(-100, -50, 200, 100));
fRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -50, 200, 100), 4, 4));
- fLightPos = SkPoint3::Make(-2, -2, 6);
+ fLightPos = SkPoint3::Make(-700, -700, 2800);
}
// overrides from SkEventSink
SkScalar umbraAlpha = 1 / (1 + SkMaxScalar(zValue*kHeightFactor, 0));
SkScalar radius = zValue*kHeightFactor*kGeomFactor;
+ // distance to outer of edge of geometry from original shape edge
+ SkScalar offset = radius*umbraAlpha;
SkRect pathRect;
SkRRect pathRRect;
- if (radius >= 64 ||
+ SkScalar scaleFactors[2];
+ if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
+ return;
+ }
+ if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 64 ||
!((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
(path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
path.isRect(&pathRect))) {
return;
}
- // For all of these, we outset the rect by half the radius to get our stroke shape.
- SkScalar halfRadius = SK_ScalarHalf*radius;
+ // For all of these, we inset the offset rect by half the radius to get our stroke shape.
+ SkScalar strokeOutset = offset - SK_ScalarHalf*radius;
+ // Make sure we'll have a radius of at least 0.5 after xform
+ if (strokeOutset*scaleFactors[0] < 0.5f) {
+ strokeOutset = 0.5f / scaleFactors[0];
+ }
if (path.isOval(nullptr)) {
- pathRect.outset(halfRadius, halfRadius);
+ pathRect.outset(strokeOutset, strokeOutset);
pathRRect = SkRRect::MakeOval(pathRect);
} else if (path.isRect(nullptr)) {
- pathRect.outset(halfRadius, halfRadius);
- pathRRect = SkRRect::MakeRectXY(pathRect, halfRadius, halfRadius);
+ pathRect.outset(strokeOutset, strokeOutset);
+ pathRRect = SkRRect::MakeRectXY(pathRect, strokeOutset, strokeOutset);
} else {
- pathRRect.outset(halfRadius, halfRadius);
+ pathRRect.outset(strokeOutset, strokeOutset);
}
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 < 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);
+ SkScalar pad = 0.5f;
+ paint.setStrokeWidth(radius + 2*pad);
+ // handle scale of radius and pad due to CTM
+ radius *= scaleFactors[0];
+ pad *= scaleFactors[0];
+ SkASSERT(radius < 16384);
+ SkASSERT(pad < 64);
+ // Convert radius to 14.2 fixed point and place in the R & G components.
+ // Convert pad to 6.2 fixed point and place in the B component.
+ uint16_t iRadius = (uint16_t)(radius*4.0f);
+ unsigned char alpha = (unsigned char)(ambientAlpha*255.999f);
+ paint.setColor(SkColorSetARGB(alpha, iRadius >> 8, iRadius & 0xff,
+ (unsigned char)(4.0f*pad)));
+
+ paint.setShader(SkGaussianEdgeShader::Make(true));
canvas->drawRRect(pathRRect, paint);
}
} else if (zRatio > 0.95f) {
zRatio = 0.95f;
}
- SkScalar radius = lightWidth*zRatio;
+ SkScalar blurRadius = lightWidth*zRatio;
// compute the transformation params
SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
- canvas->getTotalMatrix().mapPoints(¢er, 1);
- SkPoint offset = SkPoint::Make(-zRatio*(lightPos.fX - center.fX),
- -zRatio*(lightPos.fY - center.fY));
- SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
- if (scale < 1.0f) {
- scale = 1.0f;
- } else if (scale > 1024.f) {
- scale = 1024.f;
+ SkMatrix ctmInverse;
+ if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+ return;
}
+ SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
+ ctmInverse.mapPoints(&lightPos2D, 1);
+ SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+ zRatio*(center.fY - lightPos2D.fY));
+ SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
SkAutoCanvasRestore acr(canvas, true);
- SkRect occlRect;
- GetOcclRect(path, &occlRect);
- // apply inverse transform
- occlRect.offset(-offset);
-#if 0
- // It looks like the scale may be invalid
- SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
- if (scale < 1.0f) {
- scale = 1.0f;
- } else if (scale > 1024.f) {
- scale = 1024.f;
- }
- occlRect.fLeft /= scale;
- occlRect.fRight /= scale;
- occlRect.fTop /= scale;
- occlRect.fBottom /= scale;
-#endif
sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
- SkBlurMask::ConvertRadiusToSigma(radius),
- occlRect,
+ SkBlurMask::ConvertRadiusToSigma(blurRadius),
SkBlurMaskFilter::kNone_BlurFlag);
SkPaint paint;
paint.setColor(SkColorSetARGB((unsigned char)(spotAlpha*255.999f), 0, 0, 0));
// apply transformation to shadow
- canvas->translate(offset.fX, offset.fY);
-#if 0
- // It looks like the scale may be invalid
canvas->scale(scale, scale);
-#endif
+ canvas->translate(offset.fX, offset.fY);
canvas->drawPath(path, paint);
-
- // draw occlusion rect
-#if DRAW_OCCL_RECT
- SkPaint stroke;
- stroke.setStyle(SkPaint::kStroke_Style);
- stroke.setColor(SK_ColorRED);
- canvas->drawRect(occlRect, stroke)
-#endif
}
void drawSpotShadowAlt(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
} else if (zRatio > 0.95f) {
zRatio = 0.95f;
}
- SkScalar radius = lightWidth*zRatio;
+ SkScalar radius = 2.0f*lightWidth*zRatio;
SkRect pathRect;
SkRRect pathRRect;
- if (radius >= 64 ||
+ SkScalar scaleFactors[2];
+ if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
+ return;
+ }
+ if (scaleFactors[0] != scaleFactors[1] || radius*scaleFactors[0] >= 16384 ||
!((path.isOval(&pathRect) && pathRect.width() == pathRect.height()) ||
(path.isRRect(&pathRRect) && pathRRect.allCornersCircular()) ||
path.isRect(&pathRect))) {
return;
}
- // For all of these, we outset the rect by half the radius to get our stroke shape.
- SkScalar halfRadius = SK_ScalarHalf*radius;
+ // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
+ SkScalar minRadius = SK_ScalarHalf/scaleFactors[0];
if (path.isOval(nullptr)) {
- pathRect.outset(halfRadius, halfRadius);
pathRRect = SkRRect::MakeOval(pathRect);
} else if (path.isRect(nullptr)) {
- pathRect.outset(halfRadius, halfRadius);
- pathRRect = SkRRect::MakeRectXY(pathRect, halfRadius, halfRadius);
+ pathRRect = SkRRect::MakeRectXY(pathRect, minRadius, minRadius);
} else {
- pathRRect.outset(halfRadius, halfRadius);
+ if (pathRRect.getSimpleRadii().fX < minRadius) {
+ pathRRect.setRectXY(pathRRect.rect(), minRadius, minRadius);
+ }
}
- // compute the transformation params
- SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
- canvas->getTotalMatrix().mapPoints(¢er, 1);
- SkPoint offset = SkPoint::Make(-zRatio*(lightPos.fX - center.fX),
- -zRatio*(lightPos.fY - center.fY));
+ // compute the scale and translation for the shadow
+ SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
+ SkRRect shadowRRect;
+ pathRRect.transform(SkMatrix::MakeScale(scale, scale), &shadowRRect);
+ SkPoint center = SkPoint::Make(shadowRRect.rect().centerX(), shadowRRect.rect().centerY());
+ SkMatrix ctmInverse;
+ if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+ return;
+ }
+ SkPoint lightPos2D = SkPoint::Make(lightPos.fX, lightPos.fY);
+ ctmInverse.mapPoints(&lightPos2D, 1);
+ SkPoint offset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+ zRatio*(center.fY - lightPos2D.fY));
SkAutoCanvasRestore acr(canvas, true);
SkPaint paint;
// 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);
+ SkScalar strokeWidth = radius + 2.0f*pad/scaleFactors[0];
+ SkScalar strokedArea = 2.0f*strokeWidth*(shadowRRect.width() + shadowRRect.height());
+ SkScalar filledArea = (shadowRRect.height() + radius)*(shadowRRect.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) {
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(strokeWidth);
}
- sk_sp<SkShader> gaussShader = SkGaussianEdgeShader::Make();
- paint.setShader(gaussShader);
+ paint.setShader(SkGaussianEdgeShader::Make(true));
// handle scale of radius due to CTM
- SkScalar maxScale = canvas->getTotalMatrix().getMaxScale();
- radius *= maxScale;
- unsigned char gray = (unsigned char)(spotAlpha*255.999f);
- SkASSERT(radius < 64);
+ radius *= scaleFactors[0];
+ // don't need to scale pad as it was computed from the transformed offset
+ SkASSERT(radius < 16384);
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)));
+ // Convert radius to 14.2 fixed point and place in the R & G components.
+ // Convert pad to 6.2 fixed point and place in the B component.
+ uint16_t iRadius = (uint16_t)(radius*4.0f);
+ unsigned char alpha = (unsigned char)(spotAlpha*255.999f);
+ paint.setColor(SkColorSetARGB(alpha, iRadius >> 8, iRadius & 0xff,
+ (unsigned char)(4.0f*pad)));
// apply transformation to shadow
canvas->translate(offset.fX, offset.fY);
-#if 0
- // It looks like the scale may be invalid
- SkScalar scale = lightPos.fZ / (lightPos.fZ - zValue);
- if (scale < 1.0f) {
- scale = 1.0f;
- } else if (scale > 1024.f) {
- scale = 1024.f;
- }
- canvas->scale(scale, scale);
-#endif
- canvas->drawRRect(pathRRect, paint);
+ canvas->drawRRect(shadowRRect, paint);
}
void drawShadowedPath(SkCanvas* canvas, const SkPath& path, SkScalar zValue,
void onDrawContent(SkCanvas* canvas) override {
this->drawBG(canvas);
- const SkScalar kLightWidth = 3;
+ const SkScalar kLightWidth = 2800;
const SkScalar kAmbientAlpha = 0.25f;
const SkScalar kSpotAlpha = 0.25f;
canvas->translate(200, 90);
lightPos.fX += 200;
lightPos.fY += 90;
- this->drawShadowedPath(canvas, fRectPath, 5, paint, kAmbientAlpha,
+ this->drawShadowedPath(canvas, fRectPath, 2, paint, kAmbientAlpha,
lightPos, kLightWidth, kSpotAlpha);
paint.setColor(SK_ColorRED);
canvas->translate(250, 0);
lightPos.fX += 250;
- this->drawShadowedPath(canvas, fRRPath, 5, paint, kAmbientAlpha,
+ this->drawShadowedPath(canvas, fRRPath, 4, paint, kAmbientAlpha,
lightPos, kLightWidth, kSpotAlpha);
paint.setColor(SK_ColorBLUE);
canvas->translate(-250, 110);
lightPos.fX -= 250;
lightPos.fY += 110;
- this->drawShadowedPath(canvas, fCirclePath, 5, paint, 0.0f,
+ this->drawShadowedPath(canvas, fCirclePath, 8, paint, 0.0f,
lightPos, kLightWidth, 0.5f);
paint.setColor(SK_ColorGREEN);
canvas->translate(250, 0);
lightPos.fX += 250;
- this->drawShadowedPath(canvas, fRRPath, 5, paint, kAmbientAlpha,
+ this->drawShadowedPath(canvas, fRRPath, 64, paint, kAmbientAlpha,
lightPos, kLightWidth, kSpotAlpha);
}
/** \class SkGaussianEdgeShaderImpl
This subclass of shader applies a Gaussian to shadow edge
+ If largerBlur is false:
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.
+
+ If largerBlur is true:
+ The radius of the Gaussian blur is specified by the r & g values of the color in 14.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 a value represents the max final alpha.
+
+ LargerBlur will be removed once Android is migrated to the updated shader.
*/
class SkGaussianEdgeShaderImpl : public SkShader {
public:
- SkGaussianEdgeShaderImpl() {}
+ SkGaussianEdgeShaderImpl()
+ : fLargerBlur(false) {}
+
+ SkGaussianEdgeShaderImpl(bool largerBlur)
+ : fLargerBlur(largerBlur) {}
bool isOpaque() const override;
private:
friend class SkGaussianEdgeShader;
+ bool fLargerBlur;
typedef SkShader INHERITED;
};
class GaussianEdgeFP : public GrFragmentProcessor {
public:
- GaussianEdgeFP() {
+ GaussianEdgeFP(bool largerBlur) : fLargerBlur(largerBlur) {
this->initClassID<GaussianEdgeFP>();
// enable output of distance information for shape
class GLSLGaussianEdgeFP : public GrGLSLFragmentProcessor {
public:
- GLSLGaussianEdgeFP() {}
+ GLSLGaussianEdgeFP(bool largerBlur) : fLargerBlur(largerBlur) {}
void emitCode(EmitArgs& args) override {
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
" returning grey in GLSLGaussianEdgeFP\n");
fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
fragBuilder->codeAppendf("%s = vec4(0, 0, 0, color.r);", args.fOutputColor);
+ } else if (fLargerBlur) {
+ fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
+ fragBuilder->codeAppend("float radius = color.r*256*64 + color.g*64;");
+ fragBuilder->codeAppend("float pad = color.b*64;");
+
+ fragBuilder->codeAppendf("float factor = 1 - clamp((%s.z - pad)/radius, 0, 1);",
+ fragBuilder->distanceVectorName());
+ fragBuilder->codeAppend("factor = exp(-factor * factor * 4) - 0.018;");
+ fragBuilder->codeAppendf("%s = factor*vec4(0, 0, 0, color.a);",
+ args.fOutputColor);
} else {
fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor);
- fragBuilder->codeAppend("float radius = color.g*64.0;");
- fragBuilder->codeAppend("float pad = color.b*64.0;");
+ fragBuilder->codeAppend("float radius = color.g*64;");
+ fragBuilder->codeAppend("float pad = color.b*64;");
- fragBuilder->codeAppendf("float factor = 1.0 - clamp((%s.z - pad)/radius,"
- "0.0, 1.0);",
+ fragBuilder->codeAppendf("float factor = 1 - clamp((%s.z - pad)/radius, 0, 1);",
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);",
+ fragBuilder->codeAppend("factor = exp(-factor * factor * 4) - 0.018;");
+ fragBuilder->codeAppendf("%s = factor*vec4(0, 0, 0, color.r);",
args.fOutputColor);
}
}
static void GenKey(const GrProcessor& proc, const GrGLSLCaps&,
GrProcessorKeyBuilder* b) {
- // only one shader generated currently
- b->add32(0x0);
+ const GaussianEdgeFP& gefp = proc.cast<GaussianEdgeFP>();
+ b->add32(gefp.fLargerBlur ? 0x1 : 0x0);
}
protected:
void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {}
+
+ bool fLargerBlur;
};
void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
}
private:
- GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLGaussianEdgeFP; }
+ GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
+ return new GLSLGaussianEdgeFP(fLargerBlur);
+ }
bool onIsEqual(const GrFragmentProcessor& proc) const override { return true; }
+
+ bool fLargerBlur;
};
////////////////////////////////////////////////////////////////////////////
sk_sp<GrFragmentProcessor> SkGaussianEdgeShaderImpl::asFragmentProcessor(const AsFPArgs& args) const {
- return sk_make_sp<GaussianEdgeFP>();
+ return sk_make_sp<GaussianEdgeFP>(fLargerBlur);
}
#endif
///////////////////////////////////////////////////////////////////////////////
-sk_sp<SkShader> SkGaussianEdgeShader::Make() {
- return sk_make_sp<SkGaussianEdgeShaderImpl>();
+sk_sp<SkShader> SkGaussianEdgeShader::Make(bool largerBlur) {
+ return sk_make_sp<SkGaussianEdgeShaderImpl>(largerBlur);
}
///////////////////////////////////////////////////////////////////////////////