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);