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 #ifdef __ANDROID__ // Layoutlib does not support AnimatedImageThread
19 #include "AnimatedImageThread.h"
20 #endif
21 
22 #include <gui/TraceUtils.h>
23 #include "pipeline/skia/SkiaUtils.h"
24 
25 #include <SkPicture.h>
26 #include <SkRefCnt.h>
27 
28 #include <optional>
29 
30 namespace android {
31 
AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage,size_t bytesUsed,SkEncodedImageFormat format)32 AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed,
33                                              SkEncodedImageFormat format)
34         : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed), mFormat(format) {
35     mTimeToShowNextSnapshot = ms2ns(currentFrameDuration());
36     setStagingBounds(mSkAnimatedImage->getBounds());
37 }
38 
syncProperties()39 void AnimatedImageDrawable::syncProperties() {
40     mProperties = mStagingProperties;
41 }
42 
start()43 bool AnimatedImageDrawable::start() {
44     if (mRunning) {
45         return false;
46     }
47 
48     mStarting = true;
49 
50     mRunning = true;
51     return true;
52 }
53 
stop()54 bool AnimatedImageDrawable::stop() {
55     bool wasRunning = mRunning;
56     mRunning = false;
57     return wasRunning;
58 }
59 
isRunning()60 bool AnimatedImageDrawable::isRunning() {
61     return mRunning;
62 }
63 
nextSnapshotReady() const64 bool AnimatedImageDrawable::nextSnapshotReady() const {
65     return mNextSnapshot.valid() &&
66            mNextSnapshot.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
67 }
68 
69 // Only called on the RenderThread while UI thread is locked.
isDirty(nsecs_t * outDelay)70 bool AnimatedImageDrawable::isDirty(nsecs_t* outDelay) {
71     *outDelay = 0;
72     const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
73     const nsecs_t lastWallTime = mLastWallTime;
74 
75     mLastWallTime = currentTime;
76     if (!mRunning) {
77         return false;
78     }
79 
80     std::unique_lock lock{mSwapLock};
81     mCurrentTime += currentTime - lastWallTime;
82 
83     if (!mNextSnapshot.valid()) {
84         // Need to trigger onDraw in order to start decoding the next frame.
85         *outDelay = mTimeToShowNextSnapshot - mCurrentTime;
86         return true;
87     }
88 
89     if (mTimeToShowNextSnapshot > mCurrentTime) {
90         *outDelay = mTimeToShowNextSnapshot - mCurrentTime;
91     } else if (nextSnapshotReady()) {
92         // We have not yet updated mTimeToShowNextSnapshot. Read frame duration
93         // directly from mSkAnimatedImage.
94         lock.unlock();
95         std::unique_lock imageLock{mImageLock};
96         *outDelay = ms2ns(currentFrameDuration());
97         return true;
98     } else {
99         // The next snapshot has not yet been decoded, but we've already passed
100         // time to draw it. There's not a good way to know when decoding will
101         // finish, so request an update immediately.
102         *outDelay = 0;
103     }
104 
105     return false;
106 }
107 
108 // Only called on the AnimatedImageThread.
decodeNextFrame()109 AnimatedImageDrawable::Snapshot AnimatedImageDrawable::decodeNextFrame() {
110     Snapshot snap;
111     {
112         std::unique_lock lock{mImageLock};
113         snap.mDurationMS = adjustFrameDuration(mSkAnimatedImage->decodeNextFrame());
114         snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
115     }
116 
117     return snap;
118 }
119 
120 // Only called on the AnimatedImageThread.
reset()121 AnimatedImageDrawable::Snapshot AnimatedImageDrawable::reset() {
122     Snapshot snap;
123     {
124         std::unique_lock lock{mImageLock};
125         mSkAnimatedImage->reset();
126         snap.mPic.reset(mSkAnimatedImage->newPictureSnapshot());
127         snap.mDurationMS = currentFrameDuration();
128     }
129 
130     return snap;
131 }
132 
133 // Update the matrix to map from the intrinsic bounds of the SkAnimatedImage to
134 // the bounds specified by Drawable#setBounds.
handleBounds(SkMatrix * matrix,const SkRect & intrinsicBounds,const SkRect & bounds)135 static void handleBounds(SkMatrix* matrix, const SkRect& intrinsicBounds, const SkRect& bounds) {
136     matrix->preTranslate(bounds.left(), bounds.top());
137     matrix->preScale(bounds.width()  / intrinsicBounds.width(),
138                      bounds.height() / intrinsicBounds.height());
139 }
140 
141 // Only called on the RenderThread.
onDraw(SkCanvas * canvas)142 void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
143     // Store the matrix used to handle bounds and mirroring separate from the
144     // canvas. We may need to invert the matrix to determine the proper bounds
145     // to pass to saveLayer, and this matrix (as opposed to, potentially, the
146     // canvas' matrix) only uses scale and translate, so it must be invertible.
147     SkMatrix matrix;
148     SkAutoCanvasRestore acr(canvas, true);
149     handleBounds(&matrix, mSkAnimatedImage->getBounds(), mProperties.mBounds);
150 
151     if (mProperties.mMirrored) {
152         matrix.preTranslate(mSkAnimatedImage->getBounds().width(), 0);
153         matrix.preScale(-1, 1);
154     }
155 
156     std::optional<SkPaint> lazyPaint;
157     if (mProperties.mAlpha != SK_AlphaOPAQUE || mProperties.mColorFilter.get()) {
158         lazyPaint.emplace();
159         lazyPaint->setAlpha(mProperties.mAlpha);
160         lazyPaint->setColorFilter(mProperties.mColorFilter);
161     }
162 
163     canvas->concat(matrix);
164 
165     const bool starting = mStarting;
166     mStarting = false;
167 
168     const bool drawDirectly = !mSnapshot.mPic;
169     if (drawDirectly) {
170         // The image is not animating, and never was. Draw directly from
171         // mSkAnimatedImage.
172         if (lazyPaint) {
173             SkMatrix inverse;
174             (void) matrix.invert(&inverse);
175             SkRect r = mProperties.mBounds;
176             inverse.mapRect(&r);
177             canvas->saveLayer(r, &*lazyPaint);
178         }
179 
180         std::unique_lock lock{mImageLock};
181         mSkAnimatedImage->draw(canvas);
182         if (!mRunning) {
183             return;
184         }
185     } else if (starting) {
186         // The image has animated, and now is being reset. Queue up the first
187         // frame, but keep showing the current frame until the first is ready.
188 #ifdef __ANDROID__ // Layoutlib does not support AnimatedImageThread
189         auto& thread = uirenderer::AnimatedImageThread::getInstance();
190         mNextSnapshot = thread.reset(sk_ref_sp(this));
191 #endif
192     }
193 
194     bool finalFrame = false;
195     if (mRunning && nextSnapshotReady()) {
196         std::unique_lock lock{mSwapLock};
197         if (mCurrentTime >= mTimeToShowNextSnapshot) {
198             mSnapshot = mNextSnapshot.get();
199             const nsecs_t timeToShowCurrentSnap = mTimeToShowNextSnapshot;
200             if (mSnapshot.mDurationMS == SkAnimatedImage::kFinished) {
201                 finalFrame = true;
202                 mRunning = false;
203             } else {
204                 mTimeToShowNextSnapshot += ms2ns(mSnapshot.mDurationMS);
205                 if (mCurrentTime >= mTimeToShowNextSnapshot) {
206                     // This would mean showing the current frame very briefly. It's
207                     // possible that not being displayed for a time resulted in
208                     // mCurrentTime being far ahead. Prevent showing many frames
209                     // rapidly by going back to the beginning of this frame time.
210                     mCurrentTime = timeToShowCurrentSnap;
211                 }
212             }
213         }
214     }
215 
216     if (mRunning && !mNextSnapshot.valid()) {
217 #ifdef __ANDROID__ // Layoutlib does not support AnimatedImageThread
218         auto& thread = uirenderer::AnimatedImageThread::getInstance();
219         mNextSnapshot = thread.decodeNextFrame(sk_ref_sp(this));
220 #endif
221     }
222 
223     if (!drawDirectly) {
224         // No other thread will modify mCurrentSnap so this should be safe to
225         // use without locking.
226         canvas->drawPicture(mSnapshot.mPic, nullptr, lazyPaint ? &*lazyPaint : nullptr);
227     }
228 
229     if (finalFrame) {
230         if (mEndListener) {
231             mEndListener->onAnimationEnd();
232         }
233     }
234 }
235 
drawStaging(SkCanvas * canvas)236 int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) {
237     // Store the matrix used to handle bounds and mirroring separate from the
238     // canvas. We may need to invert the matrix to determine the proper bounds
239     // to pass to saveLayer, and this matrix (as opposed to, potentially, the
240     // canvas' matrix) only uses scale and translate, so it must be invertible.
241     SkMatrix matrix;
242     SkAutoCanvasRestore acr(canvas, true);
243     handleBounds(&matrix, mSkAnimatedImage->getBounds(), mStagingProperties.mBounds);
244 
245     if (mStagingProperties.mMirrored) {
246         matrix.preTranslate(mSkAnimatedImage->getBounds().width(), 0);
247         matrix.preScale(-1, 1);
248     }
249 
250     canvas->concat(matrix);
251 
252     if (mStagingProperties.mAlpha != SK_AlphaOPAQUE || mStagingProperties.mColorFilter.get()) {
253         SkPaint paint;
254         paint.setAlpha(mStagingProperties.mAlpha);
255         paint.setColorFilter(mStagingProperties.mColorFilter);
256 
257         SkMatrix inverse;
258         (void) matrix.invert(&inverse);
259         SkRect r = mStagingProperties.mBounds;
260         inverse.mapRect(&r);
261         canvas->saveLayer(r, &paint);
262     }
263 
264     if (!mRunning) {
265         // Continue drawing the current frame, and return 0 to indicate no need
266         // to redraw.
267         std::unique_lock lock{mImageLock};
268         canvas->drawDrawable(mSkAnimatedImage.get());
269         return 0;
270     }
271 
272     if (mStarting) {
273         mStarting = false;
274         int durationMS = 0;
275         {
276             std::unique_lock lock{mImageLock};
277             mSkAnimatedImage->reset();
278             durationMS = currentFrameDuration();
279         }
280         {
281             std::unique_lock lock{mSwapLock};
282             mLastWallTime = 0;
283             // The current time will be added later, below.
284             mTimeToShowNextSnapshot = ms2ns(durationMS);
285         }
286     }
287 
288     bool update = false;
289     {
290         const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
291         std::unique_lock lock{mSwapLock};
292         // mLastWallTime starts off at 0. If it is still 0, just update it to
293         // the current time and avoid updating
294         if (mLastWallTime == 0) {
295             mCurrentTime = currentTime;
296             // mTimeToShowNextSnapshot is already set to the duration of the
297             // first frame.
298             mTimeToShowNextSnapshot += currentTime;
299         } else if (mRunning) {
300             mCurrentTime += currentTime - mLastWallTime;
301             update = mCurrentTime >= mTimeToShowNextSnapshot;
302         }
303         mLastWallTime = currentTime;
304     }
305 
306     int durationMS = 0;
307     {
308         std::unique_lock lock{mImageLock};
309         if (update) {
310             durationMS = adjustFrameDuration(mSkAnimatedImage->decodeNextFrame());
311         }
312 
313         canvas->drawDrawable(mSkAnimatedImage.get());
314     }
315 
316     std::unique_lock lock{mSwapLock};
317     if (update) {
318         if (durationMS == SkAnimatedImage::kFinished) {
319             mRunning = false;
320             return SkAnimatedImage::kFinished;
321         }
322 
323         const nsecs_t timeToShowCurrentSnapshot = mTimeToShowNextSnapshot;
324         mTimeToShowNextSnapshot += ms2ns(durationMS);
325         if (mCurrentTime >= mTimeToShowNextSnapshot) {
326             // As in onDraw, prevent speedy catch-up behavior.
327             mCurrentTime = timeToShowCurrentSnapshot;
328         }
329     }
330 
331     return ns2ms(mTimeToShowNextSnapshot - mCurrentTime);
332 }
333 
onGetBounds()334 SkRect AnimatedImageDrawable::onGetBounds() {
335     // This must return a bounds that is valid for all possible states,
336     // including if e.g. the client calls setBounds.
337     return SkRectMakeLargest();
338 }
339 
adjustFrameDuration(int durationMs)340 int AnimatedImageDrawable::adjustFrameDuration(int durationMs) {
341     if (durationMs == SkAnimatedImage::kFinished) {
342         return SkAnimatedImage::kFinished;
343     }
344 
345     if (mFormat == SkEncodedImageFormat::kGIF) {
346         // Match Chrome & Firefox behavior that gifs with a duration <= 10ms is bumped to 100ms
347         return durationMs <= 10 ? 100 : durationMs;
348     }
349     return durationMs;
350 }
351 
currentFrameDuration()352 int AnimatedImageDrawable::currentFrameDuration() {
353     return adjustFrameDuration(mSkAnimatedImage->currentFrameDuration());
354 }
355 
356 }  // namespace android
357