From 0621313fe65f78359f5c3b604b324fb2fd8dba07 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Mon, 28 Dec 2020 15:31:48 -0500 Subject: [PATCH] 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 --- libs/hwui/hwui/ImageDecoder.cpp | 29 +++++++++ libs/hwui/hwui/ImageDecoder.h | 2 + native/graphics/jni/imagedecoder.cpp | 75 ++++++++++++++++++++++ native/graphics/jni/libjnigraphics.map.txt | 8 +++ 4 files changed, 114 insertions(+) diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index b713f88c52c9f..b3f7627b1cf7a 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -282,6 +282,35 @@ bool ImageDecoder::advanceFrame() { 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 498593240425e..90261b16e57ef 100644 --- a/libs/hwui/hwui/ImageDecoder.h +++ b/libs/hwui/hwui/ImageDecoder.h @@ -71,6 +71,8 @@ public: 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 971ba374d6418..e3b575e066d6d 100644 --- a/native/graphics/jni/imagedecoder.cpp +++ b/native/graphics/jni/imagedecoder.cpp @@ -429,3 +429,78 @@ int AImageDecoder_rewind(AImageDecoder* decoder) { return imageDecoder->rewind() ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_SEEK_ERROR; } + +AImageDecoderFrameInfo* AImageDecoderFrameInfo_create() { + return reinterpret_cast(new SkCodec::FrameInfo); +} + +static SkCodec::FrameInfo* toFrameInfo(AImageDecoderFrameInfo* info) { + return reinterpret_cast(info); +} + +static const SkCodec::FrameInfo* toFrameInfo(const AImageDecoderFrameInfo* info) { + return reinterpret_cast(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(SkCodecAnimation::DisposalMethod::kKeep) + == ANDROID_IMAGE_DECODER_DISPOSE_OP_NONE); + static_assert(static_cast(SkCodecAnimation::DisposalMethod::kRestoreBGColor) + == ANDROID_IMAGE_DECODER_DISPOSE_OP_BACKGROUND); + static_assert(static_cast(SkCodecAnimation::DisposalMethod::kRestorePrevious) + == ANDROID_IMAGE_DECODER_DISPOSE_OP_PREVIOUS); + return static_cast(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 bd659e0e9247e..c8f115178e231 100644 --- a/native/graphics/jni/libjnigraphics.map.txt +++ b/native/graphics/jni/libjnigraphics.map.txt @@ -17,12 +17,20 @@ LIBJNIGRAPHICS { 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;