diff --git a/frameworks/innerkitsimpl/codec/src/image_source.cpp b/frameworks/innerkitsimpl/codec/src/image_source.cpp index 87099cc3c28dac3cbc89614a97bab7145a7fff0c..ac1f544e0e4e3e28d838711622ec1107b384ba75 100644 --- a/frameworks/innerkitsimpl/codec/src/image_source.cpp +++ b/frameworks/innerkitsimpl/codec/src/image_source.cpp @@ -255,6 +255,7 @@ const static std::map ORIENTATION_INT_MAP = { }; const static string IMAGE_DELAY_TIME = "DelayTime"; const static string IMAGE_DISPOSAL_TYPE = "DisposalType"; +const static string IMAGE_GIFLOOPCOUNT_TYPE = "GIFLoopCount"; const static int32_t ZERO = 0; static void UpdatepPlImageInfo(DecodeContext context, bool isHdr, ImagePlugin::PlImageInfo &plInfo); @@ -1402,6 +1403,26 @@ uint32_t ImageSource::GetImagePropertyInt(uint32_t index, const std::string &key uint32_t ImageSource::GetImagePropertyString(uint32_t index, const std::string &key, std::string &value) { + if (key.empty()) { + return Media::ERR_IMAGE_DECODE_EXIF_UNSUPPORT; + } + uint32_t ret = SUCCESS; + if (IMAGE_GIFLOOPCOUNT_TYPE.compare(key) == ZERO) { + IMAGE_LOGD("GetImagePropertyString special key: %{public}s", key.c_str()); + (void)GetFrameCount(ret); + if (ret != SUCCESS || mainDecoder_ == nullptr) { + IMAGE_LOGE("[ImageSource]GetFrameCount get frame sum error."); + return ret; + } else { + ret = mainDecoder_->GetImagePropertyString(index, key, value); + if (ret != SUCCESS) { + IMAGE_LOGE("[ImageSource]GetLoopCount get loop count issue. errorCode=%{public}u", ret); + return ret; + } + } + return ret; + } + std::unique_lock guard(decodingMutex_); return GetImagePropertyCommon(index, key, value); } @@ -2763,6 +2784,27 @@ unique_ptr> ImageSource::GetDisposalType(uint32_t &errorCode) return disposalTypes; } +int32_t ImageSource::GetLoopCount(uint32_t &errorCode) +{ + (void)GetFrameCount(errorCode); + if (errorCode != SUCCESS || mainDecoder_ == nullptr) { + IMAGE_LOGE("[ImageSource]GetLoopCount get frame sum error."); + return errorCode; + } + + int32_t loopCount = 0; + const string IMAGE_LOOP_COUNT = "GIFLoopCount"; + errorCode = mainDecoder_->GetImagePropertyInt(0, IMAGE_LOOP_COUNT, loopCount); + if (errorCode != SUCCESS) { + IMAGE_LOGE("[ImageSource]GetLoopCount get loop count issue. errorCode=%{public}u", errorCode); + return errorCode; + } + + errorCode = SUCCESS; + + return loopCount; +} + uint32_t ImageSource::GetFrameCount(uint32_t &errorCode) { uint32_t frameCount = GetSourceInfo(errorCode).topLevelImageNum; diff --git a/frameworks/innerkitsimpl/test/unittest/image_source_test/image_source_gif_ex_test.cpp b/frameworks/innerkitsimpl/test/unittest/image_source_test/image_source_gif_ex_test.cpp index b4b25fd53088a7d02e7216c8284423113f189f17..a8e3a598f6ec8a4508df8c8d7290ab0d5666e991 100644 --- a/frameworks/innerkitsimpl/test/unittest/image_source_test/image_source_gif_ex_test.cpp +++ b/frameworks/innerkitsimpl/test/unittest/image_source_test/image_source_gif_ex_test.cpp @@ -40,6 +40,12 @@ static const std::string TEST_FILE_MULTI_FRAME_GIF = "moving_test.gif"; static const size_t TEST_FILE_MULTI_FRAME_GIF_FRAME_COUNT = 3; static const std::string TEST_FILE_JPG = "test.jpg"; static const size_t TEST_FILE_JPG_FRAME_COUNT = 1; +static const std::string TEST_FILE_MULTI_FRAME_LOOP0_GIF = "moving_test_loop0.gif"; +static const std::string TEST_FILE_MULTI_FRAME_LOOP1_GIF = "moving_test_loop1.gif"; +static const std::string TEST_FILE_MULTI_FRAME_LOOP5_GIF = "moving_test_loop5.gif"; +static const int32_t TEST_FILE_MULTI_FRAME_GIF_LOOP_COUNT_1 = 1; +static const int32_t TEST_FILE_MULTI_FRAME_GIF_LOOP_COUNT_5 = 5; +static const int32_t TEST_FILE_MULTI_FRAME_GIF_LOOP_COUNT_INF = 0; } class ImageSourceGifExTest : public testing::Test { @@ -420,5 +426,121 @@ HWTEST_F(ImageSourceGifExTest, GetEncodedFormat001, TestSize.Level3) GTEST_LOG_(INFO) << "ImageSourceGifExTest: imageInfo2 encodedFormat " << imageInfo2.encodedFormat; GTEST_LOG_(INFO) << "ImageSourceGifExTest: GetEncodedFormat001 end"; } + +/** + * @tc.name: GetLoopCount001 + * @tc.desc: test GetLoopCount + * @tc.type: FUNC + */ +HWTEST_F(ImageSourceGifExTest, GetLoopCount001, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "ImageSourceGifExTest: GetLoopCount001 start"; + + const std::string testName = TEST_FILE_MULTI_FRAME_LOOP5_GIF; + + uint32_t errorCode = 0; + const SourceOptions opts; + const std::string inputName = INPUT_PATH + testName; + auto imageSource = ImageSource::CreateImageSource(inputName, opts, errorCode); + ASSERT_NE(imageSource, nullptr); + + auto loopCount = imageSource->GetLoopCount(errorCode); + ASSERT_EQ(loopCount, TEST_FILE_MULTI_FRAME_GIF_LOOP_COUNT_5); + + GTEST_LOG_(INFO) << "ImageSourceGifExTest: GetLoopCount001 end"; +} + +/** + * @tc.name: GetLoopCount002 + * @tc.desc: test GetLoopCount + * @tc.type: FUNC + */ +HWTEST_F(ImageSourceGifExTest, GetLoopCount002, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "ImageSourceGifExTest: GetLoopCount002 start"; + + const std::string testName = TEST_FILE_MULTI_FRAME_LOOP1_GIF; + + uint32_t errorCode = 0; + const SourceOptions opts; + const std::string inputName = INPUT_PATH + testName; + auto imageSource = ImageSource::CreateImageSource(inputName, opts, errorCode); + ASSERT_NE(imageSource, nullptr); + + auto loopCount = imageSource->GetLoopCount(errorCode); + ASSERT_EQ(loopCount, TEST_FILE_MULTI_FRAME_GIF_LOOP_COUNT_1); + + GTEST_LOG_(INFO) << "ImageSourceGifExTest: GetLoopCount002 end"; +} + +/** + * @tc.name: GetLoopCount003 + * @tc.desc: test GetLoopCount + * @tc.type: FUNC + */ +HWTEST_F(ImageSourceGifExTest, GetLoopCount003, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "ImageSourceGifExTest: GetLoopCount003 start"; + + const std::string testName = TEST_FILE_MULTI_FRAME_LOOP0_GIF; + + uint32_t errorCode = 0; + const SourceOptions opts; + const std::string inputName = INPUT_PATH + testName; + auto imageSource = ImageSource::CreateImageSource(inputName, opts, errorCode); + ASSERT_NE(imageSource, nullptr); + + auto loopCount = imageSource->GetLoopCount(errorCode); + ASSERT_EQ(loopCount, TEST_FILE_MULTI_FRAME_GIF_LOOP_COUNT_INF); + + GTEST_LOG_(INFO) << "ImageSourceGifExTest: GetLoopCount003 end"; +} + +/** + * @tc.name: GetLoopCount004 + * @tc.desc: test GetLoopCount + * @tc.type: FUNC + */ +HWTEST_F(ImageSourceGifExTest, GetLoopCount004, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "ImageSourceGifExTest: GetLoopCount004 start"; + + const std::string testName = TEST_FILE_MULTI_FRAME_GIF; + + uint32_t errorCode = 0; + const SourceOptions opts; + const std::string inputName = INPUT_PATH + testName; + auto imageSource = ImageSource::CreateImageSource(inputName, opts, errorCode); + ASSERT_NE(imageSource, nullptr); + + auto loopCount = imageSource->GetLoopCount(errorCode); + ASSERT_EQ(loopCount, TEST_FILE_MULTI_FRAME_GIF_LOOP_COUNT_INF); + + GTEST_LOG_(INFO) << "ImageSourceGifExTest: GetLoopCount004 end"; +} + +/** + * @tc.name: GetLoopCount005 + * @tc.desc: test GetLoopCount + * @tc.type: FUNC + */ +HWTEST_F(ImageSourceGifExTest, GetLoopCount005, TestSize.Level3) +{ + GTEST_LOG_(INFO) << "ImageSourceGifExTest: GetLoopCount005 start"; + + const std::string testName = TEST_FILE_JPG; + + uint32_t errorCode = 0; + const SourceOptions opts; + const std::string inputName = INPUT_PATH + testName; + auto imageSource = ImageSource::CreateImageSource(inputName, opts, errorCode); + ASSERT_NE(imageSource, nullptr); + + imageSource->GetLoopCount(errorCode); + + ASSERT_EQ(errorCode, ERR_MEDIA_INVALID_PARAM); + + GTEST_LOG_(INFO) << "ImageSourceGifExTest: GetLoopCount005 end"; +} } // namespace Multimedia } // namespace OHOS \ No newline at end of file diff --git a/frameworks/kits/js/common/image_source_napi.cpp b/frameworks/kits/js/common/image_source_napi.cpp index 4449ae271d2f1cb5c7e3111e2c8c713745df6dd4..e6e0355fecc677ee02e21c3b1222614fdf87b128 100644 --- a/frameworks/kits/js/common/image_source_napi.cpp +++ b/frameworks/kits/js/common/image_source_napi.cpp @@ -188,6 +188,7 @@ static std::vector sPropertyKeyMap = { {"SCENE_TEXT_CONF", 0, "HwMnoteSceneTextConf"}, {"FACE_COUNT", 0, "HwMnoteFaceCount"}, {"FOCUS_MODE", 0, "HwMnoteFocusMode"}, + {"GIF_LOOP_COUNT", 0, "GIFLoopCount"}, }; static std::vector sImageFormatMap = { {"YCBCR_422_SP", 1000, ""}, diff --git a/interfaces/innerkits/include/image_source.h b/interfaces/innerkits/include/image_source.h index a4be89fbcef2d5ebebcd7708ee135da1ced03f76..9760ae1068ac34baea5366035c7b365fb88f2a1e 100644 --- a/interfaces/innerkits/include/image_source.h +++ b/interfaces/innerkits/include/image_source.h @@ -216,6 +216,7 @@ public: uint32_t &errorCode); NATIVEEXPORT std::unique_ptr> GetDelayTime(uint32_t &errorCode); NATIVEEXPORT std::unique_ptr> GetDisposalType(uint32_t &errorCode); + NATIVEEXPORT int32_t GetLoopCount(uint32_t &errorCode); NATIVEEXPORT uint32_t GetFrameCount(uint32_t &errorCode); #ifdef IMAGE_PURGEABLE_PIXELMAP NATIVEEXPORT size_t GetSourceSize() const; diff --git a/interfaces/kits/native/include/image/image_common.h b/interfaces/kits/native/include/image/image_common.h index a234a05002a00b6bd9d7c9ce27ce79572c950580..28852fc290caea75f51170e7d1555afa195763c4 100644 --- a/interfaces/kits/native/include/image/image_common.h +++ b/interfaces/kits/native/include/image/image_common.h @@ -1258,6 +1258,14 @@ static const char *OHOS_IMAGE_PROPERTY_SCENE_POINTER = "HwMnoteScenePointer"; * @since 12 */ static const char *OHOS_IMAGE_PROPERTY_SCENE_VERSION = "HwMnoteSceneVersion"; + +/** + * @brief Scene Version + * It is used in {@link OH_ImageSource_GetImageProperty} and {@link OH_ImageSource_ModifyImageProperty}. + * + * @since 12 + */ +static const char *OHOS_IMAGE_PROPERTY_GIF_LOOP_COUNT = "GIFLoopCount"; #ifdef __cplusplus }; #endif diff --git a/plugins/common/libs/image/libextplugin/src/ext_decoder.cpp b/plugins/common/libs/image/libextplugin/src/ext_decoder.cpp index ca181cea144ed3ccb7c41c59f76ee919df7e9a75..204878759600f00a253ed53caf26b2f626233366 100644 --- a/plugins/common/libs/image/libextplugin/src/ext_decoder.cpp +++ b/plugins/common/libs/image/libextplugin/src/ext_decoder.cpp @@ -73,6 +73,9 @@ namespace { constexpr static float ONE_EIGHTH = 0.125; constexpr static uint64_t ICC_HEADER_SIZE = 132; constexpr static size_t SMALL_FILE_SIZE = 1000 * 1000 * 10; + constexpr static int32_t LOOP_COUNT_INFINITE = 0; + constexpr static int32_t SK_REPETITION_COUNT_INFINITE = -1; + constexpr static int32_t SK_REPETITION_COUNT_ERROR_VALUE = -2; } namespace OHOS { @@ -92,6 +95,7 @@ const static string TAG_ORIENTATION_STRING = "Orientation"; const static string TAG_ORIENTATION_INT = "OrientationInt"; const static string IMAGE_DELAY_TIME = "DelayTime"; const static string IMAGE_DISPOSAL_TYPE = "DisposalType"; +const static string IMAGE_LOOP_COUNT = "GIFLoopCount"; const static std::string HW_MNOTE_TAG_HEADER = "HwMnote"; const static std::string HW_MNOTE_CAPTURE_MODE = "HwMnoteCaptureMode"; const static std::string HW_MNOTE_PHYSICAL_APERTURE = "HwMnotePhysicalAperture"; @@ -1393,6 +1397,24 @@ static uint32_t GetDisposalType(SkCodec * codec, uint32_t index, int32_t &value) return SUCCESS; } +static uint32_t GetLoopCount(SkCodec *codec, int32_t &value) +{ + if (codec->getEncodedFormat() != SkEncodedImageFormat::kGIF) { + IMAGE_LOGE("[GetLoopCount] Should not get loop count in %{public}d", codec->getEncodedFormat()); + return ERR_MEDIA_INVALID_PARAM; + } + auto count = codec->getRepetitionCount(); + if (count == LOOP_COUNT_INFINITE || count <= SK_REPETITION_COUNT_ERROR_VALUE) { + IMAGE_LOGE("[GetLoopCount] getRepetitionCount error"); + return ERR_IMAGE_SOURCE_DATA; + } + if (count == SK_REPETITION_COUNT_INFINITE) { + count = LOOP_COUNT_INFINITE; + } + value = static_cast(count); + return SUCCESS; +} + uint32_t ExtDecoder::GetImagePropertyInt(uint32_t index, const std::string &key, int32_t &value) { IMAGE_LOGD("[GetImagePropertyInt] enter ExtDecoder plugin, key:%{public}s", key.c_str()); @@ -1406,6 +1428,9 @@ uint32_t ExtDecoder::GetImagePropertyInt(uint32_t index, const std::string &key, if (IMAGE_DISPOSAL_TYPE.compare(key) == ZERO) { return GetDisposalType(codec_.get(), index, value); } + if (IMAGE_LOOP_COUNT.compare(key) == ZERO) { + return GetLoopCount(codec_.get(), value); + } // There can add some not need exif property if (res == Media::ERR_IMAGE_DECODE_EXIF_UNSUPPORT) { return res; @@ -1445,6 +1470,11 @@ uint32_t ExtDecoder::GetImagePropertyString(uint32_t index, const std::string &k res = GetDisposalType(codec_.get(), index, disposalType); value = std::to_string(disposalType); return res; + } else if (IMAGE_LOOP_COUNT.compare(key) == ZERO) { + int loopCount = ZERO; + res = GetLoopCount(codec_.get(), loopCount); + value = std::to_string(loopCount); + return res; } if (res == Media::ERR_IMAGE_DECODE_EXIF_UNSUPPORT) { return res; diff --git a/test/resource/image/images/moving_test_loop0.gif b/test/resource/image/images/moving_test_loop0.gif new file mode 100644 index 0000000000000000000000000000000000000000..6643f465ae71b561e337349063f9fa13500ad4bf Binary files /dev/null and b/test/resource/image/images/moving_test_loop0.gif differ diff --git a/test/resource/image/images/moving_test_loop1.gif b/test/resource/image/images/moving_test_loop1.gif new file mode 100644 index 0000000000000000000000000000000000000000..ccfb06616aa6e45472ae9d66a22b73a69fdeef15 Binary files /dev/null and b/test/resource/image/images/moving_test_loop1.gif differ diff --git a/test/resource/image/images/moving_test_loop5.gif b/test/resource/image/images/moving_test_loop5.gif new file mode 100644 index 0000000000000000000000000000000000000000..a6402560e4ffb02c2a0fe1091e31a7eaea5342ea Binary files /dev/null and b/test/resource/image/images/moving_test_loop5.gif differ diff --git a/test/resource/image/ohos_test.xml b/test/resource/image/ohos_test.xml index 1a254fbc3eb2d05ab992e5e21b98e6e91233f8a5..bee0e6d693a4f06efcdc9d2d7a9c683cf5add2e3 100644 --- a/test/resource/image/ohos_test.xml +++ b/test/resource/image/ohos_test.xml @@ -79,6 +79,9 @@