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 "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 0 : 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 0 : 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 :
79 0 : 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 0 : lightRadius, spotAlpha, flags));
86 : }
87 :
88 : ///////////////////////////////////////////////////////////////////////////////////////////////////
89 :
90 0 : SkSpotShadowMaskFilterImpl::SkSpotShadowMaskFilterImpl(SkScalar occluderHeight,
91 : const SkPoint3& lightPos,
92 : SkScalar lightRadius,
93 : SkScalar spotAlpha,
94 0 : uint32_t flags)
95 : : fOccluderHeight(occluderHeight)
96 : , fLightPos(lightPos)
97 : , fLightRadius(lightRadius)
98 : , fSpotAlpha(spotAlpha)
99 0 : , fFlags(flags) {
100 0 : SkASSERT(fOccluderHeight > 0);
101 0 : SkASSERT(fLightPos.z() > 0 && fLightPos.z() > fOccluderHeight);
102 0 : SkASSERT(fLightRadius > 0);
103 0 : SkASSERT(fSpotAlpha >= 0);
104 0 : }
105 :
106 0 : SkMask::Format SkSpotShadowMaskFilterImpl::getFormat() const {
107 0 : return SkMask::kA8_Format;
108 : }
109 :
110 0 : bool SkSpotShadowMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
111 : const SkMatrix& matrix,
112 : SkIPoint* margin) const {
113 : // TODO something
114 0 : return false;
115 : }
116 :
117 0 : void SkSpotShadowMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) const {
118 : // TODO compute based on ambient + spot data
119 0 : dst->set(src.fLeft, src.fTop, src.fRight, src.fBottom);
120 0 : }
121 :
122 0 : sk_sp<SkFlattenable> SkSpotShadowMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
123 0 : const SkScalar occluderHeight = buffer.readScalar();
124 0 : const SkScalar lightX = buffer.readScalar();
125 0 : const SkScalar lightY = buffer.readScalar();
126 0 : const SkScalar lightZ = buffer.readScalar();
127 0 : const SkPoint3 lightPos = SkPoint3::Make(lightX, lightY, lightZ);
128 0 : const SkScalar lightRadius = buffer.readScalar();
129 0 : const SkScalar spotAlpha = buffer.readScalar();
130 0 : const uint32_t flags = buffer.readUInt();
131 :
132 0 : return SkSpotShadowMaskFilter::Make(occluderHeight, lightPos, lightRadius,
133 0 : spotAlpha, flags);
134 : }
135 :
136 0 : void SkSpotShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
137 0 : buffer.writeScalar(fOccluderHeight);
138 0 : buffer.writeScalar(fLightPos.fX);
139 0 : buffer.writeScalar(fLightPos.fY);
140 0 : buffer.writeScalar(fLightPos.fZ);
141 0 : buffer.writeScalar(fLightRadius);
142 0 : buffer.writeScalar(fSpotAlpha);
143 0 : buffer.writeUInt(fFlags);
144 0 : }
145 :
146 : #if SK_SUPPORT_GPU
147 :
148 : ///////////////////////////////////////////////////////////////////////////////////////////////////
149 :
150 0 : bool SkSpotShadowMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
151 : const SkIRect& clipBounds,
152 : const SkMatrix& ctm,
153 : SkRect* maskRect) const {
154 : // TODO
155 0 : *maskRect = devRRect.rect();
156 0 : return true;
157 : }
158 :
159 0 : 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 0 : SkASSERT(rtContext);
167 : // TODO: this will not handle local coordinates properly
168 :
169 0 : if (fSpotAlpha <= 0.0f) {
170 0 : return true;
171 : }
172 :
173 : // only convex paths for now
174 0 : if (!path.isConvex()) {
175 0 : return false;
176 : }
177 :
178 0 : if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) {
179 0 : 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 0 : if (path.isOval(nullptr) && path.getBounds().width() == path.getBounds().height()) {
186 0 : SkRRect rrect = SkRRect::MakeOval(path.getBounds());
187 0 : return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
188 0 : SkMatrix::I(), strokeRec, rrect, rrect);
189 0 : } else if (path.isRect(nullptr)) {
190 0 : SkRRect rrect = SkRRect::MakeRect(path.getBounds());
191 0 : return this->directFilterRRectMaskGPU(context, rtContext, std::move(paint), clip,
192 0 : SkMatrix::I(), strokeRec, rrect, rrect);
193 : }
194 :
195 0 : return false;
196 : }
197 :
198 0 : 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 0 : if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
212 0 : return false;
213 : }
214 : // Fast path only supports simple rrects with circular corners.
215 0 : SkASSERT(devRRect.allCornersCircular());
216 0 : if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
217 0 : return false;
218 : }
219 : // Fast path only supports uniform scale.
220 : SkScalar scaleFactors[2];
221 0 : if (!viewMatrix.getMinMaxScales(scaleFactors)) {
222 : // matrix is degenerate
223 0 : return false;
224 : }
225 0 : if (scaleFactors[0] != scaleFactors[1]) {
226 0 : return false;
227 : }
228 0 : 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 0 : const SkScalar minRadius = 0.5f / scaleFactor;
232 0 : bool isRect = rrect.getSimpleRadii().fX <= minRadius;
233 :
234 : // TODO: take flags into account when generating shadow data
235 :
236 0 : if (fSpotAlpha > 0.0f) {
237 0 : float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f);
238 :
239 0 : SkScalar srcSpaceSpotRadius = 2.0f * fLightRadius * zRatio;
240 :
241 0 : SkRRect spotRRect;
242 0 : if (isRect) {
243 0 : spotRRect = SkRRect::MakeRectXY(rrect.rect(), minRadius, minRadius);
244 : } else {
245 0 : spotRRect = rrect;
246 : }
247 :
248 0 : SkRRect spotShadowRRect;
249 : // Compute the scale and translation for the spot shadow.
250 0 : const SkScalar scale = fLightPos.fZ / (fLightPos.fZ - fOccluderHeight);
251 0 : spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
252 :
253 0 : SkPoint center = SkPoint::Make(spotShadowRRect.rect().centerX(),
254 0 : spotShadowRRect.rect().centerY());
255 : SkMatrix ctmInverse;
256 0 : if (!viewMatrix.invert(&ctmInverse)) {
257 0 : SkDebugf("Matrix is degenerate. Will not render spot shadow!\n");
258 : //**** TODO: this is not good
259 0 : return true;
260 : }
261 0 : SkPoint lightPos2D = SkPoint::Make(fLightPos.fX, fLightPos.fY);
262 0 : ctmInverse.mapPoints(&lightPos2D, 1);
263 0 : const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
264 0 : 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 0 : SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(rrect.rect().fLeft),
270 0 : SkTAbs(rrect.rect().fRight)),
271 0 : SkTMax(SkTAbs(rrect.rect().fTop),
272 0 : SkTAbs(rrect.rect().fBottom)));
273 0 : SkScalar insetAmount = spotOffset.length() - (0.5f * srcSpaceSpotRadius) + scaleOffset;
274 :
275 : // Compute area
276 0 : SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount;
277 0 : SkScalar strokedArea = 2.0f*strokeWidth *
278 0 : (spotShadowRRect.width() + spotShadowRRect.height());
279 0 : SkScalar filledArea = (spotShadowRRect.height() + srcSpaceSpotRadius) *
280 0 : (spotShadowRRect.width() + srcSpaceSpotRadius);
281 :
282 0 : GrColor4f color = paint.getColor4f();
283 0 : color.fRGBA[3] *= fSpotAlpha;
284 0 : paint.setColor4f(color);
285 :
286 0 : 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 0 : if (strokedArea > filledArea ||
290 0 : fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag) {
291 0 : 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 0 : SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount / 2.0f,
296 0 : insetAmount / 2.0f);
297 0 : SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount / 2.0f,
298 0 : minRadius);
299 0 : spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
300 0 : spotStrokeRec.setStrokeStyle(strokeWidth, false);
301 : }
302 :
303 : // handle scale of radius and pad due to CTM
304 0 : const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
305 :
306 0 : spotShadowRRect.offset(spotOffset.fX, spotOffset.fY);
307 :
308 0 : rtContext->drawShadowRRect(clip, std::move(paint), viewMatrix, spotShadowRRect,
309 0 : devSpaceSpotRadius, GrStyle(spotStrokeRec, nullptr));
310 : }
311 :
312 0 : return true;
313 : }
314 :
315 0 : 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 0 : return nullptr;
321 : }
322 :
323 : #endif
324 :
325 : #ifndef SK_IGNORE_TO_STRING
326 0 : void SkSpotShadowMaskFilterImpl::toString(SkString* str) const {
327 0 : str->append("SkSpotShadowMaskFilterImpl: (");
328 :
329 0 : str->append("occluderHeight: ");
330 0 : str->appendScalar(fOccluderHeight);
331 0 : str->append(" ");
332 :
333 0 : str->append("lightPos: (");
334 0 : str->appendScalar(fLightPos.fX);
335 0 : str->append(", ");
336 0 : str->appendScalar(fLightPos.fY);
337 0 : str->append(", ");
338 0 : str->appendScalar(fLightPos.fZ);
339 0 : str->append(") ");
340 :
341 0 : str->append("lightRadius: ");
342 0 : str->appendScalar(fLightRadius);
343 0 : str->append(" ");
344 :
345 0 : str->append("spotAlpha: ");
346 0 : str->appendScalar(fSpotAlpha);
347 0 : str->append(" ");
348 :
349 0 : str->append("flags: (");
350 0 : if (fFlags) {
351 0 : bool needSeparator = false;
352 0 : SkAddFlagToString(str,
353 0 : SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
354 0 : "TransparentOccluder", &needSeparator);
355 0 : SkAddFlagToString(str,
356 0 : SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
357 0 : "GaussianEdge", &needSeparator);
358 0 : SkAddFlagToString(str,
359 0 : SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
360 0 : "LargerUmbra", &needSeparator);
361 : } else {
362 0 : str->append("None");
363 : }
364 0 : str->append("))");
365 0 : }
366 : #endif
367 :
368 0 : SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkSpotShadowMaskFilter)
369 0 : SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSpotShadowMaskFilterImpl)
370 0 : SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
|