aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gn/samples.gni1
-rw-r--r--include/private/SkShadowFlags.h4
-rw-r--r--include/utils/SkShadowUtils.h36
-rw-r--r--samplecode/SampleAndroidShadows.cpp1
-rwxr-xr-xsamplecode/SampleShadowColor.cpp223
-rw-r--r--src/gpu/GrRenderTargetContext.cpp27
-rw-r--r--src/utils/SkShadowUtils.cpp31
7 files changed, 315 insertions, 8 deletions
diff --git a/gn/samples.gni b/gn/samples.gni
index 4a503136b9..80e0663bf5 100644
--- a/gn/samples.gni
+++ b/gn/samples.gni
@@ -81,6 +81,7 @@ samples_sources = [
"$_samplecode/SampleRepeatTile.cpp",
"$_samplecode/SampleShaders.cpp",
"$_samplecode/SampleShaderText.cpp",
+ "$_samplecode/SampleShadowColor.cpp",
"$_samplecode/SampleShadowReference.cpp",
"$_samplecode/SampleShadowUtils.cpp",
"$_samplecode/SampleShip.cpp",
diff --git a/include/private/SkShadowFlags.h b/include/private/SkShadowFlags.h
index 8caf632988..185bfada6a 100644
--- a/include/private/SkShadowFlags.h
+++ b/include/private/SkShadowFlags.h
@@ -16,8 +16,10 @@ enum SkShadowFlags {
kTransparentOccluder_ShadowFlag = 0x01,
/** Don't try to use analytic shadows. */
kGeometricOnly_ShadowFlag = 0x02,
+ /** Use tonal values when applying color */
+ kTonalColor_ShadowFlag = 0x04,
/** mask for all shadow flags */
- kAll_ShadowFlag = 0x03
+ kAll_ShadowFlag = 0x07
};
#endif
diff --git a/include/utils/SkShadowUtils.h b/include/utils/SkShadowUtils.h
index 838a12bba8..0ac2f3708d 100644
--- a/include/utils/SkShadowUtils.h
+++ b/include/utils/SkShadowUtils.h
@@ -69,6 +69,42 @@ public:
DrawShadow(canvas, path, zPlane, lightPos, lightRadius, ambientAlpha, spotAlpha,
color, flags);
}
+
+ /**
+ * Helper routine to compute scale alpha values for one-pass tonal alpha.
+ *
+ * The final color we want to emulate is generated by rendering a color shadow (C_rgb) using an
+ * alpha computed from the color's luminance (C_a), and then a black shadow with alpha (S_a)
+ * which is an adjusted value of 'a'. Assuming SrcOver, a background color of B_rgb, and
+ * ignoring edge falloff, this becomes
+ *
+ * (C_a - S_a*C_a)*C_rgb + (1 - (S_a + C_a - S_a*C_a))*B_rgb
+ *
+ * Since we use premultiplied alpha, this means we can scale the color by (C_a - S_a*C_a) and
+ * set the alpha to (S_a + C_a - S_a*C_a).
+ *
+ * @param r Red value of color
+ * @param g Red value of color
+ * @param b Red value of color
+ * @param a Red value of color
+ * @param colorScale Factor to scale color values by
+ * @param tonalAlpha Value to set alpha to
+ */
+ static inline void ComputeTonalColorParams(SkScalar r, SkScalar g, SkScalar b, SkScalar a,
+ SkScalar* colorScale, SkScalar* tonalAlpha) {
+ SkScalar max = SkTMax(SkTMax(r, g), b);
+ SkScalar min = SkTMin(SkTMin(r, g), b);
+ SkScalar luminance = 0.5f*(max + min);
+
+ // We get best results with a luminance between 0.3 and 0.5, with smoothstep applied
+ SkScalar adjustedLuminance = (0.6f - 0.4f*luminance)*luminance*luminance + 0.3f;
+ // Similarly, we need to tone down the given greyscale alpha depending on how
+ // much color we're applying.
+ a -= (0.5f*adjustedLuminance - 0.15f);
+
+ *colorScale = adjustedLuminance*(SK_Scalar1 - a);
+ *tonalAlpha = *colorScale + a;
+ }
};
#endif
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp
index 0f133404e8..f15df9fe37 100644
--- a/samplecode/SampleAndroidShadows.cpp
+++ b/samplecode/SampleAndroidShadows.cpp
@@ -144,6 +144,7 @@ protected:
if (fUseAlt) {
flags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
}
+
SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
lightPos, lightWidth,
ambientAlpha, spotAlpha, SK_ColorBLACK, flags);
diff --git a/samplecode/SampleShadowColor.cpp b/samplecode/SampleShadowColor.cpp
new file mode 100755
index 0000000000..8a7e462c0d
--- /dev/null
+++ b/samplecode/SampleShadowColor.cpp
@@ -0,0 +1,223 @@
+
+/*
+ * 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 "SampleCode.h"
+#include "Resources.h"
+#include "SkCanvas.h"
+#include "SkImage.h"
+#include "SkPath.h"
+#include "SkPoint3.h"
+#include "SkShadowUtils.h"
+
+////////////////////////////////////////////////////////////////////////////
+// Sample to demonstrate tonal color shadows
+
+class ShadowColorView : public SampleView {
+ SkPath fRectPath;
+ int fZIndex;
+
+ bool fShowAmbient;
+ bool fShowSpot;
+ bool fUseAlt;
+ bool fShowObject;
+ bool fTwoPassColor;
+ bool fDarkBackground;
+
+public:
+ ShadowColorView()
+ : fZIndex(8)
+ , fShowAmbient(true)
+ , fShowSpot(true)
+ , fUseAlt(false)
+ , fShowObject(true)
+ , fTwoPassColor(false)
+ , fDarkBackground(false) {}
+
+protected:
+ void onOnceBeforeDraw() override {
+ fRectPath.addRect(SkRect::MakeXYWH(-50, -50, 100, 100));
+ }
+
+ // overrides from SkEventSink
+ bool onQuery(SkEvent* evt) override {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "ShadowColor");
+ return true;
+ }
+
+ SkUnichar uni;
+ if (SampleCode::CharQ(*evt, &uni)) {
+ bool handled = false;
+ switch (uni) {
+ case 'W':
+ fShowAmbient = !fShowAmbient;
+ handled = true;
+ break;
+ case 'S':
+ fShowSpot = !fShowSpot;
+ handled = true;
+ break;
+ case 'T':
+ fUseAlt = !fUseAlt;
+ handled = true;
+ break;
+ case 'O':
+ fShowObject = !fShowObject;
+ handled = true;
+ break;
+ case 'X':
+ fTwoPassColor = !fTwoPassColor;
+ handled = true;
+ break;
+ case 'Z':
+ fDarkBackground = !fDarkBackground;
+ handled = true;
+ break;
+ case '>':
+ fZIndex = SkTMin(9, fZIndex+1);
+ handled = true;
+ break;
+ case '<':
+ fZIndex = SkTMax(0, fZIndex-1);
+ handled = true;
+ break;
+ default:
+ break;
+ }
+ if (handled) {
+ this->inval(nullptr);
+ return true;
+ }
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawShadowedPath(SkCanvas* canvas, const SkPath& path,
+ const SkPoint3& zPlaneParams,
+ const SkPaint& paint, SkScalar ambientAlpha,
+ const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha) {
+ if (!fShowAmbient) {
+ ambientAlpha = 0;
+ }
+ if (!fShowSpot) {
+ spotAlpha = 0;
+ }
+ uint32_t flags = 0;
+ if (fUseAlt) {
+ flags |= SkShadowFlags::kGeometricOnly_ShadowFlag;
+ }
+
+ if (fTwoPassColor) {
+ SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
+ lightPos, lightWidth,
+ ambientAlpha, 0, SK_ColorBLACK, flags);
+
+ if (paint.getColor() != SK_ColorBLACK) {
+ SkColor color = paint.getColor();
+
+ uint8_t max = SkTMax(SkTMax(SkColorGetR(color), SkColorGetG(color)),
+ SkColorGetB(color));
+ uint8_t min = SkTMin(SkTMin(SkColorGetR(color), SkColorGetG(color)),
+ SkColorGetB(color));
+ SkScalar luminance = 0.5f*(max + min) / 255.f;
+ SkScalar alpha = (.6 - .4*luminance)*luminance*luminance + 0.3f;
+ spotAlpha -= (alpha - 0.3f)*.5f;
+
+ SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
+ lightPos, lightWidth,
+ 0, alpha, paint.getColor(), flags);
+ }
+
+ SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
+ lightPos, lightWidth,
+ 0, spotAlpha, SK_ColorBLACK, flags);
+ } else {
+ flags |= SkShadowFlags::kTonalColor_ShadowFlag;
+ SkShadowUtils::DrawShadow(canvas, path, zPlaneParams,
+ lightPos, lightWidth,
+ ambientAlpha, spotAlpha, paint.getColor(), flags);
+ }
+ if (fShowObject) {
+ canvas->drawPath(path, paint);
+ } else {
+ SkPaint strokePaint;
+
+ strokePaint.setColor(paint.getColor());
+ strokePaint.setStyle(SkPaint::kStroke_Style);
+
+ canvas->drawPath(path, strokePaint);
+ }
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ const SkScalar kLightWidth = 600;
+ const SkScalar kAmbientAlpha = 0.03f;
+ const SkScalar kSpotAlpha = 0.25f;
+
+ const SkScalar kZValues[10] = { 1, 2, 3, 4, 6, 8, 9, 12, 16, 24 };
+
+ const SkColor kColors[30] = {
+ // purples
+ 0xFF3A0072, 0xFF5D0099, 0xFF7F12B2, 0xFFA02AD1, 0xFFC245E5,
+ 0xFFE95AF9, 0xFFFC79F0, 0xFFFDA6F0, 0xFFFFCCF8, 0xFFFFE1F9,
+ // oranges
+ 0xFFEA3200, 0xFFFF4E00, 0xFFFF7300, 0xFFFF9100, 0xFFFFB000,
+ 0xFFFFCE00, 0xFFFFE000, 0xFFFFF64D, 0xFFFFF98F, 0xFFFFFBCC,
+ // teals
+ 0xFF004D51, 0xFF066266, 0xFF057F7F, 0xFF009999, 0xFF00B2B2,
+ 0xFF15CCBE, 0xFF25E5CE, 0xFF2CFFE0, 0xFF80FFEA, 0xFFB3FFF0
+ };
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ if (fDarkBackground) {
+ canvas->drawColor(0xFF111111);
+ paint.setColor(SK_ColorWHITE);
+ } else {
+ canvas->drawColor(0xFFEAEAEA);
+ paint.setColor(SK_ColorBLACK);
+ }
+
+ if (fTwoPassColor) {
+ canvas->drawText("Two pass", 8, 10, 15, paint);
+ } else {
+ canvas->drawText("One pass", 8, 10, 15, paint);
+ }
+
+ SkPoint3 lightPos = { 75, -400, 600 };
+ SkPoint3 zPlaneParams = SkPoint3::Make(0, 0, kZValues[fZIndex]);
+ SkScalar yPos = 75;
+
+ for (int row = 0; row < 3; ++row) {
+ lightPos.fX = 75;
+ SkScalar xPos = 75;
+ for (int col = 0; col < 10; ++col) {
+ paint.setColor(kColors[10*row + col]);
+
+ canvas->save();
+ canvas->translate(xPos, yPos);
+ this->drawShadowedPath(canvas, fRectPath, zPlaneParams, paint, kAmbientAlpha,
+ lightPos, kLightWidth, kSpotAlpha);
+ canvas->restore();
+
+ lightPos.fX += 120;
+ xPos += 120;
+ }
+
+ lightPos.fY += 200;
+ yPos += 200;
+ }
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ShadowColorView; }
+static SkViewRegister reg(MyFactory);
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 0bbfbf65b3..8480ffac40 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -24,6 +24,7 @@
#include "SkDrawShadowRec.h"
#include "SkLatticeIter.h"
#include "SkMatrixPriv.h"
+#include "SkShadowUtils.h"
#include "SkSurfacePriv.h"
#include "effects/GrRRectEffect.h"
#include "instanced/InstancedRendering.h"
@@ -1012,6 +1013,7 @@ bool GrRenderTargetContext::drawFastShadow(const GrClip& clip,
SkScalar occluderHeight = rec.fZPlaneParams.fZ;
GrColor4f color = paint.getColor4f();
bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
+ bool tonalColor = SkToBool(rec.fFlags & SkShadowFlags::kTonalColor_ShadowFlag);
if (rec.fAmbientAlpha > 0) {
static constexpr float kHeightFactor = 1.0f / 128.0f;
@@ -1034,7 +1036,13 @@ bool GrRenderTargetContext::drawFastShadow(const GrClip& clip,
ambientRRect = SkRRect::MakeRectXY(outsetRect, outsetRad, outsetRad);
}
- GrColor ambientColor = color.mulByScalar(rec.fAmbientAlpha).toGrColor();
+ GrColor ambientColor;
+ if (tonalColor) {
+ // with tonal color, the color only applies to the spot shadow
+ ambientColor = GrColorPackRGBA(0, 0, 0, 255.999f*rec.fAmbientAlpha);
+ } else {
+ ambientColor = color.mulByScalar(rec.fAmbientAlpha).toGrColor();
+ }
if (transparent) {
// set a large inset to force a fill
devSpaceInsetWidth = ambientRRect.width();
@@ -1139,7 +1147,22 @@ bool GrRenderTargetContext::drawFastShadow(const GrClip& clip,
spotShadowRRect = SkRRect::MakeRectXY(outsetRect, outsetRad, outsetRad);
}
- GrColor spotColor = color.mulByScalar(rec.fSpotAlpha).toGrColor();
+ GrColor spotColor;
+ if (tonalColor) {
+ SkScalar colorScale;
+ SkScalar tonalAlpha;
+ SkShadowUtils::ComputeTonalColorParams(color.fRGBA[0], color.fRGBA[1],
+ color.fRGBA[2], rec.fSpotAlpha,
+ &colorScale, &tonalAlpha);
+ color.fRGBA[0] *= colorScale;
+ color.fRGBA[1] *= colorScale;
+ color.fRGBA[2] *= colorScale;
+ color.fRGBA[3] = tonalAlpha;
+ spotColor = color.toGrColor();
+ } else {
+ spotColor = color.mulByScalar(rec.fSpotAlpha).toGrColor();
+ }
+
std::unique_ptr<GrLegacyMeshDrawOp> op = GrShadowRRectOp::Make(spotColor, viewMatrix,
spotShadowRRect,
devSpaceSpotBlur,
diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp
index 4b5ed5f168..c5b04f311b 100644
--- a/src/utils/SkShadowUtils.cpp
+++ b/src/utils/SkShadowUtils.cpp
@@ -528,7 +528,24 @@ static SkPoint3 map(const SkMatrix& m, const SkPoint3& pt) {
return result;
}
-static SkColor compute_render_color(SkColor color, float alpha) {
+static SkColor compute_render_color(SkColor color, float alpha, bool useTonalColor) {
+ if (useTonalColor) {
+ SkScalar colorScale;
+ SkScalar tonalAlpha;
+ SkColor4f color4f = SkColor4f::FromColor(color);
+ SkShadowUtils::ComputeTonalColorParams(color4f.fR,
+ color4f.fG,
+ color4f.fB,
+ alpha,
+ &colorScale, &tonalAlpha);
+ // After pre-multiplying, we want the alpha to be scaled by tonalAlpha, and
+ // the color scaled by colorScale. This scale factor gives that.
+ SkScalar unPremulScale = colorScale / tonalAlpha;
+
+ return SkColorSetARGB(tonalAlpha*255.999f, unPremulScale*SkColorGetR(color),
+ unPremulScale*SkColorGetG(color), unPremulScale*SkColorGetB(color));
+ }
+
return SkColorSetARGB(alpha*SkColorGetA(color), SkColorGetR(color),
SkColorGetG(color), SkColorGetB(color));
}
@@ -572,6 +589,7 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
bool tiltZPlane = tilted(rec.fZPlaneParams);
bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
bool uncached = tiltZPlane || path.isVolatile();
+ bool useTonalColor = SkToBool(rec.fFlags & kTonalColor_ShadowFlag);
SkColor color = rec.fColor;
SkPoint3 zPlaneParams = rec.fZPlaneParams;
@@ -581,11 +599,16 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
float ambientAlpha = rec.fAmbientAlpha;
if (ambientAlpha > 0) {
ambientAlpha = SkTMin(ambientAlpha, 1.f);
+ SkColor renderColor;
+ if (useTonalColor) {
+ renderColor = compute_render_color(SK_ColorBLACK, ambientAlpha, false);
+ } else {
+ renderColor = compute_render_color(color, ambientAlpha, false);
+ }
if (uncached) {
sk_sp<SkVertices> vertices = SkShadowTessellator::MakeAmbient(path, viewMatrix,
zPlaneParams,
transparent);
- SkColor renderColor = compute_render_color(color, ambientAlpha);
SkPaint paint;
// Run the vertex color through a GaussianColorFilter and then modulate the grayscale
// result of that against our 'color' param.
@@ -604,7 +627,6 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
factory.fOffset.fY = viewMatrix.getTranslateY();
}
- SkColor renderColor = compute_render_color(color, ambientAlpha);
draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
}
}
@@ -612,12 +634,12 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
float spotAlpha = rec.fSpotAlpha;
if (spotAlpha > 0) {
spotAlpha = SkTMin(spotAlpha, 1.f);
+ SkColor renderColor = compute_render_color(color, spotAlpha, useTonalColor);
if (uncached) {
sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix,
zPlaneParams,
devLightPos, lightRadius,
transparent);
- SkColor renderColor = compute_render_color(color, spotAlpha);
SkPaint paint;
// Run the vertex color through a GaussianColorFilter and then modulate the grayscale
// result of that against our 'color' param.
@@ -671,7 +693,6 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
break;
}
#endif
- SkColor renderColor = compute_render_color(color, spotAlpha);
draw_shadow(factory, drawVertsProc, shadowedPath, renderColor);
}
}