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/skottie/src/effects/MotionBlurEffect.h" |
9 | |
10 | #include "include/core/SkCanvas.h" |
11 | #include "include/core/SkMath.h" |
12 | #include "include/core/SkPixmap.h" |
13 | #include "include/private/SkVx.h" |
14 | #include "modules/skottie/src/animator/Animator.h" |
15 | #include "src/core/SkMathPriv.h" |
16 | |
17 | namespace skottie { |
18 | namespace internal { |
19 | |
20 | class MotionBlurEffect::AutoInvalBlocker { |
21 | public: |
22 | AutoInvalBlocker(const MotionBlurEffect* mb, const sk_sp<RenderNode>& child) |
23 | : fMBNode(const_cast<MotionBlurEffect*>(mb)) |
24 | , fChild(child) { |
25 | fMBNode->unobserveInval(fChild); |
26 | } |
27 | |
28 | ~AutoInvalBlocker() { |
29 | fMBNode->observeInval(fChild); |
30 | } |
31 | |
32 | private: |
33 | MotionBlurEffect* fMBNode; |
34 | const sk_sp<RenderNode>& fChild; |
35 | }; |
36 | |
37 | sk_sp<MotionBlurEffect> MotionBlurEffect::Make(sk_sp<Animator> animator, |
38 | sk_sp<sksg::RenderNode> child, |
39 | size_t samples_per_frame, |
40 | float shutter_angle, float shutter_phase) { |
41 | if (!samples_per_frame || shutter_angle <= 0) { |
42 | return nullptr; |
43 | } |
44 | |
45 | // shutter_angle is [ 0 .. 720], mapped to [ 0 .. 2] (frame space) |
46 | // shutter_phase is [-360 .. 360], mapped to [-1 .. 1] (frame space) |
47 | const auto samples_duration = shutter_angle / 360, |
48 | phase = shutter_phase / 360, |
49 | dt = samples_duration / (samples_per_frame - 1); |
50 | |
51 | return sk_sp<MotionBlurEffect>(new MotionBlurEffect(std::move(animator), |
52 | std::move(child), |
53 | samples_per_frame, |
54 | phase, dt)); |
55 | } |
56 | |
57 | MotionBlurEffect::MotionBlurEffect(sk_sp<Animator> animator, |
58 | sk_sp<sksg::RenderNode> child, |
59 | size_t samples, float phase, float dt) |
60 | : INHERITED({std::move(child)}) |
61 | , fAnimator(std::move(animator)) |
62 | , fSampleCount(samples) |
63 | , fPhase(phase) |
64 | , fDT(dt) {} |
65 | |
66 | const sksg::RenderNode* MotionBlurEffect::onNodeAt(const SkPoint&) const { |
67 | return nullptr; |
68 | } |
69 | |
70 | SkRect MotionBlurEffect::seekToSample(size_t sample_idx, const SkMatrix& ctm) const { |
71 | SkASSERT(sample_idx < fSampleCount); |
72 | fAnimator->seek(fT + fPhase + fDT * sample_idx); |
73 | |
74 | SkASSERT(this->children().size() == 1ul); |
75 | return this->children()[0]->revalidate(nullptr, ctm); |
76 | } |
77 | |
78 | SkRect MotionBlurEffect::onRevalidate(sksg::InvalidationController*, const SkMatrix& ctm) { |
79 | SkRect bounds = SkRect::MakeEmpty(); |
80 | fVisibleSampleCount = 0; |
81 | |
82 | for (size_t i = 0; i < fSampleCount; ++i) { |
83 | bounds.join(this->seekToSample(i, ctm)); |
84 | fVisibleSampleCount += SkToSizeT(this->children()[0]->isVisible()); |
85 | } |
86 | |
87 | return bounds; |
88 | } |
89 | |
90 | void MotionBlurEffect::renderToRaster8888Pow2Samples(SkCanvas* canvas, |
91 | const RenderContext* ctx) const { |
92 | // canvas is raster backed and RGBA 8888 or BGRA 8888, and fSamples is a power of 2. |
93 | // We can play dirty tricks. |
94 | |
95 | // Don't worry about "Next"... this is exact. |
96 | const int shift = SkNextLog2(fVisibleSampleCount); |
97 | SkASSERT((size_t(1)<<shift) == fVisibleSampleCount); |
98 | |
99 | SkASSERT(this->children().size() == 1ul); |
100 | const sk_sp<RenderNode>& child = this->children()[0]; |
101 | |
102 | SkAutoCanvasRestore acr(canvas, false); |
103 | canvas->saveLayer(this->bounds(), nullptr); |
104 | |
105 | SkImageInfo info; |
106 | size_t rowBytes; |
107 | auto layer = (uint32_t*)canvas->accessTopLayerPixels(&info, &rowBytes); |
108 | SkASSERT(layer); |
109 | SkASSERT(info.colorType() == kRGBA_8888_SkColorType || |
110 | info.colorType() == kBGRA_8888_SkColorType); |
111 | |
112 | SkASSERT(!info.isEmpty()); |
113 | std::vector<uint64_t> accum(info.width() * info.height()); |
114 | |
115 | SkDEBUGCODE(size_t frames_rendered = 0;) |
116 | bool needs_clear = false; // Cleared initially by saveLayer(). |
117 | for (size_t i = 0; i < fSampleCount; ++i) { |
118 | this->seekToSample(i, canvas->getTotalMatrix()); |
119 | |
120 | if (!child->isVisible()) { |
121 | continue; |
122 | } |
123 | |
124 | // Draw this subframe. |
125 | if (needs_clear) { |
126 | canvas->clear(0); |
127 | } |
128 | needs_clear = true; |
129 | child->render(canvas, ctx); |
130 | SkDEBUGCODE(frames_rendered++;) |
131 | |
132 | // Pluck out the pixels we've drawn in the layer. |
133 | const uint32_t* src = layer; |
134 | uint64_t* dst = accum.data(); |
135 | |
136 | for (int y = 0; y < info.height(); y++) { |
137 | // Expand 8-bit to 16-bit and accumulate. |
138 | int n = info.width(); |
139 | const auto row = src; |
140 | while (n >= 4) { |
141 | auto s = skvx::Vec<16, uint8_t >::Load(src); |
142 | auto d = skvx::Vec<16, uint16_t>::Load(dst); |
143 | |
144 | (d + skvx::cast<uint16_t>(s)).store(dst); |
145 | |
146 | src += 4; |
147 | dst += 4; |
148 | n -= 4; |
149 | } |
150 | while (n) { |
151 | auto s = skvx::Vec<4, uint8_t >::Load(src); |
152 | auto d = skvx::Vec<4, uint16_t>::Load(dst); |
153 | |
154 | (d + skvx::cast<uint16_t>(s)).store(dst); |
155 | |
156 | src += 1; |
157 | dst += 1; |
158 | n -= 1; |
159 | } |
160 | src = (const uint32_t*)( (const char*)row + rowBytes ); |
161 | } |
162 | } |
163 | SkASSERT(frames_rendered == fVisibleSampleCount); |
164 | |
165 | // Actually draw the frame using the accumulated subframes. |
166 | const uint64_t* src = accum.data(); |
167 | uint32_t* dst = layer; |
168 | for (int y = 0; y < info.height(); y++) { |
169 | // Divide accumulated subframes through by sample count. |
170 | int n = info.width(); |
171 | const auto row = dst; |
172 | while (n >= 4) { |
173 | auto s = skvx::Vec<16, uint16_t>::Load(src); |
174 | skvx::cast<uint8_t>(s >> shift).store(dst); |
175 | |
176 | src += 4; |
177 | dst += 4; |
178 | n -= 4; |
179 | } |
180 | while (n) { |
181 | auto s = skvx::Vec<4, uint16_t>::Load(src); |
182 | skvx::cast<uint8_t>(s >> shift).store(dst); |
183 | |
184 | src += 1; |
185 | dst += 1; |
186 | n -= 1; |
187 | } |
188 | |
189 | dst = (uint32_t*)( (char*)row + rowBytes ); |
190 | } |
191 | } |
192 | |
193 | void MotionBlurEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const { |
194 | if (!fVisibleSampleCount) { |
195 | return; |
196 | } |
197 | |
198 | SkASSERT(this->children().size() == 1ul); |
199 | const auto& child = this->children()[0]; |
200 | |
201 | // We're about to mutate/revalidate the subtree for sampling. Capture the invalidation |
202 | // at this scope, to prevent dirtying ancestor SG nodes (no way to revalidate the global scene). |
203 | AutoInvalBlocker aib(this, child); |
204 | |
205 | SkPixmap pm; |
206 | if (canvas->peekPixels(&pm) && (canvas->imageInfo().colorType() == kRGBA_8888_SkColorType || |
207 | canvas->imageInfo().colorType() == kBGRA_8888_SkColorType ) |
208 | && SkIsPow2(fVisibleSampleCount)) { |
209 | this->renderToRaster8888Pow2Samples(canvas, ctx); |
210 | return; |
211 | } |
212 | |
213 | SkAutoCanvasRestore acr(canvas, false); |
214 | |
215 | // Accumulate in F16 for more precision. |
216 | canvas->saveLayer(SkCanvas::SaveLayerRec(&this->bounds(), nullptr, SkCanvas::kF16ColorType)); |
217 | |
218 | const float frame_alpha = 1.0f / fVisibleSampleCount; |
219 | |
220 | // Depending on whether we can defer frame blending, |
221 | // use a local (deferred) RenderContext or an explicit layer for frame/content rendering. |
222 | ScopedRenderContext frame_ctx(canvas, ctx); |
223 | SkPaint frame_paint; |
224 | |
225 | const bool isolate_frames = frame_ctx->fBlendMode != SkBlendMode::kSrcOver; |
226 | if (isolate_frames) { |
227 | frame_paint.setAlphaf(frame_alpha); |
228 | frame_paint.setBlendMode(SkBlendMode::kPlus); |
229 | } else { |
230 | frame_ctx = frame_ctx.modulateOpacity(frame_alpha) |
231 | .modulateBlendMode(SkBlendMode::kPlus); |
232 | } |
233 | |
234 | SkDEBUGCODE(size_t frames_rendered = 0;) |
235 | for (size_t i = 0; i < fSampleCount; ++i) { |
236 | this->seekToSample(i, canvas->getTotalMatrix()); |
237 | |
238 | if (!child->isVisible()) { |
239 | continue; |
240 | } |
241 | |
242 | SkAutoCanvasRestore acr(canvas, false); |
243 | if (isolate_frames) { |
244 | canvas->saveLayer(nullptr, &frame_paint); |
245 | } |
246 | |
247 | child->render(canvas, frame_ctx); |
248 | SkDEBUGCODE(frames_rendered++;) |
249 | } |
250 | |
251 | SkASSERT(frames_rendered == fVisibleSampleCount); |
252 | } |
253 | |
254 | } // namespace internal |
255 | } // namespace skottie |
256 | |