Line data Source code
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 0 : 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 0 : 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 :
76 0 : 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 0 : flags));
82 : }
83 :
84 : ///////////////////////////////////////////////////////////////////////////////////////////////////
85 :
86 0 : SkAmbientShadowMaskFilterImpl::SkAmbientShadowMaskFilterImpl(SkScalar occluderHeight,
87 : SkScalar ambientAlpha,
88 0 : uint32_t flags)
89 : : fOccluderHeight(occluderHeight)
90 : , fAmbientAlpha(ambientAlpha)
91 0 : , fFlags(flags) {
92 0 : SkASSERT(fOccluderHeight > 0);
93 0 : SkASSERT(fAmbientAlpha >= 0);
94 0 : }
95 :
96 0 : SkMask::Format SkAmbientShadowMaskFilterImpl::getFormat() const {
97 0 : return SkMask::kA8_Format;
98 : }
99 :
100 0 : bool SkAmbientShadowMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
101 : const SkMatrix& matrix,
102 : SkIPoint* margin) const {
103 : // TODO something
104 0 : return false;
105 : }
106 :
107 0 : void SkAmbientShadowMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) const {
108 : // TODO compute based on ambient data
109 0 : dst->set(src.fLeft, src.fTop, src.fRight, src.fBottom);
110 0 : }
111 :
112 0 : sk_sp<SkFlattenable> SkAmbientShadowMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
113 0 : const SkScalar occluderHeight = buffer.readScalar();
114 0 : const SkScalar ambientAlpha = buffer.readScalar();
115 0 : const uint32_t flags = buffer.readUInt();
116 :
117 0 : return SkAmbientShadowMaskFilter::Make(occluderHeight, ambientAlpha, flags);
118 : }
119 :
120 0 : void SkAmbientShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
121 0 : buffer.writeScalar(fOccluderHeight);
122 0 : buffer.writeScalar(fAmbientAlpha);
123 0 : buffer.writeUInt(fFlags);
124 0 : }
125 :
126 : #if SK_SUPPORT_GPU
127 :
128 : ///////////////////////////////////////////////////////////////////////////////////////////////////
129 :
130 0 : bool SkAmbientShadowMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
131 : const SkIRect& clipBounds,
132 : const SkMatrix& ctm,
133 : SkRect* maskRect) const {
134 : // TODO
135 0 : *maskRect = devRRect.rect();
136 0 : return true;
137 : }
138 :
139 : static const float kHeightFactor = 1.0f / 128.0f;
140 : static const float kGeomFactor = 64.0f;
141 :
142 0 : 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 0 : SkASSERT(rtContext);
150 : // TODO: this will not handle local coordinates properly
151 :
152 0 : if (fAmbientAlpha <= 0.0f) {
153 0 : return true;
154 : }
155 :
156 : // only convex paths for now
157 0 : if (!path.isConvex()) {
158 0 : return false;
159 : }
160 :
161 0 : if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) {
162 0 : 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 0 : if (path.isOval(nullptr) && path.getBounds().width() == path.getBounds().height()) {
169 0 : SkRRect rrect = SkRRect::MakeOval(path.getBounds());
170 0 : return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
171 0 : SkMatrix::I(), strokeRec, rrect, rrect);
172 0 : } else if (path.isRect(nullptr)) {
173 0 : SkRRect rrect = SkRRect::MakeRect(path.getBounds());
174 0 : return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
175 0 : SkMatrix::I(), strokeRec, rrect, rrect);
176 : }
177 :
178 : // TODO
179 0 : return false;
180 : }
181 :
182 0 : 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 0 : if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
196 0 : return false;
197 : }
198 : // Fast path only supports simple rrects with circular corners.
199 0 : SkASSERT(devRRect.allCornersCircular());
200 0 : if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
201 0 : return false;
202 : }
203 : // Fast path only supports uniform scale.
204 : SkScalar scaleFactors[2];
205 0 : if (!viewMatrix.getMinMaxScales(scaleFactors)) {
206 : // matrix is degenerate
207 0 : return false;
208 : }
209 0 : if (scaleFactors[0] != scaleFactors[1]) {
210 0 : return false;
211 : }
212 0 : 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 0 : const SkScalar minRadius = 0.5f / scaleFactor;
216 0 : bool isRect = rrect.getSimpleRadii().fX <= minRadius;
217 :
218 : // TODO: take flags into account when generating shadow data
219 :
220 0 : if (fAmbientAlpha > 0.0f) {
221 0 : SkScalar srcSpaceAmbientRadius = fOccluderHeight * kHeightFactor * kGeomFactor;
222 0 : const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f));
223 0 : 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 0 : SkScalar ambientPathOutset = SkTMax(srcSpaceAmbientRadius - strokeWidth * 0.5f,
228 0 : minRadius);
229 :
230 0 : SkRRect ambientRRect;
231 0 : if (isRect) {
232 0 : const SkRect temp = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset);
233 0 : ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
234 : } else {
235 0 : rrect.outset(ambientPathOutset, ambientPathOutset, &ambientRRect);
236 : }
237 :
238 0 : const SkScalar devSpaceAmbientRadius = strokeWidth * scaleFactor;
239 :
240 0 : GrPaint newPaint(paint);
241 0 : GrColor4f color = newPaint.getColor4f();
242 0 : color.fRGBA[3] *= fAmbientAlpha;
243 0 : newPaint.setColor4f(color);
244 0 : SkStrokeRec ambientStrokeRec(SkStrokeRec::kHairline_InitStyle);
245 0 : ambientStrokeRec.setStrokeStyle(strokeWidth, false);
246 :
247 0 : rtContext->drawShadowRRect(clip, std::move(newPaint), viewMatrix, ambientRRect,
248 : devSpaceAmbientRadius,
249 0 : GrStyle(ambientStrokeRec, nullptr));
250 : }
251 :
252 0 : return true;
253 : }
254 :
255 0 : 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 0 : return nullptr;
261 : }
262 :
263 : #endif // SK_SUPPORT_GPU
264 :
265 : #ifndef SK_IGNORE_TO_STRING
266 0 : void SkAmbientShadowMaskFilterImpl::toString(SkString* str) const {
267 0 : str->append("SkAmbientShadowMaskFilterImpl: (");
268 :
269 0 : str->append("occluderHeight: ");
270 0 : str->appendScalar(fOccluderHeight);
271 0 : str->append(" ");
272 :
273 0 : str->append("ambientAlpha: ");
274 0 : str->appendScalar(fAmbientAlpha);
275 0 : str->append(" ");
276 :
277 0 : str->append("flags: (");
278 0 : if (fFlags) {
279 0 : bool needSeparator = false;
280 0 : SkAddFlagToString(str,
281 0 : SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
282 0 : "TransparentOccluder", &needSeparator);
283 0 : SkAddFlagToString(str,
284 0 : SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
285 0 : "GaussianEdge", &needSeparator);
286 0 : SkAddFlagToString(str,
287 0 : SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
288 0 : "LargerUmbra", &needSeparator);
289 : } else {
290 0 : str->append("None");
291 : }
292 0 : str->append("))");
293 0 : }
294 : #endif
295 :
296 0 : SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkAmbientShadowMaskFilter)
297 0 : SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAmbientShadowMaskFilterImpl)
298 0 : SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
|