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 26namespace android { 27 28AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed) 29 : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) { 30 mTimeToShowNextSnapshot = ms2ns(mSkAnimatedImage->currentFrameDuration()); 31} 32 33void AnimatedImageDrawable::syncProperties() { 34 mProperties = mStagingProperties; 35} 36 37bool AnimatedImageDrawable::start() { 38 if (mRunning) { 39 return false; 40 } 41 42 mStarting = true; 43 44 mRunning = true; 45 return true; 46} 47 48bool AnimatedImageDrawable::stop() { 49 bool wasRunning = mRunning; 50 mRunning = false; 51 return wasRunning; 52} 53 54bool AnimatedImageDrawable::isRunning() { 55 return mRunning; 56} 57 58bool 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. 64bool 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. 103AnimatedImageDrawable::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. 115AnimatedImageDrawable::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. 128void 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 206int 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