Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkAnimatedImage.cpp
Go to the documentation of this file.
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
19
20#include <limits.h>
21#include <utility>
22
23sk_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
43sk_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();
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
57SkAnimatedImage::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();
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
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
107
109 return SkRect::MakeIWH(fCropRect.width(), fCropRect.height());
110}
111
112SkAnimatedImage::Frame::Frame()
113 : fIndex(SkCodec::kNoFrame)
114{}
115
116bool 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
142bool 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
154 fFinished = false;
155 fRepetitionsCompleted = 0;
156 if (fDisplayFrame.fIndex != 0) {
157 fDisplayFrame.fIndex = SkCodec::kNoFrame;
158 this->decodeNextFrame();
159 }
160}
161
165
166int 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
184double SkAnimatedImage::finish() {
185 fFinished = true;
186 fCurrentFrameDuration = kFinished;
187 return kFinished;
188}
189
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.
211 frameInfo.fAlphaType = fCodec->getInfo().alphaType();
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.
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 ?
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
337 auto image = this->getCurrentFrameSimple();
338
339 if (this->simple()) {
340 canvas->drawImage(image, 0, 0);
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(SkFilterMode::kLinear), nullptr);
353 }
354 if (fPostProcess) {
355 canvas->drawPicture(fPostProcess);
356 canvas->restore();
357 }
358}
359
361 fRepetitionCount = newCount;
362}
363
364sk_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,
370}
371
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);
389}
const char * options
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
@ kOpaque_SkAlphaType
pixel is opaque
Definition SkAlphaType.h:28
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
static bool is_restore_previous(SkCodecAnimation::DisposalMethod dispose)
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkCodecPrintf(...)
Definition SkCodecPriv.h:23
@ kDefault_SkEncodedOrigin
static bool SkEncodedOriginSwapsWidthHeight(SkEncodedOrigin origin)
static SkMatrix SkEncodedOriginToMatrix(SkEncodedOrigin origin, int w, int h)
SK_SPI sk_sp< SkImage > SkMakeImageFromRasterBitmap(const SkBitmap &, SkCopyPixelsMode)
@ kNever_SkCopyPixelsMode
never copy src pixels (even if they are marked mutable)
Definition SkImagePriv.h:20
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition SkRefCnt.h:341
static sk_sp< SkAnimatedImage > Make(std::unique_ptr< SkAndroidCodec >, const SkImageInfo &info, SkIRect cropRect, sk_sp< SkPicture > postProcess)
void onDraw(SkCanvas *) override
static constexpr int kFinished
~SkAnimatedImage() override
SkRect onGetBounds() override
void setRepetitionCount(int count)
sk_sp< SkImage > getCurrentFrame()
void * getPixels() const
Definition SkBitmap.h:283
bool tryAllocPixels(const SkImageInfo &info, size_t rowBytes)
Definition SkBitmap.cpp:271
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition SkCanvas.cpp:500
void clipRect(const SkRect &rect, SkClipOp op, bool doAntiAlias)
void restore()
Definition SkCanvas.cpp:465
void concat(const SkMatrix &matrix)
void drawPicture(const SkPicture *picture)
Definition SkCanvas.h:1961
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition SkCanvas.h:1528
static const char * ResultToString(Result)
Definition SkCodec.cpp:880
@ kSuccess
Definition SkCodec.h:80
static constexpr int kRepetitionCountInfinite
Definition SkCodec.h:759
static constexpr int kNoFrame
Definition SkCodec.h:650
SkRect getBounds()
void draw(SkCanvas *, const SkMatrix *=nullptr)
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition SkMatrix.h:75
SkMatrix & postConcat(const SkMatrix &other)
Definition SkMatrix.cpp:683
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition SkMatrix.h:91
SkMatrix & preConcat(const SkMatrix &other)
Definition SkMatrix.cpp:674
sk_sp< SkImage > image
Definition examples.cpp:29
double frame
Definition examples.cpp:31
GAsyncResult * result
SK_API SkImageInfo SwapWidthHeight(const SkImageInfo &info)
dst
Definition cp.py:12
Definition ref_ptr.h:256
SkCodecAnimation::DisposalMethod fDisposalMethod
Definition SkCodec.h:704
SkAlphaType fAlphaType
Definition SkCodec.h:689
constexpr SkISize size() const
Definition SkRect.h:172
constexpr int32_t height() const
Definition SkRect.h:165
int32_t fTop
smaller y-axis bounds
Definition SkRect.h:34
static constexpr SkIRect MakeSize(const SkISize &size)
Definition SkRect.h:66
constexpr int32_t width() const
Definition SkRect.h:158
int32_t fLeft
smaller x-axis bounds
Definition SkRect.h:33
bool contains(int32_t x, int32_t y) const
Definition SkRect.h:463
SkImageInfo makeAlphaType(SkAlphaType newAlphaType) const
SkIRect bounds() const
SkImageInfo makeDimensions(SkISize newSize) const
SkISize dimensions() const
int width() const
int height() const
static SkRect MakeIWH(int w, int h)
Definition SkRect.h:623