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 "include/utils/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 static DEFINE_string(animatedGif, "images/test640x479.gif", "Animated gif in resources folder");
31
32 class AnimatedGifGM : public skiagm::GM {
33 private:
34 std::unique_ptr<SkCodec> fCodec;
35 int fFrame;
36 double fNextUpdate;
37 int fTotalFrames;
38 std::vector<SkCodec::FrameInfo> fFrameInfos;
39 std::vector<SkBitmap> fFrames;
40
drawFrame(SkCanvas * canvas,int frameIndex)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;
53 const int requiredFrame = fFrameInfos[frameIndex].fRequiredFrame;
54 if (requiredFrame != SkCodec::kNoFrame) {
55 SkASSERT(requiredFrame >= 0
56 && static_cast<size_t>(requiredFrame) < fFrames.size());
57 SkBitmap& requiredBitmap = fFrames[requiredFrame];
58 // For simplicity, do not try to cache old frames
59 if (requiredBitmap.getPixels() &&
60 ToolUtils::copy_to(&bm, requiredBitmap.colorType(), requiredBitmap)) {
61 opts.fPriorFrame = requiredFrame;
62 }
63 }
64
65 if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(),
66 bm.rowBytes(), &opts)) {
67 SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]);
68 return;
69 }
70 }
71
72 canvas->drawImage(bm.asImage(), 0, 0);
73 }
74
75 public:
AnimatedGifGM()76 AnimatedGifGM()
77 : fFrame(0)
78 , fNextUpdate (-1)
79 , fTotalFrames (-1) {}
80
81 private:
onShortName()82 SkString onShortName() override {
83 return SkString("animatedGif");
84 }
85
onISize()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
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 };
177 DEF_GM(return new AnimatedGifGM);
178
179
load_codec(const char filename[])180 static std::unique_ptr<SkCodec> load_codec(const char filename[]) {
181 return SkCodec::MakeFromData(SkData::MakeFromFileName(filename));
182 }
183
184 class AnimCodecPlayerGM : public skiagm::GM {
185 private:
186 std::vector<std::unique_ptr<SkAnimCodecPlayer> > fPlayers;
187 uint32_t fBaseMSec = 0;
188
189 public:
AnimCodecPlayerGM()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) {
199 fPlayers.push_back(std::make_unique<SkAnimCodecPlayer>(std::move(codec)));
200 }
201 }
202 }
203
204 private:
onShortName()205 SkString onShortName() override {
206 return SkString("AnimCodecPlayer");
207 }
208
onISize()209 SkISize onISize() override {
210 return { 1024, 768 };
211 }
212
onDraw(SkCanvas * canvas)213 void onDraw(SkCanvas* canvas) override {
214 canvas->scale(0.25f, 0.25f);
215 for (auto& p : fPlayers) {
216 canvas->drawImage(p->getFrame(), 0, 0);
217 canvas->translate(p->dimensions().width(), 0);
218 }
219 }
220
onAnimate(double nanos)221 bool onAnimate(double nanos) override {
222 if (fBaseMSec == 0) {
223 fBaseMSec = TimeUtils::NanosToMSec(nanos);
224 }
225 for (auto& p : fPlayers) {
226 (void)p->seek(TimeUtils::NanosToMSec(nanos) - fBaseMSec);
227 }
228 return true;
229 }
230 };
231 DEF_GM(return new AnimCodecPlayerGM);
232
233 class 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
init()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
onShortName()262 SkString onShortName() override {
263 return SkStringPrintf("AnimCodecPlayerExif_%s", strrchr(fPath, '/') + 1);
264 }
265
onISize()266 SkISize onISize() override {
267 this->init();
268 return fSize;
269 }
270
onDraw(SkCanvas * canvas)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();
288 canvas->drawImage(image, 0, 0);
289 duration += fFrameInfos[frame].fDuration;
290 fPlayer->seek(duration);
291 }
292 }
293 public:
AnimCodecPlayerExifGM(const char * path)294 AnimCodecPlayerExifGM(const char* path)
295 : fPath(path)
296 {}
297
298 ~AnimCodecPlayerExifGM() override = default;
299 };
300
301 DEF_GM(return new AnimCodecPlayerExifGM("images/required.webp");)
302 DEF_GM(return new AnimCodecPlayerExifGM("images/required.gif");)
303 DEF_GM(return new AnimCodecPlayerExifGM("images/stoplight_h.webp");)
304