SkShadowUtils.cpp revision efe3dedbb3493b738abdb56041b093245e4e8711
1/* 2* Copyright 2017 Google Inc. 3* 4* Use of this source code is governed by a BSD-style license that can be 5* found in the LICENSE file. 6*/ 7 8#include "SkShadowUtils.h" 9#include "SkCanvas.h" 10#include "SkColorFilter.h" 11#include "SkPath.h" 12#include "SkShadowTessellator.h" 13 14/** 15* Gaussian color filter -- produces a Gaussian ramp based on the color's B value, 16* then blends with the color's G value. 17* Final result is black with alpha of Gaussian(B)*G. 18* The assumption is that the original color's alpha is 1. 19*/ 20class SK_API SkGaussianColorFilter : public SkColorFilter { 21public: 22 static sk_sp<SkColorFilter> Make() { 23 return sk_sp<SkColorFilter>(new SkGaussianColorFilter); 24 } 25 26 void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override; 27 28#if SK_SUPPORT_GPU 29 sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*, SkColorSpace*) const override; 30#endif 31 32 SK_TO_STRING_OVERRIDE() 33 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkGaussianColorFilter) 34 35protected: 36 void flatten(SkWriteBuffer&) const override {} 37 38private: 39 SkGaussianColorFilter() : INHERITED() {} 40 41 typedef SkColorFilter INHERITED; 42}; 43 44void SkGaussianColorFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const { 45 for (int i = 0; i < count; ++i) { 46 SkPMColor c = src[i]; 47 48 SkScalar factor = SK_Scalar1 - SkGetPackedB32(c) / 255.f; 49 factor = SkScalarExp(-factor * factor * 4) - 0.018f; 50 51 dst[i] = SkPackARGB32(factor*SkGetPackedG32(c), 0, 0, 0); 52 } 53} 54 55sk_sp<SkFlattenable> SkGaussianColorFilter::CreateProc(SkReadBuffer&) { 56 return Make(); 57} 58 59#ifndef SK_IGNORE_TO_STRING 60void SkGaussianColorFilter::toString(SkString* str) const { 61 str->append("SkGaussianColorFilter "); 62} 63#endif 64 65#if SK_SUPPORT_GPU 66#include "effects/GrBlurredEdgeFragmentProcessor.h" 67 68sk_sp<GrFragmentProcessor> SkGaussianColorFilter::asFragmentProcessor(GrContext*, 69 SkColorSpace*) const { 70 return GrBlurredEdgeFP::Make(GrBlurredEdgeFP::kGaussian_Mode); 71} 72#endif 73 74/////////////////////////////////////////////////////////////////////////////////////////////////// 75static const float kHeightFactor = 1.0f / 128.0f; 76static const float kGeomFactor = 64.0f; 77 78// Draw an offset spot shadow and outlining ambient shadow for the given path. 79void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, SkScalar occluderHeight, 80 const SkPoint3& lightPos, SkScalar lightRadius, 81 SkScalar ambientAlpha, SkScalar spotAlpha, SkColor color, 82 uint32_t flags) { 83 84 SkPath xformedPath; 85 // TODO: handle transforming the path as part of the tessellator 86 path.transform(canvas->getTotalMatrix(), &xformedPath); 87 canvas->save(); 88 canvas->resetMatrix(); 89 90 if (ambientAlpha > 0) { 91 SkScalar radius = occluderHeight * kHeightFactor * kGeomFactor; 92 SkScalar umbraAlpha = SkScalarInvert((1.0f + SkTMax(occluderHeight*kHeightFactor, 0.0f))); 93 // umbraColor is the interior value, penumbraColor the exterior value. 94 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and 95 // then "blurred" by the GrBlurredEdgeFP. It is then multiplied by fAmbientAlpha to get 96 // the final alpha. 97 SkColor umbraColor = SkColorSetARGB(255, 0, ambientAlpha*255.9999f, umbraAlpha*255.9999f); 98 SkColor penumbraColor = SkColorSetARGB(255, 0, ambientAlpha*255.9999f, 0); 99 100 SkAmbientShadowTessellator tess(xformedPath, radius, umbraColor, penumbraColor, 101 SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag)); 102 103 SkPaint paint; 104 paint.setColor(color); 105 paint.setColorFilter(SkGaussianColorFilter::Make()); 106 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, tess.vertexCount(), tess.positions(), 107 nullptr, tess.colors(), tess.indices(), tess.indexCount(), paint); 108 } 109 110 if (spotAlpha > 0) { 111 float zRatio = SkTPin(occluderHeight / (lightPos.fZ - occluderHeight), 0.0f, 0.95f); 112 SkScalar radius = lightRadius * zRatio; 113 114 // Compute the scale and translation for the spot shadow. 115 const SkScalar scale = lightPos.fZ / (lightPos.fZ - occluderHeight); 116 117 SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY()); 118 const SkVector spotOffset = SkVector::Make(zRatio*(center.fX - lightPos.fX), 119 zRatio*(center.fY - lightPos.fY)); 120 121 SkColor umbraColor = SkColorSetARGB(255, 0, spotAlpha*255.9999f, 255); 122 SkColor penumbraColor = SkColorSetARGB(255, 0, spotAlpha*255.9999f, 0); 123 SkSpotShadowTessellator tess(xformedPath, scale, spotOffset, radius, 124 umbraColor, penumbraColor, 125 SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag)); 126 127 SkPaint paint; 128 paint.setColor(color); 129 paint.setColorFilter(SkGaussianColorFilter::Make()); 130 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, tess.vertexCount(), tess.positions(), 131 nullptr, tess.colors(), tess.indices(), tess.indexCount(), paint); 132 } 133 134 canvas->restore(); 135} 136