Coverage Report

Created: 2021-08-22 09:07

/src/skia/modules/skottie/src/effects/MotionBlurEffect.cpp
Line
Count
Source (jump to first uncovered line)
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
0
        , fChild(child) {
25
0
        fMBNode->unobserveInval(fChild);
26
0
    }
27
28
0
    ~AutoInvalBlocker() {
29
0
        fMBNode->observeInval(fChild);
30
0
    }
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
0
                                               float shutter_angle, float shutter_phase) {
41
0
    if (!samples_per_frame || shutter_angle <= 0) {
42
0
        return nullptr;
43
0
    }
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
0
    const auto samples_duration = shutter_angle / 360,
48
0
                          phase = shutter_phase / 360,
49
0
                             dt = samples_duration / (samples_per_frame - 1);
50
51
0
    return sk_sp<MotionBlurEffect>(new MotionBlurEffect(std::move(animator),
52
0
                                                        std::move(child),
53
0
                                                        samples_per_frame,
54
0
                                                        phase, dt));
55
0
}
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
0
    , fDT(dt) {}
65
66
0
const sksg::RenderNode* MotionBlurEffect::onNodeAt(const SkPoint&) const {
67
0
    return nullptr;
68
0
}
69
70
0
SkRect MotionBlurEffect::seekToSample(size_t sample_idx, const SkMatrix& ctm) const {
71
0
    SkASSERT(sample_idx < fSampleCount);
72
0
    fAnimator->seek(fT + fPhase + fDT * sample_idx);
73
74
0
    SkASSERT(this->children().size() == 1ul);
75
0
    return this->children()[0]->revalidate(nullptr, ctm);
76
0
}
77
78
0
SkRect MotionBlurEffect::onRevalidate(sksg::InvalidationController*, const SkMatrix& ctm) {
79
0
    SkRect bounds       = SkRect::MakeEmpty();
80
0
    fVisibleSampleCount = 0;
81
82
0
    for (size_t i = 0; i < fSampleCount; ++i) {
83
0
        bounds.join(this->seekToSample(i, ctm));
84
0
        fVisibleSampleCount += SkToSizeT(this->children()[0]->isVisible());
85
0
    }
86
87
0
    return bounds;
88
0
}
89
90
void MotionBlurEffect::renderToRaster8888Pow2Samples(SkCanvas* canvas,
91
0
                                                     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
0
    const int shift = SkNextLog2(fVisibleSampleCount);
97
0
    SkASSERT((size_t(1)<<shift) == fVisibleSampleCount);
98
99
0
    SkASSERT(this->children().size() == 1ul);
100
0
    const sk_sp<RenderNode>& child = this->children()[0];
101
102
0
    SkAutoCanvasRestore acr(canvas, false);
103
0
    canvas->saveLayer(this->bounds(), nullptr);
104
105
0
    SkImageInfo info;
106
0
    size_t rowBytes;
107
0
    auto layer = (uint32_t*)canvas->accessTopLayerPixels(&info, &rowBytes);
108
0
    SkASSERT(layer);
109
0
    SkASSERT(info.colorType() == kRGBA_8888_SkColorType ||
110
0
             info.colorType() == kBGRA_8888_SkColorType);
111
112
0
    SkASSERT(!info.isEmpty());
113
0
    std::vector<uint64_t> accum(info.width() * info.height());
114
115
0
    SkDEBUGCODE(size_t frames_rendered = 0;)
116
0
    bool needs_clear = false;  // Cleared initially by saveLayer().
117
0
    for (size_t i = 0; i < fSampleCount; ++i) {
118
0
        this->seekToSample(i, canvas->getTotalMatrix());
119
120
0
        if (!child->isVisible()) {
121
0
            continue;
122
0
        }
123
124
        // Draw this subframe.
125
0
        if (needs_clear) {
126
0
            canvas->clear(0);
127
0
        }
128
0
        needs_clear = true;
129
0
        child->render(canvas, ctx);
130
0
        SkDEBUGCODE(frames_rendered++;)
131
132
        // Pluck out the pixels we've drawn in the layer.
133
0
        const uint32_t* src = layer;
134
0
              uint64_t* dst = accum.data();
135
136
0
        for (int y = 0; y < info.height(); y++) {
137
            // Expand 8-bit to 16-bit and accumulate.
138
0
            int n = info.width();
139
0
            const auto row = src;
140
0
            while (n >= 4) {
141
0
                auto s = skvx::Vec<16, uint8_t >::Load(src);
142
0
                auto d = skvx::Vec<16, uint16_t>::Load(dst);
143
144
0
                (d + skvx::cast<uint16_t>(s)).store(dst);
145
146
0
                src += 4;
147
0
                dst += 4;
148
0
                n   -= 4;
149
0
            }
150
0
            while (n) {
151
0
                auto s = skvx::Vec<4, uint8_t >::Load(src);
152
0
                auto d = skvx::Vec<4, uint16_t>::Load(dst);
153
154
0
                (d + skvx::cast<uint16_t>(s)).store(dst);
155
156
0
                src += 1;
157
0
                dst += 1;
158
0
                n   -= 1;
159
0
            }
160
0
            src = (const uint32_t*)( (const char*)row + rowBytes );
161
0
        }
162
0
    }
163
0
    SkASSERT(frames_rendered == fVisibleSampleCount);
164
165
    // Actually draw the frame using the accumulated subframes.
166
0
    const uint64_t* src = accum.data();
167
0
          uint32_t* dst = layer;
168
0
    for (int y = 0; y < info.height(); y++) {
169
        // Divide accumulated subframes through by sample count.
170
0
        int n = info.width();
171
0
        const auto row = dst;
172
0
        while (n >= 4) {
173
0
            auto s = skvx::Vec<16, uint16_t>::Load(src);
174
0
            skvx::cast<uint8_t>(s >> shift).store(dst);
175
176
0
            src += 4;
177
0
            dst += 4;
178
0
            n   -= 4;
179
0
        }
180
0
        while (n) {
181
0
            auto s = skvx::Vec<4, uint16_t>::Load(src);
182
0
            skvx::cast<uint8_t>(s >> shift).store(dst);
183
184
0
            src += 1;
185
0
            dst += 1;
186
0
            n   -= 1;
187
0
        }
188
189
0
        dst = (uint32_t*)( (char*)row + rowBytes );
190
0
    }
191
0
}
192
193
0
void MotionBlurEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
194
0
    if (!fVisibleSampleCount) {
195
0
        return;
196
0
    }
197
198
0
    SkASSERT(this->children().size() == 1ul);
199
0
    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
0
    AutoInvalBlocker aib(this, child);
204
205
0
    SkPixmap pm;
206
0
    if (canvas->peekPixels(&pm) && (canvas->imageInfo().colorType() == kRGBA_8888_SkColorType ||
207
0
                                    canvas->imageInfo().colorType() == kBGRA_8888_SkColorType   )
208
0
                                && SkIsPow2(fVisibleSampleCount)) {
209
0
        this->renderToRaster8888Pow2Samples(canvas, ctx);
210
0
        return;
211
0
    }
212
213
0
    SkAutoCanvasRestore acr1(canvas, false);
214
215
    // Accumulate in F16 for more precision.
216
0
    canvas->saveLayer(SkCanvas::SaveLayerRec(&this->bounds(), nullptr, SkCanvas::kF16ColorType));
217
218
0
    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
0
    ScopedRenderContext frame_ctx(canvas, ctx);
223
0
    SkPaint             frame_paint;
224
225
0
    const bool isolate_frames = frame_ctx->fBlendMode != SkBlendMode::kSrcOver;
226
0
    if (isolate_frames) {
227
0
        frame_paint.setAlphaf(frame_alpha);
228
0
        frame_paint.setBlendMode(SkBlendMode::kPlus);
229
0
    } else {
230
0
        frame_ctx = frame_ctx.modulateOpacity(frame_alpha)
231
0
                             .modulateBlendMode(SkBlendMode::kPlus);
232
0
    }
233
234
0
    SkDEBUGCODE(size_t frames_rendered = 0;)
235
0
    for (size_t i = 0; i < fSampleCount; ++i) {
236
0
        this->seekToSample(i, canvas->getTotalMatrix());
237
238
0
        if (!child->isVisible()) {
239
0
            continue;
240
0
        }
241
242
0
        SkAutoCanvasRestore acr2(canvas, false);
243
0
        if (isolate_frames) {
244
0
            canvas->saveLayer(nullptr, &frame_paint);
245
0
        }
246
247
0
        child->render(canvas, frame_ctx);
248
0
        SkDEBUGCODE(frames_rendered++;)
249
0
    }
250
251
0
    SkASSERT(frames_rendered == fVisibleSampleCount);
252
0
}
253
254
} // namespace internal
255
} // namespace skottie