blob: 319465db49a9d9a508b518d7248731c9598ce247 [file] [log] [blame]
scroggo19b91532016-10-24 09:03:26 -07001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
9#include "include/codec/SkCodec.h"
Ben Wagnerd1701ba2019-04-30 13:44:26 -040010#include "include/core/SkBitmap.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkCanvas.h"
Ben Wagnerd1701ba2019-04-30 13:44:26 -040012#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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "include/core/SkString.h"
Ben Wagnerd1701ba2019-04-30 13:44:26 -040018#include "include/core/SkTypes.h"
K Lubick3a6c5c42024-02-02 11:02:26 -050019#include "modules/skresources/src/SkAnimCodecPlayer.h"
Ben Wagnerd1701ba2019-04-30 13:44:26 -040020#include "src/core/SkOSFile.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "tools/Resources.h"
22#include "tools/ToolUtils.h"
23#include "tools/flags/CommandLineFlags.h"
Hal Canary41248072019-07-11 16:32:53 -040024#include "tools/timer/TimeUtils.h"
scroggo19b91532016-10-24 09:03:26 -070025
Ben Wagnerd1701ba2019-04-30 13:44:26 -040026#include <memory>
27#include <utility>
scroggo19b91532016-10-24 09:03:26 -070028#include <vector>
29
K Lubick35a72b22024-02-02 16:26:51 -050030#if defined(SK_ENABLE_SKOTTIE)
31
Mike Klein84836b72019-03-21 11:31:36 -050032static DEFINE_string(animatedGif, "images/test640x479.gif", "Animated gif in resources folder");
scroggo19b91532016-10-24 09:03:26 -070033
scroggo19b91532016-10-24 09:03:26 -070034class AnimatedGifGM : public skiagm::GM {
35private:
36 std::unique_ptr<SkCodec> fCodec;
Leon Scroggins III249b8e32017-04-17 12:46:33 -040037 int fFrame;
scroggo19b91532016-10-24 09:03:26 -070038 double fNextUpdate;
Leon Scroggins III249b8e32017-04-17 12:46:33 -040039 int fTotalFrames;
scroggo19b91532016-10-24 09:03:26 -070040 std::vector<SkCodec::FrameInfo> fFrameInfos;
41 std::vector<SkBitmap> fFrames;
42
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;
Leon Scroggins III249b8e32017-04-17 12:46:33 -040055 const int requiredFrame = fFrameInfos[frameIndex].fRequiredFrame;
Nigel Tao66bc5242018-08-22 10:56:03 +100056 if (requiredFrame != SkCodec::kNoFrame) {
Leon Scroggins III249b8e32017-04-17 12:46:33 -040057 SkASSERT(requiredFrame >= 0
58 && static_cast<size_t>(requiredFrame) < fFrames.size());
scroggo19b91532016-10-24 09:03:26 -070059 SkBitmap& requiredBitmap = fFrames[requiredFrame];
60 // For simplicity, do not try to cache old frames
Matt Sarett68b8e3d2017-04-28 11:15:22 -040061 if (requiredBitmap.getPixels() &&
Mike Kleinea3f0142019-03-20 11:12:10 -050062 ToolUtils::copy_to(&bm, requiredBitmap.colorType(), requiredBitmap)) {
Leon Scroggins III33deb7e2017-06-07 12:31:51 -040063 opts.fPriorFrame = requiredFrame;
scroggo19b91532016-10-24 09:03:26 -070064 }
65 }
66
67 if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(),
Leon Scroggins571b30f2017-07-11 17:35:31 +000068 bm.rowBytes(), &opts)) {
scroggo19b91532016-10-24 09:03:26 -070069 SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]);
70 return;
71 }
72 }
73
Mike Reed568f0ae2021-01-24 08:57:23 -050074 canvas->drawImage(bm.asImage(), 0, 0);
scroggo19b91532016-10-24 09:03:26 -070075 }
76
77public:
78 AnimatedGifGM()
79 : fFrame(0)
80 , fNextUpdate (-1)
81 , fTotalFrames (-1) {}
82
83private:
Leandro Lovisolo24fa2112023-08-15 19:05:17 +000084 SkString getName() const override { return SkString("animatedGif"); }
scroggo19b91532016-10-24 09:03:26 -070085
Leandro Lovisolo8f023882023-08-15 21:13:52 +000086 SkISize getISize() override {
scroggo19b91532016-10-24 09:03:26 -070087 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
scroggo19b91532016-10-24 09:03:26 -070098 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
Mike Reedede7bac2017-07-23 15:30:02 -0400112 fCodec = SkCodec::MakeFromStream(std::move(stream));
scroggo19b91532016-10-24 09:03:26 -0700113 if (!fCodec) {
scroggo19b91532016-10-24 09:03:26 -0700114 return false;
115 }
116
117 fFrame = 0;
118 fFrameInfos = fCodec->getFrameInfo();
119 fTotalFrames = fFrameInfos.size();
120 return true;
121 }
122
Chris Dalton50e24d72019-02-07 16:20:09 -0700123 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
Chris Dalton21ca3702019-02-01 12:15:42 -0700124 if (!this->initCodec()) {
Chris Dalton50e24d72019-02-07 16:20:09 -0700125 errorMsg->printf("Could not create codec from %s", FLAGS_animatedGif[0]);
126 return DrawResult::kFail;
scroggo19b91532016-10-24 09:03:26 -0700127 }
128
Chris Dalton21ca3702019-02-01 12:15:42 -0700129 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
scroggo19b91532016-10-24 09:03:26 -0700136 SkAutoCanvasRestore acr(canvas, true);
Jim Van Verth3cfdf6c2016-10-26 09:45:23 -0400137 canvas->translate(0, SkIntToScalar(fCodec->getInfo().height()));
scroggo19b91532016-10-24 09:03:26 -0700138 this->drawFrame(canvas, fFrame);
Chris Dalton50e24d72019-02-07 16:20:09 -0700139 return DrawResult::kOk;
scroggo19b91532016-10-24 09:03:26 -0700140 }
141
Hal Canary41248072019-07-11 16:32:53 -0400142 bool onAnimate(double nanos) override {
scroggo19b91532016-10-24 09:03:26 -0700143 if (!fCodec || fTotalFrames == 1) {
144 return false;
145 }
146
Hal Canary41248072019-07-11 16:32:53 -0400147 double secs = TimeUtils::NanosToMSec(nanos) * .1;
scroggo19b91532016-10-24 09:03:26 -0700148 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};
Leandro Lovisolo37021ef2023-06-29 19:18:55 +0000177DEF_GM(return new AnimatedGifGM;)
Mike Reed964aedb2018-10-03 10:16:55 -0400178
Mike Reed964aedb2018-10-03 10:16:55 -0400179static std::unique_ptr<SkCodec> load_codec(const char filename[]) {
180 return SkCodec::MakeFromData(SkData::MakeFromFileName(filename));
181}
182
183class AnimCodecPlayerGM : public skiagm::GM {
184private:
185 std::vector<std::unique_ptr<SkAnimCodecPlayer> > fPlayers;
186 uint32_t fBaseMSec = 0;
187
188public:
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) {
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500198 fPlayers.push_back(std::make_unique<SkAnimCodecPlayer>(std::move(codec)));
Mike Reed964aedb2018-10-03 10:16:55 -0400199 }
200 }
201 }
202
203private:
Leandro Lovisolo24fa2112023-08-15 19:05:17 +0000204 SkString getName() const override { return SkString("AnimCodecPlayer"); }
Mike Reed964aedb2018-10-03 10:16:55 -0400205
Leandro Lovisolo8f023882023-08-15 21:13:52 +0000206 SkISize getISize() override { return {1024, 768}; }
Mike Reed964aedb2018-10-03 10:16:55 -0400207
208 void onDraw(SkCanvas* canvas) override {
209 canvas->scale(0.25f, 0.25f);
210 for (auto& p : fPlayers) {
Mike Reed8d29ab62021-01-23 18:10:39 -0500211 canvas->drawImage(p->getFrame(), 0, 0);
Mike Reed964aedb2018-10-03 10:16:55 -0400212 canvas->translate(p->dimensions().width(), 0);
213 }
214 }
215
Hal Canary41248072019-07-11 16:32:53 -0400216 bool onAnimate(double nanos) override {
Mike Reed964aedb2018-10-03 10:16:55 -0400217 if (fBaseMSec == 0) {
Hal Canary41248072019-07-11 16:32:53 -0400218 fBaseMSec = TimeUtils::NanosToMSec(nanos);
Mike Reed964aedb2018-10-03 10:16:55 -0400219 }
220 for (auto& p : fPlayers) {
Hal Canary41248072019-07-11 16:32:53 -0400221 (void)p->seek(TimeUtils::NanosToMSec(nanos) - fBaseMSec);
Mike Reed964aedb2018-10-03 10:16:55 -0400222 }
223 return true;
224 }
225};
Leandro Lovisolo37021ef2023-06-29 19:18:55 +0000226DEF_GM(return new AnimCodecPlayerGM;)
Leon Scrogginsbc098ef2020-10-27 15:24:18 -0400227
228class 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
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
Leandro Lovisolo24fa2112023-08-15 19:05:17 +0000257 SkString getName() const override {
Leon Scrogginsbc098ef2020-10-27 15:24:18 -0400258 return SkStringPrintf("AnimCodecPlayerExif_%s", strrchr(fPath, '/') + 1);
259 }
260
Leandro Lovisolo8f023882023-08-15 21:13:52 +0000261 SkISize getISize() override {
Leon Scrogginsbc098ef2020-10-27 15:24:18 -0400262 this->init();
263 return fSize;
264 }
265
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();
Mike Reed8d29ab62021-01-23 18:10:39 -0500283 canvas->drawImage(image, 0, 0);
Leon Scrogginsbc098ef2020-10-27 15:24:18 -0400284 duration += fFrameInfos[frame].fDuration;
285 fPlayer->seek(duration);
286 }
287 }
288public:
289 AnimCodecPlayerExifGM(const char* path)
290 : fPath(path)
291 {}
292
293 ~AnimCodecPlayerExifGM() override = default;
294};
295
296DEF_GM(return new AnimCodecPlayerExifGM("images/required.webp");)
297DEF_GM(return new AnimCodecPlayerExifGM("images/required.gif");)
298DEF_GM(return new AnimCodecPlayerExifGM("images/stoplight_h.webp");)
K Lubick35a72b22024-02-02 16:26:51 -0500299
300#endif