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 "SkFont.h" 16 #include "SkPaint.h" 17 #include "SkString.h" 18 #include "Resources.h" 19 20 #include <vector> 21 22 DEFINE_string(animatedGif, "images/test640x479.gif", "Animated gif in resources folder"); 23 24 class AnimatedGifGM : public skiagm::GM { 25 private: 26 std::unique_ptr<SkCodec> fCodec; 27 int fFrame; 28 double fNextUpdate; 29 int fTotalFrames; 30 std::vector<SkCodec::FrameInfo> fFrameInfos; 31 std::vector<SkBitmap> fFrames; 32 drawFrame(SkCanvas * canvas,int frameIndex)33 void drawFrame(SkCanvas* canvas, int frameIndex) { 34 // FIXME: Create from an Image/ImageGenerator? 35 if (frameIndex >= (int) fFrames.size()) { 36 fFrames.resize(frameIndex + 1); 37 } 38 SkBitmap& bm = fFrames[frameIndex]; 39 if (!bm.getPixels()) { 40 const SkImageInfo info = fCodec->getInfo().makeColorType(kN32_SkColorType); 41 bm.allocPixels(info); 42 43 SkCodec::Options opts; 44 opts.fFrameIndex = frameIndex; 45 const int requiredFrame = fFrameInfos[frameIndex].fRequiredFrame; 46 if (requiredFrame != SkCodec::kNoFrame) { 47 SkASSERT(requiredFrame >= 0 48 && static_cast<size_t>(requiredFrame) < fFrames.size()); 49 SkBitmap& requiredBitmap = fFrames[requiredFrame]; 50 // For simplicity, do not try to cache old frames 51 if (requiredBitmap.getPixels() && 52 sk_tool_utils::copy_to(&bm, requiredBitmap.colorType(), requiredBitmap)) { 53 opts.fPriorFrame = requiredFrame; 54 } 55 } 56 57 if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(), 58 bm.rowBytes(), &opts)) { 59 SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]); 60 return; 61 } 62 } 63 64 canvas->drawBitmap(bm, 0, 0); 65 } 66 67 public: AnimatedGifGM()68 AnimatedGifGM() 69 : fFrame(0) 70 , fNextUpdate (-1) 71 , fTotalFrames (-1) {} 72 73 private: onShortName()74 SkString onShortName() override { 75 return SkString("animatedGif"); 76 } 77 onISize()78 SkISize onISize() override { 79 if (this->initCodec()) { 80 SkISize dim = fCodec->getInfo().dimensions(); 81 // Wide enough to display all the frames. 82 dim.fWidth *= fTotalFrames; 83 // Tall enough to show the row of frames plus an animating version. 84 dim.fHeight *= 2; 85 return dim; 86 } 87 return SkISize::Make(640, 480); 88 } 89 initCodec()90 bool initCodec() { 91 if (fCodec) { 92 return true; 93 } 94 if (FLAGS_animatedGif.isEmpty()) { 95 SkDebugf("Nothing specified for --animatedGif!"); 96 return false; 97 } 98 99 std::unique_ptr<SkStream> stream(GetResourceAsStream(FLAGS_animatedGif[0])); 100 if (!stream) { 101 return false; 102 } 103 104 fCodec = SkCodec::MakeFromStream(std::move(stream)); 105 if (!fCodec) { 106 return false; 107 } 108 109 fFrame = 0; 110 fFrameInfos = fCodec->getFrameInfo(); 111 fTotalFrames = fFrameInfos.size(); 112 return true; 113 } 114 onDraw(SkCanvas * canvas,SkString * errorMsg)115 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override { 116 if (!this->initCodec()) { 117 errorMsg->printf("Could not create codec from %s", FLAGS_animatedGif[0]); 118 return DrawResult::kFail; 119 } 120 121 canvas->save(); 122 for (int frameIndex = 0; frameIndex < fTotalFrames; frameIndex++) { 123 this->drawFrame(canvas, frameIndex); 124 canvas->translate(SkIntToScalar(fCodec->getInfo().width()), 0); 125 } 126 canvas->restore(); 127 128 SkAutoCanvasRestore acr(canvas, true); 129 canvas->translate(0, SkIntToScalar(fCodec->getInfo().height())); 130 this->drawFrame(canvas, fFrame); 131 return DrawResult::kOk; 132 } 133 onAnimate(const SkAnimTimer & timer)134 bool onAnimate(const SkAnimTimer& timer) override { 135 if (!fCodec || fTotalFrames == 1) { 136 return false; 137 } 138 139 double secs = timer.msec() * .1; 140 if (fNextUpdate < double(0)) { 141 // This is a sentinel that we have not done any updates yet. 142 // I'm assuming this gets called *after* onOnceBeforeDraw, so our first frame should 143 // already have been retrieved. 144 SkASSERT(fFrame == 0); 145 fNextUpdate = secs + fFrameInfos[fFrame].fDuration; 146 147 return true; 148 } 149 150 if (secs < fNextUpdate) { 151 return true; 152 } 153 154 while (secs >= fNextUpdate) { 155 // Retrieve the next frame. 156 fFrame++; 157 if (fFrame == fTotalFrames) { 158 fFrame = 0; 159 } 160 161 // Note that we loop here. This is not safe if we need to draw the intermediate frame 162 // in order to draw correctly. 163 fNextUpdate += fFrameInfos[fFrame].fDuration; 164 } 165 166 return true; 167 } 168 }; 169 DEF_GM(return new AnimatedGifGM); 170 171 172 #include "SkAnimCodecPlayer.h" 173 #include "SkOSFile.h" 174 #include "SkMakeUnique.h" 175 load_codec(const char filename[])176 static std::unique_ptr<SkCodec> load_codec(const char filename[]) { 177 return SkCodec::MakeFromData(SkData::MakeFromFileName(filename)); 178 } 179 180 class AnimCodecPlayerGM : public skiagm::GM { 181 private: 182 std::vector<std::unique_ptr<SkAnimCodecPlayer> > fPlayers; 183 uint32_t fBaseMSec = 0; 184 185 public: AnimCodecPlayerGM()186 AnimCodecPlayerGM() { 187 const char* root = "/skia/anim/"; 188 SkOSFile::Iter iter(root); 189 SkString path; 190 while (iter.next(&path)) { 191 SkString completepath; 192 completepath.printf("%s%s", root, path.c_str()); 193 auto codec = load_codec(completepath.c_str()); 194 if (codec) { 195 fPlayers.push_back(skstd::make_unique<SkAnimCodecPlayer>(std::move(codec))); 196 } 197 } 198 } 199 200 private: onShortName()201 SkString onShortName() override { 202 return SkString("AnimCodecPlayer"); 203 } 204 onISize()205 SkISize onISize() override { 206 return { 1024, 768 }; 207 } 208 onDraw(SkCanvas * canvas)209 void onDraw(SkCanvas* canvas) override { 210 canvas->scale(0.25f, 0.25f); 211 for (auto& p : fPlayers) { 212 canvas->drawImage(p->getFrame(), 0, 0, nullptr); 213 canvas->translate(p->dimensions().width(), 0); 214 } 215 } 216 onAnimate(const SkAnimTimer & timer)217 bool onAnimate(const SkAnimTimer& timer) override { 218 if (fBaseMSec == 0) { 219 fBaseMSec = timer.msec(); 220 } 221 for (auto& p : fPlayers) { 222 (void)p->seek(timer.msec() - fBaseMSec); 223 } 224 return true; 225 } 226 }; 227 DEF_GM(return new AnimCodecPlayerGM); 228