• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkSpotShadowMaskFilter.h"
9 #include "SkReadBuffer.h"
10 #include "SkStringUtils.h"
11 #include "SkWriteBuffer.h"
12 
13 #if SK_SUPPORT_GPU
14 #include "GrContext.h"
15 #include "GrRenderTargetContext.h"
16 #include "GrFragmentProcessor.h"
17 #include "GrStyle.h"
18 #include "GrTexture.h"
19 #include "GrTextureProxy.h"
20 #include "SkStrokeRec.h"
21 #endif
22 
23 class SkSpotShadowMaskFilterImpl : public SkMaskFilter {
24 public:
25     SkSpotShadowMaskFilterImpl(SkScalar occluderHeight, const SkPoint3& lightPos,
26                                SkScalar lightRadius, SkScalar spotAlpha, uint32_t flags);
27 
28     // overrides from SkMaskFilter
29     SkMask::Format getFormat() const override;
30     bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
31                     SkIPoint* margin) const override;
32 
33 #if SK_SUPPORT_GPU
34     bool canFilterMaskGPU(const SkRRect& devRRect,
35                           const SkIRect& clipBounds,
36                           const SkMatrix& ctm,
37                           SkRect* maskRect) const override;
38     bool directFilterMaskGPU(GrContext*,
39                              GrRenderTargetContext* drawContext,
40                              GrPaint&&,
41                              const GrClip&,
42                              const SkMatrix& viewMatrix,
43                              const SkStrokeRec& strokeRec,
44                              const SkPath& path) const override;
45     bool directFilterRRectMaskGPU(GrContext*,
46                                   GrRenderTargetContext* drawContext,
47                                   GrPaint&&,
48                                   const GrClip&,
49                                   const SkMatrix& viewMatrix,
50                                   const SkStrokeRec& strokeRec,
51                                   const SkRRect& rrect,
52                                   const SkRRect& devRRect) const override;
53     sk_sp<GrTextureProxy> filterMaskGPU(GrContext*,
54                                         sk_sp<GrTextureProxy> srcProxy,
55                                         const SkMatrix& ctm,
56                                         const SkIRect& maskRect) const override;
57 #endif
58 
59     void computeFastBounds(const SkRect&, SkRect*) const override;
60 
61     SK_TO_STRING_OVERRIDE()
62     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpotShadowMaskFilterImpl)
63 
64 private:
65     SkScalar fOccluderHeight;
66     SkPoint3 fLightPos;
67     SkScalar fLightRadius;
68     SkScalar fSpotAlpha;
69     uint32_t fFlags;
70 
71     SkSpotShadowMaskFilterImpl(SkReadBuffer&);
72     void flatten(SkWriteBuffer&) const override;
73 
74     friend class SkSpotShadowMaskFilter;
75 
76     typedef SkMaskFilter INHERITED;
77 };
78 
Make(SkScalar occluderHeight,const SkPoint3 & lightPos,SkScalar lightRadius,SkScalar spotAlpha,uint32_t flags)79 sk_sp<SkMaskFilter> SkSpotShadowMaskFilter::Make(SkScalar occluderHeight, const SkPoint3& lightPos,
80                                                  SkScalar lightRadius, SkScalar spotAlpha,
81                                                  uint32_t flags) {
82     // add some param checks here for early exit
83 
84     return sk_sp<SkMaskFilter>(new SkSpotShadowMaskFilterImpl(occluderHeight, lightPos,
85                                                               lightRadius, spotAlpha, flags));
86 }
87 
88 ///////////////////////////////////////////////////////////////////////////////////////////////////
89 
SkSpotShadowMaskFilterImpl(SkScalar occluderHeight,const SkPoint3 & lightPos,SkScalar lightRadius,SkScalar spotAlpha,uint32_t flags)90 SkSpotShadowMaskFilterImpl::SkSpotShadowMaskFilterImpl(SkScalar occluderHeight,
91                                                        const SkPoint3& lightPos,
92                                                        SkScalar lightRadius,
93                                                        SkScalar spotAlpha,
94                                                        uint32_t flags)
95     : fOccluderHeight(occluderHeight)
96     , fLightPos(lightPos)
97     , fLightRadius(lightRadius)
98     , fSpotAlpha(spotAlpha)
99     , fFlags(flags) {
100     SkASSERT(fOccluderHeight > 0);
101     SkASSERT(fLightPos.z() > 0 && fLightPos.z() > fOccluderHeight);
102     SkASSERT(fLightRadius > 0);
103     SkASSERT(fSpotAlpha >= 0);
104 }
105 
getFormat() const106 SkMask::Format SkSpotShadowMaskFilterImpl::getFormat() const {
107     return SkMask::kA8_Format;
108 }
109 
filterMask(SkMask * dst,const SkMask & src,const SkMatrix & matrix,SkIPoint * margin) const110 bool SkSpotShadowMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
111                                             const SkMatrix& matrix,
112                                             SkIPoint* margin) const {
113     // TODO something
114     return false;
115 }
116 
computeFastBounds(const SkRect & src,SkRect * dst) const117 void SkSpotShadowMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) const {
118     // TODO compute based on ambient + spot data
119     dst->set(src.fLeft, src.fTop, src.fRight, src.fBottom);
120 }
121 
CreateProc(SkReadBuffer & buffer)122 sk_sp<SkFlattenable> SkSpotShadowMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
123     const SkScalar occluderHeight = buffer.readScalar();
124     const SkScalar lightX = buffer.readScalar();
125     const SkScalar lightY = buffer.readScalar();
126     const SkScalar lightZ = buffer.readScalar();
127     const SkPoint3 lightPos = SkPoint3::Make(lightX, lightY, lightZ);
128     const SkScalar lightRadius = buffer.readScalar();
129     const SkScalar spotAlpha = buffer.readScalar();
130     const uint32_t flags = buffer.readUInt();
131 
132     return SkSpotShadowMaskFilter::Make(occluderHeight, lightPos, lightRadius,
133                                         spotAlpha, flags);
134 }
135 
flatten(SkWriteBuffer & buffer) const136 void SkSpotShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
137     buffer.writeScalar(fOccluderHeight);
138     buffer.writeScalar(fLightPos.fX);
139     buffer.writeScalar(fLightPos.fY);
140     buffer.writeScalar(fLightPos.fZ);
141     buffer.writeScalar(fLightRadius);
142     buffer.writeScalar(fSpotAlpha);
143     buffer.writeUInt(fFlags);
144 }
145 
146 #if SK_SUPPORT_GPU
147 
148 ///////////////////////////////////////////////////////////////////////////////////////////////////
149 
canFilterMaskGPU(const SkRRect & devRRect,const SkIRect & clipBounds,const SkMatrix & ctm,SkRect * maskRect) const150 bool SkSpotShadowMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
151                                                   const SkIRect& clipBounds,
152                                                   const SkMatrix& ctm,
153                                                   SkRect* maskRect) const {
154     // TODO
155     *maskRect = devRRect.rect();
156     return true;
157 }
158 
directFilterMaskGPU(GrContext * context,GrRenderTargetContext * rtContext,GrPaint && paint,const GrClip & clip,const SkMatrix & viewMatrix,const SkStrokeRec & strokeRec,const SkPath & path) const159 bool SkSpotShadowMaskFilterImpl::directFilterMaskGPU(GrContext* context,
160                                                      GrRenderTargetContext* rtContext,
161                                                      GrPaint&& paint,
162                                                      const GrClip& clip,
163                                                      const SkMatrix& viewMatrix,
164                                                      const SkStrokeRec& strokeRec,
165                                                      const SkPath& path) const {
166     SkASSERT(rtContext);
167     // TODO: this will not handle local coordinates properly
168 
169     if (fSpotAlpha <= 0.0f) {
170         return true;
171     }
172 
173     // only convex paths for now
174     if (!path.isConvex()) {
175         return false;
176     }
177 
178     if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) {
179         return false;
180     }
181 
182     // if circle
183     // TODO: switch to SkScalarNearlyEqual when either oval renderer is updated or we
184     // have our own GeometryProc.
185     if (path.isOval(nullptr) && path.getBounds().width() == path.getBounds().height()) {
186         SkRRect rrect = SkRRect::MakeOval(path.getBounds());
187         return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
188                                               SkMatrix::I(), strokeRec, rrect, rrect);
189     } else if (path.isRect(nullptr)) {
190         SkRRect rrect = SkRRect::MakeRect(path.getBounds());
191         return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
192                                               SkMatrix::I(), strokeRec, rrect, rrect);
193     }
194 
195     return false;
196 }
197 
directFilterRRectMaskGPU(GrContext *,GrRenderTargetContext * rtContext,GrPaint && paint,const GrClip & clip,const SkMatrix & viewMatrix,const SkStrokeRec & strokeRec,const SkRRect & rrect,const SkRRect & devRRect) const198 bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
199                                                           GrRenderTargetContext* rtContext,
200                                                           GrPaint&& paint,
201                                                           const GrClip& clip,
202                                                           const SkMatrix& viewMatrix,
203                                                           const SkStrokeRec& strokeRec,
204                                                           const SkRRect& rrect,
205                                                           const SkRRect& devRRect) const {
206     // It's likely the caller has already done these checks, but we have to be sure.
207     // TODO: support analytic blurring of general rrect
208 
209     // Fast path only supports filled rrects for now.
210     // TODO: fill and stroke as well.
211     if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
212         return false;
213     }
214     // Fast path only supports simple rrects with circular corners.
215     SkASSERT(devRRect.allCornersCircular());
216     if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
217         return false;
218     }
219     // Fast path only supports uniform scale.
220     SkScalar scaleFactors[2];
221     if (!viewMatrix.getMinMaxScales(scaleFactors)) {
222         // matrix is degenerate
223         return false;
224     }
225     if (scaleFactors[0] != scaleFactors[1]) {
226         return false;
227     }
228     SkScalar scaleFactor = scaleFactors[0];
229 
230     // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
231     const SkScalar minRadius = 0.5f / scaleFactor;
232     bool isRect = rrect.getSimpleRadii().fX <= minRadius;
233 
234     // TODO: take flags into account when generating shadow data
235 
236     if (fSpotAlpha > 0.0f) {
237         float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f);
238 
239         SkScalar srcSpaceSpotRadius = 2.0f * fLightRadius * zRatio;
240 
241         SkRRect spotRRect;
242         if (isRect) {
243             spotRRect = SkRRect::MakeRectXY(rrect.rect(), minRadius, minRadius);
244         } else {
245             spotRRect = rrect;
246         }
247 
248         SkRRect spotShadowRRect;
249         // Compute the scale and translation for the spot shadow.
250         const SkScalar scale = fLightPos.fZ / (fLightPos.fZ - fOccluderHeight);
251         spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
252 
253         SkPoint center = SkPoint::Make(spotShadowRRect.rect().centerX(),
254                                        spotShadowRRect.rect().centerY());
255         SkMatrix ctmInverse;
256         if (!viewMatrix.invert(&ctmInverse)) {
257             SkDebugf("Matrix is degenerate. Will not render spot shadow!\n");
258             //**** TODO: this is not good
259             return true;
260         }
261         SkPoint lightPos2D = SkPoint::Make(fLightPos.fX, fLightPos.fY);
262         ctmInverse.mapPoints(&lightPos2D, 1);
263         const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
264                                                  zRatio*(center.fY - lightPos2D.fY));
265 
266         // We want to extend the stroked area in so that it meets up with the caster
267         // geometry. The stroked geometry will, by definition already be inset half the
268         // stroke width but we also have to account for the scaling.
269         SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(rrect.rect().fLeft),
270                                                               SkTAbs(rrect.rect().fRight)),
271                                                        SkTMax(SkTAbs(rrect.rect().fTop),
272                                                               SkTAbs(rrect.rect().fBottom)));
273         SkScalar insetAmount = spotOffset.length() - (0.5f * srcSpaceSpotRadius) + scaleOffset;
274 
275         // Compute area
276         SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount;
277         SkScalar strokedArea = 2.0f*strokeWidth *
278                                (spotShadowRRect.width() + spotShadowRRect.height());
279         SkScalar filledArea = (spotShadowRRect.height() + srcSpaceSpotRadius) *
280                               (spotShadowRRect.width() + srcSpaceSpotRadius);
281 
282         GrColor4f color = paint.getColor4f();
283         color.fRGBA[3] *= fSpotAlpha;
284         paint.setColor4f(color);
285 
286         SkStrokeRec spotStrokeRec(SkStrokeRec::kFill_InitStyle);
287         // If the area of the stroked geometry is larger than the fill geometry,
288         // or if the caster is transparent, just fill it.
289         if (strokedArea > filledArea ||
290             fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag) {
291             spotStrokeRec.setStrokeStyle(srcSpaceSpotRadius, true);
292         } else {
293             // Since we can't have unequal strokes, inset the shadow rect so the inner
294             // and outer edges of the stroke will land where we want.
295             SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount / 2.0f,
296                                                                 insetAmount / 2.0f);
297             SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount / 2.0f,
298                                        minRadius);
299             spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
300             spotStrokeRec.setStrokeStyle(strokeWidth, false);
301         }
302 
303         // handle scale of radius and pad due to CTM
304         const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
305 
306         spotShadowRRect.offset(spotOffset.fX, spotOffset.fY);
307 
308         rtContext->drawShadowRRect(clip, std::move(paint), viewMatrix, spotShadowRRect,
309                                    devSpaceSpotRadius, GrStyle(spotStrokeRec, nullptr));
310     }
311 
312     return true;
313 }
314 
filterMaskGPU(GrContext *,sk_sp<GrTextureProxy> srcProxy,const SkMatrix & ctm,const SkIRect & maskRect) const315 sk_sp<GrTextureProxy> SkSpotShadowMaskFilterImpl::filterMaskGPU(GrContext*,
316                                                                 sk_sp<GrTextureProxy> srcProxy,
317                                                                 const SkMatrix& ctm,
318                                                                 const SkIRect& maskRect) const {
319     // This filter is generative and doesn't operate on pre-existing masks
320     return nullptr;
321 }
322 
323 #endif
324 
325 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const326 void SkSpotShadowMaskFilterImpl::toString(SkString* str) const {
327     str->append("SkSpotShadowMaskFilterImpl: (");
328 
329     str->append("occluderHeight: ");
330     str->appendScalar(fOccluderHeight);
331     str->append(" ");
332 
333     str->append("lightPos: (");
334     str->appendScalar(fLightPos.fX);
335     str->append(", ");
336     str->appendScalar(fLightPos.fY);
337     str->append(", ");
338     str->appendScalar(fLightPos.fZ);
339     str->append(") ");
340 
341     str->append("lightRadius: ");
342     str->appendScalar(fLightRadius);
343     str->append(" ");
344 
345     str->append("spotAlpha: ");
346     str->appendScalar(fSpotAlpha);
347     str->append(" ");
348 
349     str->append("flags: (");
350     if (fFlags) {
351         bool needSeparator = false;
352         SkAddFlagToString(str,
353                           SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
354                           "TransparentOccluder", &needSeparator);
355         SkAddFlagToString(str,
356                           SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
357                           "GaussianEdge", &needSeparator);
358         SkAddFlagToString(str,
359                           SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
360                           "LargerUmbra", &needSeparator);
361     } else {
362         str->append("None");
363     }
364     str->append("))");
365 }
366 #endif
367 
368 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkSpotShadowMaskFilter)
369 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSpotShadowMaskFilterImpl)
370 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
371