Add a GM for SkShadowUtils and fix a few issues.
1) Transform the path center to device space before computing the shadow offset. 2) Modulate the shadow color by the color filter's output color. 3) Make the scale of path points in the spot tessellator be relative to the path centroid. 4) Clamp the shadow alphas at 1. Change-Id: I480476df79b959f11c1eca0ba2a49a134d355cbb Reviewed-on: https://skia-review.googlesource.com/7860 Reviewed-by: Jim Van Verth <jvanverth@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
44f80a24f0
commit
0bd699e497
93
gm/shadowutils.cpp
Normal file
93
gm/shadowutils.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkPath.h"
|
||||
#include "SkShadowUtils.h"
|
||||
|
||||
void draw_shadow(SkCanvas* canvas, const SkPath& path, int height, SkColor color, SkPoint3 lightPos,
|
||||
SkScalar lightR, bool isAmbient, uint32_t flags) {
|
||||
SkScalar ambientAlpha = isAmbient ? .5f : 0.f;
|
||||
SkScalar spotAlpha = isAmbient ? 0.f : .5f;
|
||||
SkShadowUtils::DrawShadow(canvas, path, height, lightPos, lightR, ambientAlpha, spotAlpha,
|
||||
color, flags);
|
||||
}
|
||||
|
||||
static constexpr int kW = 500;
|
||||
static constexpr int kH = 800;
|
||||
|
||||
DEF_SIMPLE_GM(shadow_utils, canvas, kW, kH) {
|
||||
SkTArray<SkPath> paths;
|
||||
paths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10);
|
||||
SkRRect oddRRect;
|
||||
oddRRect.setNinePatch(SkRect::MakeWH(50, 50), 9, 13, 6, 16);
|
||||
paths.push_back().addRRect(oddRRect);
|
||||
paths.push_back().addRect(SkRect::MakeWH(50, 50));
|
||||
paths.push_back().addCircle(25, 25, 25);
|
||||
paths.push_back().cubicTo(100, 50, 20, 100, 0, 0);
|
||||
|
||||
static constexpr SkScalar kPad = 15.f;
|
||||
static constexpr SkPoint3 kLightPos = {kW / 2, kH / 2, 500};
|
||||
static constexpr SkScalar kLightR = 100.f;
|
||||
static constexpr SkScalar kHeight = 50.f;
|
||||
canvas->translate(3 * kPad, 3 * kPad);
|
||||
canvas->save();
|
||||
SkScalar x = 0;
|
||||
SkScalar dy = 0;
|
||||
SkTDArray<SkMatrix> matrices;
|
||||
matrices.push()->reset();
|
||||
SkMatrix* m = matrices.push();
|
||||
m->setRotate(33.f, 25.f, 25.f);
|
||||
m->postScale(1.2f, 0.8f, 25.f, 25.f);
|
||||
for (auto& m : matrices) {
|
||||
for (auto flags : {kNone_ShadowFlag, kTransparentOccluder_ShadowFlag}) {
|
||||
for (const auto& path : paths) {
|
||||
SkRect postMBounds = path.getBounds();
|
||||
m.mapRect(&postMBounds);
|
||||
SkScalar w = postMBounds.width() + kHeight;
|
||||
SkScalar dx = w + kPad;
|
||||
if (x + dx > kW - 3 * kPad) {
|
||||
canvas->restore();
|
||||
canvas->translate(0, dy);
|
||||
canvas->save();
|
||||
x = 0;
|
||||
dy = 0;
|
||||
}
|
||||
|
||||
canvas->save();
|
||||
canvas->concat(m);
|
||||
draw_shadow(canvas, path, kHeight, SK_ColorRED, kLightPos, kLightR, true, flags);
|
||||
draw_shadow(canvas, path, kHeight, SK_ColorBLUE, kLightPos, kLightR, false, flags);
|
||||
|
||||
// Draw the path outline in green on top of the ambient and spot shadows.
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorGREEN);
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
paint.setStrokeWidth(0);
|
||||
canvas->drawPath(path, paint);
|
||||
canvas->restore();
|
||||
|
||||
canvas->translate(dx, 0);
|
||||
x += dx;
|
||||
dy = SkTMax(dy, postMBounds.height() + kPad + kHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Show where the light is in x,y as a circle (specified in device space).
|
||||
SkMatrix invCanvasM = canvas->getTotalMatrix();
|
||||
if (invCanvasM.invert(&invCanvasM)) {
|
||||
canvas->save();
|
||||
canvas->concat(invCanvasM);
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorBLACK);
|
||||
paint.setAntiAlias(true);
|
||||
canvas->drawCircle(kLightPos.fX, kLightPos.fY, kLightR / 10.f, paint);
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
@ -247,6 +247,7 @@ gm_sources = [
|
||||
"$_gm/shadertext3.cpp",
|
||||
"$_gm/shadowmaps.cpp",
|
||||
"$_gm/shadows.cpp",
|
||||
"$_gm/shadowutils.cpp",
|
||||
"$_gm/shallowgradient.cpp",
|
||||
"$_gm/shapes.cpp",
|
||||
"$_gm/showmiplevels.cpp",
|
||||
|
@ -42,11 +42,9 @@ public:
|
||||
break;
|
||||
}
|
||||
if (!args.fGpImplementsDistanceVector) {
|
||||
fragBuilder->codeAppendf("%s = factor*vec4(0.0, 0.0, 0.0, color.g);",
|
||||
args.fOutputColor);
|
||||
fragBuilder->codeAppendf("%s = vec4(factor*color.g);", args.fOutputColor);
|
||||
} else {
|
||||
fragBuilder->codeAppendf("%s = factor*vec4(0.0, 0.0, 0.0, color.a);",
|
||||
args.fOutputColor);
|
||||
fragBuilder->codeAppendf("%s = vec4(factor*color.a);", args.fOutputColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -454,7 +454,11 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path,
|
||||
|
||||
fClipPolygon.setReserve(path.countPoints());
|
||||
this->computeClipBounds(path);
|
||||
fCentroid *= scale;
|
||||
// We are going to apply 'scale' and 'xlate' (in that order) to each computed path point. We
|
||||
// want the effect to be to scale the points relative to the path centroid and then translate
|
||||
// them by the 'translate' param we were passed.
|
||||
SkVector xlate = fCentroid * (1.f - scale) + translate;
|
||||
// Also translate the centroid by the global translate.
|
||||
fCentroid += translate;
|
||||
|
||||
// walk around the path, tessellate and generate inner and outer rings
|
||||
@ -466,16 +470,16 @@ SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path,
|
||||
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
|
||||
switch (verb) {
|
||||
case SkPath::kLine_Verb:
|
||||
this->handleLine(scale, translate, pts[1]);
|
||||
this->handleLine(scale, xlate, pts[1]);
|
||||
break;
|
||||
case SkPath::kQuad_Verb:
|
||||
this->handleQuad(scale, translate, pts);
|
||||
this->handleQuad(scale, xlate, pts);
|
||||
break;
|
||||
case SkPath::kCubic_Verb:
|
||||
this->handleCubic(scale, translate, pts);
|
||||
this->handleCubic(scale, xlate, pts);
|
||||
break;
|
||||
case SkPath::kConic_Verb:
|
||||
this->handleConic(scale, translate, pts, iter.conicWeight());
|
||||
this->handleConic(scale, xlate, pts, iter.conicWeight());
|
||||
break;
|
||||
case SkPath::kMove_Verb:
|
||||
case SkPath::kClose_Verb:
|
||||
|
@ -54,7 +54,8 @@ void SkGaussianColorFilter::filterSpan(const SkPMColor src[], int count, SkPMCol
|
||||
SkScalar factor = SK_Scalar1 - SkGetPackedB32(c) / 255.f;
|
||||
factor = SkScalarExp(-factor * factor * 4) - 0.018f;
|
||||
|
||||
dst[i] = SkPackARGB32(factor*SkGetPackedG32(c), 0, 0, 0);
|
||||
SkScalar a = factor * SkGetPackedG32(c);
|
||||
dst[i] = SkPackARGB32(a, a, a, a);
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,8 +271,11 @@ void draw_shadow(const FACTORY& factory, SkCanvas* canvas, ShadowedPath& path, S
|
||||
}
|
||||
|
||||
SkPaint paint;
|
||||
paint.setColor(color);
|
||||
paint.setColorFilter(SkGaussianColorFilter::Make());
|
||||
// Run the vertex color through a GaussianColorFilter and then modulate the grayscale result of
|
||||
// that against our 'color' param.
|
||||
paint.setColorFilter(SkColorFilter::MakeComposeFilter(
|
||||
SkColorFilter::MakeModeFilter(color, SkBlendMode::kModulate),
|
||||
SkGaussianColorFilter::Make()));
|
||||
if (translate->fX || translate->fY) {
|
||||
canvas->save();
|
||||
canvas->translate(translate->fX, translate->fY);
|
||||
@ -294,12 +298,11 @@ static const float kGeomFactor = 64.0f;
|
||||
|
||||
// Draw an offset spot shadow and outlining ambient shadow for the given path.
|
||||
void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar occluderHeight,
|
||||
const SkPoint3& lightPos, SkScalar lightRadius,
|
||||
const SkPoint3& devLightPos, SkScalar lightRadius,
|
||||
SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color,
|
||||
uint32_t flags) {
|
||||
SkAutoCanvasRestore acr(canvas, true);
|
||||
SkMatrix viewMatrix = canvas->getTotalMatrix();
|
||||
|
||||
canvas->save();
|
||||
canvas->resetMatrix();
|
||||
|
||||
ShadowedPath shadowedPath(&path, &viewMatrix);
|
||||
@ -307,6 +310,7 @@ void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar oc
|
||||
bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
|
||||
|
||||
if (ambientAlpha > 0) {
|
||||
ambientAlpha = SkTMin(ambientAlpha, 1.f);
|
||||
AmbientVerticesFactory factory;
|
||||
factory.fRadius = occluderHeight * kHeightFactor * kGeomFactor;
|
||||
SkScalar umbraAlpha = SkScalarInvert((1.0f + SkTMax(occluderHeight*kHeightFactor, 0.0f)));
|
||||
@ -323,22 +327,22 @@ void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar oc
|
||||
}
|
||||
|
||||
if (spotAlpha > 0) {
|
||||
spotAlpha = SkTMin(spotAlpha, 1.f);
|
||||
SpotVerticesFactory factory;
|
||||
float zRatio = SkTPin(occluderHeight / (lightPos.fZ - occluderHeight), 0.0f, 0.95f);
|
||||
float zRatio = SkTPin(occluderHeight / (devLightPos.fZ - occluderHeight), 0.0f, 0.95f);
|
||||
factory.fRadius = lightRadius * zRatio;
|
||||
|
||||
// Compute the scale and translation for the spot shadow.
|
||||
factory.fScale = lightPos.fZ / (lightPos.fZ - occluderHeight);
|
||||
factory.fScale = devLightPos.fZ / (devLightPos.fZ - occluderHeight);
|
||||
|
||||
SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY());
|
||||
factory.fOffset = SkVector::Make(zRatio * (center.fX - lightPos.fX),
|
||||
zRatio * (center.fY - lightPos.fY));
|
||||
viewMatrix.mapPoints(¢er, 1);
|
||||
factory.fOffset = SkVector::Make(zRatio * (center.fX - devLightPos.fX),
|
||||
zRatio * (center.fY - devLightPos.fY));
|
||||
factory.fUmbraColor = SkColorSetARGB(255, 0, spotAlpha * 255.9999f, 255);
|
||||
factory.fPenumbraColor = SkColorSetARGB(255, 0, spotAlpha * 255.9999f, 0);
|
||||
factory.fTransparent = transparent;
|
||||
|
||||
draw_shadow(factory, canvas, shadowedPath, color);
|
||||
}
|
||||
|
||||
canvas->restore();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user