• 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 "SkAmbientShadowMaskFilter.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 SkAmbientShadowMaskFilterImpl : public SkMaskFilter {
24 public:
25     SkAmbientShadowMaskFilterImpl(SkScalar occluderHeight, SkScalar ambientAlpha, uint32_t flags);
26 
27     // overrides from SkMaskFilter
28     SkMask::Format getFormat() const override;
29     bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
30                     SkIPoint* margin) const override;
31 
32 #if SK_SUPPORT_GPU
33     bool canFilterMaskGPU(const SkRRect& devRRect,
34                           const SkIRect& clipBounds,
35                           const SkMatrix& ctm,
36                           SkRect* maskRect) const override;
37     bool directFilterMaskGPU(GrContext*,
38                              GrRenderTargetContext* drawContext,
39                              GrPaint&&,
40                              const GrClip&,
41                              const SkMatrix& viewMatrix,
42                              const SkStrokeRec& strokeRec,
43                              const SkPath& path) const override;
44     bool directFilterRRectMaskGPU(GrContext*,
45                                   GrRenderTargetContext* drawContext,
46                                   GrPaint&&,
47                                   const GrClip&,
48                                   const SkMatrix& viewMatrix,
49                                   const SkStrokeRec& strokeRec,
50                                   const SkRRect& rrect,
51                                   const SkRRect& devRRect) const override;
52     sk_sp<GrTextureProxy> filterMaskGPU(GrContext*,
53                                         sk_sp<GrTextureProxy> srcProxy,
54                                         const SkMatrix& ctm,
55                                         const SkIRect& maskRect) const override;
56 #endif
57 
58     void computeFastBounds(const SkRect&, SkRect*) const override;
59 
60     SK_TO_STRING_OVERRIDE()
61     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAmbientShadowMaskFilterImpl)
62 
63 private:
64     SkScalar fOccluderHeight;
65     SkScalar fAmbientAlpha;
66     uint32_t fFlags;
67 
68     SkAmbientShadowMaskFilterImpl(SkReadBuffer&);
69     void flatten(SkWriteBuffer&) const override;
70 
71     friend class SkAmbientShadowMaskFilter;
72 
73     typedef SkMaskFilter INHERITED;
74 };
75 
Make(SkScalar occluderHeight,SkScalar ambientAlpha,uint32_t flags)76 sk_sp<SkMaskFilter> SkAmbientShadowMaskFilter::Make(SkScalar occluderHeight, SkScalar ambientAlpha,
77                                                     uint32_t flags) {
78     // add some param checks here for early exit
79 
80     return sk_sp<SkMaskFilter>(new SkAmbientShadowMaskFilterImpl(occluderHeight, ambientAlpha,
81                                                                  flags));
82 }
83 
84 ///////////////////////////////////////////////////////////////////////////////////////////////////
85 
SkAmbientShadowMaskFilterImpl(SkScalar occluderHeight,SkScalar ambientAlpha,uint32_t flags)86 SkAmbientShadowMaskFilterImpl::SkAmbientShadowMaskFilterImpl(SkScalar occluderHeight,
87                                                              SkScalar ambientAlpha,
88                                                              uint32_t flags)
89     : fOccluderHeight(occluderHeight)
90     , fAmbientAlpha(ambientAlpha)
91     , fFlags(flags) {
92     SkASSERT(fOccluderHeight > 0);
93     SkASSERT(fAmbientAlpha >= 0);
94 }
95 
getFormat() const96 SkMask::Format SkAmbientShadowMaskFilterImpl::getFormat() const {
97     return SkMask::kA8_Format;
98 }
99 
filterMask(SkMask * dst,const SkMask & src,const SkMatrix & matrix,SkIPoint * margin) const100 bool SkAmbientShadowMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
101                                                const SkMatrix& matrix,
102                                                SkIPoint* margin) const {
103     // TODO something
104     return false;
105 }
106 
computeFastBounds(const SkRect & src,SkRect * dst) const107 void SkAmbientShadowMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) const {
108     // TODO compute based on ambient data
109     dst->set(src.fLeft, src.fTop, src.fRight, src.fBottom);
110 }
111 
CreateProc(SkReadBuffer & buffer)112 sk_sp<SkFlattenable> SkAmbientShadowMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
113     const SkScalar occluderHeight = buffer.readScalar();
114     const SkScalar ambientAlpha = buffer.readScalar();
115     const uint32_t flags = buffer.readUInt();
116 
117     return SkAmbientShadowMaskFilter::Make(occluderHeight, ambientAlpha, flags);
118 }
119 
flatten(SkWriteBuffer & buffer) const120 void SkAmbientShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
121     buffer.writeScalar(fOccluderHeight);
122     buffer.writeScalar(fAmbientAlpha);
123     buffer.writeUInt(fFlags);
124 }
125 
126 #if SK_SUPPORT_GPU
127 
128 ///////////////////////////////////////////////////////////////////////////////////////////////////
129 
canFilterMaskGPU(const SkRRect & devRRect,const SkIRect & clipBounds,const SkMatrix & ctm,SkRect * maskRect) const130 bool SkAmbientShadowMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
131                                                      const SkIRect& clipBounds,
132                                                      const SkMatrix& ctm,
133                                                      SkRect* maskRect) const {
134     // TODO
135     *maskRect = devRRect.rect();
136     return true;
137 }
138 
139 static const float kHeightFactor = 1.0f / 128.0f;
140 static const float kGeomFactor = 64.0f;
141 
directFilterMaskGPU(GrContext * context,GrRenderTargetContext * rtContext,GrPaint && paint,const GrClip & clip,const SkMatrix & viewMatrix,const SkStrokeRec & strokeRec,const SkPath & path) const142 bool SkAmbientShadowMaskFilterImpl::directFilterMaskGPU(GrContext* context,
143                                                         GrRenderTargetContext* rtContext,
144                                                         GrPaint&& paint,
145                                                         const GrClip& clip,
146                                                         const SkMatrix& viewMatrix,
147                                                         const SkStrokeRec& strokeRec,
148                                                         const SkPath& path) const {
149     SkASSERT(rtContext);
150     // TODO: this will not handle local coordinates properly
151 
152     if (fAmbientAlpha <= 0.0f) {
153         return true;
154     }
155 
156     // only convex paths for now
157     if (!path.isConvex()) {
158         return false;
159     }
160 
161     if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) {
162         return false;
163     }
164 
165     // if circle
166     // TODO: switch to SkScalarNearlyEqual when either oval renderer is updated or we
167     // have our own GeometryProc.
168     if (path.isOval(nullptr) && path.getBounds().width() == path.getBounds().height()) {
169         SkRRect rrect = SkRRect::MakeOval(path.getBounds());
170         return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
171                                               SkMatrix::I(), strokeRec, rrect, rrect);
172     } else if (path.isRect(nullptr)) {
173         SkRRect rrect = SkRRect::MakeRect(path.getBounds());
174         return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
175                                               SkMatrix::I(), strokeRec, rrect, rrect);
176     }
177 
178     // TODO
179     return false;
180 }
181 
directFilterRRectMaskGPU(GrContext *,GrRenderTargetContext * rtContext,GrPaint && paint,const GrClip & clip,const SkMatrix & viewMatrix,const SkStrokeRec & strokeRec,const SkRRect & rrect,const SkRRect & devRRect) const182 bool SkAmbientShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
183                                                              GrRenderTargetContext* rtContext,
184                                                              GrPaint&& paint,
185                                                              const GrClip& clip,
186                                                              const SkMatrix& viewMatrix,
187                                                              const SkStrokeRec& strokeRec,
188                                                              const SkRRect& rrect,
189                                                              const SkRRect& devRRect) const {
190     // It's likely the caller has already done these checks, but we have to be sure.
191     // TODO: support analytic blurring of general rrect
192 
193     // Fast path only supports filled rrects for now.
194     // TODO: fill and stroke as well.
195     if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
196         return false;
197     }
198     // Fast path only supports simple rrects with circular corners.
199     SkASSERT(devRRect.allCornersCircular());
200     if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
201         return false;
202     }
203     // Fast path only supports uniform scale.
204     SkScalar scaleFactors[2];
205     if (!viewMatrix.getMinMaxScales(scaleFactors)) {
206         // matrix is degenerate
207         return false;
208     }
209     if (scaleFactors[0] != scaleFactors[1]) {
210         return false;
211     }
212     SkScalar scaleFactor = scaleFactors[0];
213 
214     // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
215     const SkScalar minRadius = 0.5f / scaleFactor;
216     bool isRect = rrect.getSimpleRadii().fX <= minRadius;
217 
218     // TODO: take flags into account when generating shadow data
219 
220     if (fAmbientAlpha > 0.0f) {
221         SkScalar srcSpaceAmbientRadius = fOccluderHeight * kHeightFactor * kGeomFactor;
222         const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f));
223         const SkScalar strokeWidth = srcSpaceAmbientRadius * umbraAlpha;
224 
225         // For the ambient rrect, we outset the offset rect by srcSpaceAmbientRadius
226         // minus half the strokeWidth to get our stroke shape.
227         SkScalar ambientPathOutset = SkTMax(srcSpaceAmbientRadius - strokeWidth * 0.5f,
228                                             minRadius);
229 
230         SkRRect ambientRRect;
231         if (isRect) {
232             const SkRect temp = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset);
233             ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
234         } else {
235              rrect.outset(ambientPathOutset, ambientPathOutset, &ambientRRect);
236         }
237 
238         const SkScalar devSpaceAmbientRadius = strokeWidth * scaleFactor;
239 
240         GrPaint newPaint(paint);
241         GrColor4f color = newPaint.getColor4f();
242         color.fRGBA[3] *= fAmbientAlpha;
243         newPaint.setColor4f(color);
244         SkStrokeRec ambientStrokeRec(SkStrokeRec::kHairline_InitStyle);
245         ambientStrokeRec.setStrokeStyle(strokeWidth, false);
246 
247         rtContext->drawShadowRRect(clip, std::move(newPaint), viewMatrix, ambientRRect,
248                                    devSpaceAmbientRadius,
249                                    GrStyle(ambientStrokeRec, nullptr));
250     }
251 
252     return true;
253 }
254 
filterMaskGPU(GrContext *,sk_sp<GrTextureProxy> srcProxy,const SkMatrix & ctm,const SkIRect & maskRect) const255 sk_sp<GrTextureProxy> SkAmbientShadowMaskFilterImpl::filterMaskGPU(GrContext*,
256                                                                    sk_sp<GrTextureProxy> srcProxy,
257                                                                    const SkMatrix& ctm,
258                                                                    const SkIRect& maskRect) const {
259     // This filter is generative and doesn't operate on pre-existing masks
260     return nullptr;
261 }
262 
263 #endif // SK_SUPPORT_GPU
264 
265 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const266 void SkAmbientShadowMaskFilterImpl::toString(SkString* str) const {
267     str->append("SkAmbientShadowMaskFilterImpl: (");
268 
269     str->append("occluderHeight: ");
270     str->appendScalar(fOccluderHeight);
271     str->append(" ");
272 
273     str->append("ambientAlpha: ");
274     str->appendScalar(fAmbientAlpha);
275     str->append(" ");
276 
277     str->append("flags: (");
278     if (fFlags) {
279         bool needSeparator = false;
280         SkAddFlagToString(str,
281                           SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
282                           "TransparentOccluder", &needSeparator);
283         SkAddFlagToString(str,
284                           SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
285                           "GaussianEdge", &needSeparator);
286         SkAddFlagToString(str,
287                           SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
288                           "LargerUmbra", &needSeparator);
289     } else {
290         str->append("None");
291     }
292     str->append("))");
293 }
294 #endif
295 
296 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkAmbientShadowMaskFilter)
297 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAmbientShadowMaskFilterImpl)
298 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
299