Flutter Engine
The Flutter Engine
MotionBlurEffect.cpp
Go to the documentation of this file.
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
9
14#include "src/base/SkMathPriv.h"
15#include "src/base/SkVx.h"
16
17namespace skottie {
18namespace internal {
19
21public:
23 : fMBNode(const_cast<MotionBlurEffect*>(mb))
24 , fChild(child) {
25 fMBNode->unobserveInval(fChild);
26 }
27
29 fMBNode->observeInval(fChild);
30 }
31
32private:
33 MotionBlurEffect* fMBNode;
34 const sk_sp<RenderNode>& fChild;
35};
36
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
57MotionBlurEffect::MotionBlurEffect(sk_sp<Animator> animator,
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
66const sksg::RenderNode* MotionBlurEffect::onNodeAt(const SkPoint&) const {
67 return nullptr;
68}
69
70SkRect 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
78SkRect MotionBlurEffect::onRevalidate(sksg::InvalidationController*, const SkMatrix& ctm) {
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
90void 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
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) {
143
144 (d + skvx::cast<uint16_t>(s)).store(dst);
145
146 src += 4;
147 dst += 4;
148 n -= 4;
149 }
150 while (n) {
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) {
174 skvx::cast<uint8_t>(s >> shift).store(dst);
175
176 src += 4;
177 dst += 4;
178 n -= 4;
179 }
180 while (n) {
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
193void 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 ||
208 && SkIsPow2(fVisibleSampleCount)) {
209 this->renderToRaster8888Pow2Samples(canvas, ctx);
210 return;
211 }
212
213 SkAutoCanvasRestore acr1(canvas, false);
214
215 // Accumulate in F16 for more precision.
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->fBlender;
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 .modulateBlender(SkBlender::Mode(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 acr2(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
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
SkScalar fPhase
Definition: DashOp.cpp:189
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kPlus
r = min(s + d, 1)
@ kBGRA_8888_SkColorType
pixel with 8 bits for blue, green, red, alpha; in 32-bit word
Definition: SkColorType.h:26
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
static int SkNextLog2(uint32_t value)
Definition: SkMathPriv.h:238
constexpr bool SkIsPow2(T value)
Definition: SkMath.h:51
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
constexpr size_t SkToSizeT(S x)
Definition: SkTo.h:31
SI void store(P *ptr, const T &val)
static sk_sp< SkBlender > Mode(SkBlendMode mode)
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition: SkCanvas.cpp:496
bool peekPixels(SkPixmap *pixmap)
Definition: SkCanvas.cpp:1237
void clear(SkColor color)
Definition: SkCanvas.h:1199
SkMatrix getTotalMatrix() const
Definition: SkCanvas.cpp:1629
@ kF16ColorType
Definition: SkCanvas.h:674
SkImageInfo imageInfo() const
Definition: SkCanvas.cpp:1206
void * accessTopLayerPixels(SkImageInfo *info, size_t *rowBytes, SkIPoint *origin=nullptr)
Definition: SkCanvas.cpp:1245
void setBlendMode(SkBlendMode mode)
Definition: SkPaint.cpp:151
void setAlphaf(float a)
Definition: SkPaint.cpp:130
AutoInvalBlocker(const MotionBlurEffect *mb, const sk_sp< RenderNode > &child)
static sk_sp< MotionBlurEffect > Make(sk_sp< Animator > animator, sk_sp< sksg::RenderNode > child, size_t samples_per_frame, float shutter_angle, float shutter_phase)
const std::vector< sk_sp< RenderNode > > & children() const
void observeInval(const sk_sp< Node > &)
Definition: SkSGNode.cpp:61
void unobserveInval(const sk_sp< Node > &)
Definition: SkSGNode.cpp:84
const SkRect & bounds() const
Definition: SkSGNode.h:55
bool isVisible() const
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
struct MyStruct s
double y
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
SkColorType colorType() const
Definition: SkImageInfo.h:373
static constexpr SkRect MakeEmpty()
Definition: SkRect.h:595
void join(const SkRect &r)
Definition: SkRect.cpp:126
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition: SkVx.h:109