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/gm.h" 9 #include "include/codec/SkCodec.h" 10 #include "include/core/SkBitmap.h" 11 #include "include/core/SkCanvas.h" 12 #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" 17 #include "include/core/SkString.h" 18 #include "include/core/SkTypes.h" 19 #include "modules/skresources/src/SkAnimCodecPlayer.h" 20 #include "src/core/SkOSFile.h" 21 #include "tools/Resources.h" 22 #include "tools/ToolUtils.h" 23 #include "tools/flags/CommandLineFlags.h" 24 #include "tools/timer/TimeUtils.h" 25 26 #include <memory> 27 #include <utility> 28 #include <vector> 29 30 #if defined(SK_ENABLE_SKOTTIE) 31 32 static DEFINE_string(animatedGif, "images/test640x479.gif", "Animated gif in resources folder"); 33 34 class AnimatedGifGM : public skiagm::GM { 35 private: 36 std::unique_ptr<SkCodec> fCodec; 37 int fFrame; 38 double fNextUpdate; 39 int 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 const int requiredFrame = fFrameInfos[frameIndex].fRequiredFrame; 56 if (requiredFrame != SkCodec::kNoFrame) { 57 SkASSERT(requiredFrame >= 0 58 && static_cast<size_t>(requiredFrame) < fFrames.size()); 59 SkBitmap& requiredBitmap = fFrames[requiredFrame]; 60 // For simplicity, do not try to cache old frames 61 if (requiredBitmap.getPixels() && 62 ToolUtils::copy_to(&bm, requiredBitmap.colorType(), requiredBitmap)) { 63 opts.fPriorFrame = requiredFrame; 64 } 65 } 66 67 if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(), 68 bm.rowBytes(), &opts)) { 69 SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]); 70 return; 71 } 72 } 73 74 canvas->drawImage(bm.asImage(), 0, 0); 75 } 76 77 public: AnimatedGifGM()78 AnimatedGifGM() 79 : fFrame(0) 80 , fNextUpdate (-1) 81 , fTotalFrames (-1) {} 82 83 private: getName() const84 SkString getName() const override { return SkString("animatedGif"); } 85 getISize()86 SkISize getISize() override { 87 if (this->initCodec()) { 88 SkISize dim = fCodec->getInfo().dimensions(); 89 // Wide enough to display all the frames. 90 dim.fWidth *= fTotalFrames; 91 // Tall enough to show the row of frames plus an animating version. 92 dim.fHeight *= 2; 93 return dim; 94 } 95 return SkISize::Make(640, 480); 96 } 97 initCodec()98 bool initCodec() { 99 if (fCodec) { 100 return true; 101 } 102 if (FLAGS_animatedGif.isEmpty()) { 103 SkDebugf("Nothing specified for --animatedGif!"); 104 return false; 105 } 106 107 std::unique_ptr<SkStream> stream(GetResourceAsStream(FLAGS_animatedGif[0])); 108 if (!stream) { 109 return false; 110 } 111 112 fCodec = SkCodec::MakeFromStream(std::move(stream)); 113 if (!fCodec) { 114 return false; 115 } 116 117 fFrame = 0; 118 fFrameInfos = fCodec->getFrameInfo(); 119 fTotalFrames = fFrameInfos.size(); 120 return true; 121 } 122 onDraw(SkCanvas * canvas,SkString * errorMsg)123 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override { 124 if (!this->initCodec()) { 125 errorMsg->printf("Could not create codec from %s", FLAGS_animatedGif[0]); 126 return DrawResult::kFail; 127 } 128 129 canvas->save(); 130 for (int frameIndex = 0; frameIndex < fTotalFrames; frameIndex++) { 131 this->drawFrame(canvas, frameIndex); 132 canvas->translate(SkIntToScalar(fCodec->getInfo().width()), 0); 133 } 134 canvas->restore(); 135 136 SkAutoCanvasRestore acr(canvas, true); 137 canvas->translate(0, SkIntToScalar(fCodec->getInfo().height())); 138 this->drawFrame(canvas, fFrame); 139 return DrawResult::kOk; 140 } 141 onAnimate(double nanos)142 bool onAnimate(double nanos) override { 143 if (!fCodec || fTotalFrames == 1) { 144 return false; 145 } 146 147 double secs = TimeUtils::NanosToMSec(nanos) * .1; 148 if (fNextUpdate < double(0)) { 149 // This is a sentinel that we have not done any updates yet. 150 // I'm assuming this gets called *after* onOnceBeforeDraw, so our first frame should 151 // already have been retrieved. 152 SkASSERT(fFrame == 0); 153 fNextUpdate = secs + fFrameInfos[fFrame].fDuration; 154 155 return true; 156 } 157 158 if (secs < fNextUpdate) { 159 return true; 160 } 161 162 while (secs >= fNextUpdate) { 163 // Retrieve the next frame. 164 fFrame++; 165 if (fFrame == fTotalFrames) { 166 fFrame = 0; 167 } 168 169 // Note that we loop here. This is not safe if we need to draw the intermediate frame 170 // in order to draw correctly. 171 fNextUpdate += fFrameInfos[fFrame].fDuration; 172 } 173 174 return true; 175 } 176 }; 177 DEF_GM(return new AnimatedGifGM;) 178 179 class AnimCodecPlayerExifGM : public skiagm::GM { 180 const char* fPath; 181 SkISize fSize = SkISize::MakeEmpty(); 182 std::unique_ptr<SkAnimCodecPlayer> fPlayer; 183 std::vector<SkCodec::FrameInfo> fFrameInfos; 184 init()185 void init() { 186 if (!fPlayer) { 187 auto data = GetResourceAsData(fPath); 188 if (!data) return; 189 190 auto codec = SkCodec::MakeFromData(std::move(data)); 191 fFrameInfos = codec->getFrameInfo(); 192 fPlayer = std::make_unique<SkAnimCodecPlayer>(std::move(codec)); 193 if (!fPlayer) return; 194 195 // We'll draw one of each frame, so make it big enough to hold them all 196 // in a grid. The grid will be roughly square, with "factor" frames per 197 // row and up to "factor" rows. 198 const size_t count = fFrameInfos.size(); 199 const float root = sqrt((float) count); 200 const int factor = sk_float_ceil2int(root); 201 202 auto imageSize = fPlayer->dimensions(); 203 fSize.fWidth = imageSize.fWidth * factor; 204 fSize.fHeight = imageSize.fHeight * sk_float_ceil2int((float) count / (float) factor); 205 } 206 } 207 getName() const208 SkString getName() const override { 209 return SkStringPrintf("AnimCodecPlayerExif_%s", strrchr(fPath, '/') + 1); 210 } 211 getISize()212 SkISize getISize() override { 213 this->init(); 214 return fSize; 215 } 216 onDraw(SkCanvas * canvas)217 void onDraw(SkCanvas* canvas) override { 218 this->init(); 219 if (!fPlayer) return; 220 221 const float root = sqrt((float) fFrameInfos.size()); 222 const int factor = sk_float_ceil2int(root); 223 auto dimensions = fPlayer->dimensions(); 224 225 uint32_t duration = 0; 226 for (int frame = 0; duration < fPlayer->duration(); frame++) { 227 SkAutoCanvasRestore acr(canvas, true); 228 const int xTranslate = (frame % factor) * dimensions.width(); 229 const int yTranslate = (frame / factor) * dimensions.height(); 230 canvas->translate(SkIntToScalar(xTranslate), SkIntToScalar(yTranslate)); 231 232 233 auto image = fPlayer->getFrame(); 234 canvas->drawImage(image, 0, 0); 235 duration += fFrameInfos[frame].fDuration; 236 fPlayer->seek(duration); 237 } 238 } 239 public: AnimCodecPlayerExifGM(const char * path)240 AnimCodecPlayerExifGM(const char* path) 241 : fPath(path) 242 {} 243 244 ~AnimCodecPlayerExifGM() override = default; 245 }; 246 247 DEF_GM(return new AnimCodecPlayerExifGM("images/required.webp");) 248 DEF_GM(return new AnimCodecPlayerExifGM("images/required.gif");) 249 DEF_GM(return new AnimCodecPlayerExifGM("images/stoplight_h.webp");) 250 251 #endif 252