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