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