• 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.h"
9 #include "SkAnimTimer.h"
10 #include "SkCanvas.h"
11 #include "SkCodec.h"
12 #include "SkColor.h"
13 #include "SkCommandLineFlags.h"
14 #include "SkPaint.h"
15 #include "SkString.h"
16 #include "Resources.h"
17 
18 #include <vector>
19 
20 DEFINE_string(animatedGif, "test640x479.gif", "Animated gif in resources folder");
21 
22 namespace {
error(SkCanvas * canvas,const SkString & errorText)23     void error(SkCanvas* canvas, const SkString& errorText) {
24         constexpr SkScalar kOffset = 5.0f;
25         canvas->drawColor(SK_ColorRED);
26         SkPaint paint;
27         SkRect bounds;
28         paint.measureText(errorText.c_str(), errorText.size(), &bounds);
29         canvas->drawText(errorText.c_str(), errorText.size(), kOffset, bounds.height() + kOffset,
30                          paint);
31     }
32 }
33 
34 class AnimatedGifGM : public skiagm::GM {
35 private:
36     std::unique_ptr<SkCodec>        fCodec;
37     size_t                          fFrame;
38     double                          fNextUpdate;
39     size_t                          fTotalFrames;
40     std::vector<SkCodec::FrameInfo> fFrameInfos;
41     std::vector<SkBitmap>           fFrames;
42 
drawFrame(SkCanvas * canvas,int frameIndex)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 
53             SkCodec::Options opts;
54             opts.fFrameIndex = frameIndex;
55             opts.fHasPriorFrame = false;
56             const size_t requiredFrame = fFrameInfos[frameIndex].fRequiredFrame;
57             if (requiredFrame != SkCodec::kNone) {
58                 SkASSERT(requiredFrame < fFrames.size());
59                 SkBitmap& requiredBitmap = fFrames[requiredFrame];
60                 // For simplicity, do not try to cache old frames
61                 if (requiredBitmap.getPixels() && requiredBitmap.copyTo(&bm)) {
62                     opts.fHasPriorFrame = true;
63                 }
64             }
65 
66             if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(),
67                                                        bm.rowBytes(), &opts,
68                                                        nullptr, nullptr)) {
69                 SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]);
70                 return;
71             }
72         }
73 
74         canvas->drawBitmap(bm, 0, 0);
75     }
76 
77 public:
AnimatedGifGM()78     AnimatedGifGM()
79     : fFrame(0)
80     , fNextUpdate (-1)
81     , fTotalFrames (-1) {}
82 
83 private:
onShortName()84     SkString onShortName() override {
85         return SkString("animatedGif");
86     }
87 
onISize()88     SkISize onISize() override {
89         if (this->initCodec()) {
90             SkISize dim = fCodec->getInfo().dimensions();
91             // Wide enough to display all the frames.
92             dim.fWidth *= fTotalFrames;
93             // Tall enough to show the row of frames plus an animating version.
94             dim.fHeight *= 2;
95             return dim;
96         }
97         return SkISize::Make(640, 480);
98     }
99 
onDrawBackground(SkCanvas * canvas)100     void onDrawBackground(SkCanvas* canvas) override {
101         canvas->clear(SK_ColorWHITE);
102         if (this->initCodec()) {
103             SkAutoCanvasRestore acr(canvas, true);
104             for (size_t frameIndex = 0; frameIndex < fTotalFrames; frameIndex++) {
105                 this->drawFrame(canvas, frameIndex);
106                 canvas->translate(SkIntToScalar(fCodec->getInfo().width()), 0);
107             }
108         }
109     }
110 
initCodec()111     bool initCodec() {
112         if (fCodec) {
113             return true;
114         }
115         if (FLAGS_animatedGif.isEmpty()) {
116             SkDebugf("Nothing specified for --animatedGif!");
117             return false;
118         }
119 
120         std::unique_ptr<SkStream> stream(GetResourceAsStream(FLAGS_animatedGif[0]));
121         if (!stream) {
122             return false;
123         }
124 
125         fCodec.reset(SkCodec::NewFromStream(stream.release()));
126         if (!fCodec) {
127             SkDebugf("Could create codec from %s", FLAGS_animatedGif[0]);
128             return false;
129         }
130 
131         fFrame = 0;
132         fFrameInfos = fCodec->getFrameInfo();
133         fTotalFrames = fFrameInfos.size();
134         return true;
135     }
136 
onDraw(SkCanvas * canvas)137     void onDraw(SkCanvas* canvas) override {
138         if (!fCodec) {
139             SkString errorText = SkStringPrintf("Nothing to draw; %s", FLAGS_animatedGif[0]);
140             error(canvas, errorText);
141             return;
142         }
143 
144         SkAutoCanvasRestore acr(canvas, true);
145         canvas->translate(0, SkIntToScalar(fCodec->getInfo().height()));
146         this->drawFrame(canvas, fFrame);
147     }
148 
onAnimate(const SkAnimTimer & timer)149     bool onAnimate(const SkAnimTimer& timer) override {
150         if (!fCodec || fTotalFrames == 1) {
151             return false;
152         }
153 
154         double secs = timer.msec() * .1;
155         if (fNextUpdate < double(0)) {
156             // This is a sentinel that we have not done any updates yet.
157             // I'm assuming this gets called *after* onOnceBeforeDraw, so our first frame should
158             // already have been retrieved.
159             SkASSERT(fFrame == 0);
160             fNextUpdate = secs + fFrameInfos[fFrame].fDuration;
161 
162             return true;
163         }
164 
165         if (secs < fNextUpdate) {
166             return true;
167         }
168 
169         while (secs >= fNextUpdate) {
170             // Retrieve the next frame.
171             fFrame++;
172             if (fFrame == fTotalFrames) {
173                 fFrame = 0;
174             }
175 
176             // Note that we loop here. This is not safe if we need to draw the intermediate frame
177             // in order to draw correctly.
178             fNextUpdate += fFrameInfos[fFrame].fDuration;
179         }
180 
181         return true;
182     }
183 };
184 
185 DEF_GM(return new AnimatedGifGM);
186 
187