• 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 "modules/sksg/include/SkSGInvalidationController.h"
12 
13 namespace skottie {
14 namespace internal {
15 
Make(sk_sp<sksg::Animator> animator,sk_sp<sksg::RenderNode> child,size_t samples_per_frame,float shutter_angle,float shutter_phase)16 sk_sp<MotionBlurEffect> MotionBlurEffect::Make(sk_sp<sksg::Animator> animator,
17                                                sk_sp<sksg::RenderNode> child,
18                                                size_t samples_per_frame,
19                                                float shutter_angle, float shutter_phase) {
20     if (!samples_per_frame || shutter_angle <= 0) {
21         return nullptr;
22     }
23 
24     // shutter_angle is [   0 .. 720], mapped to [ 0 .. 2] (frame space)
25     // shutter_phase is [-360 .. 360], mapped to [-1 .. 1] (frame space)
26     const auto samples_duration = shutter_angle / 360,
27                           phase = shutter_phase / 360,
28                              dt = samples_duration / (samples_per_frame - 1);
29 
30     return sk_sp<MotionBlurEffect>(new MotionBlurEffect(std::move(animator),
31                                                         std::move(child),
32                                                         samples_per_frame,
33                                                         phase, dt));
34 }
35 
MotionBlurEffect(sk_sp<sksg::Animator> animator,sk_sp<sksg::RenderNode> child,size_t samples,float phase,float dt)36 MotionBlurEffect::MotionBlurEffect(sk_sp<sksg::Animator> animator,
37                                    sk_sp<sksg::RenderNode> child,
38                                    size_t samples, float phase, float dt)
39     : INHERITED({std::move(child)})
40     , fAnimator(std::move(animator))
41     , fSampleCount(samples)
42     , fPhase(phase)
43     , fDT(dt) {}
44 
onNodeAt(const SkPoint &) const45 const sksg::RenderNode* MotionBlurEffect::onNodeAt(const SkPoint&) const {
46     return nullptr;
47 }
48 
onRevalidate(sksg::InvalidationController *,const SkMatrix & ctm)49 SkRect MotionBlurEffect::onRevalidate(sksg::InvalidationController*, const SkMatrix& ctm) {
50     SkASSERT(this->children().size() == 1ul);
51     const auto& child = this->children()[0];
52 
53     auto bounds = SkRect::MakeEmpty();
54 
55     // Use a local inval controller to suppress descendent invals during sampling
56     // (superseded by our local inval bounds).
57     sksg::InvalidationController ic;
58 
59     auto t = fT + fPhase;
60 
61     for (size_t i = 0; i < fSampleCount; ++i) {
62         fAnimator->tick(t);
63         t += fDT;
64 
65         if (!child->isVisible()) {
66             continue;
67         }
68 
69         bounds.join(child->revalidate(&ic, ctm));
70     }
71 
72     return bounds;
73 }
74 
onRender(SkCanvas * canvas,const RenderContext * ctx) const75 void MotionBlurEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
76     SkASSERT(this->children().size() == 1ul);
77     const auto& child = this->children()[0];
78 
79     SkAutoCanvasRestore acr(canvas, false);
80 
81     // Accumulate in F16 for more precision.
82     canvas->saveLayer(SkCanvas::SaveLayerRec(&this->bounds(), nullptr, SkCanvas::kF16ColorType));
83 
84     const auto frame_alpha = 1.0f / fSampleCount;
85 
86     // Depending on whether we can defer frame blending,
87     // use a local (deferred) RenderContext or an explicit layer for frame/content rendering.
88     ScopedRenderContext frame_ctx(canvas, ctx);
89     SkPaint             frame_paint;
90 
91     const auto isolate_frames = ctx->fBlendMode != SkBlendMode::kSrcOver;
92     if (isolate_frames) {
93         frame_paint.setAlphaf(frame_alpha);
94         frame_paint.setBlendMode(SkBlendMode::kPlus);
95     } else {
96         frame_ctx = frame_ctx.modulateOpacity(frame_alpha)
97                              .modulateBlendMode(SkBlendMode::kPlus);
98     }
99 
100     sksg::InvalidationController ic;
101 
102     auto t = fT + fPhase;
103 
104     for (size_t i = 0; i < fSampleCount; ++i) {
105         fAnimator->tick(t);
106         t += fDT;
107 
108         if (!child->isVisible()) {
109             continue;
110         }
111 
112         child->revalidate(&ic, canvas->getTotalMatrix());
113 
114         SkAutoCanvasRestore acr(canvas, false);
115         if (isolate_frames) {
116             canvas->saveLayer(nullptr, &frame_paint);
117         }
118 
119         child->render(canvas, frame_ctx);
120     }
121 }
122 
123 } // namespace internal
124 } // namespace skottie
125