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