diff options
-rw-r--r-- | gn/samples.gni | 1 | ||||
-rw-r--r-- | include/private/SkShadowFlags.h | 4 | ||||
-rw-r--r-- | include/utils/SkShadowUtils.h | 36 | ||||
-rw-r--r-- | samplecode/SampleAndroidShadows.cpp | 1 | ||||
-rwxr-xr-x | samplecode/SampleShadowColor.cpp | 223 | ||||
-rw-r--r-- | src/gpu/GrRenderTargetContext.cpp | 27 | ||||
-rw-r--r-- | src/utils/SkShadowUtils.cpp | 31 |
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); } } |