1 /*
2 * Copyright 2016 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 "gm/gm.h"
9 #include "include/codec/SkCodec.h"
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkData.h"
13 #include "include/core/SkImageInfo.h"
14 #include "include/core/SkScalar.h"
15 #include "include/core/SkSize.h"
16 #include "include/core/SkStream.h"
17 #include "include/core/SkString.h"
18 #include "include/core/SkTypes.h"
19 #include "modules/skresources/src/SkAnimCodecPlayer.h"
20 #include "src/core/SkOSFile.h"
21 #include "tools/Resources.h"
22 #include "tools/ToolUtils.h"
23 #include "tools/flags/CommandLineFlags.h"
24 #include "tools/timer/TimeUtils.h"
25
26 #include <memory>
27 #include <utility>
28 #include <vector>
29
30 #if defined(SK_ENABLE_SKOTTIE)
31
32 static DEFINE_string(animatedGif, "images/test640x479.gif", "Animated gif in resources folder");
33
34 class AnimatedGifGM : public skiagm::GM {
35 private:
36 std::unique_ptr<SkCodec> fCodec;
37 int fFrame;
38 double fNextUpdate;
39 int fTotalFrames;
40 std::vector<SkCodec::FrameInfo> fFrameInfos;
41 std::vector<SkBitmap> fFrames;
42
drawFrame(SkCanvas * canvas,int frameIndex)43 void drawFrame(SkCanvas* canvas, int frameIndex) {
44 // FIXME: Create from an Image/ImageGenerator?
45 if (frameIndex >= (int) fFrames.size()) {
46 fFrames.resize(frameIndex + 1);
47 }
48 SkBitmap& bm = fFrames[frameIndex];
49 if (!bm.getPixels()) {
50 const SkImageInfo info = fCodec->getInfo().makeColorType(kN32_SkColorType);
51 bm.allocPixels(info);
52
53 SkCodec::Options opts;
54 opts.fFrameIndex = frameIndex;
55 const int requiredFrame = fFrameInfos[frameIndex].fRequiredFrame;
56 if (requiredFrame != SkCodec::kNoFrame) {
57 SkASSERT(requiredFrame >= 0
58 && static_cast<size_t>(requiredFrame) < fFrames.size());
59 SkBitmap& requiredBitmap = fFrames[requiredFrame];
60 // For simplicity, do not try to cache old frames
61 if (requiredBitmap.getPixels() &&
62 ToolUtils::copy_to(&bm, requiredBitmap.colorType(), requiredBitmap)) {
63 opts.fPriorFrame = requiredFrame;
64 }
65 }
66
67 if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(),
68 bm.rowBytes(), &opts)) {
69 SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]);
70 return;
71 }
72 }
73
74 canvas->drawImage(bm.asImage(), 0, 0);
75 }
76
77 public:
AnimatedGifGM()78 AnimatedGifGM()
79 : fFrame(0)
80 , fNextUpdate (-1)
81 , fTotalFrames (-1) {}
82
83 private:
getName() const84 SkString getName() const override { return SkString("animatedGif"); }
85
getISize()86 SkISize getISize() override {
87 if (this->initCodec()) {
88 SkISize dim = fCodec->getInfo().dimensions();
89 // Wide enough to display all the frames.
90 dim.fWidth *= fTotalFrames;
91 // Tall enough to show the row of frames plus an animating version.
92 dim.fHeight *= 2;
93 return dim;
94 }
95 return SkISize::Make(640, 480);
96 }
97
initCodec()98 bool initCodec() {
99 if (fCodec) {
100 return true;
101 }
102 if (FLAGS_animatedGif.isEmpty()) {
103 SkDebugf("Nothing specified for --animatedGif!");
104 return false;
105 }
106
107 std::unique_ptr<SkStream> stream(GetResourceAsStream(FLAGS_animatedGif[0]));
108 if (!stream) {
109 return false;
110 }
111
112 fCodec = SkCodec::MakeFromStream(std::move(stream));
113 if (!fCodec) {
114 return false;
115 }
116
117 fFrame = 0;
118 fFrameInfos = fCodec->getFrameInfo();
119 fTotalFrames = fFrameInfos.size();
120 return true;
121 }
122
onDraw(SkCanvas * canvas,SkString * errorMsg)123 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
124 if (!this->initCodec()) {
125 errorMsg->printf("Could not create codec from %s", FLAGS_animatedGif[0]);
126 return DrawResult::kFail;
127 }
128
129 canvas->save();
130 for (int frameIndex = 0; frameIndex < fTotalFrames; frameIndex++) {
131 this->drawFrame(canvas, frameIndex);
132 canvas->translate(SkIntToScalar(fCodec->getInfo().width()), 0);
133 }
134 canvas->restore();
135
136 SkAutoCanvasRestore acr(canvas, true);
137 canvas->translate(0, SkIntToScalar(fCodec->getInfo().height()));
138 this->drawFrame(canvas, fFrame);
139 return DrawResult::kOk;
140 }
141
onAnimate(double nanos)142 bool onAnimate(double nanos) override {
143 if (!fCodec || fTotalFrames == 1) {
144 return false;
145 }
146
147 double secs = TimeUtils::NanosToMSec(nanos) * .1;
148 if (fNextUpdate < double(0)) {
149 // This is a sentinel that we have not done any updates yet.
150 // I'm assuming this gets called *after* onOnceBeforeDraw, so our first frame should
151 // already have been retrieved.
152 SkASSERT(fFrame == 0);
153 fNextUpdate = secs + fFrameInfos[fFrame].fDuration;
154
155 return true;
156 }
157
158 if (secs < fNextUpdate) {
159 return true;
160 }
161
162 while (secs >= fNextUpdate) {
163 // Retrieve the next frame.
164 fFrame++;
165 if (fFrame == fTotalFrames) {
166 fFrame = 0;
167 }
168
169 // Note that we loop here. This is not safe if we need to draw the intermediate frame
170 // in order to draw correctly.
171 fNextUpdate += fFrameInfos[fFrame].fDuration;
172 }
173
174 return true;
175 }
176 };
DEF_GM(return new AnimatedGifGM;)177 DEF_GM(return new AnimatedGifGM;)
178
179 static std::unique_ptr<SkCodec> load_codec(const char filename[]) {
180 return SkCodec::MakeFromData(SkData::MakeFromFileName(filename));
181 }
182
183 class AnimCodecPlayerGM : public skiagm::GM {
184 private:
185 std::vector<std::unique_ptr<SkAnimCodecPlayer> > fPlayers;
186 uint32_t fBaseMSec = 0;
187
188 public:
AnimCodecPlayerGM()189 AnimCodecPlayerGM() {
190 const char* root = "/skia/anim/";
191 SkOSFile::Iter iter(root);
192 SkString path;
193 while (iter.next(&path)) {
194 SkString completepath;
195 completepath.printf("%s%s", root, path.c_str());
196 auto codec = load_codec(completepath.c_str());
197 if (codec) {
198 fPlayers.push_back(std::make_unique<SkAnimCodecPlayer>(std::move(codec)));
199 }
200 }
201 }
202
203 private:
getName() const204 SkString getName() const override { return SkString("AnimCodecPlayer"); }
205
getISize()206 SkISize getISize() override { return {1024, 768}; }
207
onDraw(SkCanvas * canvas)208 void onDraw(SkCanvas* canvas) override {
209 canvas->scale(0.25f, 0.25f);
210 for (auto& p : fPlayers) {
211 canvas->drawImage(p->getFrame(), 0, 0);
212 canvas->translate(p->dimensions().width(), 0);
213 }
214 }
215
onAnimate(double nanos)216 bool onAnimate(double nanos) override {
217 if (fBaseMSec == 0) {
218 fBaseMSec = TimeUtils::NanosToMSec(nanos);
219 }
220 for (auto& p : fPlayers) {
221 (void)p->seek(TimeUtils::NanosToMSec(nanos) - fBaseMSec);
222 }
223 return true;
224 }
225 };
226 DEF_GM(return new AnimCodecPlayerGM;)
227
228 class AnimCodecPlayerExifGM : public skiagm::GM {
229 const char* fPath;
230 SkISize fSize = SkISize::MakeEmpty();
231 std::unique_ptr<SkAnimCodecPlayer> fPlayer;
232 std::vector<SkCodec::FrameInfo> fFrameInfos;
233
init()234 void init() {
235 if (!fPlayer) {
236 auto data = GetResourceAsData(fPath);
237 if (!data) return;
238
239 auto codec = SkCodec::MakeFromData(std::move(data));
240 fFrameInfos = codec->getFrameInfo();
241 fPlayer = std::make_unique<SkAnimCodecPlayer>(std::move(codec));
242 if (!fPlayer) return;
243
244 // We'll draw one of each frame, so make it big enough to hold them all
245 // in a grid. The grid will be roughly square, with "factor" frames per
246 // row and up to "factor" rows.
247 const size_t count = fFrameInfos.size();
248 const float root = sqrt((float) count);
249 const int factor = sk_float_ceil2int(root);
250
251 auto imageSize = fPlayer->dimensions();
252 fSize.fWidth = imageSize.fWidth * factor;
253 fSize.fHeight = imageSize.fHeight * sk_float_ceil2int((float) count / (float) factor);
254 }
255 }
256
getName() const257 SkString getName() const override {
258 return SkStringPrintf("AnimCodecPlayerExif_%s", strrchr(fPath, '/') + 1);
259 }
260
getISize()261 SkISize getISize() override {
262 this->init();
263 return fSize;
264 }
265
onDraw(SkCanvas * canvas)266 void onDraw(SkCanvas* canvas) override {
267 this->init();
268 if (!fPlayer) return;
269
270 const float root = sqrt((float) fFrameInfos.size());
271 const int factor = sk_float_ceil2int(root);
272 auto dimensions = fPlayer->dimensions();
273
274 uint32_t duration = 0;
275 for (int frame = 0; duration < fPlayer->duration(); frame++) {
276 SkAutoCanvasRestore acr(canvas, true);
277 const int xTranslate = (frame % factor) * dimensions.width();
278 const int yTranslate = (frame / factor) * dimensions.height();
279 canvas->translate(SkIntToScalar(xTranslate), SkIntToScalar(yTranslate));
280
281
282 auto image = fPlayer->getFrame();
283 canvas->drawImage(image, 0, 0);
284 duration += fFrameInfos[frame].fDuration;
285 fPlayer->seek(duration);
286 }
287 }
288 public:
AnimCodecPlayerExifGM(const char * path)289 AnimCodecPlayerExifGM(const char* path)
290 : fPath(path)
291 {}
292
293 ~AnimCodecPlayerExifGM() override = default;
294 };
295
296 DEF_GM(return new AnimCodecPlayerExifGM("images/required.webp");)
297 DEF_GM(return new AnimCodecPlayerExifGM("images/required.gif");)
298 DEF_GM(return new AnimCodecPlayerExifGM("images/stoplight_h.webp");)
299
300 #endif
301