Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
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
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)
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
#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
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static int SkNextLog2(uint32_t value)
Definition SkMathPriv.h:238
constexpr bool SkIsPow2(T value)
Definition SkMath.h:51
#define INHERITED(method,...)
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:500
bool peekPixels(SkPixmap *pixmap)
void clear(SkColor color)
Definition SkCanvas.h:1199
SkMatrix getTotalMatrix() const
@ kF16ColorType
Definition SkCanvas.h:674
SkImageInfo imageInfo() const
void * accessTopLayerPixels(SkImageInfo *info, size_t *rowBytes, SkIPoint *origin=nullptr)
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)
void onRender(SkCanvas *canvas, const RenderContext *ctx) const override
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 RenderNode * onNodeAt(const SkPoint &) const override
SkRect onRevalidate(sksg::InvalidationController *ic, const SkMatrix &ctm) override
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
ScopedRenderContext && modulateOpacity(float opacity)
ScopedRenderContext && modulateBlender(sk_sp< SkBlender >)
bool isVisible() const
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
struct MyStruct s
double y
dst
Definition cp.py:12
SkColorType colorType() const
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