blob: 8d0dfeeab6337c24e0efb1f42d3c3c0275b10ab2 [file] [log] [blame]
scroggo19b91532016-10-24 09:03:26 -07001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
9#include "include/codec/SkCodec.h"
Ben Wagnerd1701ba2019-04-30 13:44:26 -040010#include "include/core/SkBitmap.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkCanvas.h"
Ben Wagnerd1701ba2019-04-30 13:44:26 -040012#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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "include/core/SkString.h"
Ben Wagnerd1701ba2019-04-30 13:44:26 -040018#include "include/core/SkTypes.h"
19#include "include/utils/SkAnimCodecPlayer.h"
20#include "src/core/SkMakeUnique.h"
21#include "src/core/SkOSFile.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050022#include "tools/Resources.h"
23#include "tools/ToolUtils.h"
24#include "tools/flags/CommandLineFlags.h"
Hal Canary41248072019-07-11 16:32:53 -040025#include "tools/timer/TimeUtils.h"
scroggo19b91532016-10-24 09:03:26 -070026
Ben Wagnerd1701ba2019-04-30 13:44:26 -040027#include <memory>
28#include <utility>
scroggo19b91532016-10-24 09:03:26 -070029#include <vector>
30
Mike Klein84836b72019-03-21 11:31:36 -050031static DEFINE_string(animatedGif, "images/test640x479.gif", "Animated gif in resources folder");
scroggo19b91532016-10-24 09:03:26 -070032
scroggo19b91532016-10-24 09:03:26 -070033class AnimatedGifGM : public skiagm::GM {
34private:
35 std::unique_ptr<SkCodec> fCodec;
Leon Scroggins III249b8e32017-04-17 12:46:33 -040036 int fFrame;
scroggo19b91532016-10-24 09:03:26 -070037 double fNextUpdate;
Leon Scroggins III249b8e32017-04-17 12:46:33 -040038 int fTotalFrames;
scroggo19b91532016-10-24 09:03:26 -070039 std::vector<SkCodec::FrameInfo> fFrameInfos;
40 std::vector<SkBitmap> fFrames;
41
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;
Leon Scroggins III249b8e32017-04-17 12:46:33 -040054 const int requiredFrame = fFrameInfos[frameIndex].fRequiredFrame;
Nigel Tao66bc5242018-08-22 10:56:03 +100055 if (requiredFrame != SkCodec::kNoFrame) {
Leon Scroggins III249b8e32017-04-17 12:46:33 -040056 SkASSERT(requiredFrame >= 0
57 && static_cast<size_t>(requiredFrame) < fFrames.size());
scroggo19b91532016-10-24 09:03:26 -070058 SkBitmap& requiredBitmap = fFrames[requiredFrame];
59 // For simplicity, do not try to cache old frames
Matt Sarett68b8e3d2017-04-28 11:15:22 -040060 if (requiredBitmap.getPixels() &&
Mike Kleinea3f0142019-03-20 11:12:10 -050061 ToolUtils::copy_to(&bm, requiredBitmap.colorType(), requiredBitmap)) {
Leon Scroggins III33deb7e2017-06-07 12:31:51 -040062 opts.fPriorFrame = requiredFrame;
scroggo19b91532016-10-24 09:03:26 -070063 }
64 }
65
66 if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(),
Leon Scroggins571b30f2017-07-11 17:35:31 +000067 bm.rowBytes(), &opts)) {
scroggo19b91532016-10-24 09:03:26 -070068 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
76public:
77 AnimatedGifGM()
78 : fFrame(0)
79 , fNextUpdate (-1)
80 , fTotalFrames (-1) {}
81
82private:
83 SkString onShortName() override {
84 return SkString("animatedGif");
85 }
86
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
scroggo19b91532016-10-24 09:03:26 -070099 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
Mike Reedede7bac2017-07-23 15:30:02 -0400113 fCodec = SkCodec::MakeFromStream(std::move(stream));
scroggo19b91532016-10-24 09:03:26 -0700114 if (!fCodec) {
scroggo19b91532016-10-24 09:03:26 -0700115 return false;
116 }
117
118 fFrame = 0;
119 fFrameInfos = fCodec->getFrameInfo();
120 fTotalFrames = fFrameInfos.size();
121 return true;
122 }
123
Chris Dalton50e24d72019-02-07 16:20:09 -0700124 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
Chris Dalton21ca3702019-02-01 12:15:42 -0700125 if (!this->initCodec()) {
Chris Dalton50e24d72019-02-07 16:20:09 -0700126 errorMsg->printf("Could not create codec from %s", FLAGS_animatedGif[0]);
127 return DrawResult::kFail;
scroggo19b91532016-10-24 09:03:26 -0700128 }
129
Chris Dalton21ca3702019-02-01 12:15:42 -0700130 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
scroggo19b91532016-10-24 09:03:26 -0700137 SkAutoCanvasRestore acr(canvas, true);
Jim Van Verth3cfdf6c2016-10-26 09:45:23 -0400138 canvas->translate(0, SkIntToScalar(fCodec->getInfo().height()));
scroggo19b91532016-10-24 09:03:26 -0700139 this->drawFrame(canvas, fFrame);
Chris Dalton50e24d72019-02-07 16:20:09 -0700140 return DrawResult::kOk;
scroggo19b91532016-10-24 09:03:26 -0700141 }
142
Hal Canary41248072019-07-11 16:32:53 -0400143 bool onAnimate(double nanos) override {
scroggo19b91532016-10-24 09:03:26 -0700144 if (!fCodec || fTotalFrames == 1) {
145 return false;
146 }
147
Hal Canary41248072019-07-11 16:32:53 -0400148 double secs = TimeUtils::NanosToMSec(nanos) * .1;
scroggo19b91532016-10-24 09:03:26 -0700149 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};
scroggo19b91532016-10-24 09:03:26 -0700178DEF_GM(return new AnimatedGifGM);
179
Mike Reed964aedb2018-10-03 10:16:55 -0400180
Mike Reed964aedb2018-10-03 10:16:55 -0400181static std::unique_ptr<SkCodec> load_codec(const char filename[]) {
182 return SkCodec::MakeFromData(SkData::MakeFromFileName(filename));
183}
184
185class AnimCodecPlayerGM : public skiagm::GM {
186private:
187 std::vector<std::unique_ptr<SkAnimCodecPlayer> > fPlayers;
188 uint32_t fBaseMSec = 0;
189
190public:
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
205private:
206 SkString onShortName() override {
207 return SkString("AnimCodecPlayer");
208 }
209
210 SkISize onISize() override {
211 return { 1024, 768 };
212 }
213
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
Hal Canary41248072019-07-11 16:32:53 -0400222 bool onAnimate(double nanos) override {
Mike Reed964aedb2018-10-03 10:16:55 -0400223 if (fBaseMSec == 0) {
Hal Canary41248072019-07-11 16:32:53 -0400224 fBaseMSec = TimeUtils::NanosToMSec(nanos);
Mike Reed964aedb2018-10-03 10:16:55 -0400225 }
226 for (auto& p : fPlayers) {
Hal Canary41248072019-07-11 16:32:53 -0400227 (void)p->seek(TimeUtils::NanosToMSec(nanos) - fBaseMSec);
Mike Reed964aedb2018-10-03 10:16:55 -0400228 }
229 return true;
230 }
231};
232DEF_GM(return new AnimCodecPlayerGM);