| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 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 { | 
|  | 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.he
     ight() + 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 | 
|  | 43     void drawBounds(SkCanvas* canvas) { | 
|  | 44         if (!fCodec) { | 
|  | 45             return; | 
|  | 46         } | 
|  | 47         const SkIRect bounds = fCodec->getInfo().bounds().makeOutset(1, 1); | 
|  | 48         SkPaint boundsPaint; | 
|  | 49         boundsPaint.setStyle(SkPaint::kStroke_Style); | 
|  | 50         canvas->drawIRect(bounds, boundsPaint); | 
|  | 51     } | 
|  | 52 | 
|  | 53     void drawFrame(SkCanvas* canvas, int frameIndex) { | 
|  | 54         // FIXME: Create from an Image/ImageGenerator? | 
|  | 55         if (frameIndex >= (int) fFrames.size()) { | 
|  | 56             fFrames.resize(frameIndex + 1); | 
|  | 57         } | 
|  | 58         SkBitmap& bm = fFrames[frameIndex]; | 
|  | 59         if (!bm.getPixels()) { | 
|  | 60             const SkImageInfo info = fCodec->getInfo().makeColorType(kN32_SkColo
     rType); | 
|  | 61             bm.allocPixels(info); | 
|  | 62 | 
|  | 63             SkCodec::MultiFrameOptions multiOpts; | 
|  | 64             multiOpts.fIndex = frameIndex; | 
|  | 65             multiOpts.fHasPriorFrame = false; | 
|  | 66             const size_t requiredFrame = fFrameInfos[frameIndex].fRequiredFrame; | 
|  | 67             if (requiredFrame != SkCodec::kIndependentFrame) { | 
|  | 68                 SkASSERT(requiredFrame < fFrames.size()); | 
|  | 69                 SkBitmap& requiredBitmap = fFrames[requiredFrame]; | 
|  | 70                 // For simplicity, do not try to cache old frames | 
|  | 71                 if (requiredBitmap.getPixels() && requiredBitmap.copyTo(&bm)) { | 
|  | 72                     multiOpts.fHasPriorFrame = true; | 
|  | 73                 } | 
|  | 74             } | 
|  | 75 | 
|  | 76             SkCodec::Options opts; | 
|  | 77             opts.fFrameOptions = &multiOpts; | 
|  | 78             if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(), | 
|  | 79                                                        bm.rowBytes(), &opts, | 
|  | 80                                                        nullptr, nullptr)) { | 
|  | 81                 SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLA
     GS_animatedGif[0]); | 
|  | 82                 return; | 
|  | 83             } | 
|  | 84         } | 
|  | 85 | 
|  | 86         canvas->drawBitmap(bm, 0, 0); | 
|  | 87     } | 
|  | 88 | 
|  | 89 public: | 
|  | 90     AnimatedGifGM() | 
|  | 91     : fFrame(0) | 
|  | 92     , fNextUpdate (-1) | 
|  | 93     , fTotalFrames (-1) {} | 
|  | 94 | 
|  | 95 private: | 
|  | 96     SkString onShortName() override { | 
|  | 97         return SkString("animatedGif"); | 
|  | 98     } | 
|  | 99 | 
|  | 100     SkISize onISize() override { | 
|  | 101         if (this->initCodec()) { | 
|  | 102             SkISize dim = fCodec->getInfo().dimensions(); | 
|  | 103             // Wide enough to display all the frames. | 
|  | 104             dim.fWidth *= fTotalFrames; | 
|  | 105             // Tall enough to show the row of frames plus an animating version. | 
|  | 106             dim.fHeight *= 2; | 
|  | 107             return dim; | 
|  | 108         } | 
|  | 109         return SkISize::Make(640, 480); | 
|  | 110     } | 
|  | 111 | 
|  | 112     void onDrawBackground(SkCanvas* canvas) override { | 
|  | 113         if (this->initCodec()) { | 
|  | 114             SkAutoCanvasRestore acr(canvas, true); | 
|  | 115             // FIXME: Draw a checkerboard here | 
|  | 116             for (size_t frameIndex = 0; frameIndex < fTotalFrames; frameIndex++)
      { | 
|  | 117                 this->drawBounds(canvas); | 
|  | 118                 this->drawFrame(canvas, frameIndex); | 
|  | 119                 canvas->translate(fCodec->getInfo().width(), 0); | 
|  | 120             } | 
|  | 121         } | 
|  | 122     } | 
|  | 123 | 
|  | 124     bool initCodec() { | 
|  | 125         if (fCodec) { | 
|  | 126             return true; | 
|  | 127         } | 
|  | 128         if (FLAGS_animatedGif.isEmpty()) { | 
|  | 129             SkDebugf("Nothing specified for --animatedGif!"); | 
|  | 130             return false; | 
|  | 131         } | 
|  | 132 | 
|  | 133         std::unique_ptr<SkStream> stream(GetResourceAsStream(FLAGS_animatedGif[0
     ])); | 
|  | 134         if (!stream) { | 
|  | 135             SkDebugf("Could not find %s", FLAGS_animatedGif[0]); | 
|  | 136             return false; | 
|  | 137         } | 
|  | 138 | 
|  | 139         fCodec.reset(SkCodec::NewFromStream(stream.release())); | 
|  | 140         if (!fCodec) { | 
|  | 141             SkDebugf("Could create codec from %s", FLAGS_animatedGif[0]); | 
|  | 142             return false; | 
|  | 143         } | 
|  | 144 | 
|  | 145         fFrame = 0; | 
|  | 146         fFrameInfos = fCodec->getFrameInfo(); | 
|  | 147         fTotalFrames = fFrameInfos.size(); | 
|  | 148         return true; | 
|  | 149     } | 
|  | 150 | 
|  | 151     void onDraw(SkCanvas* canvas) { | 
|  | 152         if (!fCodec) { | 
|  | 153             SkString errorText = SkStringPrintf("Nothing to draw; %s", FLAGS_ani
     matedGif[0]); | 
|  | 154             error(canvas, errorText); | 
|  | 155             return; | 
|  | 156         } | 
|  | 157 | 
|  | 158         // Now draw it animated. | 
|  | 159         SkAutoCanvasRestore acr(canvas, true); | 
|  | 160         canvas->translate(0, fCodec->getInfo().height()); | 
|  | 161         this->drawBounds(canvas); | 
|  | 162         this->drawFrame(canvas, fFrame); | 
|  | 163     } | 
|  | 164 | 
|  | 165     bool onAnimate(const SkAnimTimer& timer) override { | 
|  | 166         if (!fCodec || fTotalFrames == 1) { | 
|  | 167             return false; | 
|  | 168         } | 
|  | 169 | 
|  | 170         double secs = timer.msec() * .1; | 
|  | 171         if (fNextUpdate < double(0)) { | 
|  | 172             // This is a sentinel that we have not done any updates yet. | 
|  | 173             // I'm assuming this gets called *after* onOnceBeforeDraw, so our fi
     rst frame should | 
|  | 174             // already have been retrieved. | 
|  | 175             SkASSERT(fFrame == 0); | 
|  | 176             fNextUpdate = secs + fFrameInfos[fFrame].fDuration; | 
|  | 177 | 
|  | 178             return true; | 
|  | 179         } | 
|  | 180 | 
|  | 181         if (secs < fNextUpdate) { | 
|  | 182             return true; | 
|  | 183         } | 
|  | 184 | 
|  | 185         while (secs >= fNextUpdate) { | 
|  | 186             // Retrieve the next frame. | 
|  | 187             fFrame++; | 
|  | 188             if (fFrame == fTotalFrames) { | 
|  | 189                 fFrame = 0; | 
|  | 190             } | 
|  | 191 | 
|  | 192             // Note that we loop here. This is not safe if we need to draw the i
     ntermediate frame | 
|  | 193             // in order to draw correctly. | 
|  | 194             fNextUpdate += fFrameInfos[fFrame].fDuration; | 
|  | 195         } | 
|  | 196 | 
|  | 197         return true; | 
|  | 198     } | 
|  | 199 }; | 
|  | 200 | 
|  | 201 DEF_GM(return new AnimatedGifGM); | 
|  | 202 | 
| OLD | NEW | 
|---|