/src/skia/src/android/SkAnimatedImage.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
22 | | sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec, |
23 | 5.50k | const SkImageInfo& requestedInfo, SkIRect cropRect, sk_sp<SkPicture> postProcess) { |
24 | 5.50k | if (!codec) { |
25 | 0 | return nullptr; |
26 | 0 | } |
27 | | |
28 | 5.50k | if (!requestedInfo.bounds().contains(cropRect)) { |
29 | 0 | return nullptr; |
30 | 0 | } |
31 | | |
32 | 5.50k | auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), requestedInfo, |
33 | 5.50k | cropRect, std::move(postProcess))); |
34 | 5.50k | if (!image->fDisplayFrame.fBitmap.getPixels()) { |
35 | | // tryAllocPixels failed. |
36 | 3.68k | return nullptr; |
37 | 3.68k | } |
38 | | |
39 | 1.81k | return image; |
40 | 1.81k | } |
41 | | |
42 | 5.50k | sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec) { |
43 | 5.50k | if (!codec) { |
44 | 0 | return nullptr; |
45 | 0 | } |
46 | | |
47 | 5.50k | auto decodeInfo = codec->getInfo(); |
48 | 5.50k | const auto origin = codec->codec()->getOrigin(); |
49 | 5.50k | if (SkEncodedOriginSwapsWidthHeight(origin)) { |
50 | 1 | decodeInfo = decodeInfo.makeWH(decodeInfo.height(), decodeInfo.width()); |
51 | 1 | } |
52 | 5.50k | const auto cropRect = SkIRect::MakeSize(decodeInfo.dimensions()); |
53 | 5.50k | return Make(std::move(codec), decodeInfo, cropRect, nullptr); |
54 | 5.50k | } |
55 | | |
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 | 5.50k | { |
68 | 5.50k | 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 | 5.50k | const auto origin = fCodec->codec()->getOrigin(); |
76 | 5.50k | if (origin != SkEncodedOrigin::kDefault_SkEncodedOrigin) { |
77 | | // The origin is applied after scaling, so use scaledSize, which is the final scaled size. |
78 | 3 | fMatrix = SkEncodedOriginToMatrix(origin, scaledSize.width(), scaledSize.height()); |
79 | | |
80 | 3 | 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 | 1 | fDecodeInfo = SkPixmapPriv::SwapWidthHeight(fDecodeInfo); |
84 | 1 | scaledSize = { scaledSize.height(), scaledSize.width() }; |
85 | 1 | } |
86 | 3 | } |
87 | | |
88 | 5.50k | auto decodeSize = scaledSize; |
89 | 5.50k | fSampleSize = fCodec->computeSampleSize(&decodeSize); |
90 | 5.50k | fDecodeInfo = fDecodeInfo.makeDimensions(decodeSize); |
91 | | |
92 | 5.50k | if (!fDecodingFrame.fBitmap.tryAllocPixels(fDecodeInfo)) { |
93 | 15 | return; |
94 | 15 | } |
95 | | |
96 | 5.48k | if (scaledSize != fDecodeInfo.dimensions()) { |
97 | 0 | float scaleX = (float) scaledSize.width() / fDecodeInfo.width(); |
98 | 0 | float scaleY = (float) scaledSize.height() / fDecodeInfo.height(); |
99 | 0 | fMatrix.preConcat(SkMatrix::Scale(scaleX, scaleY)); |
100 | 0 | } |
101 | 5.48k | fMatrix.postConcat(SkMatrix::Translate(-fCropRect.fLeft, -fCropRect.fTop)); |
102 | 5.48k | this->decodeNextFrame(); |
103 | 5.48k | } |
104 | | |
105 | 5.50k | SkAnimatedImage::~SkAnimatedImage() { } |
106 | | |
107 | 0 | SkRect SkAnimatedImage::onGetBounds() { |
108 | 0 | return SkRect::MakeIWH(fCropRect.width(), fCropRect.height()); |
109 | 0 | } |
110 | | |
111 | | SkAnimatedImage::Frame::Frame() |
112 | | : fIndex(SkCodec::kNoFrame) |
113 | 16.5k | {} |
114 | | |
115 | 56.4k | bool SkAnimatedImage::Frame::init(const SkImageInfo& info, OnInit onInit) { |
116 | 56.4k | if (fBitmap.getPixels()) { |
117 | 55.4k | if (fBitmap.pixelRef()->unique()) { |
118 | 55.4k | SkAssertResult(fBitmap.setAlphaType(info.alphaType())); |
119 | 55.4k | return true; |
120 | 55.4k | } |
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 | 0 | if (OnInit::kRestoreIfNecessary == onInit) { |
126 | 0 | SkBitmap tmp; |
127 | 0 | if (!tmp.tryAllocPixels(info)) { |
128 | 0 | return false; |
129 | 0 | } |
130 | | |
131 | 0 | memcpy(tmp.getPixels(), fBitmap.getPixels(), fBitmap.computeByteSize()); |
132 | 0 | using std::swap; |
133 | 0 | swap(tmp, fBitmap); |
134 | 0 | return true; |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | 981 | return fBitmap.tryAllocPixels(info); |
139 | 981 | } |
140 | | |
141 | 21.0k | bool SkAnimatedImage::Frame::copyTo(Frame* dst) const { |
142 | 21.0k | if (!dst->init(fBitmap.info(), OnInit::kNoRestore)) { |
143 | 0 | return false; |
144 | 0 | } |
145 | | |
146 | 21.0k | memcpy(dst->fBitmap.getPixels(), fBitmap.getPixels(), fBitmap.computeByteSize()); |
147 | 21.0k | dst->fIndex = fIndex; |
148 | 21.0k | dst->fDisposalMethod = fDisposalMethod; |
149 | 21.0k | return true; |
150 | 21.0k | } |
151 | | |
152 | 0 | void SkAnimatedImage::reset() { |
153 | 0 | fFinished = false; |
154 | 0 | fRepetitionsCompleted = 0; |
155 | 0 | if (fDisplayFrame.fIndex != 0) { |
156 | 0 | fDisplayFrame.fIndex = SkCodec::kNoFrame; |
157 | 0 | this->decodeNextFrame(); |
158 | 0 | } |
159 | 0 | } |
160 | | |
161 | 62.1k | static bool is_restore_previous(SkCodecAnimation::DisposalMethod dispose) { |
162 | 62.1k | return SkCodecAnimation::DisposalMethod::kRestorePrevious == dispose; |
163 | 62.1k | } |
164 | | |
165 | 41.5k | int SkAnimatedImage::computeNextFrame(int current, bool* animationEnded) { |
166 | 41.5k | SkASSERT(animationEnded != nullptr); |
167 | 41.5k | *animationEnded = false; |
168 | | |
169 | 41.5k | const int frameToDecode = current + 1; |
170 | 41.5k | if (frameToDecode == fFrameCount - 1) { |
171 | | // Final frame. Check to determine whether to stop. |
172 | 11.4k | fRepetitionsCompleted++; |
173 | 11.4k | if (fRepetitionCount != SkCodec::kRepetitionCountInfinite |
174 | 10.5k | && fRepetitionsCompleted > fRepetitionCount) { |
175 | 4.93k | *animationEnded = true; |
176 | 4.93k | } |
177 | 30.1k | } else if (frameToDecode == fFrameCount) { |
178 | 8.87k | return 0; |
179 | 8.87k | } |
180 | 32.7k | return frameToDecode; |
181 | 32.7k | } |
182 | | |
183 | 5.22k | double SkAnimatedImage::finish() { |
184 | 5.22k | fFinished = true; |
185 | 5.22k | fCurrentFrameDuration = kFinished; |
186 | 5.22k | return kFinished; |
187 | 5.22k | } |
188 | | |
189 | 41.5k | int SkAnimatedImage::decodeNextFrame() { |
190 | 41.5k | if (fFinished) { |
191 | 0 | return kFinished; |
192 | 0 | } |
193 | | |
194 | 41.5k | bool animationEnded = false; |
195 | 41.5k | const int frameToDecode = this->computeNextFrame(fDisplayFrame.fIndex, &animationEnded); |
196 | | |
197 | 41.5k | SkCodec::FrameInfo frameInfo; |
198 | 41.5k | if (fCodec->codec()->getFrameInfo(frameToDecode, &frameInfo)) { |
199 | 37.0k | if (!frameInfo.fFullyReceived) { |
200 | 113 | SkCodecPrintf("Frame %i not fully received\n", frameToDecode); |
201 | 113 | return this->finish(); |
202 | 113 | } |
203 | | |
204 | 36.9k | fCurrentFrameDuration = frameInfo.fDuration; |
205 | 4.51k | } else { |
206 | 4.51k | animationEnded = true; |
207 | 4.51k | if (0 == frameToDecode) { |
208 | | // Static image. This is okay. |
209 | 4.51k | frameInfo.fRequiredFrame = SkCodec::kNoFrame; |
210 | 4.51k | frameInfo.fAlphaType = fCodec->getInfo().alphaType(); |
211 | 4.51k | frameInfo.fDisposalMethod = SkCodecAnimation::DisposalMethod::kKeep; |
212 | | // These fields won't be read. |
213 | 4.51k | frameInfo.fDuration = INT_MAX; |
214 | 4.51k | frameInfo.fFullyReceived = true; |
215 | 4.51k | fCurrentFrameDuration = kFinished; |
216 | 0 | } else { |
217 | 0 | SkCodecPrintf("Error getting frameInfo for frame %i\n", |
218 | 0 | frameToDecode); |
219 | 0 | return this->finish(); |
220 | 0 | } |
221 | 41.4k | } |
222 | | |
223 | 41.4k | if (frameToDecode == fDisplayFrame.fIndex) { |
224 | 2.50k | if (animationEnded) { |
225 | 0 | return this->finish(); |
226 | 0 | } |
227 | 2.50k | return fCurrentFrameDuration; |
228 | 2.50k | } |
229 | | |
230 | 77.3k | for (Frame* frame : { &fRestoreFrame, &fDecodingFrame }) { |
231 | 77.3k | if (frameToDecode == frame->fIndex) { |
232 | 3.50k | using std::swap; |
233 | 3.50k | swap(fDisplayFrame, *frame); |
234 | 3.50k | if (animationEnded) { |
235 | 37 | return this->finish(); |
236 | 37 | } |
237 | 3.46k | return fCurrentFrameDuration; |
238 | 3.46k | } |
239 | 77.3k | } |
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 | 35.4k | SkAndroidCodec::AndroidOptions options; |
248 | 35.4k | options.fSampleSize = fSampleSize; |
249 | 35.4k | options.fFrameIndex = frameToDecode; |
250 | 35.4k | if (frameInfo.fRequiredFrame == SkCodec::kNoFrame) { |
251 | 13.2k | 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 | 1.37k | if (fDecodingFrame.fIndex != SkCodec::kNoFrame && |
256 | 1.31k | !is_restore_previous(fDecodingFrame.fDisposalMethod)) { |
257 | 198 | using std::swap; |
258 | 198 | swap(fDecodingFrame, fRestoreFrame); |
259 | 198 | } |
260 | 1.37k | } |
261 | 22.2k | } else { |
262 | 44.8k | auto validPriorFrame = [&frameInfo, &frameToDecode](const Frame& frame) { |
263 | 44.8k | if (SkCodec::kNoFrame == frame.fIndex || |
264 | 44.1k | is_restore_previous(frame.fDisposalMethod)) { |
265 | 5.60k | return false; |
266 | 5.60k | } |
267 | | |
268 | 39.2k | return frame.fIndex >= frameInfo.fRequiredFrame && frame.fIndex < frameToDecode; |
269 | 39.2k | }; |
270 | 22.2k | if (validPriorFrame(fDecodingFrame)) { |
271 | 1.59k | 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 | 831 | fDecodingFrame.copyTo(&fRestoreFrame); |
275 | 831 | } |
276 | 1.59k | options.fPriorFrame = fDecodingFrame.fIndex; |
277 | 20.6k | } else if (validPriorFrame(fDisplayFrame)) { |
278 | 18.6k | if (!fDisplayFrame.copyTo(&fDecodingFrame)) { |
279 | 0 | SkCodecPrintf("Failed to allocate pixels for frame\n"); |
280 | 0 | return this->finish(); |
281 | 0 | } |
282 | 18.6k | options.fPriorFrame = fDecodingFrame.fIndex; |
283 | 1.95k | } else if (validPriorFrame(fRestoreFrame)) { |
284 | 1.90k | if (!is_restore_previous(frameInfo.fDisposalMethod)) { |
285 | 400 | using std::swap; |
286 | 400 | swap(fDecodingFrame, fRestoreFrame); |
287 | 1.50k | } else if (!fRestoreFrame.copyTo(&fDecodingFrame)) { |
288 | 0 | SkCodecPrintf("Failed to restore frame\n"); |
289 | 0 | return this->finish(); |
290 | 0 | } |
291 | 1.90k | options.fPriorFrame = fDecodingFrame.fIndex; |
292 | 1.90k | } |
293 | 22.2k | } |
294 | | |
295 | 35.4k | auto alphaType = kOpaque_SkAlphaType == frameInfo.fAlphaType ? |
296 | 30.6k | kOpaque_SkAlphaType : kPremul_SkAlphaType; |
297 | 35.4k | auto info = fDecodeInfo.makeAlphaType(alphaType); |
298 | 35.4k | SkBitmap* dst = &fDecodingFrame.fBitmap; |
299 | 35.4k | if (!fDecodingFrame.init(info, Frame::OnInit::kRestoreIfNecessary)) { |
300 | 0 | return this->finish(); |
301 | 0 | } |
302 | | |
303 | 35.4k | auto result = fCodec->getAndroidPixels(dst->info(), dst->getPixels(), dst->rowBytes(), |
304 | 35.4k | &options); |
305 | 35.4k | if (result != SkCodec::kSuccess) { |
306 | 3.93k | SkCodecPrintf("error %i, frame %i of %i\n", result, frameToDecode, fFrameCount); |
307 | 3.93k | return this->finish(); |
308 | 3.93k | } |
309 | | |
310 | 31.5k | fDecodingFrame.fIndex = frameToDecode; |
311 | 31.5k | fDecodingFrame.fDisposalMethod = frameInfo.fDisposalMethod; |
312 | | |
313 | 31.5k | using std::swap; |
314 | 31.5k | swap(fDecodingFrame, fDisplayFrame); |
315 | 31.5k | fDisplayFrame.fBitmap.notifyPixelsChanged(); |
316 | | |
317 | 31.5k | if (animationEnded) { |
318 | 1.14k | return this->finish(); |
319 | 30.3k | } 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 | 0 | if (fCodec->codec()->getFrameInfo(frameToDecode, &frameInfo)) { |
326 | 0 | fCurrentFrameDuration = frameInfo.fDuration; |
327 | 0 | } else { |
328 | 0 | SkCodecPrintf("Failed to getFrameInfo on second attempt (HEIF)"); |
329 | 0 | } |
330 | 0 | } |
331 | 30.3k | return fCurrentFrameDuration; |
332 | 31.5k | } |
333 | | |
334 | 36.0k | void SkAnimatedImage::onDraw(SkCanvas* canvas) { |
335 | 36.0k | auto image = this->getCurrentFrameSimple(); |
336 | | |
337 | 36.0k | if (this->simple()) { |
338 | 36.0k | canvas->drawImage(image, 0, 0); |
339 | 36.0k | return; |
340 | 36.0k | } |
341 | | |
342 | 0 | SkRect bounds = this->getBounds(); |
343 | 0 | if (fPostProcess) { |
344 | 0 | canvas->saveLayer(&bounds, nullptr); |
345 | 0 | } |
346 | 0 | canvas->clipRect(bounds); |
347 | 0 | { |
348 | 0 | SkAutoCanvasRestore acr(canvas, fPostProcess != nullptr); |
349 | 0 | canvas->concat(fMatrix); |
350 | 0 | canvas->drawImage(image, 0, 0, SkSamplingOptions(SkFilterMode::kLinear), nullptr); |
351 | 0 | } |
352 | 0 | if (fPostProcess) { |
353 | 0 | canvas->drawPicture(fPostProcess); |
354 | 0 | canvas->restore(); |
355 | 0 | } |
356 | 0 | } |
357 | | |
358 | 0 | void SkAnimatedImage::setRepetitionCount(int newCount) { |
359 | 0 | fRepetitionCount = newCount; |
360 | 0 | } |
361 | | |
362 | 36.0k | 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 | 36.0k | return SkMakeImageFromRasterBitmap(fDisplayFrame.fBitmap, |
367 | 36.0k | kNever_SkCopyPixelsMode); |
368 | 36.0k | } |
369 | | |
370 | 0 | sk_sp<SkImage> SkAnimatedImage::getCurrentFrame() { |
371 | 0 | if (this->simple()) return this->getCurrentFrameSimple(); |
372 | | |
373 | 0 | auto imageInfo = fDisplayFrame.fBitmap.info().makeDimensions(fCropRect.size()); |
374 | 0 | if (fPostProcess) { |
375 | | // Defensively use premul in case the post process adds alpha. |
376 | 0 | imageInfo = imageInfo.makeAlphaType(kPremul_SkAlphaType); |
377 | 0 | } |
378 | |
|
379 | 0 | SkBitmap dst; |
380 | 0 | if (!dst.tryAllocPixels(imageInfo)) { |
381 | 0 | return nullptr; |
382 | 0 | } |
383 | | |
384 | 0 | SkCanvas canvas(dst); |
385 | 0 | this->draw(&canvas); |
386 | 0 | return SkMakeImageFromRasterBitmap(dst, kNever_SkCopyPixelsMode); |
387 | 0 | } |