blob: 398ef211efe429c161fd5fd63fa1d74fb9cbcdfa [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"
19#include "include/utils/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
Mike Klein84836b72019-03-21 11:31:36 -050030static DEFINE_string(animatedGif, "images/test640x479.gif", "Animated gif in resources folder");
scroggo19b91532016-10-24 09:03:26 -070031
scroggo19b91532016-10-24 09:03:26 -070032class AnimatedGifGM : public skiagm::GM {
33private:
34 std::unique_ptr<SkCodec> fCodec;
Leon Scroggins III249b8e32017-04-17 12:46:33 -040035 int fFrame;
scroggo19b91532016-10-24 09:03:26 -070036 double fNextUpdate;
Leon Scroggins III249b8e32017-04-17 12:46:33 -040037 int fTotalFrames;
scroggo19b91532016-10-24 09:03:26 -070038 std::vector<SkCodec::FrameInfo> fFrameInfos;
39 std::vector<SkBitmap> fFrames;
40
41 void drawFrame(SkCanvas* canvas, int frameIndex) {
42 // FIXME: Create from an Image/ImageGenerator?
43 if (frameIndex >= (int) fFrames.size()) {
44 fFrames.resize(frameIndex + 1);
45 }
46 SkBitmap& bm = fFrames[frameIndex];
47 if (!bm.getPixels()) {
48 const SkImageInfo info = fCodec->getInfo().makeColorType(kN32_SkColorType);
49 bm.allocPixels(info);
50
51 SkCodec::Options opts;
52 opts.fFrameIndex = frameIndex;
Leon Scroggins III249b8e32017-04-17 12:46:33 -040053 const int requiredFrame = fFrameInfos[frameIndex].fRequiredFrame;
Nigel Tao66bc5242018-08-22 10:56:03 +100054 if (requiredFrame != SkCodec::kNoFrame) {
Leon Scroggins III249b8e32017-04-17 12:46:33 -040055 SkASSERT(requiredFrame >= 0
56 && static_cast<size_t>(requiredFrame) < fFrames.size());
scroggo19b91532016-10-24 09:03:26 -070057 SkBitmap& requiredBitmap = fFrames[requiredFrame];
58 // For simplicity, do not try to cache old frames
Matt Sarett68b8e3d2017-04-28 11:15:22 -040059 if (requiredBitmap.getPixels() &&
Mike Kleinea3f0142019-03-20 11:12:10 -050060 ToolUtils::copy_to(&bm, requiredBitmap.colorType(), requiredBitmap)) {
Leon Scroggins III33deb7e2017-06-07 12:31:51 -040061 opts.fPriorFrame = requiredFrame;
scroggo19b91532016-10-24 09:03:26 -070062 }
63 }
64
65 if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(),
Leon Scroggins571b30f2017-07-11 17:35:31 +000066 bm.rowBytes(), &opts)) {
scroggo19b91532016-10-24 09:03:26 -070067 SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]);
68 return;
69 }
70 }
71
Mike Reed568f0ae2021-01-24 08:57:23 -050072 canvas->drawImage(bm.asImage(), 0, 0);
scroggo19b91532016-10-24 09:03:26 -070073 }
74
75public:
76 AnimatedGifGM()
77 : fFrame(0)
78 , fNextUpdate (-1)
79 , fTotalFrames (-1) {}
80
81private:
82 SkString onShortName() override {
83 return SkString("animatedGif");
84 }
85
86 SkISize onISize() 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
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};
scroggo19b91532016-10-24 09:03:26 -0700177DEF_GM(return new AnimatedGifGM);
178
Mike Reed964aedb2018-10-03 10:16:55 -0400179
Mike Reed964aedb2018-10-03 10:16:55 -0400180static std::unique_ptr<SkCodec> load_codec(const char filename[]) {
181 return SkCodec::MakeFromData(SkData::MakeFromFileName(filename));
182}
183
184class AnimCodecPlayerGM : public skiagm::GM {
185private:
186 std::vector<std::unique_ptr<SkAnimCodecPlayer> > fPlayers;
187 uint32_t fBaseMSec = 0;
188
189public:
190 AnimCodecPlayerGM() {
191 const char* root = "/skia/anim/";
192 SkOSFile::Iter iter(root);
193 SkString path;
194 while (iter.next(&path)) {
195 SkString completepath;
196 completepath.printf("%s%s", root, path.c_str());
197 auto codec = load_codec(completepath.c_str());
198 if (codec) {
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500199 fPlayers.push_back(std::make_unique<SkAnimCodecPlayer>(std::move(codec)));
Mike Reed964aedb2018-10-03 10:16:55 -0400200 }
201 }
202 }
203
204private:
205 SkString onShortName() override {
206 return SkString("AnimCodecPlayer");
207 }
208
209 SkISize onISize() override {
210 return { 1024, 768 };
211 }
212
213 void onDraw(SkCanvas* canvas) override {
214 canvas->scale(0.25f, 0.25f);
215 for (auto& p : fPlayers) {
Mike Reed8d29ab62021-01-23 18:10:39 -0500216 canvas->drawImage(p->getFrame(), 0, 0);
Mike Reed964aedb2018-10-03 10:16:55 -0400217 canvas->translate(p->dimensions().width(), 0);
218 }
219 }
220
Hal Canary41248072019-07-11 16:32:53 -0400221 bool onAnimate(double nanos) override {
Mike Reed964aedb2018-10-03 10:16:55 -0400222 if (fBaseMSec == 0) {
Hal Canary41248072019-07-11 16:32:53 -0400223 fBaseMSec = TimeUtils::NanosToMSec(nanos);
Mike Reed964aedb2018-10-03 10:16:55 -0400224 }
225 for (auto& p : fPlayers) {
Hal Canary41248072019-07-11 16:32:53 -0400226 (void)p->seek(TimeUtils::NanosToMSec(nanos) - fBaseMSec);
Mike Reed964aedb2018-10-03 10:16:55 -0400227 }
228 return true;
229 }
230};
231DEF_GM(return new AnimCodecPlayerGM);
Leon Scrogginsbc098ef2020-10-27 15:24:18 -0400232
233class AnimCodecPlayerExifGM : public skiagm::GM {
234 const char* fPath;
235 SkISize fSize = SkISize::MakeEmpty();
236 std::unique_ptr<SkAnimCodecPlayer> fPlayer;
237 std::vector<SkCodec::FrameInfo> fFrameInfos;
238
239 void init() {
240 if (!fPlayer) {
241 auto data = GetResourceAsData(fPath);
242 if (!data) return;
243
244 auto codec = SkCodec::MakeFromData(std::move(data));
245 fFrameInfos = codec->getFrameInfo();
246 fPlayer = std::make_unique<SkAnimCodecPlayer>(std::move(codec));
247 if (!fPlayer) return;
248
249 // We'll draw one of each frame, so make it big enough to hold them all
250 // in a grid. The grid will be roughly square, with "factor" frames per
251 // row and up to "factor" rows.
252 const size_t count = fFrameInfos.size();
253 const float root = sqrt((float) count);
254 const int factor = sk_float_ceil2int(root);
255
256 auto imageSize = fPlayer->dimensions();
257 fSize.fWidth = imageSize.fWidth * factor;
258 fSize.fHeight = imageSize.fHeight * sk_float_ceil2int((float) count / (float) factor);
259 }
260 }
261
262 SkString onShortName() override {
263 return SkStringPrintf("AnimCodecPlayerExif_%s", strrchr(fPath, '/') + 1);
264 }
265
266 SkISize onISize() override {
267 this->init();
268 return fSize;
269 }
270
271 void onDraw(SkCanvas* canvas) override {
272 this->init();
273 if (!fPlayer) return;
274
275 const float root = sqrt((float) fFrameInfos.size());
276 const int factor = sk_float_ceil2int(root);
277 auto dimensions = fPlayer->dimensions();
278
279 uint32_t duration = 0;
280 for (int frame = 0; duration < fPlayer->duration(); frame++) {
281 SkAutoCanvasRestore acr(canvas, true);
282 const int xTranslate = (frame % factor) * dimensions.width();
283 const int yTranslate = (frame / factor) * dimensions.height();
284 canvas->translate(SkIntToScalar(xTranslate), SkIntToScalar(yTranslate));
285
286
287 auto image = fPlayer->getFrame();
Mike Reed8d29ab62021-01-23 18:10:39 -0500288 canvas->drawImage(image, 0, 0);
Leon Scrogginsbc098ef2020-10-27 15:24:18 -0400289 duration += fFrameInfos[frame].fDuration;
290 fPlayer->seek(duration);
291 }
292 }
293public:
294 AnimCodecPlayerExifGM(const char* path)
295 : fPath(path)
296 {}
297
298 ~AnimCodecPlayerExifGM() override = default;
299};
300
301DEF_GM(return new AnimCodecPlayerExifGM("images/required.webp");)
302DEF_GM(return new AnimCodecPlayerExifGM("images/required.gif");)
303DEF_GM(return new AnimCodecPlayerExifGM("images/stoplight_h.webp");)