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