/src/skia/modules/skresources/src/SkAnimCodecPlayer.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 "modules/skresources/src/SkAnimCodecPlayer.h" |
9 | | |
10 | | #include "include/codec/SkCodec.h" |
11 | | #include "include/codec/SkEncodedOrigin.h" |
12 | | #include "include/core/SkAlphaType.h" |
13 | | #include "include/core/SkBlendMode.h" |
14 | | #include "include/core/SkCanvas.h" |
15 | | #include "include/core/SkData.h" |
16 | | #include "include/core/SkImage.h" |
17 | | #include "include/core/SkImageInfo.h" |
18 | | #include "include/core/SkMatrix.h" |
19 | | #include "include/core/SkPaint.h" |
20 | | #include "include/core/SkRefCnt.h" |
21 | | #include "include/core/SkSamplingOptions.h" |
22 | | #include "include/core/SkSize.h" |
23 | | #include "include/core/SkTypes.h" |
24 | | #include "src/codec/SkCodecImageGenerator.h" |
25 | | |
26 | | #include <algorithm> |
27 | | #include <cstddef> |
28 | | #include <memory> |
29 | | #include <utility> |
30 | | #include <vector> |
31 | | |
32 | 0 | SkAnimCodecPlayer::SkAnimCodecPlayer(std::unique_ptr<SkCodec> codec) : fCodec(std::move(codec)) { |
33 | 0 | fImageInfo = fCodec->getInfo(); |
34 | 0 | fFrameInfos = fCodec->getFrameInfo(); |
35 | 0 | fImages.resize(fFrameInfos.size()); |
36 | | |
37 | | // change the interpretation of fDuration to a end-time for that frame |
38 | 0 | size_t dur = 0; |
39 | 0 | for (auto& f : fFrameInfos) { |
40 | 0 | dur += f.fDuration; |
41 | 0 | f.fDuration = dur; |
42 | 0 | } |
43 | 0 | fTotalDuration = dur; |
44 | |
|
45 | 0 | if (!fTotalDuration) { |
46 | | // Static image -- may or may not have returned a single frame info. |
47 | 0 | fFrameInfos.clear(); |
48 | 0 | fImages.clear(); |
49 | 0 | fImages.push_back(SkImages::DeferredFromGenerator( |
50 | 0 | SkCodecImageGenerator::MakeFromCodec(std::move(fCodec)))); |
51 | 0 | } |
52 | 0 | } |
53 | | |
54 | 0 | SkAnimCodecPlayer::~SkAnimCodecPlayer() {} |
55 | | |
56 | 0 | SkISize SkAnimCodecPlayer::dimensions() const { |
57 | 0 | if (!fCodec) { |
58 | 0 | auto image = fImages.front(); |
59 | 0 | return image ? image->dimensions() : SkISize::MakeEmpty(); |
60 | 0 | } |
61 | 0 | if (SkEncodedOriginSwapsWidthHeight(fCodec->getOrigin())) { |
62 | 0 | return { fImageInfo.height(), fImageInfo.width() }; |
63 | 0 | } |
64 | 0 | return { fImageInfo.width(), fImageInfo.height() }; |
65 | 0 | } |
66 | | |
67 | 0 | sk_sp<SkImage> SkAnimCodecPlayer::getFrameAt(int index) { |
68 | 0 | SkASSERT((unsigned)index < fFrameInfos.size()); |
69 | |
|
70 | 0 | if (fImages[index]) { |
71 | 0 | return fImages[index]; |
72 | 0 | } |
73 | | |
74 | 0 | size_t rb = fImageInfo.minRowBytes(); |
75 | 0 | size_t size = fImageInfo.computeByteSize(rb); |
76 | 0 | auto data = SkData::MakeUninitialized(size); |
77 | |
|
78 | 0 | SkCodec::Options opts; |
79 | 0 | opts.fFrameIndex = index; |
80 | |
|
81 | 0 | const auto origin = fCodec->getOrigin(); |
82 | 0 | const auto orientedDims = this->dimensions(); |
83 | 0 | const auto originMatrix = SkEncodedOriginToMatrix(origin, orientedDims.width(), |
84 | 0 | orientedDims.height()); |
85 | |
|
86 | 0 | SkPaint paint; |
87 | 0 | paint.setBlendMode(SkBlendMode::kSrc); |
88 | |
|
89 | 0 | auto imageInfo = fImageInfo; |
90 | 0 | if (fFrameInfos[index].fAlphaType != kOpaque_SkAlphaType && imageInfo.isOpaque()) { |
91 | 0 | imageInfo = imageInfo.makeAlphaType(kPremul_SkAlphaType); |
92 | 0 | } |
93 | 0 | const int requiredFrame = fFrameInfos[index].fRequiredFrame; |
94 | 0 | if (requiredFrame != SkCodec::kNoFrame && fImages[requiredFrame]) { |
95 | 0 | auto requiredImage = fImages[requiredFrame]; |
96 | 0 | auto canvas = SkCanvas::MakeRasterDirect(imageInfo, data->writable_data(), rb); |
97 | 0 | if (origin != kDefault_SkEncodedOrigin) { |
98 | | // The required frame is stored after applying the origin. Undo that, |
99 | | // because the codec decodes prior to applying the origin. |
100 | | // FIXME: Another approach would be to decode the frame's delta on top |
101 | | // of transparent black, and then draw that through the origin matrix |
102 | | // onto the required frame. To do that, SkCodec needs to expose the |
103 | | // rectangle of the delta and the blend mode, so we can handle |
104 | | // kRestoreBGColor frames and Blend::kSrc. |
105 | 0 | SkMatrix inverse; |
106 | 0 | SkAssertResult(originMatrix.invert(&inverse)); |
107 | 0 | canvas->concat(inverse); |
108 | 0 | } |
109 | 0 | canvas->drawImage(requiredImage, 0, 0, SkSamplingOptions(), &paint); |
110 | 0 | opts.fPriorFrame = requiredFrame; |
111 | 0 | } |
112 | |
|
113 | 0 | if (SkCodec::kSuccess != fCodec->getPixels(imageInfo, data->writable_data(), rb, &opts)) { |
114 | 0 | return nullptr; |
115 | 0 | } |
116 | | |
117 | 0 | auto image = SkImages::RasterFromData(imageInfo, std::move(data), rb); |
118 | 0 | if (origin != kDefault_SkEncodedOrigin) { |
119 | 0 | imageInfo = imageInfo.makeDimensions(orientedDims); |
120 | 0 | rb = imageInfo.minRowBytes(); |
121 | 0 | size = imageInfo.computeByteSize(rb); |
122 | 0 | data = SkData::MakeUninitialized(size); |
123 | 0 | auto canvas = SkCanvas::MakeRasterDirect(imageInfo, data->writable_data(), rb); |
124 | 0 | canvas->concat(originMatrix); |
125 | 0 | canvas->drawImage(image, 0, 0, SkSamplingOptions(), &paint); |
126 | 0 | image = SkImages::RasterFromData(imageInfo, std::move(data), rb); |
127 | 0 | } |
128 | 0 | return fImages[index] = image; |
129 | 0 | } |
130 | | |
131 | 0 | sk_sp<SkImage> SkAnimCodecPlayer::getFrame() { |
132 | 0 | SkASSERT(fTotalDuration > 0 || fImages.size() == 1); |
133 | |
|
134 | 0 | return fTotalDuration > 0 |
135 | 0 | ? this->getFrameAt(fCurrIndex) |
136 | 0 | : fImages.front(); |
137 | 0 | } |
138 | | |
139 | 0 | bool SkAnimCodecPlayer::seek(uint32_t msec) { |
140 | 0 | if (!fTotalDuration) { |
141 | 0 | return false; |
142 | 0 | } |
143 | | |
144 | 0 | msec %= fTotalDuration; |
145 | |
|
146 | 0 | auto lower = std::lower_bound(fFrameInfos.begin(), fFrameInfos.end(), msec, |
147 | 0 | [](const SkCodec::FrameInfo& info, uint32_t msec) { |
148 | 0 | return (uint32_t)info.fDuration <= msec; |
149 | 0 | }); |
150 | 0 | int prevIndex = fCurrIndex; |
151 | 0 | fCurrIndex = lower - fFrameInfos.begin(); |
152 | 0 | return fCurrIndex != prevIndex; |
153 | 0 | } |
154 | | |
155 | | |