1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "AnimatedImageDrawable.h"
18 #include "AnimatedImageThread.h"
19 
20 #include "utils/TraceUtils.h"
21 
22 #include <SkPicture.h>
23 #include <SkRefCnt.h>
24 
25 #include <optional>
26 
27 namespace android {
28 
AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage,size_t bytesUsed)29 AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed)
30         : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) {
31     mTimeToShowNextSnapshot = ms2ns(mSkAnimatedImage->currentFrameDuration());
32 }
33 
syncProperties()34 void AnimatedImageDrawable::syncProperties() {
35     mProperties = mStagingProperties;
36 }
37 
start()38 bool AnimatedImageDrawable::start() {
39     if (mRunning) {
40         return false;
41     }
42 
43     mStarting = true;
44 
45     mRunning = true;
46     return true;
47 }
48 
stop()49 bool AnimatedImageDrawable::stop() {
50     bool wasRunning = mRunning;
51     mRunning = false;
52     return wasRunning;
53 }
54 
isRunning()55 bool AnimatedImageDrawable::isRunning() {
56     return mRunning;
57 }
58 
nextSnapshotReady() const59 bool AnimatedImageDrawable::nextSnapshotReady() const {
60     return mNextSnapshot.valid() &&
61            mNextSnapshot.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
62 }
63 
64 // Only called on the RenderThread while UI thread is locked.
isDirty(nsecs_t * outDelay)65 bool AnimatedImageDrawable::isDirty(nsecs_t* outDelay) {
66     *outDelay = 0;
67     const nsecs_t currentTime = systemTime(CLOCK_MONOTONIC);
68     const nsecs_t lastWallTime = mLastWallTime;
69 
70     mLastWallTime = currentTime;
71     if (!mRunning) {
72         return false;
73     }
74 
75     std::unique_lock lock{mSwapLock};
76     mCurrentTime += currentTime - lastWallTime;
77 
78     if (!mNextSnapshot.valid()) {
79         // Need to trigger onDraw in order to start decoding the next frame.
80         *outDelay = mTimeToShowNextSnapshot - mCurrentTime;
81         return true;
82     }
83 
84     if (mTimeToShowNextSnapshot > mCurrentTime) {
85         *outDelay = mTimeToShowNextSnapshot - mCurrentTime;
86     } else if (nextSnapshotReady()) {
87         // We have not yet updated mTimeToShowNextSnapshot. Read frame duration
88         // directly from mSkAnimatedImage.
89         lock.unlock();
90         std::unique_lock imageLock{mImageLock};
91         *outDelay = ms2ns(mSkAnimatedImage->currentFrameDuration());
92         return true;
93     } else {
94         // The next snapshot has not yet been decoded, but we've already passed
95         // time to draw it. There's not a good way to know when decoding will
96         // finish, so request an update immediately.
97         *outDelay = 0;
98     }
99 
100     return false;
101 }
102 
103 // Only called on the AnimatedImageThread.
decodeNextFrame()104 AnimatedImageDrawable::Snapshot AnimatedImageDrawable::decodeNextFrame() {
105     Snapshot snap;
106     {
107         std::unique_lock lock{mImageLock};
108         snap.mDurationMS = mSkAnimatedImage->decodeNextFrame();
109         snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
110     }
111 
112     return snap;
113 }
114 
115 // Only called on the AnimatedImageThread.
reset()116 AnimatedImageDrawable::Snapshot AnimatedImageDrawable::reset() {
117     Snapshot snap;
118     {
119         std::unique_lock lock{mImageLock};
120         mSkAnimatedImage->reset();
121         snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
122         snap.mDurationMS = mSkAnimatedImage->currentFrameDuration();
123     }
124 
125     return snap;
126 }
127 
128 // Only called on the RenderThread.
onDraw(SkCanvas * canvas)129 void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
130     std::optional<SkPaint> lazyPaint;
131     SkAutoCanvasRestore acr(canvas, false);
132     if (mProperties.mAlpha != SK_AlphaOPAQUE || mProperties.mColorFilter.get()) {
133         lazyPaint.emplace();
134         lazyPaint->setAlpha(mProperties.mAlpha);
135         lazyPaint->setColorFilter(mProperties.mColorFilter);
136         lazyPaint->setFilterQuality(kLow_SkFilterQuality);
137     }
138     if (mProperties.mMirrored) {
139         canvas->save();
140         canvas->translate(mSkAnimatedImage->getBounds().width(), 0);
141         canvas->scale(-1, 1);
142     }
143 
144     const bool starting = mStarting;
145     mStarting = false;
146 
147     const bool drawDirectly = !mSnapshot.mPic;
148     if (drawDirectly) {
149         // The image is not animating, and never was. Draw directly from
150         // mSkAnimatedImage.
151         if (lazyPaint) {
152             canvas->saveLayer(mSkAnimatedImage->getBounds(), &*lazyPaint);
153         }
154 
155         std::unique_lock lock{mImageLock};
156         mSkAnimatedImage->draw(canvas);
157         if (!mRunning) {
158             return;
159         }
160     } else if (starting) {
161         // The image has animated, and now is being reset. Queue up the first
162         // frame, but keep showing the current frame until the first is ready.
163         auto& thread = uirenderer::AnimatedImageThread::getInstance();
164         mNextSnapshot = thread.reset(sk_ref_sp(this));
165     }
166 
167     bool finalFrame = false;
168     if (mRunning && nextSnapshotReady()) {
169         std::unique_lock lock{mSwapLock};
170         if (mCurrentTime >= mTimeToShowNextSnapshot) {
171             mSnapshot = mNextSnapshot.get();
172             const nsecs_t timeToShowCurrentSnap = mTimeToShowNextSnapshot;
173             if (mSnapshot.mDurationMS == SkAnimatedImage::kFinished) {
174                 finalFrame = true;
175                 mRunning = false;
176             } else {
177                 mTimeToShowNextSnapshot += ms2ns(mSnapshot.mDurationMS);
178                 if (mCurrentTime >= mTimeToShowNextSnapshot) {
179                     // This would mean showing the current frame very briefly. It's
180                     // possible that not being displayed for a time resulted in
181                     // mCurrentTime being far ahead. Prevent showing many frames
182                     // rapidly by going back to the beginning of this frame time.
183                     mCurrentTime = timeToShowCurrentSnap;
184                 }
185             }
186         }
187     }
188 
189     if (mRunning && !mNextSnapshot.valid()) {
190         auto& thread = uirenderer::AnimatedImageThread::getInstance();
191         mNextSnapshot = thread.decodeNextFrame(sk_ref_sp(this));
192     }
193 
194     if (!drawDirectly) {
195         // No other thread will modify mCurrentSnap so this should be safe to
196         // use without locking.
197         canvas->drawPicture(mSnapshot.mPic, nullptr, lazyPaint ? &*lazyPaint : nullptr);
198     }
199 
200     if (finalFrame) {
201         if (mEndListener) {
202             mEndListener->onAnimationEnd();
203         }
204     }
205 }
206 
drawStaging(SkCanvas * canvas)207 int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) {
208     SkAutoCanvasRestore acr(canvas, false);
209     if (mStagingProperties.mAlpha != SK_AlphaOPAQUE || mStagingProperties.mColorFilter.get()) {
210         SkPaint paint;
211         paint.setAlpha(mStagingProperties.mAlpha);
212         paint.setColorFilter(mStagingProperties.mColorFilter);
213         canvas->saveLayer(mSkAnimatedImage->getBounds(), &paint);
214     }
215     if (mStagingProperties.mMirrored) {
216         canvas->save();
217         canvas->translate(mSkAnimatedImage->getBounds().width(), 0);
218         canvas->scale(-1, 1);
219     }
220 
221     if (!mRunning) {
222         // Continue drawing the current frame, and return 0 to indicate no need
223         // to redraw.
224         std::unique_lock lock{mImageLock};
225         canvas->drawDrawable(mSkAnimatedImage.get());
226         return 0;
227     }
228 
229     if (mStarting) {
230         mStarting = false;
231         int durationMS = 0;
232         {
233             std::unique_lock lock{mImageLock};
234             mSkAnimatedImage->reset();
235             durationMS = mSkAnimatedImage->currentFrameDuration();
236         }
237         {
238             std::unique_lock lock{mSwapLock};
239             mLastWallTime = 0;
240             // The current time will be added later, below.
241             mTimeToShowNextSnapshot = ms2ns(durationMS);
242         }
243     }
244 
245     bool update = false;
246     {
247         const nsecs_t currentTime = systemTime(CLOCK_MONOTONIC);
248         std::unique_lock lock{mSwapLock};
249         // mLastWallTime starts off at 0. If it is still 0, just update it to
250         // the current time and avoid updating
251         if (mLastWallTime == 0) {
252             mCurrentTime = currentTime;
253             // mTimeToShowNextSnapshot is already set to the duration of the
254             // first frame.
255             mTimeToShowNextSnapshot += currentTime;
256         } else if (mRunning) {
257             mCurrentTime += currentTime - mLastWallTime;
258             update = mCurrentTime >= mTimeToShowNextSnapshot;
259         }
260         mLastWallTime = currentTime;
261     }
262 
263     int durationMS = 0;
264     {
265         std::unique_lock lock{mImageLock};
266         if (update) {
267             durationMS = mSkAnimatedImage->decodeNextFrame();
268         }
269 
270         canvas->drawDrawable(mSkAnimatedImage.get());
271     }
272 
273     std::unique_lock lock{mSwapLock};
274     if (update) {
275         if (durationMS == SkAnimatedImage::kFinished) {
276             mRunning = false;
277             return SkAnimatedImage::kFinished;
278         }
279 
280         const nsecs_t timeToShowCurrentSnapshot = mTimeToShowNextSnapshot;
281         mTimeToShowNextSnapshot += ms2ns(durationMS);
282         if (mCurrentTime >= mTimeToShowNextSnapshot) {
283             // As in onDraw, prevent speedy catch-up behavior.
284             mCurrentTime = timeToShowCurrentSnapshot;
285         }
286     }
287 
288     return ns2ms(mTimeToShowNextSnapshot - mCurrentTime);
289 }
290 
291 }  // namespace android
292