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