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