Implement FrameInfo methods on AImageDecoder

Bug: 160984428
Test: If47d475233f6b9973abf68029b63a610ff47cdae

- AImageDecoder_getFrameInfo
- AImageDecoderFrameInfo_create
- AImageDecoderFrameInfo_delete
- AImageDecoderFrameInfo_getDuration
- AImageDecoderFrameInfo_getFrameRect
- AImageDecoderFrameInfo_getDisposeOp
- AImageDecoderFrameInfo_getBlendOp
- AImageDecoderFrameInfo_hasAlphaWithinBounds

These allow querying for information specific to a single frame in an
encoded image.

Change-Id: I6ce5665e9c25aed23f99ce88290e520d68fcb60e
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index b713f88..b3f7627 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -282,6 +282,35 @@
     return true;
 }
 
+SkCodec::FrameInfo ImageDecoder::getCurrentFrameInfo() {
+    LOG_ALWAYS_FATAL_IF(finished());
+
+    auto dims = mCodec->codec()->dimensions();
+    SkCodec::FrameInfo info;
+    if (!mCodec->codec()->getFrameInfo(mOptions.fFrameIndex, &info)) {
+        // SkCodec may return false for a non-animated image. Provide defaults.
+        info.fRequiredFrame = SkCodec::kNoFrame;
+        info.fDuration = 0;
+        info.fFullyReceived = true;
+        info.fAlphaType = mCodec->codec()->getInfo().alphaType();
+        info.fHasAlphaWithinBounds = info.fAlphaType != kOpaque_SkAlphaType;
+        info.fDisposalMethod = SkCodecAnimation::DisposalMethod::kKeep;
+        info.fBlend = SkCodecAnimation::Blend::kSrc;
+        info.fFrameRect = SkIRect::MakeSize(dims);
+    }
+
+    if (auto origin = mCodec->codec()->getOrigin(); origin != kDefault_SkEncodedOrigin) {
+        if (SkEncodedOriginSwapsWidthHeight(origin)) {
+            dims = swapped(dims);
+        }
+        auto matrix = SkEncodedOriginToMatrix(origin, dims.width(), dims.height());
+        auto rect = SkRect::Make(info.fFrameRect);
+        LOG_ALWAYS_FATAL_IF(!matrix.mapRect(&rect));
+        rect.roundIn(&info.fFrameRect);
+    }
+    return info;
+}
+
 bool ImageDecoder::finished() const {
     return mOptions.fFrameIndex >= mCodec->codec()->getFrameCount();
 }
diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h
index 4985932..90261b1 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -71,6 +71,8 @@
     bool isAnimated();
     int currentFrame() const;
 
+    SkCodec::FrameInfo getCurrentFrameInfo();
+
 private:
     // State machine for keeping track of how to handle RestorePrevious (RP)
     // frames in decode().
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 971ba37..e3b575e 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -429,3 +429,78 @@
     return imageDecoder->rewind() ? ANDROID_IMAGE_DECODER_SUCCESS
                                   : ANDROID_IMAGE_DECODER_SEEK_ERROR;
 }
+
+AImageDecoderFrameInfo* AImageDecoderFrameInfo_create() {
+    return reinterpret_cast<AImageDecoderFrameInfo*>(new SkCodec::FrameInfo);
+}
+
+static SkCodec::FrameInfo* toFrameInfo(AImageDecoderFrameInfo* info) {
+    return reinterpret_cast<SkCodec::FrameInfo*>(info);
+}
+
+static const SkCodec::FrameInfo* toFrameInfo(const AImageDecoderFrameInfo* info) {
+    return reinterpret_cast<const SkCodec::FrameInfo*>(info);
+}
+
+void AImageDecoderFrameInfo_delete(AImageDecoderFrameInfo* info) {
+    delete toFrameInfo(info);
+}
+
+int AImageDecoder_getFrameInfo(AImageDecoder* decoder,
+        AImageDecoderFrameInfo* info) {
+    if (!decoder || !info) {
+        return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+    }
+
+    auto* imageDecoder = toDecoder(decoder);
+    if (imageDecoder->finished()) {
+        return ANDROID_IMAGE_DECODER_FINISHED;
+    }
+
+    *toFrameInfo(info) = imageDecoder->getCurrentFrameInfo();
+    return ANDROID_IMAGE_DECODER_SUCCESS;
+}
+
+int64_t AImageDecoderFrameInfo_getDuration(const AImageDecoderFrameInfo* info) {
+    if (!info) return 0;
+
+    return toFrameInfo(info)->fDuration * 1'000'000;
+}
+
+ARect AImageDecoderFrameInfo_getFrameRect(const AImageDecoderFrameInfo* info) {
+    if (!info) {
+        return { 0, 0, 0, 0};
+    }
+
+    const SkIRect& r = toFrameInfo(info)->fFrameRect;
+    return { r.left(), r.top(), r.right(), r.bottom() };
+}
+
+bool AImageDecoderFrameInfo_hasAlphaWithinBounds(const AImageDecoderFrameInfo* info) {
+    if (!info) return false;
+
+    return toFrameInfo(info)->fHasAlphaWithinBounds;
+}
+
+int32_t AImageDecoderFrameInfo_getDisposeOp(const AImageDecoderFrameInfo* info) {
+    if (!info) return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+
+    static_assert(static_cast<int>(SkCodecAnimation::DisposalMethod::kKeep)
+                  == ANDROID_IMAGE_DECODER_DISPOSE_OP_NONE);
+    static_assert(static_cast<int>(SkCodecAnimation::DisposalMethod::kRestoreBGColor)
+                  == ANDROID_IMAGE_DECODER_DISPOSE_OP_BACKGROUND);
+    static_assert(static_cast<int>(SkCodecAnimation::DisposalMethod::kRestorePrevious)
+                  == ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS);
+    return static_cast<int>(toFrameInfo(info)->fDisposalMethod);
+}
+
+int32_t AImageDecoderFrameInfo_getBlendOp(const AImageDecoderFrameInfo* info) {
+    if (!info) return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+
+    switch (toFrameInfo(info)->fBlend) {
+        case SkCodecAnimation::Blend::kSrc:
+            return ANDROID_IMAGE_DECODER_BLEND_OP_SRC;
+        case SkCodecAnimation::Blend::kSrcOver:
+            return ANDROID_IMAGE_DECODER_BLEND_OP_SRC_OVER;
+    }
+}
diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt
index bd659e0..c8f1151 100644
--- a/native/graphics/jni/libjnigraphics.map.txt
+++ b/native/graphics/jni/libjnigraphics.map.txt
@@ -17,12 +17,20 @@
     AImageDecoder_getRepeatCount; # introduced=31
     AImageDecoder_advanceFrame; # introduced=31
     AImageDecoder_rewind; # introduced=31
+    AImageDecoder_getFrameInfo; # introduced = 31
     AImageDecoderHeaderInfo_getWidth; # introduced=30
     AImageDecoderHeaderInfo_getHeight; # introduced=30
     AImageDecoderHeaderInfo_getMimeType; # introduced=30
     AImageDecoderHeaderInfo_getAlphaFlags; # introduced=30
     AImageDecoderHeaderInfo_getAndroidBitmapFormat; # introduced=30
     AImageDecoderHeaderInfo_getDataSpace; # introduced=30
+    AImageDecoderFrameInfo_create; # introduced = 31
+    AImageDecoderFrameInfo_delete; # introduced = 31
+    AImageDecoderFrameInfo_getDuration; # introduced = 31
+    AImageDecoderFrameInfo_getFrameRect; # introduced = 31
+    AImageDecoderFrameInfo_hasAlphaWithinBounds; # introduced = 31
+    AImageDecoderFrameInfo_getDisposeOp; # introduced = 31
+    AImageDecoderFrameInfo_getBlendOp; # introduced = 31
     AndroidBitmap_getInfo;
     AndroidBitmap_getDataSpace;
     AndroidBitmap_lockPixels;