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