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