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