• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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