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