diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java index ebf7cea98a81e..dd64327ebfd8d 100644 --- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -570,6 +570,13 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { } } + @Override + protected void onBoundsChange(Rect bounds) { + if (mState.mNativePtr != 0) { + nSetBounds(mState.mNativePtr, bounds); + } + } + private static native long nCreate(long nativeImageDecoder, @Nullable ImageDecoder decoder, int width, int height, long colorSpaceHandle, @@ -601,4 +608,6 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { private static native long nNativeByteSize(long nativePtr); @FastNative private static native void nSetMirrored(long nativePtr, boolean mirror); + @FastNative + private static native void nSetBounds(long nativePtr, Rect rect); } diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp index 638de850a6c54..0d3d3e3f38fdf 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.cpp +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -20,6 +20,7 @@ #endif #include "utils/TraceUtils.h" +#include "pipeline/skia/SkiaUtils.h" #include #include @@ -31,6 +32,7 @@ namespace android { AnimatedImageDrawable::AnimatedImageDrawable(sk_sp animatedImage, size_t bytesUsed) : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) { mTimeToShowNextSnapshot = ms2ns(mSkAnimatedImage->currentFrameDuration()); + setStagingBounds(mSkAnimatedImage->getBounds()); } void AnimatedImageDrawable::syncProperties() { @@ -127,21 +129,38 @@ AnimatedImageDrawable::Snapshot AnimatedImageDrawable::reset() { return snap; } +// Update the matrix to map from the intrinsic bounds of the SkAnimatedImage to +// the bounds specified by Drawable#setBounds. +static void handleBounds(SkMatrix* matrix, const SkRect& intrinsicBounds, const SkRect& bounds) { + matrix->preTranslate(bounds.left(), bounds.top()); + matrix->preScale(bounds.width() / intrinsicBounds.width(), + bounds.height() / intrinsicBounds.height()); +} + // Only called on the RenderThread. void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { + // Store the matrix used to handle bounds and mirroring separate from the + // canvas. We may need to invert the matrix to determine the proper bounds + // to pass to saveLayer, and this matrix (as opposed to, potentially, the + // canvas' matrix) only uses scale and translate, so it must be invertible. + SkMatrix matrix; + SkAutoCanvasRestore acr(canvas, true); + handleBounds(&matrix, mSkAnimatedImage->getBounds(), mProperties.mBounds); + + if (mProperties.mMirrored) { + matrix.preTranslate(mSkAnimatedImage->getBounds().width(), 0); + matrix.preScale(-1, 1); + } + std::optional lazyPaint; - SkAutoCanvasRestore acr(canvas, false); if (mProperties.mAlpha != SK_AlphaOPAQUE || mProperties.mColorFilter.get()) { lazyPaint.emplace(); lazyPaint->setAlpha(mProperties.mAlpha); lazyPaint->setColorFilter(mProperties.mColorFilter); lazyPaint->setFilterQuality(kLow_SkFilterQuality); } - if (mProperties.mMirrored) { - canvas->save(); - canvas->translate(mSkAnimatedImage->getBounds().width(), 0); - canvas->scale(-1, 1); - } + + canvas->concat(matrix); const bool starting = mStarting; mStarting = false; @@ -151,7 +170,11 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { // The image is not animating, and never was. Draw directly from // mSkAnimatedImage. if (lazyPaint) { - canvas->saveLayer(mSkAnimatedImage->getBounds(), &*lazyPaint); + SkMatrix inverse; + (void) matrix.invert(&inverse); + SkRect r = mProperties.mBounds; + inverse.mapRect(&r); + canvas->saveLayer(r, &*lazyPaint); } std::unique_lock lock{mImageLock}; @@ -211,17 +234,31 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { } int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) { - SkAutoCanvasRestore acr(canvas, false); + // Store the matrix used to handle bounds and mirroring separate from the + // canvas. We may need to invert the matrix to determine the proper bounds + // to pass to saveLayer, and this matrix (as opposed to, potentially, the + // canvas' matrix) only uses scale and translate, so it must be invertible. + SkMatrix matrix; + SkAutoCanvasRestore acr(canvas, true); + handleBounds(&matrix, mSkAnimatedImage->getBounds(), mStagingProperties.mBounds); + + if (mStagingProperties.mMirrored) { + matrix.preTranslate(mSkAnimatedImage->getBounds().width(), 0); + matrix.preScale(-1, 1); + } + + canvas->concat(matrix); + if (mStagingProperties.mAlpha != SK_AlphaOPAQUE || mStagingProperties.mColorFilter.get()) { SkPaint paint; paint.setAlpha(mStagingProperties.mAlpha); paint.setColorFilter(mStagingProperties.mColorFilter); - canvas->saveLayer(mSkAnimatedImage->getBounds(), &paint); - } - if (mStagingProperties.mMirrored) { - canvas->save(); - canvas->translate(mSkAnimatedImage->getBounds().width(), 0); - canvas->scale(-1, 1); + + SkMatrix inverse; + (void) matrix.invert(&inverse); + SkRect r = mStagingProperties.mBounds; + inverse.mapRect(&r); + canvas->saveLayer(r, &paint); } if (!mRunning) { @@ -294,4 +331,10 @@ int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) { return ns2ms(mTimeToShowNextSnapshot - mCurrentTime); } +SkRect AnimatedImageDrawable::onGetBounds() { + // This must return a bounds that is valid for all possible states, + // including if e.g. the client calls setBounds. + return SkRectMakeLargest(); +} + } // namespace android diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h index f81a5a40b44e3..8ca3c7e125f13 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.h +++ b/libs/hwui/hwui/AnimatedImageDrawable.h @@ -67,9 +67,10 @@ public: mStagingProperties.mColorFilter = filter; } void setStagingMirrored(bool mirrored) { mStagingProperties.mMirrored = mirrored; } + void setStagingBounds(const SkRect& bounds) { mStagingProperties.mBounds = bounds; } void syncProperties(); - virtual SkRect onGetBounds() override { return mSkAnimatedImage->getBounds(); } + SkRect onGetBounds() override; // Draw to software canvas, and return time to next draw. // 0 means the animation is not running. @@ -109,7 +110,7 @@ public: size_t byteSize() const { return sizeof(*this) + mBytesUsed; } protected: - virtual void onDraw(SkCanvas* canvas) override; + void onDraw(SkCanvas* canvas) override; private: sk_sp mSkAnimatedImage; @@ -145,6 +146,7 @@ private: int mAlpha = SK_AlphaOPAQUE; sk_sp mColorFilter; bool mMirrored = false; + SkRect mBounds; Properties() = default; Properties(Properties&) = default; diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp index 1ff156593c41e..c9433ec8a9dae 100644 --- a/libs/hwui/jni/AnimatedImageDrawable.cpp +++ b/libs/hwui/jni/AnimatedImageDrawable.cpp @@ -244,6 +244,14 @@ static void AnimatedImageDrawable_nSetMirrored(JNIEnv* env, jobject /*clazz*/, j drawable->setStagingMirrored(mirrored); } +static void AnimatedImageDrawable_nSetBounds(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jobject jrect) { + auto* drawable = reinterpret_cast(nativePtr); + SkRect rect; + GraphicsJNI::jrect_to_rect(env, jrect, &rect); + drawable->setStagingBounds(rect); +} + static const JNINativeMethod gAnimatedImageDrawableMethods[] = { { "nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",(void*) AnimatedImageDrawable_nCreate }, { "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer }, @@ -259,6 +267,7 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = { { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener }, { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize }, { "nSetMirrored", "(JZ)V", (void*) AnimatedImageDrawable_nSetMirrored }, + { "nSetBounds", "(JLandroid/graphics/Rect;)V", (void*) AnimatedImageDrawable_nSetBounds }, }; int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {