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