Allow shadow zParams to be applied to affine transformations

Change-Id: Iedfded98ce82d15945667232fde22d046d5106b3
Reviewed-on: https://skia-review.googlesource.com/16879
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
Jim Van Verth 2017-05-15 13:49:21 -04:00 committed by Skia Commit-Bot
parent 0ec981ba49
commit 4c9b893953
3 changed files with 41 additions and 32 deletions

View File

@ -28,7 +28,6 @@ public:
* @param path The occluder used to generate the shadows.
* @param zPlaneParams Values for the plane function which returns the Z offset of the
* occluder from the canvas based on local x and y values (the current matrix is not applied).
* If the canvas matrix is not perspective, then only zPlaneParams.fZ is used.
* @param lightPos The 3D position of the light relative to the canvas plane. This is
* independent of the canvas's current matrix.
* @param lightRadius The radius of the disc light.

View File

@ -64,7 +64,7 @@ protected:
std::function<SkScalar(const SkPoint&)> fTransformedHeightFunc;
SkScalar fZOffset;
// members for perspective height function
SkPoint3 fPerspZParams;
SkPoint3 fTransformedZParams;
SkScalar fPartialDeterminants[3];
// first two points
@ -268,7 +268,7 @@ bool SkBaseShadowTessellator::addArc(const SkVector& nextNormal, bool finishArc)
}
bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
if (!ctm.hasPerspective()) {
if (SkScalarNearlyZero(fZPlaneParams.fX) && SkScalarNearlyZero(fZPlaneParams.fY)) {
fTransformedHeightFunc = [this](const SkPoint& p) {
return fZPlaneParams.fZ;
};
@ -277,9 +277,8 @@ bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
if (!ctm.invert(&ctmInverse)) {
return false;
}
// multiply by transpose
fPerspZParams = SkPoint3::Make(
fTransformedZParams = SkPoint3::Make(
ctmInverse[SkMatrix::kMScaleX] * fZPlaneParams.fX +
ctmInverse[SkMatrix::kMSkewY] * fZPlaneParams.fY +
ctmInverse[SkMatrix::kMPersp0] * fZPlaneParams.fZ,
@ -293,33 +292,41 @@ bool SkBaseShadowTessellator::setTransformedHeightFunc(const SkMatrix& ctm) {
ctmInverse[SkMatrix::kMPersp2] * fZPlaneParams.fZ
);
// We use Cramer's rule to solve for the W value for a given post-divide X and Y,
// so pre-compute those values that are independent of X and Y.
// W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
if (ctm.hasPerspective()) {
// We use Cramer's rule to solve for the W value for a given post-divide X and Y,
// so pre-compute those values that are independent of X and Y.
// W is det(ctmInverse)/(PD[0]*X + PD[1]*Y + PD[2])
fPartialDeterminants[0] = ctm[SkMatrix::kMSkewY] * ctm[SkMatrix::kMPersp1] -
ctm[SkMatrix::kMScaleY] * ctm[SkMatrix::kMPersp0];
fPartialDeterminants[1] = ctm[SkMatrix::kMPersp0] * ctm[SkMatrix::kMSkewX] -
ctm[SkMatrix::kMPersp1] * ctm[SkMatrix::kMScaleX];
fPartialDeterminants[2] = ctm[SkMatrix::kMScaleX] * ctm[SkMatrix::kMScaleY] -
ctm[SkMatrix::kMSkewX] * ctm[SkMatrix::kMSkewY];
SkScalar ctmDeterminant = ctm[SkMatrix::kMTransX] * fPartialDeterminants[0] +
ctm[SkMatrix::kMTransY] * fPartialDeterminants[1] +
ctm[SkMatrix::kMPersp2] * fPartialDeterminants[2];
// Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
// TODO: this may introduce numerical instability, but I haven't seen any issues yet.
fPerspZParams.fX *= ctmDeterminant;
fPerspZParams.fY *= ctmDeterminant;
fPerspZParams.fZ *= ctmDeterminant;
// Pre-bake the numerator of Cramer's rule into the zParams to avoid another multiply.
// TODO: this may introduce numerical instability, but I haven't seen any issues yet.
fTransformedZParams.fX *= ctmDeterminant;
fTransformedZParams.fY *= ctmDeterminant;
fTransformedZParams.fZ *= ctmDeterminant;
fTransformedHeightFunc = [this](const SkPoint& p) {
SkScalar denom = p.fX * fPartialDeterminants[0] +
p.fY * fPartialDeterminants[1] +
fPartialDeterminants[2];
SkScalar w = SkScalarFastInvert(denom);
return (fPerspZParams.fX * p.fX + fPerspZParams.fY * p.fY + fPerspZParams.fZ)*w +
fZOffset;
};
fTransformedHeightFunc = [this](const SkPoint& p) {
SkScalar denom = p.fX * fPartialDeterminants[0] +
p.fY * fPartialDeterminants[1] +
fPartialDeterminants[2];
SkScalar w = SkScalarFastInvert(denom);
return fZOffset + w*(fTransformedZParams.fX * p.fX +
fTransformedZParams.fY * p.fY +
fTransformedZParams.fZ);
};
} else {
fTransformedHeightFunc = [this](const SkPoint& p) {
return fZOffset + fTransformedZParams.fX * p.fX +
fTransformedZParams.fY * p.fY + fTransformedZParams.fZ;
};
}
}
return true;

View File

@ -583,8 +583,11 @@ void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoi
const SkPoint3& devLightPos, SkScalar lightRadius,
SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
uint32_t flags) {
// check z plane
bool tiltZPlane = !SkScalarNearlyZero(zPlaneParams.fX) || !SkScalarNearlyZero(zPlaneParams.fY);
// try fast paths
bool skipAnalytic = SkToBool(flags & SkShadowFlags::kGeometricOnly_ShadowFlag);
bool skipAnalytic = SkToBool(flags & SkShadowFlags::kGeometricOnly_ShadowFlag) || tiltZPlane;
if (!skipAnalytic && draw_analytic_shadows(canvas, path, zPlaneParams.fZ, devLightPos,
lightRadius, ambientAlpha, spotAlpha, color,
flags)) {
@ -598,7 +601,7 @@ void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoi
ShadowedPath shadowedPath(&path, &viewMatrix);
bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
bool uncached = viewMatrix.hasPerspective() || path.isVolatile();
bool uncached = tiltZPlane || path.isVolatile();
if (ambientAlpha > 0) {
ambientAlpha = SkTMin(ambientAlpha, 1.f);