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