xref: /aosp_15_r20/external/skia/src/android/SkAnimatedImage.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2018 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/android/SkAnimatedImage.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkAndroidCodec.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkCodec.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkEncodedImageFormat.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPicture.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPictureRecorder.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPixelRef.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkCodecPriv.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkPixmapUtilsPriv.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkImagePriv.h"
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker #include <limits.h>
21*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
22*c8dee2aaSAndroid Build Coastguard Worker 
Make(std::unique_ptr<SkAndroidCodec> codec,const SkImageInfo & requestedInfo,SkIRect cropRect,sk_sp<SkPicture> postProcess)23*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec,
24*c8dee2aaSAndroid Build Coastguard Worker         const SkImageInfo& requestedInfo, SkIRect cropRect, sk_sp<SkPicture> postProcess) {
25*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
26*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
27*c8dee2aaSAndroid Build Coastguard Worker     }
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker     if (!requestedInfo.bounds().contains(cropRect)) {
30*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
31*c8dee2aaSAndroid Build Coastguard Worker     }
32*c8dee2aaSAndroid Build Coastguard Worker 
33*c8dee2aaSAndroid Build Coastguard Worker     auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), requestedInfo,
34*c8dee2aaSAndroid Build Coastguard Worker                 cropRect, std::move(postProcess)));
35*c8dee2aaSAndroid Build Coastguard Worker     if (!image->fDisplayFrame.fBitmap.getPixels()) {
36*c8dee2aaSAndroid Build Coastguard Worker         // tryAllocPixels failed.
37*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
38*c8dee2aaSAndroid Build Coastguard Worker     }
39*c8dee2aaSAndroid Build Coastguard Worker 
40*c8dee2aaSAndroid Build Coastguard Worker     return image;
41*c8dee2aaSAndroid Build Coastguard Worker }
42*c8dee2aaSAndroid Build Coastguard Worker 
Make(std::unique_ptr<SkAndroidCodec> codec)43*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec) {
44*c8dee2aaSAndroid Build Coastguard Worker     if (!codec) {
45*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
46*c8dee2aaSAndroid Build Coastguard Worker     }
47*c8dee2aaSAndroid Build Coastguard Worker 
48*c8dee2aaSAndroid Build Coastguard Worker     auto decodeInfo = codec->getInfo();
49*c8dee2aaSAndroid Build Coastguard Worker     const auto origin = codec->codec()->getOrigin();
50*c8dee2aaSAndroid Build Coastguard Worker     if (SkEncodedOriginSwapsWidthHeight(origin)) {
51*c8dee2aaSAndroid Build Coastguard Worker         decodeInfo = decodeInfo.makeWH(decodeInfo.height(), decodeInfo.width());
52*c8dee2aaSAndroid Build Coastguard Worker     }
53*c8dee2aaSAndroid Build Coastguard Worker     const auto cropRect = SkIRect::MakeSize(decodeInfo.dimensions());
54*c8dee2aaSAndroid Build Coastguard Worker     return Make(std::move(codec), decodeInfo, cropRect, nullptr);
55*c8dee2aaSAndroid Build Coastguard Worker }
56*c8dee2aaSAndroid Build Coastguard Worker 
SkAnimatedImage(std::unique_ptr<SkAndroidCodec> codec,const SkImageInfo & requestedInfo,SkIRect cropRect,sk_sp<SkPicture> postProcess)57*c8dee2aaSAndroid Build Coastguard Worker SkAnimatedImage::SkAnimatedImage(std::unique_ptr<SkAndroidCodec> codec,
58*c8dee2aaSAndroid Build Coastguard Worker         const SkImageInfo& requestedInfo, SkIRect cropRect, sk_sp<SkPicture> postProcess)
59*c8dee2aaSAndroid Build Coastguard Worker     : fCodec(std::move(codec))
60*c8dee2aaSAndroid Build Coastguard Worker     , fDecodeInfo(requestedInfo)
61*c8dee2aaSAndroid Build Coastguard Worker     , fCropRect(cropRect)
62*c8dee2aaSAndroid Build Coastguard Worker     , fPostProcess(std::move(postProcess))
63*c8dee2aaSAndroid Build Coastguard Worker     , fFrameCount(fCodec->codec()->getFrameCount())
64*c8dee2aaSAndroid Build Coastguard Worker     , fSampleSize(1)
65*c8dee2aaSAndroid Build Coastguard Worker     , fFinished(false)
66*c8dee2aaSAndroid Build Coastguard Worker     , fRepetitionCount(fCodec->codec()->getRepetitionCount())
67*c8dee2aaSAndroid Build Coastguard Worker     , fRepetitionsCompleted(0)
68*c8dee2aaSAndroid Build Coastguard Worker {
69*c8dee2aaSAndroid Build Coastguard Worker     auto scaledSize = requestedInfo.dimensions();
70*c8dee2aaSAndroid Build Coastguard Worker 
71*c8dee2aaSAndroid Build Coastguard Worker     // For simplicity in decoding and compositing frames, decode directly to a size and
72*c8dee2aaSAndroid Build Coastguard Worker     // orientation that fCodec can do directly, and then use fMatrix to handle crop (along with a
73*c8dee2aaSAndroid Build Coastguard Worker     // clip), orientation, and scaling outside of fCodec. The matrices are computed individually
74*c8dee2aaSAndroid Build Coastguard Worker     // and applied in the following order:
75*c8dee2aaSAndroid Build Coastguard Worker     //      [crop] X [origin] X [scale]
76*c8dee2aaSAndroid Build Coastguard Worker     const auto origin = fCodec->codec()->getOrigin();
77*c8dee2aaSAndroid Build Coastguard Worker     if (origin != SkEncodedOrigin::kDefault_SkEncodedOrigin) {
78*c8dee2aaSAndroid Build Coastguard Worker         // The origin is applied after scaling, so use scaledSize, which is the final scaled size.
79*c8dee2aaSAndroid Build Coastguard Worker         fMatrix = SkEncodedOriginToMatrix(origin, scaledSize.width(), scaledSize.height());
80*c8dee2aaSAndroid Build Coastguard Worker 
81*c8dee2aaSAndroid Build Coastguard Worker         if (SkEncodedOriginSwapsWidthHeight(origin)) {
82*c8dee2aaSAndroid Build Coastguard Worker             // The client asked for sizes post-rotation. Swap back to the pre-rotation sizes to pass
83*c8dee2aaSAndroid Build Coastguard Worker             // to fCodec and for the scale matrix computation.
84*c8dee2aaSAndroid Build Coastguard Worker             fDecodeInfo = SkPixmapUtils::SwapWidthHeight(fDecodeInfo);
85*c8dee2aaSAndroid Build Coastguard Worker             scaledSize = { scaledSize.height(), scaledSize.width() };
86*c8dee2aaSAndroid Build Coastguard Worker         }
87*c8dee2aaSAndroid Build Coastguard Worker     }
88*c8dee2aaSAndroid Build Coastguard Worker 
89*c8dee2aaSAndroid Build Coastguard Worker     auto decodeSize = scaledSize;
90*c8dee2aaSAndroid Build Coastguard Worker     fSampleSize = fCodec->computeSampleSize(&decodeSize);
91*c8dee2aaSAndroid Build Coastguard Worker     fDecodeInfo = fDecodeInfo.makeDimensions(decodeSize);
92*c8dee2aaSAndroid Build Coastguard Worker 
93*c8dee2aaSAndroid Build Coastguard Worker     if (!fDecodingFrame.fBitmap.tryAllocPixels(fDecodeInfo)) {
94*c8dee2aaSAndroid Build Coastguard Worker         return;
95*c8dee2aaSAndroid Build Coastguard Worker     }
96*c8dee2aaSAndroid Build Coastguard Worker 
97*c8dee2aaSAndroid Build Coastguard Worker     if (scaledSize != fDecodeInfo.dimensions()) {
98*c8dee2aaSAndroid Build Coastguard Worker         float scaleX = (float) scaledSize.width()  / fDecodeInfo.width();
99*c8dee2aaSAndroid Build Coastguard Worker         float scaleY = (float) scaledSize.height() / fDecodeInfo.height();
100*c8dee2aaSAndroid Build Coastguard Worker         fMatrix.preConcat(SkMatrix::Scale(scaleX, scaleY));
101*c8dee2aaSAndroid Build Coastguard Worker     }
102*c8dee2aaSAndroid Build Coastguard Worker     fMatrix.postConcat(SkMatrix::Translate(-fCropRect.fLeft, -fCropRect.fTop));
103*c8dee2aaSAndroid Build Coastguard Worker     this->decodeNextFrame();
104*c8dee2aaSAndroid Build Coastguard Worker }
105*c8dee2aaSAndroid Build Coastguard Worker 
~SkAnimatedImage()106*c8dee2aaSAndroid Build Coastguard Worker SkAnimatedImage::~SkAnimatedImage() { }
107*c8dee2aaSAndroid Build Coastguard Worker 
onGetBounds()108*c8dee2aaSAndroid Build Coastguard Worker SkRect SkAnimatedImage::onGetBounds() {
109*c8dee2aaSAndroid Build Coastguard Worker     return SkRect::MakeIWH(fCropRect.width(), fCropRect.height());
110*c8dee2aaSAndroid Build Coastguard Worker }
111*c8dee2aaSAndroid Build Coastguard Worker 
Frame()112*c8dee2aaSAndroid Build Coastguard Worker SkAnimatedImage::Frame::Frame()
113*c8dee2aaSAndroid Build Coastguard Worker     : fIndex(SkCodec::kNoFrame)
114*c8dee2aaSAndroid Build Coastguard Worker {}
115*c8dee2aaSAndroid Build Coastguard Worker 
init(const SkImageInfo & info,OnInit onInit)116*c8dee2aaSAndroid Build Coastguard Worker bool SkAnimatedImage::Frame::init(const SkImageInfo& info, OnInit onInit) {
117*c8dee2aaSAndroid Build Coastguard Worker     if (fBitmap.getPixels()) {
118*c8dee2aaSAndroid Build Coastguard Worker         if (fBitmap.pixelRef()->unique()) {
119*c8dee2aaSAndroid Build Coastguard Worker             SkAssertResult(fBitmap.setAlphaType(info.alphaType()));
120*c8dee2aaSAndroid Build Coastguard Worker             return true;
121*c8dee2aaSAndroid Build Coastguard Worker         }
122*c8dee2aaSAndroid Build Coastguard Worker 
123*c8dee2aaSAndroid Build Coastguard Worker         // An SkCanvas provided to onDraw is still holding a reference.
124*c8dee2aaSAndroid Build Coastguard Worker         // Copy before we decode to ensure that we don't overwrite the
125*c8dee2aaSAndroid Build Coastguard Worker         // expected contents of the image.
126*c8dee2aaSAndroid Build Coastguard Worker         if (OnInit::kRestoreIfNecessary == onInit) {
127*c8dee2aaSAndroid Build Coastguard Worker             SkBitmap tmp;
128*c8dee2aaSAndroid Build Coastguard Worker             if (!tmp.tryAllocPixels(info)) {
129*c8dee2aaSAndroid Build Coastguard Worker                 return false;
130*c8dee2aaSAndroid Build Coastguard Worker             }
131*c8dee2aaSAndroid Build Coastguard Worker 
132*c8dee2aaSAndroid Build Coastguard Worker             memcpy(tmp.getPixels(), fBitmap.getPixels(), fBitmap.computeByteSize());
133*c8dee2aaSAndroid Build Coastguard Worker             using std::swap;
134*c8dee2aaSAndroid Build Coastguard Worker             swap(tmp, fBitmap);
135*c8dee2aaSAndroid Build Coastguard Worker             return true;
136*c8dee2aaSAndroid Build Coastguard Worker         }
137*c8dee2aaSAndroid Build Coastguard Worker     }
138*c8dee2aaSAndroid Build Coastguard Worker 
139*c8dee2aaSAndroid Build Coastguard Worker     return fBitmap.tryAllocPixels(info);
140*c8dee2aaSAndroid Build Coastguard Worker }
141*c8dee2aaSAndroid Build Coastguard Worker 
copyTo(Frame * dst) const142*c8dee2aaSAndroid Build Coastguard Worker bool SkAnimatedImage::Frame::copyTo(Frame* dst) const {
143*c8dee2aaSAndroid Build Coastguard Worker     if (!dst->init(fBitmap.info(), OnInit::kNoRestore)) {
144*c8dee2aaSAndroid Build Coastguard Worker         return false;
145*c8dee2aaSAndroid Build Coastguard Worker     }
146*c8dee2aaSAndroid Build Coastguard Worker 
147*c8dee2aaSAndroid Build Coastguard Worker     memcpy(dst->fBitmap.getPixels(), fBitmap.getPixels(), fBitmap.computeByteSize());
148*c8dee2aaSAndroid Build Coastguard Worker     dst->fIndex = fIndex;
149*c8dee2aaSAndroid Build Coastguard Worker     dst->fDisposalMethod = fDisposalMethod;
150*c8dee2aaSAndroid Build Coastguard Worker     return true;
151*c8dee2aaSAndroid Build Coastguard Worker }
152*c8dee2aaSAndroid Build Coastguard Worker 
reset()153*c8dee2aaSAndroid Build Coastguard Worker void SkAnimatedImage::reset() {
154*c8dee2aaSAndroid Build Coastguard Worker     fFinished = false;
155*c8dee2aaSAndroid Build Coastguard Worker     fRepetitionsCompleted = 0;
156*c8dee2aaSAndroid Build Coastguard Worker     if (fDisplayFrame.fIndex != 0) {
157*c8dee2aaSAndroid Build Coastguard Worker         fDisplayFrame.fIndex = SkCodec::kNoFrame;
158*c8dee2aaSAndroid Build Coastguard Worker         this->decodeNextFrame();
159*c8dee2aaSAndroid Build Coastguard Worker     }
160*c8dee2aaSAndroid Build Coastguard Worker }
161*c8dee2aaSAndroid Build Coastguard Worker 
is_restore_previous(SkCodecAnimation::DisposalMethod dispose)162*c8dee2aaSAndroid Build Coastguard Worker static bool is_restore_previous(SkCodecAnimation::DisposalMethod dispose) {
163*c8dee2aaSAndroid Build Coastguard Worker     return SkCodecAnimation::DisposalMethod::kRestorePrevious == dispose;
164*c8dee2aaSAndroid Build Coastguard Worker }
165*c8dee2aaSAndroid Build Coastguard Worker 
computeNextFrame(int current,bool * animationEnded)166*c8dee2aaSAndroid Build Coastguard Worker int SkAnimatedImage::computeNextFrame(int current, bool* animationEnded) {
167*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(animationEnded != nullptr);
168*c8dee2aaSAndroid Build Coastguard Worker     *animationEnded = false;
169*c8dee2aaSAndroid Build Coastguard Worker 
170*c8dee2aaSAndroid Build Coastguard Worker     const int frameToDecode = current + 1;
171*c8dee2aaSAndroid Build Coastguard Worker     if (frameToDecode == fFrameCount - 1) {
172*c8dee2aaSAndroid Build Coastguard Worker         // Final frame. Check to determine whether to stop.
173*c8dee2aaSAndroid Build Coastguard Worker         fRepetitionsCompleted++;
174*c8dee2aaSAndroid Build Coastguard Worker         if (fRepetitionCount != SkCodec::kRepetitionCountInfinite
175*c8dee2aaSAndroid Build Coastguard Worker                 && fRepetitionsCompleted > fRepetitionCount) {
176*c8dee2aaSAndroid Build Coastguard Worker             *animationEnded = true;
177*c8dee2aaSAndroid Build Coastguard Worker         }
178*c8dee2aaSAndroid Build Coastguard Worker     } else if (frameToDecode == fFrameCount) {
179*c8dee2aaSAndroid Build Coastguard Worker         return 0;
180*c8dee2aaSAndroid Build Coastguard Worker     }
181*c8dee2aaSAndroid Build Coastguard Worker     return frameToDecode;
182*c8dee2aaSAndroid Build Coastguard Worker }
183*c8dee2aaSAndroid Build Coastguard Worker 
finish()184*c8dee2aaSAndroid Build Coastguard Worker double SkAnimatedImage::finish() {
185*c8dee2aaSAndroid Build Coastguard Worker     fFinished = true;
186*c8dee2aaSAndroid Build Coastguard Worker     fCurrentFrameDuration = kFinished;
187*c8dee2aaSAndroid Build Coastguard Worker     return kFinished;
188*c8dee2aaSAndroid Build Coastguard Worker }
189*c8dee2aaSAndroid Build Coastguard Worker 
decodeNextFrame()190*c8dee2aaSAndroid Build Coastguard Worker int SkAnimatedImage::decodeNextFrame() {
191*c8dee2aaSAndroid Build Coastguard Worker     if (fFinished) {
192*c8dee2aaSAndroid Build Coastguard Worker         return kFinished;
193*c8dee2aaSAndroid Build Coastguard Worker     }
194*c8dee2aaSAndroid Build Coastguard Worker 
195*c8dee2aaSAndroid Build Coastguard Worker     bool animationEnded = false;
196*c8dee2aaSAndroid Build Coastguard Worker     const int frameToDecode = this->computeNextFrame(fDisplayFrame.fIndex, &animationEnded);
197*c8dee2aaSAndroid Build Coastguard Worker 
198*c8dee2aaSAndroid Build Coastguard Worker     SkCodec::FrameInfo frameInfo;
199*c8dee2aaSAndroid Build Coastguard Worker     if (fCodec->codec()->getFrameInfo(frameToDecode, &frameInfo)) {
200*c8dee2aaSAndroid Build Coastguard Worker         if (!frameInfo.fFullyReceived) {
201*c8dee2aaSAndroid Build Coastguard Worker             SkCodecPrintf("Frame %i not fully received\n", frameToDecode);
202*c8dee2aaSAndroid Build Coastguard Worker             return this->finish();
203*c8dee2aaSAndroid Build Coastguard Worker         }
204*c8dee2aaSAndroid Build Coastguard Worker 
205*c8dee2aaSAndroid Build Coastguard Worker         fCurrentFrameDuration = frameInfo.fDuration;
206*c8dee2aaSAndroid Build Coastguard Worker     } else {
207*c8dee2aaSAndroid Build Coastguard Worker         animationEnded = true;
208*c8dee2aaSAndroid Build Coastguard Worker         if (0 == frameToDecode) {
209*c8dee2aaSAndroid Build Coastguard Worker             // Static image. This is okay.
210*c8dee2aaSAndroid Build Coastguard Worker             frameInfo.fRequiredFrame = SkCodec::kNoFrame;
211*c8dee2aaSAndroid Build Coastguard Worker             frameInfo.fAlphaType = fCodec->getInfo().alphaType();
212*c8dee2aaSAndroid Build Coastguard Worker             frameInfo.fDisposalMethod = SkCodecAnimation::DisposalMethod::kKeep;
213*c8dee2aaSAndroid Build Coastguard Worker             // These fields won't be read.
214*c8dee2aaSAndroid Build Coastguard Worker             frameInfo.fDuration = INT_MAX;
215*c8dee2aaSAndroid Build Coastguard Worker             frameInfo.fFullyReceived = true;
216*c8dee2aaSAndroid Build Coastguard Worker             fCurrentFrameDuration = kFinished;
217*c8dee2aaSAndroid Build Coastguard Worker         } else {
218*c8dee2aaSAndroid Build Coastguard Worker             SkCodecPrintf("Error getting frameInfo for frame %i\n",
219*c8dee2aaSAndroid Build Coastguard Worker                           frameToDecode);
220*c8dee2aaSAndroid Build Coastguard Worker             return this->finish();
221*c8dee2aaSAndroid Build Coastguard Worker         }
222*c8dee2aaSAndroid Build Coastguard Worker     }
223*c8dee2aaSAndroid Build Coastguard Worker 
224*c8dee2aaSAndroid Build Coastguard Worker     if (frameToDecode == fDisplayFrame.fIndex) {
225*c8dee2aaSAndroid Build Coastguard Worker         if (animationEnded) {
226*c8dee2aaSAndroid Build Coastguard Worker             return this->finish();
227*c8dee2aaSAndroid Build Coastguard Worker         }
228*c8dee2aaSAndroid Build Coastguard Worker         return fCurrentFrameDuration;
229*c8dee2aaSAndroid Build Coastguard Worker     }
230*c8dee2aaSAndroid Build Coastguard Worker 
231*c8dee2aaSAndroid Build Coastguard Worker     for (Frame* frame : { &fRestoreFrame, &fDecodingFrame }) {
232*c8dee2aaSAndroid Build Coastguard Worker         if (frameToDecode == frame->fIndex) {
233*c8dee2aaSAndroid Build Coastguard Worker             using std::swap;
234*c8dee2aaSAndroid Build Coastguard Worker             swap(fDisplayFrame, *frame);
235*c8dee2aaSAndroid Build Coastguard Worker             if (animationEnded) {
236*c8dee2aaSAndroid Build Coastguard Worker                 return this->finish();
237*c8dee2aaSAndroid Build Coastguard Worker             }
238*c8dee2aaSAndroid Build Coastguard Worker             return fCurrentFrameDuration;
239*c8dee2aaSAndroid Build Coastguard Worker         }
240*c8dee2aaSAndroid Build Coastguard Worker     }
241*c8dee2aaSAndroid Build Coastguard Worker 
242*c8dee2aaSAndroid Build Coastguard Worker     // The following code makes an effort to avoid overwriting a frame that will
243*c8dee2aaSAndroid Build Coastguard Worker     // be used again. If frame |i| is_restore_previous, frame |i+1| will not
244*c8dee2aaSAndroid Build Coastguard Worker     // depend on frame |i|, so do not overwrite frame |i-1|, which may be needed
245*c8dee2aaSAndroid Build Coastguard Worker     // for frame |i+1|.
246*c8dee2aaSAndroid Build Coastguard Worker     // We could be even smarter about which frames to save by looking at the
247*c8dee2aaSAndroid Build Coastguard Worker     // entire dependency chain.
248*c8dee2aaSAndroid Build Coastguard Worker     SkAndroidCodec::AndroidOptions options;
249*c8dee2aaSAndroid Build Coastguard Worker     options.fSampleSize = fSampleSize;
250*c8dee2aaSAndroid Build Coastguard Worker     options.fFrameIndex = frameToDecode;
251*c8dee2aaSAndroid Build Coastguard Worker     if (frameInfo.fRequiredFrame == SkCodec::kNoFrame) {
252*c8dee2aaSAndroid Build Coastguard Worker         if (is_restore_previous(frameInfo.fDisposalMethod)) {
253*c8dee2aaSAndroid Build Coastguard Worker             // frameToDecode will be discarded immediately after drawing, so
254*c8dee2aaSAndroid Build Coastguard Worker             // do not overwrite a frame which could possibly be used in the
255*c8dee2aaSAndroid Build Coastguard Worker             // future.
256*c8dee2aaSAndroid Build Coastguard Worker             if (fDecodingFrame.fIndex != SkCodec::kNoFrame &&
257*c8dee2aaSAndroid Build Coastguard Worker                     !is_restore_previous(fDecodingFrame.fDisposalMethod)) {
258*c8dee2aaSAndroid Build Coastguard Worker                 using std::swap;
259*c8dee2aaSAndroid Build Coastguard Worker                 swap(fDecodingFrame, fRestoreFrame);
260*c8dee2aaSAndroid Build Coastguard Worker             }
261*c8dee2aaSAndroid Build Coastguard Worker         }
262*c8dee2aaSAndroid Build Coastguard Worker     } else {
263*c8dee2aaSAndroid Build Coastguard Worker         auto validPriorFrame = [&frameInfo, &frameToDecode](const Frame& frame) {
264*c8dee2aaSAndroid Build Coastguard Worker             if (SkCodec::kNoFrame == frame.fIndex ||
265*c8dee2aaSAndroid Build Coastguard Worker                     is_restore_previous(frame.fDisposalMethod)) {
266*c8dee2aaSAndroid Build Coastguard Worker                 return false;
267*c8dee2aaSAndroid Build Coastguard Worker             }
268*c8dee2aaSAndroid Build Coastguard Worker 
269*c8dee2aaSAndroid Build Coastguard Worker             return frame.fIndex >= frameInfo.fRequiredFrame && frame.fIndex < frameToDecode;
270*c8dee2aaSAndroid Build Coastguard Worker         };
271*c8dee2aaSAndroid Build Coastguard Worker         if (validPriorFrame(fDecodingFrame)) {
272*c8dee2aaSAndroid Build Coastguard Worker             if (is_restore_previous(frameInfo.fDisposalMethod)) {
273*c8dee2aaSAndroid Build Coastguard Worker                 // fDecodingFrame is a good frame to use for this one, but we
274*c8dee2aaSAndroid Build Coastguard Worker                 // don't want to overwrite it.
275*c8dee2aaSAndroid Build Coastguard Worker                 fDecodingFrame.copyTo(&fRestoreFrame);
276*c8dee2aaSAndroid Build Coastguard Worker             }
277*c8dee2aaSAndroid Build Coastguard Worker             options.fPriorFrame = fDecodingFrame.fIndex;
278*c8dee2aaSAndroid Build Coastguard Worker         } else if (validPriorFrame(fDisplayFrame)) {
279*c8dee2aaSAndroid Build Coastguard Worker             if (!fDisplayFrame.copyTo(&fDecodingFrame)) {
280*c8dee2aaSAndroid Build Coastguard Worker                 SkCodecPrintf("Failed to allocate pixels for frame\n");
281*c8dee2aaSAndroid Build Coastguard Worker                 return this->finish();
282*c8dee2aaSAndroid Build Coastguard Worker             }
283*c8dee2aaSAndroid Build Coastguard Worker             options.fPriorFrame = fDecodingFrame.fIndex;
284*c8dee2aaSAndroid Build Coastguard Worker         } else if (validPriorFrame(fRestoreFrame)) {
285*c8dee2aaSAndroid Build Coastguard Worker             if (!is_restore_previous(frameInfo.fDisposalMethod)) {
286*c8dee2aaSAndroid Build Coastguard Worker                 using std::swap;
287*c8dee2aaSAndroid Build Coastguard Worker                 swap(fDecodingFrame, fRestoreFrame);
288*c8dee2aaSAndroid Build Coastguard Worker             } else if (!fRestoreFrame.copyTo(&fDecodingFrame)) {
289*c8dee2aaSAndroid Build Coastguard Worker                 SkCodecPrintf("Failed to restore frame\n");
290*c8dee2aaSAndroid Build Coastguard Worker                 return this->finish();
291*c8dee2aaSAndroid Build Coastguard Worker             }
292*c8dee2aaSAndroid Build Coastguard Worker             options.fPriorFrame = fDecodingFrame.fIndex;
293*c8dee2aaSAndroid Build Coastguard Worker         }
294*c8dee2aaSAndroid Build Coastguard Worker     }
295*c8dee2aaSAndroid Build Coastguard Worker 
296*c8dee2aaSAndroid Build Coastguard Worker     auto alphaType = kOpaque_SkAlphaType == frameInfo.fAlphaType ?
297*c8dee2aaSAndroid Build Coastguard Worker                      kOpaque_SkAlphaType : kPremul_SkAlphaType;
298*c8dee2aaSAndroid Build Coastguard Worker     auto info = fDecodeInfo.makeAlphaType(alphaType);
299*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap* dst = &fDecodingFrame.fBitmap;
300*c8dee2aaSAndroid Build Coastguard Worker     if (!fDecodingFrame.init(info, Frame::OnInit::kRestoreIfNecessary)) {
301*c8dee2aaSAndroid Build Coastguard Worker         return this->finish();
302*c8dee2aaSAndroid Build Coastguard Worker     }
303*c8dee2aaSAndroid Build Coastguard Worker 
304*c8dee2aaSAndroid Build Coastguard Worker     auto result = fCodec->getAndroidPixels(dst->info(), dst->getPixels(), dst->rowBytes(),
305*c8dee2aaSAndroid Build Coastguard Worker                                            &options);
306*c8dee2aaSAndroid Build Coastguard Worker     if (result != SkCodec::kSuccess) {
307*c8dee2aaSAndroid Build Coastguard Worker         SkCodecPrintf("%s, frame %i of %i\n", SkCodec::ResultToString(result),
308*c8dee2aaSAndroid Build Coastguard Worker                       frameToDecode, fFrameCount);
309*c8dee2aaSAndroid Build Coastguard Worker         return this->finish();
310*c8dee2aaSAndroid Build Coastguard Worker     }
311*c8dee2aaSAndroid Build Coastguard Worker 
312*c8dee2aaSAndroid Build Coastguard Worker     fDecodingFrame.fIndex = frameToDecode;
313*c8dee2aaSAndroid Build Coastguard Worker     fDecodingFrame.fDisposalMethod = frameInfo.fDisposalMethod;
314*c8dee2aaSAndroid Build Coastguard Worker 
315*c8dee2aaSAndroid Build Coastguard Worker     using std::swap;
316*c8dee2aaSAndroid Build Coastguard Worker     swap(fDecodingFrame, fDisplayFrame);
317*c8dee2aaSAndroid Build Coastguard Worker     fDisplayFrame.fBitmap.notifyPixelsChanged();
318*c8dee2aaSAndroid Build Coastguard Worker 
319*c8dee2aaSAndroid Build Coastguard Worker     if (animationEnded) {
320*c8dee2aaSAndroid Build Coastguard Worker         return this->finish();
321*c8dee2aaSAndroid Build Coastguard Worker     } else if (fCodec->getEncodedFormat() == SkEncodedImageFormat::kHEIF) {
322*c8dee2aaSAndroid Build Coastguard Worker         // HEIF doesn't know the frame duration until after decoding. Update to
323*c8dee2aaSAndroid Build Coastguard Worker         // the correct value. Note that earlier returns in this method either
324*c8dee2aaSAndroid Build Coastguard Worker         // return kFinished, or fCurrentFrameDuration. If they return the
325*c8dee2aaSAndroid Build Coastguard Worker         // latter, it is a frame that was previously decoded, so it has the
326*c8dee2aaSAndroid Build Coastguard Worker         // updated value.
327*c8dee2aaSAndroid Build Coastguard Worker         if (fCodec->codec()->getFrameInfo(frameToDecode, &frameInfo)) {
328*c8dee2aaSAndroid Build Coastguard Worker             fCurrentFrameDuration = frameInfo.fDuration;
329*c8dee2aaSAndroid Build Coastguard Worker         } else {
330*c8dee2aaSAndroid Build Coastguard Worker             SkCodecPrintf("Failed to getFrameInfo on second attempt (HEIF)");
331*c8dee2aaSAndroid Build Coastguard Worker         }
332*c8dee2aaSAndroid Build Coastguard Worker     }
333*c8dee2aaSAndroid Build Coastguard Worker     return fCurrentFrameDuration;
334*c8dee2aaSAndroid Build Coastguard Worker }
335*c8dee2aaSAndroid Build Coastguard Worker 
onDraw(SkCanvas * canvas)336*c8dee2aaSAndroid Build Coastguard Worker void SkAnimatedImage::onDraw(SkCanvas* canvas) {
337*c8dee2aaSAndroid Build Coastguard Worker     auto image = this->getCurrentFrameSimple();
338*c8dee2aaSAndroid Build Coastguard Worker 
339*c8dee2aaSAndroid Build Coastguard Worker     if (this->simple()) {
340*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawImage(image, 0, 0, SkSamplingOptions(fFilterMode), nullptr);
341*c8dee2aaSAndroid Build Coastguard Worker         return;
342*c8dee2aaSAndroid Build Coastguard Worker     }
343*c8dee2aaSAndroid Build Coastguard Worker 
344*c8dee2aaSAndroid Build Coastguard Worker     SkRect bounds = this->getBounds();
345*c8dee2aaSAndroid Build Coastguard Worker     if (fPostProcess) {
346*c8dee2aaSAndroid Build Coastguard Worker         canvas->saveLayer(&bounds, nullptr);
347*c8dee2aaSAndroid Build Coastguard Worker     }
348*c8dee2aaSAndroid Build Coastguard Worker     canvas->clipRect(bounds);
349*c8dee2aaSAndroid Build Coastguard Worker     {
350*c8dee2aaSAndroid Build Coastguard Worker         SkAutoCanvasRestore acr(canvas, fPostProcess != nullptr);
351*c8dee2aaSAndroid Build Coastguard Worker         canvas->concat(fMatrix);
352*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawImage(image, 0, 0, SkSamplingOptions(fFilterMode), nullptr);
353*c8dee2aaSAndroid Build Coastguard Worker     }
354*c8dee2aaSAndroid Build Coastguard Worker     if (fPostProcess) {
355*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawPicture(fPostProcess);
356*c8dee2aaSAndroid Build Coastguard Worker         canvas->restore();
357*c8dee2aaSAndroid Build Coastguard Worker     }
358*c8dee2aaSAndroid Build Coastguard Worker }
359*c8dee2aaSAndroid Build Coastguard Worker 
setRepetitionCount(int newCount)360*c8dee2aaSAndroid Build Coastguard Worker void SkAnimatedImage::setRepetitionCount(int newCount) {
361*c8dee2aaSAndroid Build Coastguard Worker     fRepetitionCount = newCount;
362*c8dee2aaSAndroid Build Coastguard Worker }
363*c8dee2aaSAndroid Build Coastguard Worker 
getCurrentFrameSimple()364*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> SkAnimatedImage::getCurrentFrameSimple() {
365*c8dee2aaSAndroid Build Coastguard Worker     // This SkBitmap may be reused later to decode the following frame. But Frame::init
366*c8dee2aaSAndroid Build Coastguard Worker     // lazily copies the pixel ref if it has any other references. So it is safe to not
367*c8dee2aaSAndroid Build Coastguard Worker     // do a deep copy here.
368*c8dee2aaSAndroid Build Coastguard Worker     return SkMakeImageFromRasterBitmap(fDisplayFrame.fBitmap,
369*c8dee2aaSAndroid Build Coastguard Worker                                        kNever_SkCopyPixelsMode);
370*c8dee2aaSAndroid Build Coastguard Worker }
371*c8dee2aaSAndroid Build Coastguard Worker 
getCurrentFrame()372*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> SkAnimatedImage::getCurrentFrame() {
373*c8dee2aaSAndroid Build Coastguard Worker     if (this->simple()) return this->getCurrentFrameSimple();
374*c8dee2aaSAndroid Build Coastguard Worker 
375*c8dee2aaSAndroid Build Coastguard Worker     auto imageInfo = fDisplayFrame.fBitmap.info().makeDimensions(fCropRect.size());
376*c8dee2aaSAndroid Build Coastguard Worker     if (fPostProcess) {
377*c8dee2aaSAndroid Build Coastguard Worker         // Defensively use premul in case the post process adds alpha.
378*c8dee2aaSAndroid Build Coastguard Worker         imageInfo = imageInfo.makeAlphaType(kPremul_SkAlphaType);
379*c8dee2aaSAndroid Build Coastguard Worker     }
380*c8dee2aaSAndroid Build Coastguard Worker 
381*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap dst;
382*c8dee2aaSAndroid Build Coastguard Worker     if (!dst.tryAllocPixels(imageInfo)) {
383*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
384*c8dee2aaSAndroid Build Coastguard Worker     }
385*c8dee2aaSAndroid Build Coastguard Worker 
386*c8dee2aaSAndroid Build Coastguard Worker     SkCanvas canvas(dst);
387*c8dee2aaSAndroid Build Coastguard Worker     this->draw(&canvas);
388*c8dee2aaSAndroid Build Coastguard Worker     return SkMakeImageFromRasterBitmap(dst, kNever_SkCopyPixelsMode);
389*c8dee2aaSAndroid Build Coastguard Worker }
390*c8dee2aaSAndroid Build Coastguard Worker 
setFilterMode(SkFilterMode filterMode)391*c8dee2aaSAndroid Build Coastguard Worker void SkAnimatedImage::setFilterMode(SkFilterMode filterMode) {
392*c8dee2aaSAndroid Build Coastguard Worker     fFilterMode = filterMode;
393*c8dee2aaSAndroid Build Coastguard Worker }
394