1 /*
2 * Copyright 2019 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 "modules/sksg/include/SkSGRenderEffect.h"
9
10 #include "include/core/SkBlender.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkPaint.h"
13 #include "include/core/SkShader.h"
14 #include "include/core/SkTileMode.h"
15 #include "include/effects/SkImageFilters.h"
16 #include "modules/sksg/include/SkSGRenderNode.h"
17
18 #include <utility>
19
20 class SkMatrix;
21
22 namespace sksg {
23
Make(sk_sp<RenderNode> child,sk_sp<SkShader> sh)24 sk_sp<MaskShaderEffect> MaskShaderEffect::Make(sk_sp<RenderNode> child, sk_sp<SkShader> sh) {
25 return child ? sk_sp<MaskShaderEffect>(new MaskShaderEffect(std::move(child), std::move(sh)))
26 : nullptr;
27 }
28
MaskShaderEffect(sk_sp<RenderNode> child,sk_sp<SkShader> sh)29 MaskShaderEffect::MaskShaderEffect(sk_sp<RenderNode> child, sk_sp<SkShader> sh)
30 : INHERITED(std::move(child))
31 , fShader(std::move(sh)) {
32 }
33
onRender(SkCanvas * canvas,const RenderContext * ctx) const34 void MaskShaderEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
35 const auto local_ctx = ScopedRenderContext(canvas, ctx)
36 .modulateMaskShader(fShader, canvas->getTotalMatrix());
37
38 this->INHERITED::onRender(canvas, local_ctx);
39 }
40
Make(sk_sp<RenderNode> child,sk_sp<Shader> shader)41 sk_sp<ShaderEffect> ShaderEffect::Make(sk_sp<RenderNode> child, sk_sp<Shader> shader) {
42 return child ? sk_sp<ShaderEffect>(new ShaderEffect(std::move(child), std::move(shader)))
43 : nullptr;
44 }
45
ShaderEffect(sk_sp<RenderNode> child,sk_sp<Shader> shader)46 ShaderEffect::ShaderEffect(sk_sp<RenderNode> child, sk_sp<Shader> shader)
47 : INHERITED(std::move(child))
48 , fShader(std::move(shader)) {
49 if (fShader) {
50 this->observeInval(fShader);
51 }
52 }
53
~ShaderEffect()54 ShaderEffect::~ShaderEffect() {
55 if (fShader) {
56 this->unobserveInval(fShader);
57 }
58 }
59
setShader(sk_sp<Shader> sh)60 void ShaderEffect::setShader(sk_sp<Shader> sh) {
61 if (fShader) {
62 this->unobserveInval(fShader);
63 }
64
65 fShader = std::move(sh);
66
67 if (fShader) {
68 this->observeInval(fShader);
69 }
70 }
onRevalidate(InvalidationController * ic,const SkMatrix & ctm)71 SkRect ShaderEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
72 if (fShader) {
73 fShader->revalidate(ic, ctm);
74 }
75
76 return this->INHERITED::onRevalidate(ic, ctm);
77 }
78
onRender(SkCanvas * canvas,const RenderContext * ctx) const79 void ShaderEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
80 const auto local_ctx = ScopedRenderContext(canvas, ctx)
81 .modulateShader(fShader ? fShader->getShader() : nullptr, canvas->getTotalMatrix());
82
83 this->INHERITED::onRender(canvas, local_ctx);
84 }
85
Shader()86 Shader::Shader() : INHERITED(kBubbleDamage_Trait) {}
87
88 Shader::~Shader() = default;
89
onRevalidate(InvalidationController *,const SkMatrix &)90 SkRect Shader::onRevalidate(InvalidationController*, const SkMatrix&) {
91 SkASSERT(this->hasInval());
92
93 fShader = this->onRevalidateShader();
94 return SkRect::MakeEmpty();
95 }
96
Make(sk_sp<RenderNode> child,sk_sp<ImageFilter> filter)97 sk_sp<RenderNode> ImageFilterEffect::Make(sk_sp<RenderNode> child, sk_sp<ImageFilter> filter) {
98 return filter ? sk_sp<RenderNode>(new ImageFilterEffect(std::move(child), std::move(filter)))
99 : child;
100 }
101
ImageFilterEffect(sk_sp<RenderNode> child,sk_sp<ImageFilter> filter)102 ImageFilterEffect::ImageFilterEffect(sk_sp<RenderNode> child, sk_sp<ImageFilter> filter)
103 // filters always override descendent damage
104 : INHERITED(std::move(child), kOverrideDamage_Trait)
105 , fImageFilter(std::move(filter)) {
106 this->observeInval(fImageFilter);
107 }
108
~ImageFilterEffect()109 ImageFilterEffect::~ImageFilterEffect() {
110 this->unobserveInval(fImageFilter);
111 }
112
onRevalidate(InvalidationController * ic,const SkMatrix & ctm)113 SkRect ImageFilterEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
114 const auto content_bounds = this->INHERITED::onRevalidate(ic, ctm);
115
116 if (fCropping == Cropping::kContent) {
117 fImageFilter->setCropRect(content_bounds);
118 } else {
119 fImageFilter->setCropRect(std::nullopt);
120 }
121
122 // FIXME: image filter effects should replace the descendents' damage!
123 fImageFilter->revalidate(ic, ctm);
124
125 const auto& filter = fImageFilter->getFilter();
126
127 // Would be nice for this this to stick, but canComputeFastBounds()
128 // appears to be conservative (false negatives).
129 // SkASSERT(!filter || filter->canComputeFastBounds());
130
131 return filter ? filter->computeFastBounds(content_bounds)
132 : content_bounds;
133 }
134
onNodeAt(const SkPoint & p) const135 const RenderNode* ImageFilterEffect::onNodeAt(const SkPoint& p) const {
136 // TODO: map p through the filter DAG and dispatch to descendants?
137 // For now, image filters occlude hit-testing.
138 SkASSERT(this->bounds().contains(p.x(), p.y()));
139 return this;
140 }
141
onRender(SkCanvas * canvas,const RenderContext * ctx) const142 void ImageFilterEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
143 // Note: we're using the source content bounds for saveLayer, not our local/filtered bounds.
144 const auto filter_ctx =
145 ScopedRenderContext(canvas, ctx).setFilterIsolation(this->getChild()->bounds(),
146 canvas->getTotalMatrix(),
147 fImageFilter->getFilter());
148 this->INHERITED::onRender(canvas, filter_ctx);
149 }
150
ImageFilter()151 ImageFilter::ImageFilter() : INHERITED(kBubbleDamage_Trait) {}
152
153 ImageFilter::~ImageFilter() = default;
154
onRevalidate(InvalidationController *,const SkMatrix &)155 SkRect ImageFilter::onRevalidate(InvalidationController*, const SkMatrix&) {
156 SkASSERT(this->hasInval());
157
158 fFilter = this->onRevalidateFilter();
159 return SkRect::MakeEmpty();
160 }
161
162 ExternalImageFilter:: ExternalImageFilter() = default;
163 ExternalImageFilter::~ExternalImageFilter() = default;
164
Make()165 sk_sp<DropShadowImageFilter> DropShadowImageFilter::Make() {
166 return sk_sp<DropShadowImageFilter>(new DropShadowImageFilter());
167 }
168
DropShadowImageFilter()169 DropShadowImageFilter::DropShadowImageFilter()
170 : INHERITED() {}
171
172 DropShadowImageFilter::~DropShadowImageFilter() = default;
173
onRevalidateFilter()174 sk_sp<SkImageFilter> DropShadowImageFilter::onRevalidateFilter() {
175 if (fMode == Mode::kShadowOnly) {
176 return SkImageFilters::DropShadowOnly(fOffset.x(), fOffset.y(), fSigma.x(), fSigma.y(),
177 fColor, nullptr, this->getCropRect());
178 } else {
179 return SkImageFilters::DropShadow(fOffset.x(), fOffset.y(), fSigma.x(), fSigma.y(),
180 fColor, nullptr, this->getCropRect());
181 }
182 }
183
Make()184 sk_sp<BlurImageFilter> BlurImageFilter::Make() {
185 return sk_sp<BlurImageFilter>(new BlurImageFilter());
186 }
187
BlurImageFilter()188 BlurImageFilter::BlurImageFilter()
189 : INHERITED() {}
190
191 BlurImageFilter::~BlurImageFilter() = default;
192
onRevalidateFilter()193 sk_sp<SkImageFilter> BlurImageFilter::onRevalidateFilter() {
194 // Tile modes other than kDecal require an explicit crop rect.
195 SkASSERT(fTileMode == SkTileMode::kDecal || this->getCropRect().has_value());
196 return SkImageFilters::Blur(fSigma.x(), fSigma.y(), fTileMode, nullptr, this->getCropRect());
197 }
198
Make(sk_sp<RenderNode> child,sk_sp<SkBlender> blender)199 sk_sp<BlenderEffect> BlenderEffect::Make(sk_sp<RenderNode> child, sk_sp<SkBlender> blender) {
200 return child ? sk_sp<BlenderEffect>(new BlenderEffect(std::move(child), std::move(blender)))
201 : nullptr;
202 }
203
BlenderEffect(sk_sp<RenderNode> child,sk_sp<SkBlender> blender)204 BlenderEffect::BlenderEffect(sk_sp<RenderNode> child, sk_sp<SkBlender> blender)
205 : INHERITED(std::move(child))
206 , fBlender (std::move(blender)) {}
207
208 BlenderEffect::~BlenderEffect() = default;
209
onRender(SkCanvas * canvas,const RenderContext * ctx) const210 void BlenderEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
211 const auto local_ctx = ScopedRenderContext(canvas, ctx).modulateBlender(fBlender);
212
213 this->INHERITED::onRender(canvas, local_ctx);
214 }
215
onNodeAt(const SkPoint & p) const216 const RenderNode* BlenderEffect::onNodeAt(const SkPoint& p) const {
217 // TODO: we likely need to do something more sophisticated than delegate to descendants here.
218 return this->INHERITED::onNodeAt(p);
219 }
220
Make(sk_sp<RenderNode> child,SkBlendMode mode)221 sk_sp<LayerEffect> LayerEffect::Make(sk_sp<RenderNode> child, SkBlendMode mode) {
222 return child ? sk_sp<LayerEffect>(new LayerEffect(std::move(child), mode))
223 : nullptr;
224 }
225
LayerEffect(sk_sp<RenderNode> child,SkBlendMode mode)226 LayerEffect::LayerEffect(sk_sp<RenderNode> child, SkBlendMode mode)
227 : INHERITED(std::move(child))
228 , fMode(mode) {}
229
230 LayerEffect::~LayerEffect() = default;
231
onRender(SkCanvas * canvas,const RenderContext * ctx) const232 void LayerEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
233 SkAutoCanvasRestore acr(canvas, false);
234
235 // Commit any potential pending paint effects to their own layer.
236 const auto local_ctx = ScopedRenderContext(canvas, ctx).setIsolation(this->bounds(),
237 canvas->getTotalMatrix(),
238 true);
239
240 SkPaint layer_paint;
241 if (ctx) {
242 // Apply all optional context overrides upfront.
243 ctx->modulatePaint(canvas->getTotalMatrix(), &layer_paint);
244 }
245 layer_paint.setBlendMode(fMode);
246
247 canvas->saveLayer(nullptr, &layer_paint);
248
249 this->INHERITED::onRender(canvas, nullptr);
250 }
251
252 } // namespace sksg
253