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