blob: 561d77dd67ad9c86885fb85b212840e07aaabb3d [file] [log] [blame]
Jim Van Verth2103cf02017-01-16 13:03:37 -05001/*
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"
Jim Van Verth2103cf02017-01-16 13:03:37 -050017#include "GrStyle.h"
18#include "GrTexture.h"
19#include "GrTextureProxy.h"
Jim Van Verth2103cf02017-01-16 13:03:37 -050020#include "SkStrokeRec.h"
21#endif
22
23class SkAmbientShadowMaskFilterImpl : public SkMaskFilter {
24public:
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;
Brian Osman32342f02017-03-04 08:12:46 -050037 bool directFilterMaskGPU(GrResourceProvider* resourceProvider,
Jim Van Verth2103cf02017-01-16 13:03:37 -050038 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
63private:
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
76sk_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
86SkAmbientShadowMaskFilterImpl::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
96SkMask::Format SkAmbientShadowMaskFilterImpl::getFormat() const {
97 return SkMask::kA8_Format;
98}
99
100bool SkAmbientShadowMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
101 const SkMatrix& matrix,
102 SkIPoint* margin) const {
103 // TODO something
104 return false;
105}
106
107void 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
112sk_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
120void 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
130bool 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
Jim Van Verthbce74962017-01-25 09:39:46 -0500139static const float kHeightFactor = 1.0f / 128.0f;
140static const float kGeomFactor = 64.0f;
141
Brian Osman32342f02017-03-04 08:12:46 -0500142bool SkAmbientShadowMaskFilterImpl::directFilterMaskGPU(GrResourceProvider* resourceProvider,
Jim Van Verthbce74962017-01-25 09:39:46 -0500143 GrRenderTargetContext* rtContext,
Jim Van Verth2103cf02017-01-16 13:03:37 -0500144 GrPaint&& paint,
145 const GrClip& clip,
146 const SkMatrix& viewMatrix,
Jim Van Verth91af7272017-01-27 14:15:54 -0500147 const SkStrokeRec& strokeRec,
Jim Van Verth2103cf02017-01-16 13:03:37 -0500148 const SkPath& path) const {
Jim Van Verthbce74962017-01-25 09:39:46 -0500149 SkASSERT(rtContext);
Jim Van Verth2103cf02017-01-16 13:03:37 -0500150 // TODO: this will not handle local coordinates properly
151
Jim Van Verthbce74962017-01-25 09:39:46 -0500152 if (fAmbientAlpha <= 0.0f) {
153 return true;
154 }
155
156 // only convex paths for now
157 if (!path.isConvex()) {
158 return false;
159 }
160
Jim Van Verth91af7272017-01-27 14:15:54 -0500161 if (strokeRec.getStyle() != SkStrokeRec::kFill_Style) {
162 return false;
163 }
164
Jim Van Verth2103cf02017-01-16 13:03:37 -0500165 // 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());
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500170 return this->directFilterRRectMaskGPU(nullptr, rtContext, std::move(paint), clip,
Jim Van Verth2103cf02017-01-16 13:03:37 -0500171 SkMatrix::I(), strokeRec, rrect, rrect);
172 } else if (path.isRect(nullptr)) {
173 SkRRect rrect = SkRRect::MakeRect(path.getBounds());
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500174 return this->directFilterRRectMaskGPU(nullptr, rtContext, std::move(paint), clip,
Jim Van Verth2103cf02017-01-16 13:03:37 -0500175 SkMatrix::I(), strokeRec, rrect, rrect);
176 }
177
Jim Van Verthefe3ded2017-01-30 13:11:45 -0500178 // TODO
179 return false;
Jim Van Verth2103cf02017-01-16 13:03:37 -0500180}
181
182bool 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) {
Jim Van Verth2103cf02017-01-16 13:03:37 -0500221 SkScalar srcSpaceAmbientRadius = fOccluderHeight * kHeightFactor * kGeomFactor;
222 const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f));
Jim Van Verthcf40e302017-03-02 11:28:43 -0500223 const SkScalar strokeWidth = srcSpaceAmbientRadius * umbraAlpha;
Jim Van Verth2103cf02017-01-16 13:03:37 -0500224
Jim Van Verthcf40e302017-03-02 11:28:43 -0500225 // 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);
Jim Van Verth2103cf02017-01-16 13:03:37 -0500229
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
Jim Van Verthcf40e302017-03-02 11:28:43 -0500238 const SkScalar devSpaceAmbientRadius = strokeWidth * scaleFactor;
Jim Van Verth2103cf02017-01-16 13:03:37 -0500239
240 GrPaint newPaint(paint);
241 GrColor4f color = newPaint.getColor4f();
242 color.fRGBA[3] *= fAmbientAlpha;
243 newPaint.setColor4f(color);
244 SkStrokeRec ambientStrokeRec(SkStrokeRec::kHairline_InitStyle);
Jim Van Verthcf40e302017-03-02 11:28:43 -0500245 ambientStrokeRec.setStrokeStyle(strokeWidth, false);
Jim Van Verth2103cf02017-01-16 13:03:37 -0500246
247 rtContext->drawShadowRRect(clip, std::move(newPaint), viewMatrix, ambientRRect,
248 devSpaceAmbientRadius,
249 GrStyle(ambientStrokeRec, nullptr));
250 }
251
252 return true;
253}
254
255sk_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
Jim Van Verthbce74962017-01-25 09:39:46 -0500263#endif // SK_SUPPORT_GPU
Jim Van Verth2103cf02017-01-16 13:03:37 -0500264
265#ifndef SK_IGNORE_TO_STRING
266void 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
296SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkAmbientShadowMaskFilter)
297SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAmbientShadowMaskFilterImpl)
298SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END