add support for rendering lottie animations through a LottieDrawable

This is an initial push that only supports basic playback

Test: frameworks/base/tests/VectorDrawableTest and run LottieDrawable activity
Change-Id: Ic34366b0cd0984a512d8684d476227830903f778
Bug: 257304231
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 3e3d77b..9c4ea0e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -78,6 +78,7 @@
                 "external/skia/src/utils",
                 "external/skia/src/gpu",
                 "external/skia/src/shaders",
+                "external/skia/modules/skottie",
             ],
         },
         host: {
@@ -374,6 +375,7 @@
         "external/skia/src/effects",
         "external/skia/src/image",
         "external/skia/src/images",
+        "external/skia/modules/skottie",
     ],
 
     shared_libs: [
@@ -400,6 +402,7 @@
                 "jni/BitmapRegionDecoder.cpp",
                 "jni/GIFMovie.cpp",
                 "jni/GraphicsStatsService.cpp",
+                "jni/LottieDrawable.cpp",
                 "jni/Movie.cpp",
                 "jni/MovieImpl.cpp",
                 "jni/pdf/PdfDocument.cpp",
@@ -507,6 +510,7 @@
         "hwui/BlurDrawLooper.cpp",
         "hwui/Canvas.cpp",
         "hwui/ImageDecoder.cpp",
+        "hwui/LottieDrawable.cpp",
         "hwui/MinikinSkia.cpp",
         "hwui/MinikinUtils.cpp",
         "hwui/PaintImpl.cpp",
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index d83d78f..a1c4b49 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -736,6 +736,10 @@
     return imgDrawable->drawStaging(mCanvas);
 }
 
+void SkiaCanvas::drawLottie(LottieDrawable* lottieDrawable) {
+    lottieDrawable->drawStaging(mCanvas);
+}
+
 void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) {
     vectorDrawable->drawStaging(this);
 }
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 31e3b4c..fd8b6cd 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -145,6 +145,7 @@
                                float dstTop, float dstRight, float dstBottom,
                                const Paint* paint) override;
     virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override;
+    virtual void drawLottie(LottieDrawable* lottieDrawable) override;
 
     virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
 
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index e6cfa7b..5a96174 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -37,6 +37,7 @@
 extern int register_android_graphics_Graphics(JNIEnv* env);
 extern int register_android_graphics_ImageDecoder(JNIEnv*);
 extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*);
+extern int register_android_graphics_drawable_LottieDrawable(JNIEnv*);
 extern int register_android_graphics_Interpolator(JNIEnv* env);
 extern int register_android_graphics_MaskFilter(JNIEnv* env);
 extern int register_android_graphics_Movie(JNIEnv* env);
@@ -116,6 +117,7 @@
             REG_JNI(register_android_graphics_HardwareRendererObserver),
             REG_JNI(register_android_graphics_ImageDecoder),
             REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
+            REG_JNI(register_android_graphics_drawable_LottieDrawable),
             REG_JNI(register_android_graphics_Interpolator),
             REG_JNI(register_android_graphics_MaskFilter),
             REG_JNI(register_android_graphics_Matrix),
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 2a20191..07e2fe2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -60,6 +60,7 @@
 typedef std::function<void(uint16_t* text, float* positions)> ReadGlyphFunc;
 
 class AnimatedImageDrawable;
+class LottieDrawable;
 class Bitmap;
 class Paint;
 struct Typeface;
@@ -242,6 +243,7 @@
                                const Paint* paint) = 0;
 
     virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) = 0;
+    virtual void drawLottie(LottieDrawable* lottieDrawable) = 0;
     virtual void drawPicture(const SkPicture& picture) = 0;
 
     /**
diff --git a/libs/hwui/hwui/LottieDrawable.cpp b/libs/hwui/hwui/LottieDrawable.cpp
new file mode 100644
index 0000000..92dc51e
--- /dev/null
+++ b/libs/hwui/hwui/LottieDrawable.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LottieDrawable.h"
+
+#include <SkTime.h>
+#include <log/log.h>
+#include <pipeline/skia/SkiaUtils.h>
+
+namespace android {
+
+sk_sp<LottieDrawable> LottieDrawable::Make(sk_sp<skottie::Animation> animation, size_t bytesUsed) {
+    if (animation) {
+        return sk_sp<LottieDrawable>(new LottieDrawable(std::move(animation), bytesUsed));
+    }
+    return nullptr;
+}
+LottieDrawable::LottieDrawable(sk_sp<skottie::Animation> animation, size_t bytesUsed)
+        : mAnimation(std::move(animation)), mBytesUsed(bytesUsed) {}
+
+bool LottieDrawable::start() {
+    if (mRunning) {
+        return false;
+    }
+
+    mRunning = true;
+    return true;
+}
+
+bool LottieDrawable::stop() {
+    bool wasRunning = mRunning;
+    mRunning = false;
+    return wasRunning;
+}
+
+bool LottieDrawable::isRunning() {
+    return mRunning;
+}
+
+// TODO: Check to see if drawable is actually dirty
+bool LottieDrawable::isDirty() {
+    return true;
+}
+
+void LottieDrawable::onDraw(SkCanvas* canvas) {
+    if (mRunning) {
+        const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+        nsecs_t t = 0;
+        if (mStartTime == 0) {
+            mStartTime = currentTime;
+        } else {
+            t = currentTime - mStartTime;
+        }
+        double seekTime = std::fmod((double)t * 1e-9, mAnimation->duration());
+        mAnimation->seekFrameTime(seekTime);
+        mAnimation->render(canvas);
+    }
+}
+
+void LottieDrawable::drawStaging(SkCanvas* canvas) {
+    onDraw(canvas);
+}
+
+SkRect LottieDrawable::onGetBounds() {
+    // We do not actually know the bounds, so give a conservative answer.
+    return SkRectMakeLargest();
+}
+
+}  // namespace android
diff --git a/libs/hwui/hwui/LottieDrawable.h b/libs/hwui/hwui/LottieDrawable.h
new file mode 100644
index 0000000..9cc34bf
--- /dev/null
+++ b/libs/hwui/hwui/LottieDrawable.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkDrawable.h>
+#include <Skottie.h>
+#include <utils/Timers.h>
+
+class SkCanvas;
+
+namespace android {
+
+/**
+ * Native component of android.graphics.drawable.LottieDrawable.java.
+ * This class can be drawn into Canvas.h and maintains the state needed to drive
+ * the animation from the RenderThread.
+ */
+class LottieDrawable : public SkDrawable {
+public:
+    static sk_sp<LottieDrawable> Make(sk_sp<skottie::Animation> animation, size_t bytes);
+
+    // Draw to software canvas
+    void drawStaging(SkCanvas* canvas);
+
+    // Returns true if the animation was started; false otherwise (e.g. it was
+    // already running)
+    bool start();
+    // Returns true if the animation was stopped; false otherwise (e.g. it was
+    // already stopped)
+    bool stop();
+    bool isRunning();
+
+    // TODO: Is dirty should take in a time til next frame to determine if it is dirty
+    bool isDirty();
+
+    SkRect onGetBounds() override;
+
+    size_t byteSize() const { return sizeof(*this) + mBytesUsed; }
+
+protected:
+    void onDraw(SkCanvas* canvas) override;
+
+private:
+    LottieDrawable(sk_sp<skottie::Animation> animation, size_t bytes_used);
+
+    sk_sp<skottie::Animation> mAnimation;
+    bool mRunning = false;
+    // The start time for the drawable itself.
+    nsecs_t mStartTime = 0;
+    const size_t mBytesUsed = 0;
+};
+
+}  // namespace android
diff --git a/libs/hwui/jni/LottieDrawable.cpp b/libs/hwui/jni/LottieDrawable.cpp
new file mode 100644
index 0000000..fb6eede
--- /dev/null
+++ b/libs/hwui/jni/LottieDrawable.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <SkRect.h>
+#include <Skottie.h>
+#include <hwui/Canvas.h>
+#include <hwui/LottieDrawable.h>
+
+#include "GraphicsJNI.h"
+#include "Utils.h"
+
+using namespace android;
+
+static jclass gLottieDrawableClass;
+
+static jlong LottieDrawable_nCreate(JNIEnv* env, jobject, jstring jjson) {
+    const ScopedUtfChars cstr(env, jjson);
+    // TODO(b/259267150) provide more accurate byteSize
+    size_t bytes = strlen(cstr.c_str());
+    auto animation = skottie::Animation::Builder().make(cstr.c_str(), bytes);
+    sk_sp<LottieDrawable> drawable(LottieDrawable::Make(std::move(animation), bytes));
+    if (!drawable) {
+        return 0;
+    }
+    return reinterpret_cast<jlong>(drawable.release());
+}
+
+static void LottieDrawable_destruct(LottieDrawable* drawable) {
+    SkSafeUnref(drawable);
+}
+
+static jlong LottieDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&LottieDrawable_destruct));
+}
+
+static void LottieDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jlong canvasPtr) {
+    auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
+    auto* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    canvas->drawLottie(drawable);
+}
+
+static jboolean LottieDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
+    return drawable->isRunning();
+}
+
+static jboolean LottieDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
+    return drawable->start();
+}
+
+static jboolean LottieDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
+    return drawable->stop();
+}
+
+static jlong LottieDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr);
+    return drawable->byteSize();
+}
+
+static const JNINativeMethod gLottieDrawableMethods[] = {
+        {"nCreate", "(Ljava/lang/String;)J", (void*)LottieDrawable_nCreate},
+        {"nNativeByteSize", "(J)J", (void*)LottieDrawable_nNativeByteSize},
+        {"nGetNativeFinalizer", "()J", (void*)LottieDrawable_nGetNativeFinalizer},
+        {"nDraw", "(JJ)V", (void*)LottieDrawable_nDraw},
+        {"nIsRunning", "(J)Z", (void*)LottieDrawable_nIsRunning},
+        {"nStart", "(J)Z", (void*)LottieDrawable_nStart},
+        {"nStop", "(J)Z", (void*)LottieDrawable_nStop},
+};
+
+int register_android_graphics_drawable_LottieDrawable(JNIEnv* env) {
+    gLottieDrawableClass = reinterpret_cast<jclass>(
+            env->NewGlobalRef(FindClassOrDie(env, "android/graphics/drawable/LottieDrawable")));
+
+    return android::RegisterMethodsOrDie(env, "android/graphics/drawable/LottieDrawable",
+                                         gLottieDrawableMethods, NELEM(gLottieDrawableMethods));
+}
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index fcfc4f8..f0dc5eb 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -146,6 +146,16 @@
         }
     }
 
+    for (auto& lottie : mLotties) {
+        // If any animated image in the display list needs updated, then damage the node.
+        if (lottie->isDirty()) {
+            isDirty = true;
+        }
+        if (lottie->isRunning()) {
+            info.out.hasAnimations = true;
+        }
+    }
+
     for (auto& [vectorDrawable, cachedMatrix] : mVectorDrawables) {
         // If any vector drawable in the display list needs update, damage the node.
         if (vectorDrawable->isDirty()) {
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 2a67734..39217fc 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -22,6 +22,7 @@
 #include "RenderNodeDrawable.h"
 #include "TreeInfo.h"
 #include "hwui/AnimatedImageDrawable.h"
+#include "hwui/LottieDrawable.h"
 #include "utils/LinearAllocator.h"
 #include "utils/Pair.h"
 
@@ -186,6 +187,8 @@
         return mHasHolePunches;
     }
 
+    // TODO(b/257304231): create common base class for Lotties and AnimatedImages
+    std::vector<LottieDrawable*> mLotties;
     std::vector<AnimatedImageDrawable*> mAnimatedImages;
     DisplayListData mDisplayList;
 
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 1f87865..db449d6 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -188,6 +188,11 @@
 #endif
 }
 
+void SkiaRecordingCanvas::drawLottie(LottieDrawable* lottie) {
+    drawDrawable(lottie);
+    mDisplayList->mLotties.push_back(lottie);
+}
+
 void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
     mRecorder.drawVectorDrawable(tree);
     SkMatrix mat;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 7844e2c..c823d8d 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -78,6 +78,7 @@
                             uirenderer::CanvasPropertyPaint* paint) override;
     virtual void drawRipple(const RippleDrawableParams& params) override;
 
+    virtual void drawLottie(LottieDrawable* lottieDrawable) override;
     virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
 
     virtual void enableZ(bool enableZ) override;