From a4839616da3ddeada6d8f17cbd6883b6cdf87626 Mon Sep 17 00:00:00 2001 From: seungho Date: Wed, 1 Dec 2021 17:21:08 +0900 Subject: [PATCH] [Tizen] Refactoring Animated image loading - Open file in the first call to request frame. - In case of WebP, load one more frame in the first loop to compute exact time interval. Change-Id: I152b27c002647d4bbc39d5d085e66855a59e99b0 Signed-off-by: seungho --- dali/internal/imaging/common/gif-loading.cpp | 38 ++++++-- dali/internal/imaging/common/webp-loading.cpp | 130 +++++++++++++++++++------- dali/internal/imaging/common/webp-loading.h | 12 ++- 3 files changed, 138 insertions(+), 42 deletions(-) diff --git a/dali/internal/imaging/common/gif-loading.cpp b/dali/internal/imaging/common/gif-loading.cpp index a142447..4bffd3b 100644 --- a/dali/internal/imaging/common/gif-loading.cpp +++ b/dali/internal/imaging/common/gif-loading.cpp @@ -1239,15 +1239,19 @@ struct GifLoading::Impl public: Impl(const std::string& url, bool isLocalResource) : mUrl(url), - mLoadSucceeded(true), + mLoadSucceeded(false), mMutex() { loaderInfo.gifAccessor = nullptr; - int error; loaderInfo.fileData.fileName = mUrl.c_str(); loaderInfo.fileData.isLocalResource = isLocalResource; + } + bool LoadGifInformation() + { + int error; mLoadSucceeded = ReadHeader(loaderInfo, imageProperties, &error); + return mLoadSucceeded; } // Moveable but not copyable @@ -1286,7 +1290,10 @@ bool GifLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vecto Mutex::ScopedLock lock(mImpl->mMutex); if(!mImpl->mLoadSucceeded) { - return false; + if(!mImpl->LoadGifInformation()) + { + return false; + } } const int bufferSize = mImpl->imageProperties.w * mImpl->imageProperties.h * sizeof(uint32_t); @@ -1316,14 +1323,19 @@ Dali::Devel::PixelBuffer GifLoading::LoadFrame(uint32_t frameIndex) { int error; Dali::Devel::PixelBuffer pixelBuffer; - if(!mImpl->mLoadSucceeded) - { - return pixelBuffer; - } Mutex::ScopedLock lock(mImpl->mMutex); DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex); + // If Gif file is still not loaded, Load the information. + if(!mImpl->mLoadSucceeded) + { + if(!mImpl->LoadGifInformation()) + { + return pixelBuffer; + } + } + pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888); mImpl->loaderInfo.animated.currentFrame = 1 + (frameIndex % mImpl->loaderInfo.animated.frameCount); @@ -1338,16 +1350,28 @@ Dali::Devel::PixelBuffer GifLoading::LoadFrame(uint32_t frameIndex) ImageDimensions GifLoading::GetImageSize() const { + if(!mImpl->mLoadSucceeded) + { + DALI_LOG_ERROR("Gif file is still not loaded, this image size could not be correct value.\n"); + } return ImageDimensions(mImpl->imageProperties.w, mImpl->imageProperties.h); } uint32_t GifLoading::GetImageCount() const { + if(!mImpl->mLoadSucceeded) + { + DALI_LOG_ERROR("Gif file is still not loaded, this image count could not be correct value.\n"); + } return mImpl->loaderInfo.animated.frameCount; } uint32_t GifLoading::GetFrameInterval(uint32_t frameIndex) const { + if(!mImpl->mLoadSucceeded) + { + DALI_LOG_ERROR("Gif file is still not loaded, this frame interval could not be correct value.\n"); + } return mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10; } diff --git a/dali/internal/imaging/common/webp-loading.cpp b/dali/internal/imaging/common/webp-loading.cpp index 707821b..88a8eb5 100644 --- a/dali/internal/imaging/common/webp-loading.cpp +++ b/dali/internal/imaging/common/webp-loading.cpp @@ -57,7 +57,8 @@ namespace Debug::Filter* gWebPLoadingLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GIF_LOADING"); #endif -constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024; +static constexpr int32_t INITIAL_INDEX = -1; +static constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024; } // namespace @@ -71,11 +72,17 @@ public: mBuffer(nullptr), mBufferSize(0u), mImageSize(), - mLoadSucceeded(true) + mLoadSucceeded(false), + mIsLocalResource(isLocalResource) + { + } + + bool LoadWebPInformation() { // mFrameCount will be 1 if the input image is non-animated image or animated image with single frame. - if(ReadWebPInformation(isLocalResource)) + if(ReadWebPInformation()) { + mLoadSucceeded = true; #ifdef DALI_ANIMATED_WEBP_ENABLED WebPDataInit(&mWebPData); mWebPData.size = mBufferSize; @@ -107,12 +114,14 @@ public: mLoadSucceeded = false; DALI_LOG_ERROR("Image loading failed for: \"%s\".\n", mUrl.c_str()); } + + return mLoadSucceeded; } - bool ReadWebPInformation(bool isLocalResource) + bool ReadWebPInformation() { FILE* fp = nullptr; - if(isLocalResource) + if(mIsLocalResource) { Internal::Platform::FileReader fileReader(mUrl); fp = fileReader.GetFile(); @@ -200,7 +209,7 @@ public: std::string mUrl; std::vector mTimeStamp; - uint32_t mLoadingFrame{0}; + int32_t mLatestLoadedFrame{INITIAL_INDEX}; uint32_t mFrameCount; Mutex mMutex; // For the case the system doesn't support DALI_ANIMATED_WEBP_ENABLED @@ -208,11 +217,13 @@ public: uint32_t mBufferSize; ImageDimensions mImageSize; bool mLoadSucceeded; + bool mIsLocalResource; #ifdef DALI_ANIMATED_WEBP_ENABLED - WebPData mWebPData{0}; - WebPAnimDecoder* mWebPAnimDecoder{nullptr}; - WebPAnimInfo mWebPAnimInfo{0}; + WebPData mWebPData{0}; + WebPAnimDecoder* mWebPAnimDecoder{nullptr}; + WebPAnimInfo mWebPAnimInfo{0}; + Dali::Devel::PixelBuffer mPreLoadedFrame{}; #endif }; @@ -239,7 +250,12 @@ bool WebPLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vect for(int i = 0; i < count; ++i) { Dali::Devel::PixelBuffer pixelBuffer = LoadFrame((frameStartIndex + i) % mImpl->mFrameCount); - Dali::PixelData imageData = Devel::PixelBuffer::Convert(pixelBuffer); + if(!pixelBuffer) + { + return false; + } + + Dali::PixelData imageData = Devel::PixelBuffer::Convert(pixelBuffer); pixelData.push_back(imageData); } if(pixelData.size() != static_cast(count)) @@ -253,6 +269,15 @@ Dali::Devel::PixelBuffer WebPLoading::LoadFrame(uint32_t frameIndex) { Dali::Devel::PixelBuffer pixelBuffer; + // If WebP file is still not loaded, Load the information. + if(!mImpl->mLoadSucceeded) + { + if(!mImpl->LoadWebPInformation()) + { + return pixelBuffer; + } + } + // WebPDecodeRGBA is faster than to use demux API for loading non-animated image. // If frame count is 1, use WebPDecodeRGBA api. #ifdef DALI_WEBP_AVAILABLE @@ -303,63 +328,102 @@ Dali::Devel::PixelBuffer WebPLoading::LoadFrame(uint32_t frameIndex) DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex); - if(mImpl->mLoadingFrame > frameIndex) + if(mImpl->mPreLoadedFrame && mImpl->mLatestLoadedFrame == static_cast(frameIndex)) + { + pixelBuffer = mImpl->mPreLoadedFrame; + } + else + { + pixelBuffer = DecodeFrame(frameIndex); + } + mImpl->mPreLoadedFrame.Reset(); + + // If time stamp of next frame is unknown, load a frame more to know it. + if(frameIndex + 1 < mImpl->mWebPAnimInfo.frame_count && mImpl->mTimeStamp[frameIndex + 1] == 0u) + { + mImpl->mPreLoadedFrame = DecodeFrame(frameIndex + 1); + } + +#endif + return pixelBuffer; +} + +Dali::Devel::PixelBuffer WebPLoading::DecodeFrame(uint32_t frameIndex) +{ + Dali::Devel::PixelBuffer pixelBuffer; +#ifdef DALI_ANIMATED_WEBP_ENABLED + if(mImpl->mLatestLoadedFrame > static_cast(frameIndex)) { - mImpl->mLoadingFrame = 0; + mImpl->mLatestLoadedFrame = INITIAL_INDEX; WebPAnimDecoderReset(mImpl->mWebPAnimDecoder); } - for(; mImpl->mLoadingFrame < frameIndex; ++mImpl->mLoadingFrame) + uint8_t* frameBuffer; + int32_t timestamp; + for(; mImpl->mLatestLoadedFrame < static_cast(frameIndex);) { - uint8_t* frameBuffer; - int timestamp; WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp); - mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp; + mImpl->mTimeStamp[++mImpl->mLatestLoadedFrame] = timestamp; } - const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t); - uint8_t* frameBuffer; - int timestamp; - WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp); - pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888); memcpy(pixelBuffer.GetBuffer(), frameBuffer, bufferSize); - mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp; - mImpl->mLoadingFrame++; - if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count) - { - mImpl->mLoadingFrame = 0; - WebPAnimDecoderReset(mImpl->mWebPAnimDecoder); - } #endif return pixelBuffer; } ImageDimensions WebPLoading::GetImageSize() const { + if(!mImpl->mLoadSucceeded) + { + DALI_LOG_ERROR("WebP file is still not loaded, this image size could not be correct value.\n"); + } return mImpl->mImageSize; } uint32_t WebPLoading::GetImageCount() const { + if(!mImpl->mLoadSucceeded) + { + DALI_LOG_ERROR("WebP file is still not loaded, this image count could not be correct value.\n"); + } return mImpl->mFrameCount; } uint32_t WebPLoading::GetFrameInterval(uint32_t frameIndex) const { - // If frameIndex is above the value of ImageCount or current frame is not loading yet, return 0u. - if(frameIndex >= GetImageCount() || (frameIndex > 0 && mImpl->mTimeStamp[frameIndex - 1] > mImpl->mTimeStamp[frameIndex])) + if(!mImpl->mLoadSucceeded) + { + DALI_LOG_ERROR("WebP file is still not loaded, this frame interval could not be correct value.\n"); + } + if(frameIndex >= GetImageCount()) { + DALI_LOG_ERROR("Input frameIndex exceeded frame count of the WebP."); return 0u; } else { - if(frameIndex > 0) + int32_t interval = 0u; + if(GetImageCount() == 1u) + { + return 0u; + } + else if(frameIndex + 1 == GetImageCount()) + { + // For the interval between last frame and first frame, use last interval again. + interval = mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1]; + } + else + { + interval = mImpl->mTimeStamp[frameIndex + 1] - mImpl->mTimeStamp[frameIndex]; + } + + if(interval < 0) { - return mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1]; + return 0u; } - return mImpl->mTimeStamp[frameIndex]; + return static_cast(interval); } } diff --git a/dali/internal/imaging/common/webp-loading.h b/dali/internal/imaging/common/webp-loading.h index ecf8bbb..f3428e6 100644 --- a/dali/internal/imaging/common/webp-loading.h +++ b/dali/internal/imaging/common/webp-loading.h @@ -89,7 +89,6 @@ public: * @param[in] frameIndex The frame counter to load. Will usually be the next frame. * @return Dali::Devel::PixelBuffer The loaded PixelBuffer. If loading is fail, return empty handle. */ - Dali::Devel::PixelBuffer LoadFrame(uint32_t frameIndex) override; /** @@ -109,7 +108,7 @@ public: * * @note The frame is needed to be loaded before this function is called. * - * @return The time interval of the frame(microsecond). + * @return The time interval between frameIndex and frameIndex + 1(microsecond). */ uint32_t GetFrameInterval(uint32_t frameIndex) const override; @@ -128,6 +127,15 @@ public: bool HasLoadingSucceeded() const override; private: + /** + * @brief Decode Frame of the animated image. + * + * @param[in] frameIndex The frame counter to load. Will usually be the next frame. + * @return Dali::Devel::PixelBuffer The loaded PixelBuffer. If loading is fail, return empty handle. + */ + Dali::Devel::PixelBuffer DecodeFrame(uint32_t frameIndex); + +private: struct Impl; Impl* mImpl; }; -- 2.7.4