From eb3b38e22cb29c6a7fccb7031263f50663cc76d0 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Tue, 20 Mar 2018 11:11:13 -0400 Subject: [PATCH] Report native allocation size of AnimatedImageDrawable Bug: 73641604 Test: infeasible Fix nNativeByteSize's return value to be jlong, instead of long. Add up the bytes used by the SkAnimatedImage and SkPictures and store them on the AnimatedImageDrawable for registration. Note that this is an approximation, and it assumes it will be drawn to a hardware canvas and animated. --- .../graphics/AnimatedImageDrawable.cpp | 41 ++++++++++++++++--- .../drawable/AnimatedImageDrawable.java | 3 +- libs/hwui/hwui/AnimatedImageDrawable.cpp | 4 +- libs/hwui/hwui/AnimatedImageDrawable.h | 10 ++++- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp index d6496cdf499af..7166c757e602c 100644 --- a/core/jni/android/graphics/AnimatedImageDrawable.cpp +++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp @@ -42,7 +42,6 @@ static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, } auto* imageDecoder = reinterpret_cast(nativeImageDecoder); - auto info = imageDecoder->mCodec->getInfo(); const SkISize scaledSize = SkISize::Make(width, height); SkIRect subset; if (jsubset) { @@ -51,6 +50,35 @@ static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, 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 picture; if (jpostProcess) { SkRect bounds = SkRect::MakeWH(subset.width(), subset.height()); @@ -63,6 +91,7 @@ static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, return 0; } picture = recorder.finishRecordingAsPicture(); + bytesUsed += picture->approximateBytesUsed(); } @@ -74,7 +103,10 @@ static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, return 0; } - sk_sp drawable(new AnimatedImageDrawable(animatedImg)); + bytesUsed += sizeof(animatedImg.get()); + + sk_sp drawable(new AnimatedImageDrawable(std::move(animatedImg), + bytesUsed)); return reinterpret_cast(drawable.release()); } @@ -202,10 +234,9 @@ static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobjec } } -static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { +static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { auto* drawable = reinterpret_cast(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 c0f49208e27e1..a47ecf517c70b 100644 --- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -292,8 +292,7 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { 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 28d0bc42ad2f5..c529f8773bbd2 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.cpp +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -26,8 +26,8 @@ namespace android { -AnimatedImageDrawable::AnimatedImageDrawable(sk_sp animatedImage) - : mSkAnimatedImage(std::move(animatedImage)) { +AnimatedImageDrawable::AnimatedImageDrawable(sk_sp 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 f4e2ba751b703..a92b62db14f0a 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.h +++ b/libs/hwui/hwui/AnimatedImageDrawable.h @@ -45,7 +45,9 @@ public: */ class ANDROID_API AnimatedImageDrawable : public SkDrawable { public: - AnimatedImageDrawable(sk_sp animatedImage); + // bytesUsed includes the approximate sizes of the SkAnimatedImage and the SkPictures in the + // Snapshots. + AnimatedImageDrawable(sk_sp animatedImage, size_t bytesUsed); /** * This updates the internal time and returns true if the animation needs @@ -100,11 +102,17 @@ public: Snapshot decodeNextFrame(); Snapshot reset(); + size_t byteSize() const { + return sizeof(this) + mBytesUsed; + } + protected: virtual void onDraw(SkCanvas* canvas) override; private: sk_sp mSkAnimatedImage; + const size_t mBytesUsed; + bool mRunning = false; bool mStarting = false;