| Index: gm/animatedGif.cpp
|
| diff --git a/gm/animatedGif.cpp b/gm/animatedGif.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4ec70255628853e9a6c0fa7e55ee9959f407e108
|
| --- /dev/null
|
| +++ b/gm/animatedGif.cpp
|
| @@ -0,0 +1,200 @@
|
| +/*
|
| + * 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<SkBitmap> fFrames;
|
| +
|
| + void drawBounds(SkCanvas* canvas) {
|
| + if (!fCodec) {
|
| + return;
|
| + }
|
| + const SkIRect bounds = fCodec->getInfo().bounds().makeOutset(1, 1);
|
| + SkPaint boundsPaint;
|
| + boundsPaint.setStyle(SkPaint::kStroke_Style);
|
| + canvas->drawIRect(bounds, boundsPaint);
|
| + }
|
| +
|
| + 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::MultiFrameOptions multiOpts;
|
| + multiOpts.fIndex = frameIndex;
|
| + multiOpts.fHasPriorFrame = false;
|
| + const size_t requiredFrame = fCodec->getRequiredFrame(frameIndex);
|
| + if (requiredFrame != SkCodec::kIndependentFrame) {
|
| + SkASSERT(requiredFrame < fFrames.size());
|
| + SkBitmap& requiredBitmap = fFrames[requiredFrame];
|
| + // For simplicity, do not try to cache old frames
|
| + if (requiredBitmap.getPixels() && requiredBitmap.copyTo(&bm)) {
|
| + multiOpts.fHasPriorFrame = true;
|
| + }
|
| + }
|
| +
|
| + SkCodec::Options opts;
|
| + opts.fFrameOptions = &multiOpts;
|
| + 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 *= fCodec->getFrameCount();
|
| + // 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);
|
| + // FIXME: Draw a checkerboard here
|
| + for (size_t frameIndex = 0; frameIndex < fTotalFrames; frameIndex++) {
|
| + this->drawBounds(canvas);
|
| + 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) {
|
| + SkDebugf("Could not find %s", FLAGS_animatedGif[0]);
|
| + return false;
|
| + }
|
| +
|
| + fCodec.reset(SkCodec::NewFromStream(stream.release()));
|
| + if (!fCodec) {
|
| + SkDebugf("Could create codec from %s", FLAGS_animatedGif[0]);
|
| + return false;
|
| + }
|
| +
|
| + fFrame = 0;
|
| + fTotalFrames = fCodec->getFrameCount();
|
| + return true;
|
| + }
|
| +
|
| + void onDraw(SkCanvas* canvas) {
|
| + if (!fCodec) {
|
| + SkString errorText = SkStringPrintf("Nothing to draw; %s", FLAGS_animatedGif[0]);
|
| + error(canvas, errorText);
|
| + return;
|
| + }
|
| +
|
| + // Now draw it animated.
|
| + SkAutoCanvasRestore acr(canvas, true);
|
| + canvas->translate(0, fCodec->getInfo().height());
|
| + this->drawBounds(canvas);
|
| + 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 + fCodec->getFrameDuration(fFrame);
|
| +
|
| + 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 += fCodec->getFrameDuration(fFrame);
|
| + }
|
| +
|
| + return true;
|
| + }
|
| +};
|
| +
|
| +DEF_GM(return new AnimatedGifGM);
|
| +
|
|
|