diff options
Diffstat (limited to 'gm/animatedGif.cpp')
-rw-r--r-- | gm/animatedGif.cpp | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/gm/animatedGif.cpp b/gm/animatedGif.cpp new file mode 100644 index 0000000000..fc6fad0886 --- /dev/null +++ b/gm/animatedGif.cpp @@ -0,0 +1,185 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" +#include "SkAnimTimer.h" +#include "SkCanvas.h" +#include "SkCodec.h" +#include "SkColor.h" +#include "SkCommandLineFlags.h" +#include "SkPaint.h" +#include "SkString.h" +#include "Resources.h" + +#include <vector> + +DEFINE_string(animatedGif, "test640x479.gif", "Animated gif in resources folder"); + +namespace { + void error(SkCanvas* canvas, const SkString& errorText) { + constexpr SkScalar kOffset = 5.0f; + canvas->drawColor(SK_ColorRED); + SkPaint paint; + SkRect bounds; + paint.measureText(errorText.c_str(), errorText.size(), &bounds); + canvas->drawText(errorText.c_str(), errorText.size(), kOffset, bounds.height() + kOffset, + paint); + } +} + +class AnimatedGifGM : public skiagm::GM { +private: + std::unique_ptr<SkCodec> fCodec; + size_t fFrame; + double fNextUpdate; + size_t fTotalFrames; + std::vector<SkCodec::FrameInfo> fFrameInfos; + std::vector<SkBitmap> fFrames; + + void drawFrame(SkCanvas* canvas, int frameIndex) { + // FIXME: Create from an Image/ImageGenerator? + if (frameIndex >= (int) fFrames.size()) { + fFrames.resize(frameIndex + 1); + } + SkBitmap& bm = fFrames[frameIndex]; + if (!bm.getPixels()) { + const SkImageInfo info = fCodec->getInfo().makeColorType(kN32_SkColorType); + bm.allocPixels(info); + + SkCodec::Options opts; + opts.fFrameIndex = frameIndex; + opts.fHasPriorFrame = false; + const size_t requiredFrame = fFrameInfos[frameIndex].fRequiredFrame; + if (requiredFrame != SkCodec::kNone) { + SkASSERT(requiredFrame < fFrames.size()); + SkBitmap& requiredBitmap = fFrames[requiredFrame]; + // For simplicity, do not try to cache old frames + if (requiredBitmap.getPixels() && requiredBitmap.copyTo(&bm)) { + opts.fHasPriorFrame = true; + } + } + + if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(), + bm.rowBytes(), &opts, + nullptr, nullptr)) { + SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]); + return; + } + } + + canvas->drawBitmap(bm, 0, 0); + } + +public: + AnimatedGifGM() + : fFrame(0) + , fNextUpdate (-1) + , fTotalFrames (-1) {} + +private: + SkString onShortName() override { + return SkString("animatedGif"); + } + + SkISize onISize() override { + if (this->initCodec()) { + SkISize dim = fCodec->getInfo().dimensions(); + // Wide enough to display all the frames. + dim.fWidth *= fTotalFrames; + // Tall enough to show the row of frames plus an animating version. + dim.fHeight *= 2; + return dim; + } + return SkISize::Make(640, 480); + } + + void onDrawBackground(SkCanvas* canvas) override { + if (this->initCodec()) { + SkAutoCanvasRestore acr(canvas, true); + for (size_t frameIndex = 0; frameIndex < fTotalFrames; frameIndex++) { + this->drawFrame(canvas, frameIndex); + canvas->translate(fCodec->getInfo().width(), 0); + } + } + } + + bool initCodec() { + if (fCodec) { + return true; + } + if (FLAGS_animatedGif.isEmpty()) { + SkDebugf("Nothing specified for --animatedGif!"); + return false; + } + + std::unique_ptr<SkStream> stream(GetResourceAsStream(FLAGS_animatedGif[0])); + if (!stream) { + return false; + } + + fCodec.reset(SkCodec::NewFromStream(stream.release())); + if (!fCodec) { + SkDebugf("Could create codec from %s", FLAGS_animatedGif[0]); + return false; + } + + fFrame = 0; + fFrameInfos = fCodec->getFrameInfo(); + fTotalFrames = fFrameInfos.size(); + return true; + } + + void onDraw(SkCanvas* canvas) override { + if (!fCodec) { + SkString errorText = SkStringPrintf("Nothing to draw; %s", FLAGS_animatedGif[0]); + error(canvas, errorText); + return; + } + + SkAutoCanvasRestore acr(canvas, true); + canvas->translate(0, fCodec->getInfo().height()); + this->drawFrame(canvas, fFrame); + } + + bool onAnimate(const SkAnimTimer& timer) override { + if (!fCodec || fTotalFrames == 1) { + return false; + } + + double secs = timer.msec() * .1; + if (fNextUpdate < double(0)) { + // This is a sentinel that we have not done any updates yet. + // I'm assuming this gets called *after* onOnceBeforeDraw, so our first frame should + // already have been retrieved. + SkASSERT(fFrame == 0); + fNextUpdate = secs + fFrameInfos[fFrame].fDuration; + + return true; + } + + if (secs < fNextUpdate) { + return true; + } + + while (secs >= fNextUpdate) { + // Retrieve the next frame. + fFrame++; + if (fFrame == fTotalFrames) { + fFrame = 0; + } + + // Note that we loop here. This is not safe if we need to draw the intermediate frame + // in order to draw correctly. + fNextUpdate += fFrameInfos[fFrame].fDuration; + } + + return true; + } +}; + +DEF_GM(return new AnimatedGifGM); + |