Implement AImageDecoder_setInternallyHandleDisposePrevious

Bug: 160984428
Test: I00682f201a52f894b0e1335c00c4368ce675a805

Also fix a bug caught by the new test. If the current frame is the first
in a series of one or more RestorePrevious frames, fPriorFrame should be
set to |currentFrame - 1|. Otherwise SkCodec will decode the required
frame. This is wasted work, since the prior frame should already be
prepared (either by AImageDecoder or by the client).

Change-Id: I1fb9f91dc66fd3121f187b9a91c15f625eb17f8d
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index b3f7627..764bc4c 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -52,6 +52,7 @@
     , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType))
     , mUnpremultipliedRequired(false)
     , mOutColorSpace(getDefaultColorSpace())
+    , mHandleRestorePrevious(true)
 {
     mTargetSize = swapWidthHeight() ? SkISize { mDecodeSize.height(), mDecodeSize.width() }
                                     : mDecodeSize;
@@ -230,6 +231,13 @@
     return true;
 }
 
+void ImageDecoder::setHandleRestorePrevious(bool handle) {
+    mHandleRestorePrevious = handle;
+    if (!handle) {
+        mRestoreFrame = nullptr;
+    }
+}
+
 bool ImageDecoder::advanceFrame() {
     const int frameIndex = ++mOptions.fFrameIndex;
     const int frameCount = mCodec->codec()->getFrameCount();
@@ -255,6 +263,7 @@
             case RestoreState::kDoNothing:
             case RestoreState::kNeedsRestore:
                 mRestoreState = RestoreState::kFirstRPFrame;
+                mOptions.fPriorFrame = frameIndex - 1;
                 break;
             case RestoreState::kFirstRPFrame:
                 mRestoreState = RestoreState::kRPFrame;
@@ -315,29 +324,19 @@
     return mOptions.fFrameIndex >= mCodec->codec()->getFrameCount();
 }
 
-SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) {
-    // This was checked inside setTargetSize, but it's possible the first frame
-    // was opaque, so that method succeeded, but after calling advanceFrame, the
-    // current frame is not opaque.
-    if (mUnpremultipliedRequired && !opaque()) {
-        // Allow using a matrix to handle orientation, but not scaling.
-        if (requires_matrix_scaling(swapWidthHeight(), mDecodeSize, mTargetSize)) {
-            return SkCodec::kInvalidScale;
-        }
+bool ImageDecoder::handleRestorePrevious(const SkImageInfo& outputInfo, void* pixels,
+                                         size_t rowBytes) {
+    if (!mHandleRestorePrevious) {
+        return true;
     }
 
-    void* decodePixels = pixels;
-    size_t decodeRowBytes = rowBytes;
-    const auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(),
-                                              getOutputColorSpace());
-    const auto outputInfo = getOutputInfo();
     switch (mRestoreState) {
         case RestoreState::kFirstRPFrame:{
             // This frame is marked kRestorePrevious. The prior frame should be in
             // |pixels|, and it is what we'll restore after each consecutive
             // kRestorePrevious frame. Cache it now.
             if (!(mRestoreFrame = Bitmap::allocateHeapBitmap(outputInfo))) {
-                return SkCodec::kInternalError;
+                return false;
             }
 
             const uint8_t* srcRow = static_cast<uint8_t*>(pixels);
@@ -366,7 +365,29 @@
         case RestoreState::kDoNothing:
             break;
     }
+    return true;
+}
 
+SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) {
+    // This was checked inside setTargetSize, but it's possible the first frame
+    // was opaque, so that method succeeded, but after calling advanceFrame, the
+    // current frame is not opaque.
+    if (mUnpremultipliedRequired && !opaque()) {
+        // Allow using a matrix to handle orientation, but not scaling.
+        if (requires_matrix_scaling(swapWidthHeight(), mDecodeSize, mTargetSize)) {
+            return SkCodec::kInvalidScale;
+        }
+    }
+
+    const auto outputInfo = getOutputInfo();
+    if (!handleRestorePrevious(outputInfo, pixels, rowBytes)) {
+        return SkCodec::kInternalError;
+    }
+
+    void* decodePixels = pixels;
+    size_t decodeRowBytes = rowBytes;
+    const auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(),
+                                              getOutputColorSpace());
     // Used if we need a temporary before scaling or subsetting.
     // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
     SkBitmap tmp;
diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h
index 90261b1..1b309bc 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -73,6 +73,9 @@
 
     SkCodec::FrameInfo getCurrentFrameInfo();
 
+    // Set whether the ImageDecoder should handle RestorePrevious frames.
+    void setHandleRestorePrevious(bool handle);
+
 private:
     // State machine for keeping track of how to handle RestorePrevious (RP)
     // frames in decode().
@@ -105,6 +108,7 @@
     SkAndroidCodec::AndroidOptions mOptions;
     bool mCurrentFrameIsIndependent;
     bool mCurrentFrameIsOpaque;
+    bool mHandleRestorePrevious;
     RestoreState mRestoreState;
     sk_sp<Bitmap> mRestoreFrame;
     std::optional<SkIRect> mCropRect;
@@ -115,6 +119,8 @@
     SkAlphaType getOutAlphaType() const;
     sk_sp<SkColorSpace> getOutputColorSpace() const;
     bool swapWidthHeight() const;
+    // Store/restore a frame if necessary. Returns false on error.
+    bool handleRestorePrevious(const SkImageInfo&, void* pixels, size_t rowBytes);
 };
 
 } // namespace android
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 0f61907..5973790 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -535,3 +535,9 @@
             return ANDROID_IMAGE_DECODER_BLEND_OP_SRC_OVER;
     }
 }
+
+void AImageDecoder_setInternallyHandleDisposePrevious(AImageDecoder* decoder, bool handle) {
+    if (decoder) {
+        toDecoder(decoder)->setHandleRestorePrevious(handle);
+    }
+}
diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt
index d8c3cef..e0df794 100644
--- a/native/graphics/jni/libjnigraphics.map.txt
+++ b/native/graphics/jni/libjnigraphics.map.txt
@@ -19,6 +19,7 @@
     AImageDecoder_advanceFrame; # introduced=31
     AImageDecoder_rewind; # introduced=31
     AImageDecoder_getFrameInfo; # introduced = 31
+    AImageDecoder_setInternallyHandleDisposePrevious; # introduced = 31
     AImageDecoderHeaderInfo_getWidth; # introduced=30
     AImageDecoderHeaderInfo_getHeight; # introduced=30
     AImageDecoderHeaderInfo_getMimeType; # introduced=30