Merge "Report native allocation size of AnimatedImageDrawable" into pi-dev am: d848f75f99
am: 6413aebc02

Change-Id: Id1a101b08f8f9b9b53875bd9c784dea04a5b4110
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index d6496cd..7166c75 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -42,7 +42,6 @@
     }
 
     auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder);
-    auto info = imageDecoder->mCodec->getInfo();
     const SkISize scaledSize = SkISize::Make(width, height);
     SkIRect subset;
     if (jsubset) {
@@ -51,6 +50,35 @@
         subset = SkIRect::MakeWH(width, height);
     }
 
+    auto info = imageDecoder->mCodec->getInfo();
+    bool hasRestoreFrame = false;
+    if (imageDecoder->mCodec->getEncodedFormat() == SkEncodedImageFormat::kWEBP) {
+        if (width < info.width() && height < info.height()) {
+            // WebP will scale its SkBitmap to the scaled size.
+            // FIXME: b/73529447 GIF should do the same.
+            info = info.makeWH(width, height);
+        }
+    } else {
+        const int frameCount = imageDecoder->mCodec->codec()->getFrameCount();
+        for (int i = 0; i < frameCount; ++i) {
+            SkCodec::FrameInfo frameInfo;
+            if (!imageDecoder->mCodec->codec()->getFrameInfo(i, &frameInfo)) {
+                doThrowIOE(env, "Failed to read frame info!");
+                return 0;
+            }
+            if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious) {
+                hasRestoreFrame = true;
+                break;
+            }
+        }
+    }
+
+    size_t bytesUsed = info.computeMinByteSize();
+    // SkAnimatedImage has one SkBitmap for decoding, plus an extra one if there is a
+    // kRestorePrevious frame. AnimatedImageDrawable has two SkPictures storing the current
+    // frame and the next frame. (The former assumes that the image is animated, and the
+    // latter assumes that it is drawn to a hardware canvas.)
+    bytesUsed *= hasRestoreFrame ? 4 : 3;
     sk_sp<SkPicture> picture;
     if (jpostProcess) {
         SkRect bounds = SkRect::MakeWH(subset.width(), subset.height());
@@ -63,6 +91,7 @@
             return 0;
         }
         picture = recorder.finishRecordingAsPicture();
+        bytesUsed += picture->approximateBytesUsed();
     }
 
 
@@ -74,7 +103,10 @@
         return 0;
     }
 
-    sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(animatedImg));
+    bytesUsed += sizeof(animatedImg.get());
+
+    sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(std::move(animatedImg),
+                                                                    bytesUsed));
     return reinterpret_cast<jlong>(drawable.release());
 }
 
@@ -202,10 +234,9 @@
     }
 }
 
-static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
-    // FIXME: Report the size of the internal SkBitmap etc.
-    return sizeof(drawable);
+    return drawable->byteSize();
 }
 
 static void AnimatedImageDrawable_nMarkInvisible(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index c0f4920..a47ecf5 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -292,8 +292,7 @@
         mState = new State(nCreate(nativeImageDecoder, decoder, width, height, cropRect),
                 inputStream, afd);
 
-        // FIXME: Use the right size for the native allocation.
-        long nativeSize = 200;
+        final long nativeSize = nNativeByteSize(mState.mNativePtr);
         NativeAllocationRegistry registry = new NativeAllocationRegistry(
                 AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize);
         registry.registerNativeAllocation(mState, mState.mNativePtr);
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 28d0bc4..c529f87 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -26,8 +26,8 @@
 
 namespace android {
 
-AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage)
-        : mSkAnimatedImage(std::move(animatedImage)) {
+AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed)
+        : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) {
     mTimeToShowNextSnapshot = mSkAnimatedImage->currentFrameDuration();
 }
 
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index f4e2ba7..a92b62d 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -45,7 +45,9 @@
  */
 class ANDROID_API AnimatedImageDrawable : public SkDrawable {
 public:
-    AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage);
+    // bytesUsed includes the approximate sizes of the SkAnimatedImage and the SkPictures in the
+    // Snapshots.
+    AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed);
 
     /**
      * This updates the internal time and returns true if the animation needs
@@ -100,11 +102,17 @@
     Snapshot decodeNextFrame();
     Snapshot reset();
 
+    size_t byteSize() const {
+        return sizeof(this) + mBytesUsed;
+    }
+
 protected:
     virtual void onDraw(SkCanvas* canvas) override;
 
 private:
     sk_sp<SkAnimatedImage> mSkAnimatedImage;
+    const size_t mBytesUsed;
+
     bool mRunning = false;
     bool mStarting = false;