blob: e378c184b9167061fa04dd5ace1363f4321155a4 [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"
17#include "GrInvariantOutput.h"
18#include "GrStyle.h"
19#include "GrTexture.h"
20#include "GrTextureProxy.h"
21#include "glsl/GrGLSLFragmentProcessor.h"
22#include "glsl/GrGLSLFragmentShaderBuilder.h"
23#include "glsl/GrGLSLProgramDataManager.h"
24#include "glsl/GrGLSLUniformHandler.h"
Jim Van Verthbce74962017-01-25 09:39:46 -050025#include "SkShadowTessellator.h"
Jim Van Verth2103cf02017-01-16 13:03:37 -050026#include "SkStrokeRec.h"
27#endif
28
29class SkAmbientShadowMaskFilterImpl : public SkMaskFilter {
30public:
31 SkAmbientShadowMaskFilterImpl(SkScalar occluderHeight, SkScalar ambientAlpha, uint32_t flags);
32
33 // overrides from SkMaskFilter
34 SkMask::Format getFormat() const override;
35 bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
36 SkIPoint* margin) const override;
37
38#if SK_SUPPORT_GPU
39 bool canFilterMaskGPU(const SkRRect& devRRect,
40 const SkIRect& clipBounds,
41 const SkMatrix& ctm,
42 SkRect* maskRect) const override;
43 bool directFilterMaskGPU(GrTextureProvider* texProvider,
44 GrRenderTargetContext* drawContext,
45 GrPaint&&,
46 const GrClip&,
47 const SkMatrix& viewMatrix,
48 const SkStrokeRec& strokeRec,
49 const SkPath& path) const override;
50 bool directFilterRRectMaskGPU(GrContext*,
51 GrRenderTargetContext* drawContext,
52 GrPaint&&,
53 const GrClip&,
54 const SkMatrix& viewMatrix,
55 const SkStrokeRec& strokeRec,
56 const SkRRect& rrect,
57 const SkRRect& devRRect) const override;
58 sk_sp<GrTextureProxy> filterMaskGPU(GrContext*,
59 sk_sp<GrTextureProxy> srcProxy,
60 const SkMatrix& ctm,
61 const SkIRect& maskRect) const override;
62#endif
63
64 void computeFastBounds(const SkRect&, SkRect*) const override;
65
66 SK_TO_STRING_OVERRIDE()
67 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAmbientShadowMaskFilterImpl)
68
69private:
70 SkScalar fOccluderHeight;
71 SkScalar fAmbientAlpha;
72 uint32_t fFlags;
73
74 SkAmbientShadowMaskFilterImpl(SkReadBuffer&);
75 void flatten(SkWriteBuffer&) const override;
76
77 friend class SkAmbientShadowMaskFilter;
78
79 typedef SkMaskFilter INHERITED;
80};
81
82sk_sp<SkMaskFilter> SkAmbientShadowMaskFilter::Make(SkScalar occluderHeight, SkScalar ambientAlpha,
83 uint32_t flags) {
84 // add some param checks here for early exit
85
86 return sk_sp<SkMaskFilter>(new SkAmbientShadowMaskFilterImpl(occluderHeight, ambientAlpha,
87 flags));
88}
89
90///////////////////////////////////////////////////////////////////////////////////////////////////
91
92SkAmbientShadowMaskFilterImpl::SkAmbientShadowMaskFilterImpl(SkScalar occluderHeight,
93 SkScalar ambientAlpha,
94 uint32_t flags)
95 : fOccluderHeight(occluderHeight)
96 , fAmbientAlpha(ambientAlpha)
97 , fFlags(flags) {
98 SkASSERT(fOccluderHeight > 0);
99 SkASSERT(fAmbientAlpha >= 0);
100}
101
102SkMask::Format SkAmbientShadowMaskFilterImpl::getFormat() const {
103 return SkMask::kA8_Format;
104}
105
106bool SkAmbientShadowMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
107 const SkMatrix& matrix,
108 SkIPoint* margin) const {
109 // TODO something
110 return false;
111}
112
113void SkAmbientShadowMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) const {
114 // TODO compute based on ambient data
115 dst->set(src.fLeft, src.fTop, src.fRight, src.fBottom);
116}
117
118sk_sp<SkFlattenable> SkAmbientShadowMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
119 const SkScalar occluderHeight = buffer.readScalar();
120 const SkScalar ambientAlpha = buffer.readScalar();
121 const uint32_t flags = buffer.readUInt();
122
123 return SkAmbientShadowMaskFilter::Make(occluderHeight, ambientAlpha, flags);
124}
125
126void SkAmbientShadowMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
127 buffer.writeScalar(fOccluderHeight);
128 buffer.writeScalar(fAmbientAlpha);
129 buffer.writeUInt(fFlags);
130}
131
132#if SK_SUPPORT_GPU
133
134///////////////////////////////////////////////////////////////////////////////////////////////////
135
Jim Van Verthbce74962017-01-25 09:39:46 -0500136//
137// Shader for managing the shadow's edge. a in the input color represents the initial
138// edge color, which is transformed by a Gaussian function. b represents the blend factor,
139// which is multiplied by this transformed value.
140//
141class ShadowEdgeFP : public GrFragmentProcessor {
142public:
143 ShadowEdgeFP() {
144 this->initClassID<ShadowEdgeFP>();
145 }
146
147 class GLSLShadowEdgeFP : public GrGLSLFragmentProcessor {
148 public:
149 GLSLShadowEdgeFP() {}
150
151 void emitCode(EmitArgs& args) override {
152 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
153
154 fragBuilder->codeAppendf("float factor = 1.0 - %s.a;", args.fInputColor);
155 fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;");
156 fragBuilder->codeAppendf("%s = vec4(0.0, 0.0, 0.0, %s.b*factor);", args.fOutputColor,
157 args.fInputColor);
158 }
159
160 static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*) {}
161
162 protected:
163 void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {}
164 };
165
166 void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
167 GLSLShadowEdgeFP::GenKey(*this, caps, b);
168 }
169
170 const char* name() const override { return "ShadowEdgeFP"; }
171
172 void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
173 inout->mulByUnknownFourComponents();
174 }
175
176private:
177 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
178 return new GLSLShadowEdgeFP();
179 }
180
181 bool onIsEqual(const GrFragmentProcessor& proc) const override { return true; }
182};
183
184///////////////////////////////////////////////////////////////////////////////////////////////////
185
Jim Van Verth2103cf02017-01-16 13:03:37 -0500186bool SkAmbientShadowMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
187 const SkIRect& clipBounds,
188 const SkMatrix& ctm,
189 SkRect* maskRect) const {
190 // TODO
191 *maskRect = devRRect.rect();
192 return true;
193}
194
Jim Van Verthbce74962017-01-25 09:39:46 -0500195static const float kHeightFactor = 1.0f / 128.0f;
196static const float kGeomFactor = 64.0f;
197
Jim Van Verth2103cf02017-01-16 13:03:37 -0500198bool SkAmbientShadowMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvider,
Jim Van Verthbce74962017-01-25 09:39:46 -0500199 GrRenderTargetContext* rtContext,
Jim Van Verth2103cf02017-01-16 13:03:37 -0500200 GrPaint&& paint,
201 const GrClip& clip,
202 const SkMatrix& viewMatrix,
Jim Van Verthbce74962017-01-25 09:39:46 -0500203 const SkStrokeRec&,
Jim Van Verth2103cf02017-01-16 13:03:37 -0500204 const SkPath& path) const {
Jim Van Verthbce74962017-01-25 09:39:46 -0500205 SkASSERT(rtContext);
Jim Van Verth2103cf02017-01-16 13:03:37 -0500206 // TODO: this will not handle local coordinates properly
207
Jim Van Verthbce74962017-01-25 09:39:46 -0500208 if (fAmbientAlpha <= 0.0f) {
209 return true;
210 }
211
212 // only convex paths for now
213 if (!path.isConvex()) {
214 return false;
215 }
216
217#ifdef SUPPORT_FAST_PATH
Jim Van Verth2103cf02017-01-16 13:03:37 -0500218 // if circle
219 // TODO: switch to SkScalarNearlyEqual when either oval renderer is updated or we
220 // have our own GeometryProc.
221 if (path.isOval(nullptr) && path.getBounds().width() == path.getBounds().height()) {
222 SkRRect rrect = SkRRect::MakeOval(path.getBounds());
223 return this->directFilterRRectMaskGPU(nullptr, drawContext, std::move(paint), clip,
224 SkMatrix::I(), strokeRec, rrect, rrect);
225 } else if (path.isRect(nullptr)) {
226 SkRRect rrect = SkRRect::MakeRect(path.getBounds());
227 return this->directFilterRRectMaskGPU(nullptr, drawContext, std::move(paint), clip,
228 SkMatrix::I(), strokeRec, rrect, rrect);
229 }
Jim Van Verthbce74962017-01-25 09:39:46 -0500230#endif
Jim Van Verth2103cf02017-01-16 13:03:37 -0500231
Jim Van Verthbce74962017-01-25 09:39:46 -0500232 SkScalar radius = fOccluderHeight * kHeightFactor * kGeomFactor;
233 SkScalar umbraAlpha = SkScalarInvert((1.0f+SkTMax(fOccluderHeight * kHeightFactor, 0.0f)));
234 // umbraColor is the interior value, penumbraColor the exterior value.
235 // umbraAlpha is the factor that is linearly interpolated from outside to inside, and
236 // then "blurred" by the ShadowEdgeFP. It is then multiplied by fAmbientAlpha to get
237 // the final alpha.
238 GrColor umbraColor = GrColorPackRGBA(0, 0, fAmbientAlpha*255.9999f, umbraAlpha*255.9999f);
239 GrColor penumbraColor = GrColorPackRGBA(0, 0, fAmbientAlpha*255.9999f, 0);
240
241 SkAmbientShadowTessellator tess(SkMatrix::I(), path, radius, umbraColor, penumbraColor,
242 SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag));
243
244 sk_sp<ShadowEdgeFP> edgeFP(new ShadowEdgeFP);
245 paint.addColorFragmentProcessor(edgeFP);
246
247 rtContext->drawVertices(clip, std::move(paint), SkMatrix::I(), kTriangles_GrPrimitiveType,
248 tess.vertexCount(), tess.positions(), nullptr,
249 tess.colors(), tess.indices(), tess.indexCount());
250
251 return true;
Jim Van Verth2103cf02017-01-16 13:03:37 -0500252}
253
254bool SkAmbientShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
255 GrRenderTargetContext* rtContext,
256 GrPaint&& paint,
257 const GrClip& clip,
258 const SkMatrix& viewMatrix,
259 const SkStrokeRec& strokeRec,
260 const SkRRect& rrect,
261 const SkRRect& devRRect) const {
Jim Van Verthbce74962017-01-25 09:39:46 -0500262#ifndef SUPPORT_FAST_PATH
263 return false;
264#endif
265
Jim Van Verth2103cf02017-01-16 13:03:37 -0500266 // It's likely the caller has already done these checks, but we have to be sure.
267 // TODO: support analytic blurring of general rrect
268
269 // Fast path only supports filled rrects for now.
270 // TODO: fill and stroke as well.
271 if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
272 return false;
273 }
274 // Fast path only supports simple rrects with circular corners.
275 SkASSERT(devRRect.allCornersCircular());
276 if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
277 return false;
278 }
279 // Fast path only supports uniform scale.
280 SkScalar scaleFactors[2];
281 if (!viewMatrix.getMinMaxScales(scaleFactors)) {
282 // matrix is degenerate
283 return false;
284 }
285 if (scaleFactors[0] != scaleFactors[1]) {
286 return false;
287 }
288 SkScalar scaleFactor = scaleFactors[0];
289
290 // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
291 const SkScalar minRadius = 0.5f / scaleFactor;
292 bool isRect = rrect.getSimpleRadii().fX <= minRadius;
293
294 // TODO: take flags into account when generating shadow data
295
296 if (fAmbientAlpha > 0.0f) {
Jim Van Verth2103cf02017-01-16 13:03:37 -0500297 SkScalar srcSpaceAmbientRadius = fOccluderHeight * kHeightFactor * kGeomFactor;
298 const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f));
299 const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
300
301 // For the ambient rrect, we inset the offset rect by half the srcSpaceAmbientRadius
302 // to get our stroke shape.
303 SkScalar ambientPathOutset = SkTMax(ambientOffset - srcSpaceAmbientRadius * 0.5f,
304 minRadius);
305
306 SkRRect ambientRRect;
307 if (isRect) {
308 const SkRect temp = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset);
309 ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
310 } else {
311 rrect.outset(ambientPathOutset, ambientPathOutset, &ambientRRect);
312 }
313
314 const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
315
316 GrPaint newPaint(paint);
317 GrColor4f color = newPaint.getColor4f();
318 color.fRGBA[3] *= fAmbientAlpha;
319 newPaint.setColor4f(color);
320 SkStrokeRec ambientStrokeRec(SkStrokeRec::kHairline_InitStyle);
321 ambientStrokeRec.setStrokeStyle(srcSpaceAmbientRadius, false);
322
323 rtContext->drawShadowRRect(clip, std::move(newPaint), viewMatrix, ambientRRect,
324 devSpaceAmbientRadius,
325 GrStyle(ambientStrokeRec, nullptr));
326 }
327
328 return true;
329}
330
331sk_sp<GrTextureProxy> SkAmbientShadowMaskFilterImpl::filterMaskGPU(GrContext*,
332 sk_sp<GrTextureProxy> srcProxy,
333 const SkMatrix& ctm,
334 const SkIRect& maskRect) const {
335 // This filter is generative and doesn't operate on pre-existing masks
336 return nullptr;
337}
338
Jim Van Verthbce74962017-01-25 09:39:46 -0500339#endif // SK_SUPPORT_GPU
Jim Van Verth2103cf02017-01-16 13:03:37 -0500340
341#ifndef SK_IGNORE_TO_STRING
342void SkAmbientShadowMaskFilterImpl::toString(SkString* str) const {
343 str->append("SkAmbientShadowMaskFilterImpl: (");
344
345 str->append("occluderHeight: ");
346 str->appendScalar(fOccluderHeight);
347 str->append(" ");
348
349 str->append("ambientAlpha: ");
350 str->appendScalar(fAmbientAlpha);
351 str->append(" ");
352
353 str->append("flags: (");
354 if (fFlags) {
355 bool needSeparator = false;
356 SkAddFlagToString(str,
357 SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
358 "TransparentOccluder", &needSeparator);
359 SkAddFlagToString(str,
360 SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
361 "GaussianEdge", &needSeparator);
362 SkAddFlagToString(str,
363 SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
364 "LargerUmbra", &needSeparator);
365 } else {
366 str->append("None");
367 }
368 str->append("))");
369}
370#endif
371
372SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkAmbientShadowMaskFilter)
373SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAmbientShadowMaskFilterImpl)
374SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END