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/SkMakeUnique.h"
21 #include "src/core/SkOSFile.h"
22 #include "tools/Resources.h"
23 #include "tools/ToolUtils.h"
24 #include "tools/flags/CommandLineFlags.h"
25 #include "tools/timer/TimeUtils.h"
26
27 #include <memory>
28 #include <utility>
29 #include <vector>
30
31 static DEFINE_string(animatedGif, "images/test640x479.gif", "Animated gif in resources folder");
32
33 class AnimatedGifGM : public skiagm::GM {
34 private:
35 std::unique_ptr<SkCodec> fCodec;
36 int fFrame;
37 double fNextUpdate;
38 int fTotalFrames;
39 std::vector<SkCodec::FrameInfo> fFrameInfos;
40 std::vector<SkBitmap> fFrames;
41
drawFrame(SkCanvas * canvas,int frameIndex)42 void drawFrame(SkCanvas* canvas, int frameIndex) {
43 // FIXME: Create from an Image/ImageGenerator?
44 if (frameIndex >= (int) fFrames.size()) {
45 fFrames.resize(frameIndex + 1);
46 }
47 SkBitmap& bm = fFrames[frameIndex];
48 if (!bm.getPixels()) {
49 const SkImageInfo info = fCodec->getInfo().makeColorType(kN32_SkColorType);
50 bm.allocPixels(info);
51
52 SkCodec::Options opts;
53 opts.fFrameIndex = frameIndex;
54 const int requiredFrame = fFrameInfos[frameIndex].fRequiredFrame;
55 if (requiredFrame != SkCodec::kNoFrame) {
56 SkASSERT(requiredFrame >= 0
57 && static_cast<size_t>(requiredFrame) < fFrames.size());
58 SkBitmap& requiredBitmap = fFrames[requiredFrame];
59 // For simplicity, do not try to cache old frames
60 if (requiredBitmap.getPixels() &&
61 ToolUtils::copy_to(&bm, requiredBitmap.colorType(), requiredBitmap)) {
62 opts.fPriorFrame = requiredFrame;
63 }
64 }
65
66 if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(),
67 bm.rowBytes(), &opts)) {
68 SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]);
69 return;
70 }
71 }
72
73 canvas->drawBitmap(bm, 0, 0);
74 }
75
76 public:
AnimatedGifGM()77 AnimatedGifGM()
78 : fFrame(0)
79 , fNextUpdate (-1)
80 , fTotalFrames (-1) {}
81
82 private:
onShortName()83 SkString onShortName() override {
84 return SkString("animatedGif");
85 }
86
onISize()87 SkISize onISize() override {
88 if (this->initCodec()) {
89 SkISize dim = fCodec->getInfo().dimensions();
90 // Wide enough to display all the frames.
91 dim.fWidth *= fTotalFrames;
92 // Tall enough to show the row of frames plus an animating version.
93 dim.fHeight *= 2;
94 return dim;
95 }
96 return SkISize::Make(640, 480);
97 }
98
initCodec()99 bool initCodec() {
100 if (fCodec) {
101 return true;
102 }
103 if (FLAGS_animatedGif.isEmpty()) {
104 SkDebugf("Nothing specified for --animatedGif!");
105 return false;
106 }
107
108 std::unique_ptr<SkStream> stream(GetResourceAsStream(FLAGS_animatedGif[0]));
109 if (!stream) {
110 return false;
111 }
112
113 fCodec = SkCodec::MakeFromStream(std::move(stream));
114 if (!fCodec) {
115 return false;
116 }
117
118 fFrame = 0;
119 fFrameInfos = fCodec->getFrameInfo();
120 fTotalFrames = fFrameInfos.size();
121 return true;
122 }
123
onDraw(SkCanvas * canvas,SkString * errorMsg)124 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
125 if (!this->initCodec()) {
126 errorMsg->printf("Could not create codec from %s", FLAGS_animatedGif[0]);
127 return DrawResult::kFail;
128 }
129
130 canvas->save();
131 for (int frameIndex = 0; frameIndex < fTotalFrames; frameIndex++) {
132 this->drawFrame(canvas, frameIndex);
133 canvas->translate(SkIntToScalar(fCodec->getInfo().width()), 0);
134 }
135 canvas->restore();
136
137 SkAutoCanvasRestore acr(canvas, true);
138 canvas->translate(0, SkIntToScalar(fCodec->getInfo().height()));
139 this->drawFrame(canvas, fFrame);
140 return DrawResult::kOk;
141 }
142
onAnimate(double nanos)143 bool onAnimate(double nanos) override {
144 if (!fCodec || fTotalFrames == 1) {
145 return false;
146 }
147
148 double secs = TimeUtils::NanosToMSec(nanos) * .1;
149 if (fNextUpdate < double(0)) {
150 // This is a sentinel that we have not done any updates yet.
151 // I'm assuming this gets called *after* onOnceBeforeDraw, so our first frame should
152 // already have been retrieved.
153 SkASSERT(fFrame == 0);
154 fNextUpdate = secs + fFrameInfos[fFrame].fDuration;
155
156 return true;
157 }
158
159 if (secs < fNextUpdate) {
160 return true;
161 }
162
163 while (secs >= fNextUpdate) {
164 // Retrieve the next frame.
165 fFrame++;
166 if (fFrame == fTotalFrames) {
167 fFrame = 0;
168 }
169
170 // Note that we loop here. This is not safe if we need to draw the intermediate frame
171 // in order to draw correctly.
172 fNextUpdate += fFrameInfos[fFrame].fDuration;
173 }
174
175 return true;
176 }
177 };
178 DEF_GM(return new AnimatedGifGM);
179
180
load_codec(const char filename[])181 static std::unique_ptr<SkCodec> load_codec(const char filename[]) {
182 return SkCodec::MakeFromData(SkData::MakeFromFileName(filename));
183 }
184
185 class AnimCodecPlayerGM : public skiagm::GM {
186 private:
187 std::vector<std::unique_ptr<SkAnimCodecPlayer> > fPlayers;
188 uint32_t fBaseMSec = 0;
189
190 public:
AnimCodecPlayerGM()191 AnimCodecPlayerGM() {
192 const char* root = "/skia/anim/";
193 SkOSFile::Iter iter(root);
194 SkString path;
195 while (iter.next(&path)) {
196 SkString completepath;
197 completepath.printf("%s%s", root, path.c_str());
198 auto codec = load_codec(completepath.c_str());
199 if (codec) {
200 fPlayers.push_back(skstd::make_unique<SkAnimCodecPlayer>(std::move(codec)));
201 }
202 }
203 }
204
205 private:
onShortName()206 SkString onShortName() override {
207 return SkString("AnimCodecPlayer");
208 }
209
onISize()210 SkISize onISize() override {
211 return { 1024, 768 };
212 }
213
onDraw(SkCanvas * canvas)214 void onDraw(SkCanvas* canvas) override {
215 canvas->scale(0.25f, 0.25f);
216 for (auto& p : fPlayers) {
217 canvas->drawImage(p->getFrame(), 0, 0, nullptr);
218 canvas->translate(p->dimensions().width(), 0);
219 }
220 }
221
onAnimate(double nanos)222 bool onAnimate(double nanos) override {
223 if (fBaseMSec == 0) {
224 fBaseMSec = TimeUtils::NanosToMSec(nanos);
225 }
226 for (auto& p : fPlayers) {
227 (void)p->seek(TimeUtils::NanosToMSec(nanos) - fBaseMSec);
228 }
229 return true;
230 }
231 };
232 DEF_GM(return new AnimCodecPlayerGM);
233