Add an SkDrawable for animated images (e.g. GIF)

Bug: b/63909536

SkAnimatedImage is a simple drawable for animating a GIF. Thread-safety
is left up to the client. At most two bitmaps are stored in the
drawable; one for the current frame and one for a frame that may need to
be restored. The backup frame prevents some cases where we would
otherwise have to re-decode from the beginning of the image.

The API lets the client set the time value, and decodes to match that
time.

TODO:
- Callback for when the animation is complete
- Ability to use SkAndroidCodec
- Modify the loop count (or leave that up to client?)
- Better and/or client-specific caching

Other changes:
- Add a sample which animates a GIF
- Reenable SK_CODEC_PRINTF for debug builds and Android

Change-Id: I945ffbccdb6008f2a05ed4d9b2af869a261fb300
Reviewed-on: https://skia-review.googlesource.com/93420
Reviewed-by: Derek Sollenberger <djsollen@google.com>
Commit-Queue: Leon Scroggins <scroggo@google.com>
diff --git a/samplecode/SampleAnimatedImage.cpp b/samplecode/SampleAnimatedImage.cpp
new file mode 100644
index 0000000..96e53b8
--- /dev/null
+++ b/samplecode/SampleAnimatedImage.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkAnimatedImage.h"
+#include "SkAnimTimer.h"
+#include "SkCanvas.h"
+#include "SkCodec.h"
+#include "SkPaint.h"
+#include "SkPictureRecorder.h"
+#include "SkRect.h"
+#include "SkScalar.h"
+#include "SkString.h"
+
+#include "SampleCode.h"
+#include "Resources.h"
+
+static constexpr char kPauseKey = 'p';
+static constexpr char kResetKey = 'r';
+
+class SampleAnimatedImage : public SampleView {
+public:
+    SampleAnimatedImage()
+        : INHERITED()
+        , fRunning(false)
+        , fYOffset(0)
+    {}
+
+protected:
+    void onDrawBackground(SkCanvas* canvas) override {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        constexpr SkScalar kTextSize = 20;
+        paint.setTextSize(kTextSize);
+
+        SkString str = SkStringPrintf("Press '%c' to start/pause; '%c' to reset.",
+                kPauseKey, kResetKey);
+        const char* text = str.c_str();
+        SkRect bounds;
+        paint.measureText(text, strlen(text), &bounds);
+        fYOffset = bounds.height();
+
+        canvas->drawText(text, strlen(text), 5, fYOffset, paint);
+        fYOffset *= 2;
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        if (!fImage) {
+            return;
+        }
+
+        canvas->translate(0, fYOffset);
+
+        canvas->drawDrawable(fImage.get());
+        canvas->drawDrawable(fDrawable.get(), fImage->getBounds().width(), 0);
+    }
+
+    bool onAnimate(const SkAnimTimer& animTimer) override {
+        if (!fImage) {
+            return false;
+        }
+
+        fImage->update(animTimer.msec());
+        return true;
+    }
+
+    void onOnceBeforeDraw() override {
+        sk_sp<SkData> file(GetResourceAsData("images/alphabetAnim.gif"));
+        std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(file));
+        if (!codec) {
+            return;
+        }
+
+        fImage = SkAnimatedImage::MakeFromCodec(std::move(codec));
+        if (!fImage) {
+            return;
+        }
+
+        SkPictureRecorder recorder;
+        auto canvas = recorder.beginRecording(fImage->getBounds());
+        canvas->drawDrawable(fImage.get());
+        fDrawable = recorder.finishRecordingAsDrawable();
+    }
+
+    bool onQuery(SkEvent* evt) override {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "AnimatedImage");
+            return true;
+        }
+
+        SkUnichar uni;
+        if (fImage && SampleCode::CharQ(*evt, &uni)) {
+            switch (uni) {
+                case kPauseKey:
+                    if (fRunning) {
+                        fImage->stop();
+                        fRunning = false;
+                    } else {
+                        fImage->start();
+                        fRunning = true;
+                    }
+                    return true;
+                case kResetKey:
+                    fImage->reset();
+                    return true;
+                default:
+                    break;
+            }
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+private:
+    sk_sp<SkAnimatedImage>  fImage;
+    sk_sp<SkDrawable>       fDrawable;
+    bool                    fRunning;
+    SkScalar                fYOffset;
+    typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() {
+    return new SampleAnimatedImage;
+}
+
+static SkViewRegister reg(MyFactory);