blob: 72116eb190fdd18570c78945364626ec2771a129 [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
8#include "gm.h"
Matt Sarett68b8e3d2017-04-28 11:15:22 -04009#include "sk_tool_utils.h"
scroggo19b91532016-10-24 09:03:26 -070010#include "SkAnimTimer.h"
11#include "SkCanvas.h"
12#include "SkCodec.h"
13#include "SkColor.h"
14#include "SkCommandLineFlags.h"
Mike Reed17c574a2018-12-12 18:10:38 -050015#include "SkFont.h"
scroggo19b91532016-10-24 09:03:26 -070016#include "SkPaint.h"
17#include "SkString.h"
18#include "Resources.h"
19
20#include <vector>
21
Hal Canaryc465d132017-12-08 10:21:31 -050022DEFINE_string(animatedGif, "images/test640x479.gif", "Animated gif in resources folder");
scroggo19b91532016-10-24 09:03:26 -070023
24namespace {
25 void error(SkCanvas* canvas, const SkString& errorText) {
26 constexpr SkScalar kOffset = 5.0f;
27 canvas->drawColor(SK_ColorRED);
28 SkPaint paint;
Mike Reed17c574a2018-12-12 18:10:38 -050029 SkFont font;
scroggo19b91532016-10-24 09:03:26 -070030 SkRect bounds;
Mike Reed17c574a2018-12-12 18:10:38 -050031 font.measureText(errorText.c_str(), errorText.size(), kUTF8_SkTextEncoding, &bounds);
32 canvas->drawSimpleText(errorText.c_str(), errorText.size(), kUTF8_SkTextEncoding,
33 kOffset, bounds.height() + kOffset, font, paint);
scroggo19b91532016-10-24 09:03:26 -070034 }
35}
36
37class AnimatedGifGM : public skiagm::GM {
38private:
39 std::unique_ptr<SkCodec> fCodec;
Leon Scroggins III249b8e32017-04-17 12:46:33 -040040 int fFrame;
scroggo19b91532016-10-24 09:03:26 -070041 double fNextUpdate;
Leon Scroggins III249b8e32017-04-17 12:46:33 -040042 int fTotalFrames;
scroggo19b91532016-10-24 09:03:26 -070043 std::vector<SkCodec::FrameInfo> fFrameInfos;
44 std::vector<SkBitmap> fFrames;
45
46 void drawFrame(SkCanvas* canvas, int frameIndex) {
47 // FIXME: Create from an Image/ImageGenerator?
48 if (frameIndex >= (int) fFrames.size()) {
49 fFrames.resize(frameIndex + 1);
50 }
51 SkBitmap& bm = fFrames[frameIndex];
52 if (!bm.getPixels()) {
53 const SkImageInfo info = fCodec->getInfo().makeColorType(kN32_SkColorType);
54 bm.allocPixels(info);
55
56 SkCodec::Options opts;
57 opts.fFrameIndex = frameIndex;
Leon Scroggins III249b8e32017-04-17 12:46:33 -040058 const int requiredFrame = fFrameInfos[frameIndex].fRequiredFrame;
Nigel Tao66bc5242018-08-22 10:56:03 +100059 if (requiredFrame != SkCodec::kNoFrame) {
Leon Scroggins III249b8e32017-04-17 12:46:33 -040060 SkASSERT(requiredFrame >= 0
61 && static_cast<size_t>(requiredFrame) < fFrames.size());
scroggo19b91532016-10-24 09:03:26 -070062 SkBitmap& requiredBitmap = fFrames[requiredFrame];
63 // For simplicity, do not try to cache old frames
Matt Sarett68b8e3d2017-04-28 11:15:22 -040064 if (requiredBitmap.getPixels() &&
65 sk_tool_utils::copy_to(&bm, requiredBitmap.colorType(), requiredBitmap)) {
Leon Scroggins III33deb7e2017-06-07 12:31:51 -040066 opts.fPriorFrame = requiredFrame;
scroggo19b91532016-10-24 09:03:26 -070067 }
68 }
69
70 if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(),
Leon Scroggins571b30f2017-07-11 17:35:31 +000071 bm.rowBytes(), &opts)) {
scroggo19b91532016-10-24 09:03:26 -070072 SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]);
73 return;
74 }
75 }
76
77 canvas->drawBitmap(bm, 0, 0);
78 }
79
80public:
81 AnimatedGifGM()
82 : fFrame(0)
83 , fNextUpdate (-1)
84 , fTotalFrames (-1) {}
85
86private:
87 SkString onShortName() override {
88 return SkString("animatedGif");
89 }
90
91 SkISize onISize() override {
92 if (this->initCodec()) {
93 SkISize dim = fCodec->getInfo().dimensions();
94 // Wide enough to display all the frames.
95 dim.fWidth *= fTotalFrames;
96 // Tall enough to show the row of frames plus an animating version.
97 dim.fHeight *= 2;
98 return dim;
99 }
100 return SkISize::Make(640, 480);
101 }
102
103 void onDrawBackground(SkCanvas* canvas) override {
scroggocf280a42016-10-24 09:59:53 -0700104 canvas->clear(SK_ColorWHITE);
scroggo19b91532016-10-24 09:03:26 -0700105 if (this->initCodec()) {
106 SkAutoCanvasRestore acr(canvas, true);
Leon Scroggins III249b8e32017-04-17 12:46:33 -0400107 for (int frameIndex = 0; frameIndex < fTotalFrames; frameIndex++) {
scroggo19b91532016-10-24 09:03:26 -0700108 this->drawFrame(canvas, frameIndex);
Jim Van Verth3cfdf6c2016-10-26 09:45:23 -0400109 canvas->translate(SkIntToScalar(fCodec->getInfo().width()), 0);
scroggo19b91532016-10-24 09:03:26 -0700110 }
111 }
112 }
113
114 bool initCodec() {
115 if (fCodec) {
116 return true;
117 }
118 if (FLAGS_animatedGif.isEmpty()) {
119 SkDebugf("Nothing specified for --animatedGif!");
120 return false;
121 }
122
123 std::unique_ptr<SkStream> stream(GetResourceAsStream(FLAGS_animatedGif[0]));
124 if (!stream) {
125 return false;
126 }
127
Mike Reedede7bac2017-07-23 15:30:02 -0400128 fCodec = SkCodec::MakeFromStream(std::move(stream));
scroggo19b91532016-10-24 09:03:26 -0700129 if (!fCodec) {
130 SkDebugf("Could create codec from %s", FLAGS_animatedGif[0]);
131 return false;
132 }
133
134 fFrame = 0;
135 fFrameInfos = fCodec->getFrameInfo();
136 fTotalFrames = fFrameInfos.size();
137 return true;
138 }
139
140 void onDraw(SkCanvas* canvas) override {
141 if (!fCodec) {
142 SkString errorText = SkStringPrintf("Nothing to draw; %s", FLAGS_animatedGif[0]);
143 error(canvas, errorText);
144 return;
145 }
146
147 SkAutoCanvasRestore acr(canvas, true);
Jim Van Verth3cfdf6c2016-10-26 09:45:23 -0400148 canvas->translate(0, SkIntToScalar(fCodec->getInfo().height()));
scroggo19b91532016-10-24 09:03:26 -0700149 this->drawFrame(canvas, fFrame);
150 }
151
152 bool onAnimate(const SkAnimTimer& timer) override {
153 if (!fCodec || fTotalFrames == 1) {
154 return false;
155 }
156
157 double secs = timer.msec() * .1;
158 if (fNextUpdate < double(0)) {
159 // This is a sentinel that we have not done any updates yet.
160 // I'm assuming this gets called *after* onOnceBeforeDraw, so our first frame should
161 // already have been retrieved.
162 SkASSERT(fFrame == 0);
163 fNextUpdate = secs + fFrameInfos[fFrame].fDuration;
164
165 return true;
166 }
167
168 if (secs < fNextUpdate) {
169 return true;
170 }
171
172 while (secs >= fNextUpdate) {
173 // Retrieve the next frame.
174 fFrame++;
175 if (fFrame == fTotalFrames) {
176 fFrame = 0;
177 }
178
179 // Note that we loop here. This is not safe if we need to draw the intermediate frame
180 // in order to draw correctly.
181 fNextUpdate += fFrameInfos[fFrame].fDuration;
182 }
183
184 return true;
185 }
186};
scroggo19b91532016-10-24 09:03:26 -0700187DEF_GM(return new AnimatedGifGM);
188
Mike Reed964aedb2018-10-03 10:16:55 -0400189
190#include "SkAnimCodecPlayer.h"
191#include "SkOSFile.h"
192#include "SkMakeUnique.h"
193
194static std::unique_ptr<SkCodec> load_codec(const char filename[]) {
195 return SkCodec::MakeFromData(SkData::MakeFromFileName(filename));
196}
197
198class AnimCodecPlayerGM : public skiagm::GM {
199private:
200 std::vector<std::unique_ptr<SkAnimCodecPlayer> > fPlayers;
201 uint32_t fBaseMSec = 0;
202
203public:
204 AnimCodecPlayerGM() {
205 const char* root = "/skia/anim/";
206 SkOSFile::Iter iter(root);
207 SkString path;
208 while (iter.next(&path)) {
209 SkString completepath;
210 completepath.printf("%s%s", root, path.c_str());
211 auto codec = load_codec(completepath.c_str());
212 if (codec) {
213 fPlayers.push_back(skstd::make_unique<SkAnimCodecPlayer>(std::move(codec)));
214 }
215 }
216 }
217
218private:
219 SkString onShortName() override {
220 return SkString("AnimCodecPlayer");
221 }
222
223 SkISize onISize() override {
224 return { 1024, 768 };
225 }
226
227 void onDraw(SkCanvas* canvas) override {
228 canvas->scale(0.25f, 0.25f);
229 for (auto& p : fPlayers) {
230 canvas->drawImage(p->getFrame(), 0, 0, nullptr);
231 canvas->translate(p->dimensions().width(), 0);
232 }
233 }
234
235 bool onAnimate(const SkAnimTimer& timer) override {
236 if (fBaseMSec == 0) {
237 fBaseMSec = timer.msec();
238 }
239 for (auto& p : fPlayers) {
240 (void)p->seek(timer.msec() - fBaseMSec);
241 }
242 return true;
243 }
244};
245DEF_GM(return new AnimCodecPlayerGM);