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