Flutter Engine
The Flutter Engine
animated_gif.cpp
Go to the documentation of this file.
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"
12#include "include/core/SkData.h"
15#include "include/core/SkSize.h"
20#include "src/core/SkOSFile.h"
21#include "tools/Resources.h"
22#include "tools/ToolUtils.h"
25
26#include <memory>
27#include <utility>
28#include <vector>
29
30#if defined(SK_ENABLE_SKOTTIE)
31
32static DEFINE_string(animatedGif, "images/test640x479.gif", "Animated gif in resources folder");
33
34class AnimatedGifGM : public skiagm::GM {
35private:
36 std::unique_ptr<SkCodec> fCodec;
37 int fFrame;
38 double fNextUpdate;
39 int fTotalFrames;
40 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
54 opts.fFrameIndex = frameIndex;
55 const int requiredFrame = fFrameInfos[frameIndex].fRequiredFrame;
56 if (requiredFrame != SkCodec::kNoFrame) {
57 SkASSERT(requiredFrame >= 0
58 && static_cast<size_t>(requiredFrame) < fFrames.size());
59 SkBitmap& requiredBitmap = fFrames[requiredFrame];
60 // For simplicity, do not try to cache old frames
61 if (requiredBitmap.getPixels() &&
62 ToolUtils::copy_to(&bm, requiredBitmap.colorType(), requiredBitmap)) {
63 opts.fPriorFrame = requiredFrame;
64 }
65 }
66
67 if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(),
68 bm.rowBytes(), &opts)) {
69 SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]);
70 return;
71 }
72 }
73
74 canvas->drawImage(bm.asImage(), 0, 0);
75 }
76
77public:
78 AnimatedGifGM()
79 : fFrame(0)
80 , fNextUpdate (-1)
81 , fTotalFrames (-1) {}
82
83private:
84 SkString getName() const override { return SkString("animatedGif"); }
85
86 SkISize getISize() 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
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
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
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};
177DEF_GM(return new AnimatedGifGM;)
178
179static std::unique_ptr<SkCodec> load_codec(const char 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);
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) {
198 fPlayers.push_back(std::make_unique<SkAnimCodecPlayer>(std::move(codec)));
199 }
200 }
201 }
202
203private:
204 SkString getName() const override { return SkString("AnimCodecPlayer"); }
205
206 SkISize getISize() override { return {1024, 768}; }
207
208 void onDraw(SkCanvas* canvas) override {
209 canvas->scale(0.25f, 0.25f);
210 for (auto& p : fPlayers) {
211 canvas->drawImage(p->getFrame(), 0, 0);
212 canvas->translate(p->dimensions().width(), 0);
213 }
214 }
215
216 bool onAnimate(double nanos) override {
217 if (fBaseMSec == 0) {
218 fBaseMSec = TimeUtils::NanosToMSec(nanos);
219 }
220 for (auto& p : fPlayers) {
221 (void)p->seek(TimeUtils::NanosToMSec(nanos) - fBaseMSec);
222 }
223 return true;
224 }
225};
226DEF_GM(return new AnimCodecPlayerGM;)
227
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) {
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
257 SkString getName() const override {
258 return SkStringPrintf("AnimCodecPlayerExif_%s", strrchr(fPath, '/') + 1);
259 }
260
261 SkISize getISize() override {
262 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();
283 canvas->drawImage(image, 0, 0);
284 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");)
299
300#endif
SkPath fPath
#define DEFINE_string(name, defaultValue, helpString)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
int count
Definition: FontMgrTest.cpp:50
std::unique_ptr< SkStreamAsset > GetResourceAsStream(const char *resource, bool useFileStream)
Definition: Resources.cpp:31
sk_sp< SkData > GetResourceAsData(const char *resource)
Definition: Resources.cpp:42
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define sk_float_ceil2int(x)
#define SkIntToScalar(x)
Definition: SkScalar.h:57
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
void allocPixels(const SkImageInfo &info, size_t rowBytes)
Definition: SkBitmap.cpp:258
sk_sp< SkImage > asImage() const
Definition: SkBitmap.cpp:645
size_t rowBytes() const
Definition: SkBitmap.h:238
SkColorType colorType() const
Definition: SkBitmap.h:160
void * getPixels() const
Definition: SkBitmap.h:283
void restore()
Definition: SkCanvas.cpp:461
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
int save()
Definition: SkCanvas.cpp:447
void scale(SkScalar sx, SkScalar sy)
Definition: SkCanvas.cpp:1289
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition: SkCanvas.h:1528
static std::unique_ptr< SkCodec > MakeFromStream(std::unique_ptr< SkStream >, SkSpan< const SkCodecs::Decoder > decoders, Result *=nullptr, SkPngChunkReader *=nullptr, SelectionPolicy selectionPolicy=SelectionPolicy::kPreferStillImage)
Definition: SkCodec.cpp:163
static std::unique_ptr< SkCodec > MakeFromData(sk_sp< SkData >, SkSpan< const SkCodecs::Decoder > decoders, SkPngChunkReader *=nullptr)
Definition: SkCodec.cpp:241
@ kSuccess
Definition: SkCodec.h:80
static constexpr int kNoFrame
Definition: SkCodec.h:650
static sk_sp< SkData > MakeFromFileName(const char path[])
Definition: SkData.cpp:148
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition: SkString.cpp:534
const char * c_str() const
Definition: SkString.h:133
Definition: gm.h:110
virtual SkISize getISize()=0
virtual SkString getName() const =0
virtual bool onAnimate(double)
Definition: gm.cpp:169
virtual DrawResult onDraw(SkCanvas *, SkString *errorMsg)
Definition: gm.cpp:139
double duration
Definition: examples.cpp:30
double frame
Definition: examples.cpp:31
#define DEF_GM(CODE)
Definition: gm.h:40
static bool init()
sk_sp< const SkImage > image
Definition: SkRecords.h:269
static SkMSec NanosToMSec(double nanos)
Definition: TimeUtils.h:16
bool copy_to(SkBitmap *dst, SkColorType dstColorType, const SkBitmap &src)
Definition: ToolUtils.cpp:394
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
string root
Definition: scale_cpu.py:20
DrawResult
Definition: gm.h:104
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
Definition: SkSize.h:16
static constexpr SkISize MakeEmpty()
Definition: SkSize.h:22
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
int32_t fHeight
Definition: SkSize.h:18
int32_t fWidth
Definition: SkSize.h:17
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63