Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
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 static SkString SkStringPrintf()
Definition SkString.h:287
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:465
void translate(SkScalar dx, SkScalar dy)
int save()
Definition SkCanvas.cpp:451
void scale(SkScalar sx, SkScalar sy)
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
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
sk_sp< SkImage > image
Definition examples.cpp:29
double duration
Definition examples.cpp:30
double frame
Definition examples.cpp:31
#define DEF_GM(CODE)
Definition gm.h:40
static SkMSec NanosToMSec(double nanos)
Definition TimeUtils.h:16
bool copy_to(SkBitmap *dst, SkColorType dstColorType, const SkBitmap &src)
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
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
DrawResult
Definition gm.h:104
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition SkVx.h:706
init(device_serial, adb_binary)
Definition _adb_path.py:12
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
SkImageInfo makeColorType(SkColorType newColorType) const