| 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 | , fChild(child) { | 
|---|
| 25 | fMBNode->unobserveInval(fChild); | 
|---|
| 26 | } | 
|---|
| 27 |  | 
|---|
| 28 | ~AutoInvalBlocker() { | 
|---|
| 29 | fMBNode->observeInval(fChild); | 
|---|
| 30 | } | 
|---|
| 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 | 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 |  | 
|---|
| 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 |  | 
|---|
| 66 | const sksg::RenderNode* MotionBlurEffect::onNodeAt(const SkPoint&) const { | 
|---|
| 67 | return nullptr; | 
|---|
| 68 | } | 
|---|
| 69 |  | 
|---|
| 70 | 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 |  | 
|---|
| 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 |  | 
|---|
| 90 | 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 |  | 
|---|
| 193 | 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 |  | 
|---|