Merge "Implement AnimatedImageDrawable#setBounds" into sc-dev
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index ebf7cea..dd64327 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -570,6 +570,13 @@
         }
     }
 
+    @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 @@
     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 638de85..0d3d3e3 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 <SkPicture.h>
 #include <SkRefCnt.h>
@@ -31,6 +32,7 @@
 AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed)
         : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) {
     mTimeToShowNextSnapshot = ms2ns(mSkAnimatedImage->currentFrameDuration());
+    setStagingBounds(mSkAnimatedImage->getBounds());
 }
 
 void AnimatedImageDrawable::syncProperties() {
@@ -127,21 +129,38 @@
     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<SkPaint> 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 @@
         // 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 @@
 }
 
 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 @@
     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 f81a5a4..8ca3c7e 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -67,9 +67,10 @@
         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 @@
     size_t byteSize() const { return sizeof(*this) + mBytesUsed; }
 
 protected:
-    virtual void onDraw(SkCanvas* canvas) override;
+    void onDraw(SkCanvas* canvas) override;
 
 private:
     sk_sp<SkAnimatedImage> mSkAnimatedImage;
@@ -145,6 +146,7 @@
         int mAlpha = SK_AlphaOPAQUE;
         sk_sp<SkColorFilter> 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 1ff1565..c9433ec8 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -244,6 +244,14 @@
     drawable->setStagingMirrored(mirrored);
 }
 
+static void AnimatedImageDrawable_nSetBounds(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+                                             jobject jrect) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(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 @@
     { "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) {