Add fixes & test for isConfigTexturable and isConfigRenderable
[platform/upstream/libSkiaSharp.git] / gm / animatedGif.cpp
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, "test640x479.gif", "Animated gif in resources folder");
22
23 namespace {
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
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             opts.fHasPriorFrame = false;
57             const int requiredFrame = fFrameInfos[frameIndex].fRequiredFrame;
58             if (requiredFrame != SkCodec::kNone) {
59                 SkASSERT(requiredFrame >= 0
60                          && static_cast<size_t>(requiredFrame) < fFrames.size());
61                 SkBitmap& requiredBitmap = fFrames[requiredFrame];
62                 // For simplicity, do not try to cache old frames
63                 if (requiredBitmap.getPixels() &&
64                         sk_tool_utils::copy_to(&bm, requiredBitmap.colorType(), requiredBitmap)) {
65                     opts.fHasPriorFrame = true;
66                 }
67             }
68
69             if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(),
70                                                        bm.rowBytes(), &opts,
71                                                        nullptr, nullptr)) {
72                 SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]);
73                 return;
74             }
75         }
76
77         canvas->drawBitmap(bm, 0, 0);
78     }
79
80 public:
81     AnimatedGifGM()
82     : fFrame(0)
83     , fNextUpdate (-1)
84     , fTotalFrames (-1) {}
85
86 private:
87     SkString onShortName() override {
88         return SkString("animatedGif");
89     }
90
91     SkISize onISize() override {
92         if (this->initCodec()) {
93             SkISize dim = fCodec->getInfo().dimensions();
94             // Wide enough to display all the frames.
95             dim.fWidth *= fTotalFrames;
96             // Tall enough to show the row of frames plus an animating version.
97             dim.fHeight *= 2;
98             return dim;
99         }
100         return SkISize::Make(640, 480);
101     }
102
103     void onDrawBackground(SkCanvas* canvas) override {
104         canvas->clear(SK_ColorWHITE);
105         if (this->initCodec()) {
106             SkAutoCanvasRestore acr(canvas, true);
107             for (int frameIndex = 0; frameIndex < fTotalFrames; frameIndex++) {
108                 this->drawFrame(canvas, frameIndex);
109                 canvas->translate(SkIntToScalar(fCodec->getInfo().width()), 0);
110             }
111         }
112     }
113
114     bool initCodec() {
115         if (fCodec) {
116             return true;
117         }
118         if (FLAGS_animatedGif.isEmpty()) {
119             SkDebugf("Nothing specified for --animatedGif!");
120             return false;
121         }
122
123         std::unique_ptr<SkStream> stream(GetResourceAsStream(FLAGS_animatedGif[0]));
124         if (!stream) {
125             return false;
126         }
127
128         fCodec.reset(SkCodec::NewFromStream(stream.release()));
129         if (!fCodec) {
130             SkDebugf("Could create codec from %s", FLAGS_animatedGif[0]);
131             return false;
132         }
133
134         fFrame = 0;
135         fFrameInfos = fCodec->getFrameInfo();
136         fTotalFrames = fFrameInfos.size();
137         return true;
138     }
139
140     void onDraw(SkCanvas* canvas) override {
141         if (!fCodec) {
142             SkString errorText = SkStringPrintf("Nothing to draw; %s", FLAGS_animatedGif[0]);
143             error(canvas, errorText);
144             return;
145         }
146
147         SkAutoCanvasRestore acr(canvas, true);
148         canvas->translate(0, SkIntToScalar(fCodec->getInfo().height()));
149         this->drawFrame(canvas, fFrame);
150     }
151
152     bool onAnimate(const SkAnimTimer& timer) override {
153         if (!fCodec || fTotalFrames == 1) {
154             return false;
155         }
156
157         double secs = timer.msec() * .1;
158         if (fNextUpdate < double(0)) {
159             // This is a sentinel that we have not done any updates yet.
160             // I'm assuming this gets called *after* onOnceBeforeDraw, so our first frame should
161             // already have been retrieved.
162             SkASSERT(fFrame == 0);
163             fNextUpdate = secs + fFrameInfos[fFrame].fDuration;
164
165             return true;
166         }
167
168         if (secs < fNextUpdate) {
169             return true;
170         }
171
172         while (secs >= fNextUpdate) {
173             // Retrieve the next frame.
174             fFrame++;
175             if (fFrame == fTotalFrames) {
176                 fFrame = 0;
177             }
178
179             // Note that we loop here. This is not safe if we need to draw the intermediate frame
180             // in order to draw correctly.
181             fNextUpdate += fFrameInfos[fFrame].fDuration;
182         }
183
184         return true;
185     }
186 };
187
188 DEF_GM(return new AnimatedGifGM);
189