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/codec/SkCodec.h" |
9 | #include "include/core/SkData.h" |
10 | #include "include/core/SkImage.h" |
11 | #include "include/utils/SkAnimCodecPlayer.h" |
12 | #include "src/codec/SkCodecImageGenerator.h" |
13 | #include <algorithm> |
14 | |
15 | SkAnimCodecPlayer::SkAnimCodecPlayer(std::unique_ptr<SkCodec> codec) : fCodec(std::move(codec)) { |
16 | fImageInfo = fCodec->getInfo(); |
17 | fFrameInfos = fCodec->getFrameInfo(); |
18 | fImages.resize(fFrameInfos.size()); |
19 | |
20 | // change the interpretation of fDuration to a end-time for that frame |
21 | size_t dur = 0; |
22 | for (auto& f : fFrameInfos) { |
23 | dur += f.fDuration; |
24 | f.fDuration = dur; |
25 | } |
26 | fTotalDuration = dur; |
27 | |
28 | if (!fTotalDuration) { |
29 | // Static image -- may or may not have returned a single frame info. |
30 | fFrameInfos.clear(); |
31 | fImages.clear(); |
32 | fImages.push_back(SkImage::MakeFromGenerator( |
33 | SkCodecImageGenerator::MakeFromCodec(std::move(fCodec)))); |
34 | } |
35 | } |
36 | |
37 | SkAnimCodecPlayer::~SkAnimCodecPlayer() {} |
38 | |
39 | SkISize SkAnimCodecPlayer::dimensions() { |
40 | return { fImageInfo.width(), fImageInfo.height() }; |
41 | } |
42 | |
43 | sk_sp<SkImage> SkAnimCodecPlayer::getFrameAt(int index) { |
44 | SkASSERT((unsigned)index < fFrameInfos.size()); |
45 | |
46 | if (fImages[index]) { |
47 | return fImages[index]; |
48 | } |
49 | |
50 | size_t rb = fImageInfo.minRowBytes(); |
51 | size_t size = fImageInfo.computeByteSize(rb); |
52 | auto data = SkData::MakeUninitialized(size); |
53 | |
54 | SkCodec::Options opts; |
55 | opts.fFrameIndex = index; |
56 | |
57 | const int requiredFrame = fFrameInfos[index].fRequiredFrame; |
58 | if (requiredFrame != SkCodec::kNoFrame) { |
59 | auto requiredImage = fImages[requiredFrame]; |
60 | SkPixmap requiredPM; |
61 | if (requiredImage && requiredImage->peekPixels(&requiredPM)) { |
62 | sk_careful_memcpy(data->writable_data(), requiredPM.addr(), size); |
63 | opts.fPriorFrame = requiredFrame; |
64 | } |
65 | } |
66 | if (SkCodec::kSuccess == fCodec->getPixels(fImageInfo, data->writable_data(), rb, &opts)) { |
67 | return fImages[index] = SkImage::MakeRasterData(fImageInfo, std::move(data), rb); |
68 | } |
69 | return nullptr; |
70 | } |
71 | |
72 | sk_sp<SkImage> SkAnimCodecPlayer::getFrame() { |
73 | SkASSERT(fTotalDuration > 0 || fImages.size() == 1); |
74 | |
75 | return fTotalDuration > 0 |
76 | ? this->getFrameAt(fCurrIndex) |
77 | : fImages.front(); |
78 | } |
79 | |
80 | bool SkAnimCodecPlayer::seek(uint32_t msec) { |
81 | if (!fTotalDuration) { |
82 | return false; |
83 | } |
84 | |
85 | msec %= fTotalDuration; |
86 | |
87 | auto lower = std::lower_bound(fFrameInfos.begin(), fFrameInfos.end(), msec, |
88 | [](const SkCodec::FrameInfo& info, uint32_t msec) { |
89 | return (uint32_t)info.fDuration < msec; |
90 | }); |
91 | int prevIndex = fCurrIndex; |
92 | fCurrIndex = lower - fFrameInfos.begin(); |
93 | return fCurrIndex != prevIndex; |
94 | } |
95 | |
96 | |
97 | |