• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
AutoInvalBlocker(const MotionBlurEffect * mb,const sk_sp<RenderNode> & child)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 
~AutoInvalBlocker()28     ~AutoInvalBlocker() {
29         fMBNode->observeInval(fChild);
30     }
31 
32 private:
33     MotionBlurEffect*        fMBNode;
34     const sk_sp<RenderNode>& fChild;
35 };
36 
Make(sk_sp<Animator> animator,sk_sp<sksg::RenderNode> child,size_t samples_per_frame,float shutter_angle,float shutter_phase)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 
MotionBlurEffect(sk_sp<Animator> animator,sk_sp<sksg::RenderNode> child,size_t samples,float phase,float dt)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 
onNodeAt(const SkPoint &) const66 const sksg::RenderNode* MotionBlurEffect::onNodeAt(const SkPoint&) const {
67     return nullptr;
68 }
69 
seekToSample(size_t sample_idx,const SkMatrix & ctm) const70 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 
onRevalidate(sksg::InvalidationController *,const SkMatrix & ctm)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 
renderToRaster8888Pow2Samples(SkCanvas * canvas,const RenderContext * ctx) const90 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 
onRender(SkCanvas * canvas,const RenderContext * ctx) const193 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