Home | History | Annotate | Download | only in gm
      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 {
     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 
     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:
     79     AnimatedGifGM()
     80     : fFrame(0)
     81     , fNextUpdate (-1)
     82     , fTotalFrames (-1) {}
     83 
     84 private:
     85     SkString onShortName() override {
     86         return SkString("animatedGif");
     87     }
     88 
     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 
    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 
    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 
    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 
    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