From 6ad8c453020d459dc57bead709e74d4c7c874fcd Mon Sep 17 00:00:00 2001 From: seungho Date: Tue, 23 Nov 2021 17:46:41 +0900 Subject: [PATCH] Refactoring Animated image visual - Animated image file will be opened when the visual is on scene. - Only the first frame is cached to cache single frame image as the image visual do. - Pause timer when next frame is not cached yet. And resume it after the frame is ready. - Load policy and release policy is now supported. Change-Id: I39a1d93396865f56d6c7e0b54b9d9fffecdc88ef Signed-off-by: seungho --- .../dali-toolkit/utc-Dali-AnimatedImageVisual.cpp | 23 +- .../texture-manager/texture-cache-manager.cpp | 5 +- .../texture-manager/texture-cache-manager.h | 6 +- .../texture-manager/texture-manager-impl.cpp | 94 +++--- .../texture-manager/texture-manager-impl.h | 77 +++-- .../texture-manager/texture-manager-type.h | 8 +- .../texture-manager/texture-upload-observer.cpp | 14 + .../texture-manager/texture-upload-observer.h | 8 +- .../animated-image/animated-image-visual.cpp | 334 +++++++++++---------- .../visuals/animated-image/animated-image-visual.h | 47 +-- .../visuals/animated-image/fixed-image-cache.cpp | 138 ++++----- .../visuals/animated-image/fixed-image-cache.h | 55 ++-- .../visuals/animated-image/image-cache.cpp | 12 +- .../internal/visuals/animated-image/image-cache.h | 67 +++-- .../rolling-animated-image-cache.cpp | 227 +++++++------- .../animated-image/rolling-animated-image-cache.h | 83 ++--- .../visuals/animated-image/rolling-image-cache.cpp | 165 +++++----- .../visuals/animated-image/rolling-image-cache.h | 60 ++-- 18 files changed, 761 insertions(+), 662 deletions(-) diff --git a/automated-tests/src/dali-toolkit/utc-Dali-AnimatedImageVisual.cpp b/automated-tests/src/dali-toolkit/utc-Dali-AnimatedImageVisual.cpp index 0d2efcc..1fffc97 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-AnimatedImageVisual.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-AnimatedImageVisual.cpp @@ -306,7 +306,9 @@ int UtcDaliAnimatedImageVisualGetPropertyMap04(void) .Add(ImageVisual::Property::URL, TEST_GIF_FILE_NAME) .Add(ImageVisual::Property::BATCH_SIZE, 1) .Add(ImageVisual::Property::CACHE_SIZE, 1) - .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, false) + .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, true) + .Add(ImageVisual::Property::RELEASE_POLICY, ImageVisual::ReleasePolicy::DETACHED) + .Add(ImageVisual::Property::LOAD_POLICY, ImageVisual::LoadPolicy::ATTACHED) .Add(DevelVisual::Property::BORDERLINE_WIDTH, 0.4f)); Property::Map resultMap; @@ -329,6 +331,18 @@ int UtcDaliAnimatedImageVisualGetPropertyMap04(void) DALI_TEST_CHECK(value); DALI_TEST_CHECK(value->Get() == 2); + value = resultMap.Find(ImageVisual::Property::SYNCHRONOUS_LOADING, Property::BOOLEAN); + DALI_TEST_CHECK(value); + DALI_TEST_CHECK(value->Get() == true); + + value = resultMap.Find(ImageVisual::Property::RELEASE_POLICY, Property::INTEGER); + DALI_TEST_CHECK(value); + DALI_TEST_CHECK(value->Get() == ImageVisual::ReleasePolicy::DETACHED); + + value = resultMap.Find(ImageVisual::Property::LOAD_POLICY, Property::INTEGER); + DALI_TEST_CHECK(value); + DALI_TEST_CHECK(value->Get() == ImageVisual::LoadPolicy::ATTACHED); + value = resultMap.Find(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, "totalFrameNumber"); DALI_TEST_CHECK(value); DALI_TEST_EQUALS(value->Get(), 4, TEST_LOCATION); @@ -427,7 +441,8 @@ int UtcDaliAnimatedImageVisualSynchronousLoading(void) application.SendNotification(); application.Render(20); - DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION); + // The first frame is loaded synchronously and load next batch. + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); application.SendNotification(); application.Render(); @@ -440,12 +455,12 @@ int UtcDaliAnimatedImageVisualSynchronousLoading(void) application.SendNotification(); application.Render(20); - DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION); + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); application.SendNotification(); application.Render(); - DALI_TEST_EQUALS(gl.GetNumGeneratedTextures(), 3, TEST_LOCATION); + DALI_TEST_EQUALS(gl.GetNumGeneratedTextures(), 2, TEST_LOCATION); dummyControl.Unparent(); } diff --git a/dali-toolkit/internal/texture-manager/texture-cache-manager.cpp b/dali-toolkit/internal/texture-manager/texture-cache-manager.cpp index bdc0b6a..9524c44 100644 --- a/dali-toolkit/internal/texture-manager/texture-cache-manager.cpp +++ b/dali-toolkit/internal/texture-manager/texture-cache-manager.cpp @@ -398,7 +398,8 @@ TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture( const Dali::SamplingMode::Type& samplingMode, const TextureCacheManager::UseAtlas& useAtlas, const TextureCacheManager::TextureId& maskTextureId, - const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad) + const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad, + bool isAnimatedImage) { // Default to an invalid ID, in case we do not find a match. TextureCacheIndex cacheIndex = INVALID_CACHE_INDEX; @@ -416,6 +417,7 @@ TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture( (useAtlas == textureInfo.useAtlas) && (maskTextureId == textureInfo.maskTextureId) && (size == textureInfo.desiredSize) && + (isAnimatedImage == textureInfo.isAnimatedImageFormat) && ((size.GetWidth() == 0 && size.GetHeight() == 0) || (fittingMode == textureInfo.fittingMode && samplingMode == textureInfo.samplingMode))) @@ -449,7 +451,6 @@ void TextureCacheManager::RemoveCache(const TextureCacheManager::TextureId& text if(textureInfoIndex != INVALID_CACHE_INDEX) { TextureInfo& textureInfo(mTextureInfoContainer[textureInfoIndex]); - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::Remove(textureId:%d) url:%s\n cacheIdx:%d loadState:%s reference count = %d\n", textureId, textureInfo.url.GetUrl().c_str(), textureInfoIndex, GET_LOAD_STATE_STRING(textureInfo.loadState), textureInfo.referenceCount); // Decrement the reference count and check if this is the last user of this Texture. diff --git a/dali-toolkit/internal/texture-manager/texture-cache-manager.h b/dali-toolkit/internal/texture-manager/texture-cache-manager.h index 5e82d47..7d941d1 100644 --- a/dali-toolkit/internal/texture-manager/texture-cache-manager.h +++ b/dali-toolkit/internal/texture-manager/texture-cache-manager.h @@ -204,7 +204,8 @@ public: * @param[in] samplingMode The SamplingMode to use * @param[in] useAtlas True if atlased * @param[in] maskTextureId Optional texture ID to use to mask this image - * @param[in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha. + * @param[in] preMultiplyOnLoad If the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha. + * @param[in] isAnimatedImage True if the texture is from animated image. * @return A TextureCacheId of a cached Texture if found. Or INVALID_CACHE_INDEX if not found. */ TextureCacheManager::TextureCacheIndex FindCachedTexture( @@ -215,7 +216,8 @@ public: const Dali::SamplingMode::Type& samplingMode, const TextureCacheManager::UseAtlas& useAtlas, const TextureCacheManager::TextureId& maskTextureId, - const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad); + const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad, + bool isAnimatedImage); /** * @brief Append a Texture to the TextureCacheManager. diff --git a/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp b/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp index 31c57c6..2890e6a 100644 --- a/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp +++ b/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp @@ -137,15 +137,15 @@ TextureManager::~TextureManager() } } -TextureSet TextureManager::LoadAnimatedImageTexture( - Dali::AnimatedImageLoading animatedImageLoading, - const std::uint32_t& frameIndex, - const Dali::SamplingMode::Type& samplingMode, - const bool& synchronousLoading, - TextureManager::TextureId& textureId, - const Dali::WrapMode::Type& wrapModeU, - const Dali::WrapMode::Type& wrapModeV, - TextureUploadObserver* textureObserver) +TextureSet TextureManager::LoadAnimatedImageTexture(Dali::AnimatedImageLoading animatedImageLoading, + const uint32_t& frameIndex, + TextureManager::TextureId& textureId, + const Dali::SamplingMode::Type& samplingMode, + const Dali::WrapMode::Type& wrapModeU, + const Dali::WrapMode::Type& wrapModeV, + const bool& synchronousLoading, + const bool& useCache, + TextureUploadObserver* textureObserver) { TextureSet textureSet; @@ -175,7 +175,7 @@ TextureSet TextureManager::LoadAnimatedImageTexture( else { auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - textureId = RequestLoadInternal(animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, UseAtlas::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex, false); + textureId = RequestLoadInternal(animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, UseAtlas::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex, false, useCache); TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId); if(loadState == TextureManager::LoadState::UPLOADED) { @@ -229,7 +229,7 @@ Devel::PixelBuffer TextureManager::LoadPixelBuffer( } else { - RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false); + RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false, false); } return pixelBuffer; @@ -415,7 +415,7 @@ TextureManager::TextureId TextureManager::RequestLoad( TextureManager::MultiplyOnLoad& preMultiplyOnLoad, const bool& synchronousLoading) { - return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading); + return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading, true); } TextureManager::TextureId TextureManager::RequestLoad( @@ -433,7 +433,7 @@ TextureManager::TextureId TextureManager::RequestLoad( TextureManager::MultiplyOnLoad& preMultiplyOnLoad, const bool& synchronousLoading) { - return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading); + return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading, true); } TextureManager::TextureId TextureManager::RequestMaskLoad( @@ -442,7 +442,7 @@ TextureManager::TextureId TextureManager::RequestMaskLoad( { // Use the normal load procedure to get the alpha mask. auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, UseAtlas::NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading); + return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, UseAtlas::NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading, true); } TextureManager::TextureId TextureManager::RequestLoadInternal( @@ -461,19 +461,17 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( TextureManager::MultiplyOnLoad& preMultiplyOnLoad, Dali::AnimatedImageLoading animatedImageLoading, const std::uint32_t& frameIndex, - const bool& synchronousLoading) + const bool& synchronousLoading, + const bool& useCache) { - // First check if the requested Texture is cached. - bool isAnimatedImage = (animatedImageLoading) ? true : false; - TextureHash textureHash = INITIAL_HASH_NUMBER; TextureCacheIndex cacheIndex = INVALID_CACHE_INDEX; - if(storageType != StorageType::RETURN_PIXEL_BUFFER && !isAnimatedImage) + if(storageType != StorageType::RETURN_PIXEL_BUFFER && useCache) { textureHash = mTextureCacheManager.GenerateHash(url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId); // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision. - cacheIndex = mTextureCacheManager.FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad); + cacheIndex = mTextureCacheManager.FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad, (animatedImageLoading) ? true : false); } TextureManager::TextureId textureId = INVALID_TEXTURE_ID; @@ -602,12 +600,8 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( { // If the image is already finished to load, use cached texture. // We don't need to consider Observer because this is synchronous loading. - if(textureInfo.loadState == TextureManager::LoadState::UPLOADED || - textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED) - { - return textureId; - } - else + if(!(textureInfo.loadState == TextureManager::LoadState::UPLOADED || + textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED)) { Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection); @@ -648,8 +642,6 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( } } } - - // Return the TextureId for which this Texture can now be referenced by externally. return textureId; } @@ -746,7 +738,7 @@ void TextureManager::LoadOrQueueTexture(TextureManager::TextureInfo& textureInfo { // The Texture has already loaded. The other observers have already been notified. // We need to send a "late" loaded notification for this observer. - observer->LoadComplete(true, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, textureInfo.textureId, textureInfo.textureSet, (textureInfo.useAtlas == UseAtlas::USE_ATLAS) ? true : false, textureInfo.atlasRect, textureInfo.preMultiplied)); + EmitLoadComplete(observer, textureInfo, true); } break; } @@ -806,13 +798,9 @@ void TextureManager::ProcessQueuedTextures() if(cacheIndex != INVALID_CACHE_INDEX) { TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]); - if(textureInfo.loadState == LoadState::UPLOADED) - { - element.mObserver->LoadComplete(true, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, textureInfo.textureId, textureInfo.textureSet, (textureInfo.useAtlas == UseAtlas::USE_ATLAS) ? true : false, textureInfo.atlasRect, textureInfo.preMultiplied)); - } - else if(textureInfo.loadState == LoadState::LOAD_FINISHED && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER) + if((textureInfo.loadState == LoadState::UPLOADED) || (textureInfo.loadState == LoadState::LOAD_FINISHED && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER)) { - element.mObserver->LoadComplete(true, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::PIXEL_BUFFER, textureInfo.pixelBuffer, textureInfo.url.GetUrl(), textureInfo.preMultiplied)); + EmitLoadComplete(element.mObserver, textureInfo, true); } else { @@ -1017,6 +1005,17 @@ void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, c // and erase it from the list TextureInfo* info = &textureInfo; + if(info->animatedImageLoading) + { + // If loading failed, we don't need to get frameCount and frameInterval. + if(success) + { + info->frameCount = info->animatedImageLoading.GetImageCount(); + info->frameInterval = info->animatedImageLoading.GetFrameInterval(info->frameIndex); + } + info->animatedImageLoading.Reset(); + } + mQueueLoadFlag = true; while(info->observerList.Count()) @@ -1039,14 +1038,7 @@ void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, c info->observerList.Erase(info->observerList.Begin()); - if(info->storageType == StorageType::RETURN_PIXEL_BUFFER) - { - observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::PIXEL_BUFFER, info->pixelBuffer, info->url.GetUrl(), info->preMultiplied)); - } - else - { - observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, info->textureId, info->textureSet, (info->useAtlas == UseAtlas::USE_ATLAS) ? true : false, info->atlasRect, info->preMultiplied)); - } + EmitLoadComplete(observer, *info, success); // Get the textureInfo from the container again as it may have been invalidated. TextureCacheIndex textureInfoIndex = mTextureCacheManager.GetCacheIndexFromId(textureId); @@ -1101,6 +1093,22 @@ Dali::Geometry TextureManager::GetRenderGeometry(const TextureManager::TextureId return RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().GetGeometry(textureId, frontElements, backElements) : Geometry(); } +void TextureManager::EmitLoadComplete(TextureUploadObserver* observer, TextureManager::TextureInfo& textureInfo, const bool& success) +{ + if(textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER) + { + observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::PIXEL_BUFFER, textureInfo.pixelBuffer, textureInfo.url.GetUrl(), textureInfo.preMultiplied)); + } + else if(textureInfo.isAnimatedImageFormat) + { + observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::ANIMATED_IMAGE_TEXTURE, textureInfo.textureId, textureInfo.frameCount, textureInfo.frameInterval)); + } + else + { + observer->LoadComplete(success, TextureUploadObserver::TextureInformation(TextureUploadObserver::ReturnType::TEXTURE, textureInfo.textureId, textureInfo.textureSet, (textureInfo.useAtlas == UseAtlas::USE_ATLAS) ? true : false, textureInfo.atlasRect, textureInfo.preMultiplied)); + } +} + } // namespace Internal } // namespace Toolkit diff --git a/dali-toolkit/internal/texture-manager/texture-manager-impl.h b/dali-toolkit/internal/texture-manager/texture-manager-impl.h index ee06c53..01f62ee 100644 --- a/dali-toolkit/internal/texture-manager/texture-manager-impl.h +++ b/dali-toolkit/internal/texture-manager/texture-manager-impl.h @@ -112,27 +112,28 @@ public: * The parameters are used to specify how the animated image is loaded. * The observer has the LoadComplete method called when the load is ready. * - * @param[in] animatedImageLoading The AnimatedImageLoading that contain the animated image information - * @param[in] frameIndex The frame index to load. - * @param[in] samplingMode The SamplingMode to use - * @param[in] synchronousLoading true if the frame should be loaded synchronously - * @param[out] textureId The textureId of the frame - * @param[in] wrapModeU Horizontal Wrap mode - * @param[in] wrapModeV Vertical Wrap mode - * @param[in] textureObserver The client object should inherit from this and provide the "UploadCompleted" virtual. - * This is called when an image load completes (or fails). + * @param[in] animatedImageLoading The AnimatedImageLoading that contain the animated image information + * @param[in] frameIndex The frame index to load. + * @param[out] textureId The textureId of the frame + * @param[in] samplingMode The SamplingMode to use + * @param[in] wrapModeU Horizontal Wrap mode + * @param[in] wrapModeV Vertical Wrap mode + * @param[in] synchronousLoading true if the frame should be loaded synchronously + * @param[in] useCache true if this frame loading uses cache. + * @param[in] textureObserver The client object should inherit from this and provide the "LoadCompleted" virtual. + * This is called when an image load completes (or fails). * - * @return The texture set containing the frame of animated image, or empty if still loading. + * @return The texture set containing the frame of animated image, or empty if still loading. */ - TextureSet LoadAnimatedImageTexture( - Dali::AnimatedImageLoading animatedImageLoading, - const std::uint32_t& frameIndex, - const Dali::SamplingMode::Type& samplingMode, - const bool& synchronousLoading, - TextureManager::TextureId& textureId, - const Dali::WrapMode::Type& wrapModeU, - const Dali::WrapMode::Type& wrapModeV, - TextureUploadObserver* textureObserver); + TextureSet LoadAnimatedImageTexture(Dali::AnimatedImageLoading animatedImageLoading, + const uint32_t& frameIndex, + TextureManager::TextureId& textureId, + const Dali::SamplingMode::Type& samplingMode, + const Dali::WrapMode::Type& wrapModeU, + const Dali::WrapMode::Type& wrapModeV, + const bool& synchronousLoading, + const bool& useCache, + TextureUploadObserver* textureObserver); /** * @brief Requests an image load of the given URL to get PixelBuffer. @@ -145,7 +146,7 @@ public: * @param[in] fittingMode The FittingMode to use * @param[in] samplingMode The SamplingMode to use * @param[in] synchronousLoading true if the URL should be loaded synchronously - * @param[in] textureObserver The client object should inherit from this and provide the "UploadCompleted" virtual. + * @param[in] textureObserver The client object should inherit from this and provide the "LoadCompleted" virtual. * This is called when an image load completes (or fails). * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data * @param[in,out] preMultiplyOnLoad True if the image color should be multiplied by it's alpha. Set to false if the @@ -188,10 +189,10 @@ public: * @param[out] loadingStatus The loading status of the texture * @param[in] wrapModeU Horizontal Wrap mode * @param[in] wrapModeV Vertical Wrap mode - * @param[in] textureObserver The client object should inherit from this and provide the "UploadCompleted" virtual. + * @param[in] textureObserver The client object should inherit from this and provide the "LoadCompleted" virtual. * This is called when an image load completes (or fails). * @param[in] atlasObserver This is used if the texture is atlased, and will be called instead of - * textureObserver.UploadCompleted + * textureObserver.LoadCompleted * @param[in] imageAtlasManager The atlas manager to use for atlasing textures * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data * @param[in] reloadPolicy Forces a reload of the texture even if already cached @@ -335,8 +336,8 @@ public: // Load Request API * @param[in] fittingMode The FittingMode to use * @param[in] samplingMode The SamplingMode to use * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful, - * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver. - * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual. + * but "useAtlasing" will be set to false in the "LoadCompleted" callback from the TextureManagerUploadObserver. + * @param[in] observer The client object should inherit from this and provide the "LoadCompleted" virtual. * This is called when an image load completes (or fails). * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data * @param[in] reloadPolicy Forces a reload of the texture even if already cached @@ -345,7 +346,7 @@ public: // Load Request API * default is false. * @return A TextureId to use as a handle to reference this Texture */ - TextureManager::TextureId RequestLoad( + TextureId RequestLoad( const VisualUrl& url, const ImageDimensions& desiredSize, const Dali::FittingMode::Type& fittingMode, @@ -376,11 +377,11 @@ public: // Load Request API * @param[in] samplingMode The SamplingMode to use * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still * be loaded, and marked successful, - * but "useAtlasing" will be set to false in the "UploadCompleted" callback from + * but "useAtlasing" will be set to false in the "LoadCompleted" callback from * the TextureManagerUploadObserver. * @param[in] cropToMask Only used with masking, this will crop the scaled image to the mask size. * If false, then the mask will be scaled to fit the image before being applied. - * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" + * @param[in] observer The client object should inherit from this and provide the "LoadCompleted" * virtual. * This is called when an image load completes (or fails). * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data @@ -391,7 +392,7 @@ public: // Load Request API * default is false. * @return A TextureId to use as a handle to reference this Texture */ - TextureManager::TextureId RequestLoad( + TextureId RequestLoad( const VisualUrl& url, const TextureManager::TextureId& maskTextureId, const float& contentScale, @@ -414,7 +415,7 @@ public: // Load Request API * default is false. * @return A TextureId to use as a handle to reference this mask Texture */ - TextureManager::TextureId RequestMaskLoad( + TextureId RequestMaskLoad( const VisualUrl& maskUrl, const bool& synchronousLoading = false); @@ -438,11 +439,11 @@ private: * @param[in] samplingMode The SamplingMode to use * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be * loaded, and marked successful, but "useAtlasing" will be set to false in the - * "UploadCompleted" callback from the TextureManagerUploadObserver. + * "LoadCompleted" callback from the TextureManagerUploadObserver. * @param[in] cropToMask Whether to crop the target after masking, or scale the mask to the image before * masking. * @param[in] storageType, Whether the pixel data is stored in the cache or uploaded to the GPU - * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" + * @param[in] observer The client object should inherit from this and provide the "LoadCompleted" * virtual. * This is called when an image load completes (or fails). * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data @@ -453,9 +454,10 @@ private: * @param[in] frameIndex The frame index of a frame to be loaded frame * @param[in] synchronousLoading True if the frame should be loaded synchronously. If you skip this parameter, * default is false. + * @param[in] useCache True if the texture will be cached. * @return A TextureId to use as a handle to reference this Texture */ - TextureManager::TextureId RequestLoadInternal( + TextureId RequestLoadInternal( const VisualUrl& url, const TextureManager::TextureId& maskTextureId, const float& contentScale, @@ -471,7 +473,8 @@ private: TextureManager::MultiplyOnLoad& preMultiplyOnLoad, Dali::AnimatedImageLoading animatedImageLoading, const std::uint32_t& frameIndex, - const bool& synchronousLoading); + const bool& synchronousLoading, + const bool& useCache); /** * @brief Load a new image synchronously. @@ -580,6 +583,14 @@ private: */ void NotifyObservers(TextureManager::TextureInfo& textureInfo, const bool& success); + /** + * Call LoadComplete to the observer. + * @param[in] observer The client object should inherit from this and provide the "LoadCompleted" + * @param[in] textureInfo The struct associated with this Texture + * @param[in] success If the pixel data was retrieved successfully and uploaded to GPU + */ + void EmitLoadComplete(TextureUploadObserver* observer, TextureManager::TextureInfo& textureInfo, const bool& success); + public: /** * @brief Common method to handle loading completion. diff --git a/dali-toolkit/internal/texture-manager/texture-manager-type.h b/dali-toolkit/internal/texture-manager/texture-manager-type.h index 5f07c1f..514e544 100644 --- a/dali-toolkit/internal/texture-manager/texture-manager-type.h +++ b/dali-toolkit/internal/texture-manager/texture-manager-type.h @@ -153,6 +153,8 @@ struct TextureInfo storageType(StorageType::UPLOAD_TO_TEXTURE), animatedImageLoading(animatedImageLoading), frameIndex(frameIndex), + frameCount(0u), + frameInterval(0u), useAtlas(useAtlas), loadSynchronously(loadSynchronously), cropToMask(cropToMask), @@ -160,6 +162,7 @@ struct TextureInfo preMultiplyOnLoad(preMultiplyOnLoad), preMultiplied(false) { + isAnimatedImageFormat = (animatedImageLoading) ? true : false; } /** @@ -185,7 +188,9 @@ struct TextureInfo Dali::SamplingMode::Type samplingMode : 3; ///< The requested SamplingMode StorageType storageType; ///< CPU storage / GPU upload; Dali::AnimatedImageLoading animatedImageLoading; ///< AnimatedImageLoading that contains animated image information. - std::uint32_t frameIndex; ///< frame index that be loaded, in case of animated image + uint32_t frameIndex; ///< Frame index that be loaded, in case of animated image + uint32_t frameCount; ///< Total frame count of input animated image. If this variable is not 0, this textureInfo is for animated image file format. + uint32_t frameInterval; ///< Time interval between this frame and next frame of animated image. UseAtlas useAtlas; ///< USE_ATLAS if an atlas was requested. bool loadSynchronously : 1; ///< True if synchronous loading was requested @@ -193,6 +198,7 @@ struct TextureInfo bool orientationCorrection : 1; ///< True if the image should be rotated to match exif orientation data bool preMultiplyOnLoad : 1; ///< True if the image's color should be multiplied by it's alpha bool preMultiplied : 1; ///< True if the image's color was multiplied by it's alpha + bool isAnimatedImageFormat : 1; ///< true if the image is requested from animated image visual. }; } // namespace TextureManagerType diff --git a/dali-toolkit/internal/texture-manager/texture-upload-observer.cpp b/dali-toolkit/internal/texture-manager/texture-upload-observer.cpp index 524ef67..a4cdc64 100644 --- a/dali-toolkit/internal/texture-manager/texture-upload-observer.cpp +++ b/dali-toolkit/internal/texture-manager/texture-upload-observer.cpp @@ -49,6 +49,20 @@ TextureUploadObserver::TextureInformation::TextureInformation(ReturnType returnT { } +TextureUploadObserver::TextureInformation::TextureInformation(ReturnType returnType, int32_t textureId, uint32_t frameCount, uint32_t interval) +: returnType(returnType), + textureId(textureId), + textureSet(), + useAtlasing(false), + atlasRect(Vector4::ZERO), + preMultiplied(false), + pixelBuffer(), + url(), + frameCount(frameCount), + interval(interval) +{ +} + TextureUploadObserver::TextureUploadObserver() { } diff --git a/dali-toolkit/internal/texture-manager/texture-upload-observer.h b/dali-toolkit/internal/texture-manager/texture-upload-observer.h index 211cad7..5848654 100644 --- a/dali-toolkit/internal/texture-manager/texture-upload-observer.h +++ b/dali-toolkit/internal/texture-manager/texture-upload-observer.h @@ -44,14 +44,16 @@ public: enum class ReturnType { - TEXTURE = 0, - PIXEL_BUFFER + PIXEL_BUFFER = 0, + TEXTURE, + ANIMATED_IMAGE_TEXTURE }; struct TextureInformation { TextureInformation(ReturnType returnType, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied); TextureInformation(ReturnType returnType, Devel::PixelBuffer pixelBuffer, const std::string& url, bool preMultiplied); + TextureInformation(ReturnType returnType, int32_t textureId, uint32_t frameCount, uint32_t interval); TextureInformation(); @@ -63,6 +65,8 @@ public: bool preMultiplied; ///< True if the image had pre-multiplied alpha applied Devel::PixelBuffer pixelBuffer; ///< The PixelBuffer of the loaded image. std::string_view url; ///< The url address of the loaded image. + uint32_t frameCount{0}; ///< The frameCount of the animated image + uint32_t interval{0}; ///< Time interval between currently loaded frame and next frame. }; public: diff --git a/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp b/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp index b8eb497..d5dcdaa 100644 --- a/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp +++ b/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp @@ -63,8 +63,25 @@ DALI_ENUM_TO_STRING_TABLE_BEGIN(WRAP_MODE) DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, MIRRORED_REPEAT) DALI_ENUM_TO_STRING_TABLE_END(WRAP_MODE) -const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f); -constexpr auto LOOP_FOREVER = -1; +// load policies +DALI_ENUM_TO_STRING_TABLE_BEGIN(LOAD_POLICY) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::LoadPolicy, IMMEDIATE) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::LoadPolicy, ATTACHED) +DALI_ENUM_TO_STRING_TABLE_END(LOAD_POLICY) + +// release policies +DALI_ENUM_TO_STRING_TABLE_BEGIN(RELEASE_POLICY) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, DETACHED) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, DESTROYED) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, NEVER) +DALI_ENUM_TO_STRING_TABLE_END(RELEASE_POLICY) + +static constexpr uint32_t SINGLE_IMAGE_COUNT = 1u; +static constexpr uint32_t FIRST_FRAME_INDEX = 0u; +static constexpr uint16_t MINIMUM_CACHESIZE = 1; +static constexpr Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f); +static constexpr auto LOOP_FOREVER = -1; +static constexpr auto FIRST_LOOP = 0u; #if defined(DEBUG_ENABLED) Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE"); @@ -76,18 +93,15 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, " * * | New * | DoSetProperties() - * | LoadFirstBatch() - * | new cache - * | cache->LoadBatch() + * | OnInitialize() + * | CreateImageCache() * | * | DoSetOnScene() * | PrepareTextureSet() * | cache->FirstFrame() - * | CreateRenderer() (Doesn't become ready until first frame loads) - * | StartFirstFrame() * | * | FrameReady(textureSet) - * | start first frame: + * | StartFirstFrame: * | actor.AddRenderer * | start timer * | mRenderer.SetTextures(textureSet) @@ -97,8 +111,7 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, " * | if front frame is ready, * | mRenderer.SetTextures( front frame's texture ) * | else - * | mWaitingForTexture=true - * | cache->LoadBatch() + * | Waiting for frame ready. * | * | FrameReady(textureSet) * | mRenderer.SetTextures(textureSet) @@ -112,11 +125,6 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache visual->InitializeAnimatedImage(imageUrl); visual->SetProperties(properties); - if(visual->mFrameCount > 0) - { - visual->LoadFirstBatch(); - } - visual->Initialize(); return visual; @@ -138,11 +146,6 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache visual->mFrameCount = imageUrls.Count(); visual->SetProperties(properties); - if(visual->mFrameCount > 0) - { - visual->LoadFirstBatch(); - } - visual->Initialize(); return visual; @@ -153,11 +156,6 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory)); visual->InitializeAnimatedImage(imageUrl); - if(visual->mFrameCount > 0) - { - visual->LoadFirstBatch(); - } - visual->Initialize(); return visual; @@ -165,9 +163,41 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache void AnimatedImageVisual::InitializeAnimatedImage(const VisualUrl& imageUrl) { - mImageUrl = imageUrl; + mImageUrl = imageUrl; mAnimatedImageLoading = AnimatedImageLoading::New(imageUrl.GetUrl(), imageUrl.IsLocalResource()); - mFrameCount = mAnimatedImageLoading.GetImageCount(); +} + +void AnimatedImageVisual::CreateImageCache() +{ + DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::CreateImageCache() batchSize:%d cacheSize:%d\n", mBatchSize, mCacheSize); + + TextureManager& textureManager = mFactoryCache.GetTextureManager(); + + if(mAnimatedImageLoading) + { + mImageCache = new RollingAnimatedImageCache(textureManager, mAnimatedImageLoading, *this, mCacheSize, mBatchSize, IsSynchronousLoadingRequired()); + } + else if(mImageUrls) + { + // Ensure the batch size and cache size are no bigger than the number of URLs, + // and that the cache is at least as big as the batch size. + uint16_t numUrls = mImageUrls->size(); + uint16_t batchSize = std::max(std::min(mBatchSize, numUrls), MINIMUM_CACHESIZE); + uint16_t cacheSize = std::max(std::min(std::max(batchSize, mCacheSize), numUrls), MINIMUM_CACHESIZE); + if(cacheSize < numUrls) + { + mImageCache = new RollingImageCache(textureManager, *mImageUrls, *this, cacheSize, batchSize, mFrameDelay); + } + else + { + mImageCache = new FixedImageCache(textureManager, *mImageUrls, *this, batchSize, mFrameDelay); + } + } + + if(!mImageCache) + { + DALI_LOG_ERROR("mImageCache is null\n"); + } } AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory) @@ -179,14 +209,16 @@ AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, Image mImageUrl(), mAnimatedImageLoading(), mFrameIndexForJumpTo(0), + mCurrentFrameIndex(FIRST_FRAME_INDEX), mImageUrls(NULL), mImageCache(NULL), mCacheSize(2), mBatchSize(2), mFrameDelay(100), mLoopCount(LOOP_FOREVER), - mCurrentLoopIndex(0), - mUrlIndex(0), + mCurrentLoopIndex(FIRST_LOOP), + mLoadPolicy(Toolkit::ImageVisual::LoadPolicy::ATTACHED), + mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED), mFrameCount(0), mImageSize(), mActionStatus(DevelAnimatedImageVisual::Action::PLAY), @@ -200,6 +232,12 @@ AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, Image AnimatedImageVisual::~AnimatedImageVisual() { + // AnimatedImageVisual destroyed so remove texture unless ReleasePolicy is set to never release + // If this is animated image, clear cache. Else if this is single frame image, this is affected be release policy. + if(mFrameCount > SINGLE_IMAGE_COUNT || mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER) + { + mImageCache->ClearCache(); + } delete mImageCache; delete mImageUrls; } @@ -255,9 +293,13 @@ void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const map.Insert(Toolkit::ImageVisual::Property::FRAME_DELAY, static_cast(mFrameDelay)); map.Insert(Toolkit::DevelImageVisual::Property::LOOP_COUNT, static_cast(mLoopCount)); map.Insert(Toolkit::DevelImageVisual::Property::CURRENT_FRAME_NUMBER, (mImageCache) ? static_cast(mImageCache->GetCurrentFrameIndex()) : -1); - map.Insert(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, (mImageCache) ? static_cast(mImageCache->GetTotalFrameCount()) : -1); + map.Insert(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, (mImageCache) ? static_cast((mAnimatedImageLoading) ? mAnimatedImageLoading.GetImageCount() : + mImageCache->GetTotalFrameCount()) : -1); map.Insert(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mStopBehavior); + + map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy); + map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy); } void AnimatedImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const @@ -296,6 +338,7 @@ void AnimatedImageVisual::OnDoAction(const Dali::Property::Index actionId, const // STOP reset functionality will actually be done in a future change // Stop will be executed on next timer tick mActionStatus = DevelAnimatedImageVisual::Action::STOP; + mCurrentLoopIndex = FIRST_LOOP; if(IsOnScene()) { DisplayNextFrame(); @@ -370,8 +413,25 @@ void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap) { DoSetProperty(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, keyValue.second); } + else if(keyValue.first == LOAD_POLICY_NAME) + { + DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second); + } + else if(keyValue.first == RELEASE_POLICY_NAME) + { + DoSetProperty(Toolkit::ImageVisual::Property::RELEASE_POLICY, keyValue.second); + } + else if(keyValue.first == SYNCHRONOUS_LOADING) + { + DoSetProperty(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second); + } } } + // Load image immediately if LOAD_POLICY requires it + if(mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::IMMEDIATE) + { + PrepareTextureSet(); + } } void AnimatedImageVisual::DoSetProperty(Property::Index index, @@ -451,6 +511,10 @@ void AnimatedImageVisual::DoSetProperty(Property::Index index, if(value.Get(frameDelay)) { mFrameDelay = frameDelay; + if(mImageCache) + { + mImageCache->SetInterval(static_cast(mFrameDelay)); + } } break; } @@ -489,29 +553,30 @@ void AnimatedImageVisual::DoSetProperty(Property::Index index, } break; } + + case Toolkit::ImageVisual::Property::RELEASE_POLICY: + { + int releasePolicy = 0; + Scripting::GetEnumerationProperty(value, RELEASE_POLICY_TABLE, RELEASE_POLICY_TABLE_COUNT, releasePolicy); + mReleasePolicy = Toolkit::ImageVisual::ReleasePolicy::Type(releasePolicy); + break; + } + + case Toolkit::ImageVisual::Property::LOAD_POLICY: + { + int loadPolicy = 0; + Scripting::GetEnumerationProperty(value, LOAD_POLICY_TABLE, LOAD_POLICY_TABLE_COUNT, loadPolicy); + mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type(loadPolicy); + break; + } } } void AnimatedImageVisual::DoSetOnScene(Actor& actor) { + mStartFirstFrame = true; mPlacementActor = actor; - TextureSet textureSet = PrepareTextureSet(); - - // Loading animated image file is failed. - if(!mImageCache || - (mAnimatedImageLoading && !mAnimatedImageLoading.HasLoadingSucceeded())) - { - textureSet = SetLoadingFailed(); - } - - if(textureSet) // if the image loading is successful - { - StartFirstFrame(textureSet); - } - else - { - mStartFirstFrame = true; - } + PrepareTextureSet(); } void AnimatedImageVisual::DoSetOffScene(Actor& actor) @@ -525,8 +590,19 @@ void AnimatedImageVisual::DoSetOffScene(Actor& actor) } actor.RemoveRenderer(mImpl->mRenderer); + if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED) + { + mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas + mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING; + + TextureSet textureSet = TextureSet::New(); + mImpl->mRenderer.SetTextures(textureSet); + } + mPlacementActor.Reset(); mStartFirstFrame = false; + mCurrentFrameIndex = FIRST_FRAME_INDEX; + mCurrentLoopIndex = FIRST_LOOP; } void AnimatedImageVisual::OnSetTransform() @@ -546,8 +622,23 @@ void AnimatedImageVisual::UpdateShader() } } +Shader AnimatedImageVisual::GenerateShader() const +{ + bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE; + Shader shader; + shader = mImageVisualShaderFactory.GetShader( + mFactoryCache, + ImageVisualShaderFeature::FeatureBuilder() + .ApplyDefaultTextureWrapMode(defaultWrapMode) + .EnableRoundedCorner(IsRoundedCornerRequired()) + .EnableBorderline(IsBorderlineRequired())); + return shader; +} + void AnimatedImageVisual::OnInitialize() { + CreateImageCache(); + bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE; Shader shader = GenerateShader(); @@ -572,61 +663,7 @@ void AnimatedImageVisual::OnInitialize() } } -void AnimatedImageVisual::LoadFirstBatch() -{ - // Ensure the batch size and cache size are no bigger than the number of URLs, - // and that the cache is at least as big as the batch size. - uint16_t numUrls = 0; - uint16_t batchSize = 1; - uint16_t cacheSize = 1; - - if(mImageUrls) - { - numUrls = mImageUrls->size(); - } - else - { - numUrls = mFrameCount; - } - - batchSize = std::min(mBatchSize, numUrls); - cacheSize = std::min(std::max(batchSize, mCacheSize), numUrls); - - DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::LoadFirstBatch() batchSize:%d cacheSize:%d\n", batchSize, cacheSize); - - mUrlIndex = 0; - TextureManager& textureManager = mFactoryCache.GetTextureManager(); - - if(mAnimatedImageLoading) - { - mImageCache = new RollingAnimatedImageCache(textureManager, mAnimatedImageLoading, mFrameCount, *this, cacheSize, batchSize, IsSynchronousLoadingRequired()); - } - else if(mImageUrls) - { - if(batchSize > 0 && cacheSize > 0) - { - if(cacheSize < numUrls) - { - mImageCache = new RollingImageCache(textureManager, *mImageUrls, *this, cacheSize, batchSize); - } - else - { - mImageCache = new FixedImageCache(textureManager, *mImageUrls, *this, batchSize); - } - } - else - { - mImageCache = new RollingImageCache(textureManager, *mImageUrls, *this, 1, 1); - } - } - - if(!mImageCache) - { - DALI_LOG_ERROR("mImageCache is null\n"); - } -} - -void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet) +void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval) { DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::StartFirstFrame()\n"); @@ -643,26 +680,23 @@ void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet) } } - if(mFrameCount > 1) + if(mImpl->mResourceStatus != Toolkit::Visual::ResourceStatus::FAILED) { - int frameDelay = mImageCache->GetFrameInterval(0); - if(frameDelay == 0u) + if(mFrameCount > SINGLE_IMAGE_COUNT) { - frameDelay = mFrameDelay; // from URL array + mFrameDelayTimer = Timer::New(firstInterval); + mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame); + mFrameDelayTimer.Start(); } - mFrameDelayTimer = Timer::New(frameDelay); - mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame); - mFrameDelayTimer.Start(); - } - if(mImpl->mResourceStatus != Toolkit::Visual::ResourceStatus::FAILED) - { DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::READY)\n"); ResourceReady(Toolkit::Visual::ResourceStatus::READY); } + + mCurrentFrameIndex = FIRST_FRAME_INDEX; } -TextureSet AnimatedImageVisual::PrepareTextureSet() +void AnimatedImageVisual::PrepareTextureSet() { TextureSet textureSet; if(mImageCache) @@ -670,12 +704,11 @@ TextureSet AnimatedImageVisual::PrepareTextureSet() textureSet = mImageCache->FirstFrame(); } + // Check whether synchronous loading is true or false for the first frame. if(textureSet) { SetImageSize(textureSet); } - - return textureSet; } void AnimatedImageVisual::SetImageSize(TextureSet& textureSet) @@ -691,24 +724,28 @@ void AnimatedImageVisual::SetImageSize(TextureSet& textureSet) } } -void AnimatedImageVisual::FrameReady(TextureSet textureSet) +void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval) { // When image visual requested to load new frame to mImageCache and it is failed. - if(!textureSet) + if(!mImageCache || !textureSet) { textureSet = SetLoadingFailed(); } - SetImageSize(textureSet); if(mStartFirstFrame) { - StartFirstFrame(textureSet); + mFrameCount = mImageCache->GetTotalFrameCount(); + StartFirstFrame(textureSet, interval); } else { if(mImpl->mRenderer) { + if(mFrameDelayTimer && interval > 0u) + { + mFrameDelayTimer.SetInterval(interval); + } mImpl->mRenderer.SetTextures(textureSet); } } @@ -721,7 +758,6 @@ bool AnimatedImageVisual::DisplayNextFrame() if(mImageCache) { - bool nextFrame = false; uint32_t frameIndex = mImageCache->GetCurrentFrameIndex(); if(mIsJumpTo) @@ -735,10 +771,10 @@ bool AnimatedImageVisual::DisplayNextFrame() } else if(mActionStatus == DevelAnimatedImageVisual::Action::STOP) { - frameIndex = 0; + mCurrentLoopIndex = FIRST_LOOP; if(mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME) { - frameIndex = 0; + frameIndex = FIRST_FRAME_INDEX; } else if(mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME) { @@ -751,13 +787,12 @@ bool AnimatedImageVisual::DisplayNextFrame() } else { - if(mFrameCount > 1) + if(mFrameCount > SINGLE_IMAGE_COUNT) { - nextFrame = true; frameIndex++; if(frameIndex >= mFrameCount) { - frameIndex %= mFrameCount; + frameIndex = FIRST_FRAME_INDEX; ++mCurrentLoopIndex; } @@ -768,38 +803,24 @@ bool AnimatedImageVisual::DisplayNextFrame() return DisplayNextFrame(); } } - - unsigned int delay = mImageCache->GetFrameInterval(frameIndex); - if(delay > 0u) - { - if(mFrameDelayTimer.GetInterval() != delay) - { - mFrameDelayTimer.SetInterval(delay); - } - } } DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex); - if(nextFrame) - { - textureSet = mImageCache->NextFrame(); - } - else - { - textureSet = mImageCache->Frame(frameIndex); - } - - continueTimer = (mActionStatus == DevelAnimatedImageVisual::Action::PLAY) ? true : false; - } + textureSet = mImageCache->Frame(frameIndex); - if(textureSet) - { - SetImageSize(textureSet); - if(mImpl->mRenderer) + if(textureSet) { - mImpl->mRenderer.SetTextures(textureSet); + SetImageSize(textureSet); + if(mImpl->mRenderer) + { + mImpl->mRenderer.SetTextures(textureSet); + } + mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex)); } + + mCurrentFrameIndex = frameIndex; + continueTimer = (mActionStatus == DevelAnimatedImageVisual::Action::PLAY && textureSet) ? true : false; } return continueTimer; @@ -830,19 +851,6 @@ TextureSet AnimatedImageVisual::SetLoadingFailed() return textureSet; } -Shader AnimatedImageVisual::GenerateShader() const -{ - bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE; - Shader shader; - shader = mImageVisualShaderFactory.GetShader( - mFactoryCache, - ImageVisualShaderFeature::FeatureBuilder() - .ApplyDefaultTextureWrapMode(defaultWrapMode) - .EnableRoundedCorner(IsRoundedCornerRequired()) - .EnableBorderline(IsBorderlineRequired())); - return shader; -} - } // namespace Internal } // namespace Toolkit diff --git a/dali-toolkit/internal/visuals/animated-image/animated-image-visual.h b/dali-toolkit/internal/visuals/animated-image/animated-image-visual.h index ad8b5ac..5e70964 100644 --- a/dali-toolkit/internal/visuals/animated-image/animated-image-visual.h +++ b/dali-toolkit/internal/visuals/animated-image/animated-image-visual.h @@ -193,54 +193,51 @@ protected: private: /** - * Creates the renderer for the animated image + * @brief Initialize the animated image variables. + * @param[in] imageUrl The url of the animated image */ - void CreateRenderer(); + void InitializeAnimatedImage(const VisualUrl& imageUrl); /** - * Starts the Load of the first batch of URLs + * @brief Create image cache for animated image or image array. */ - void LoadFirstBatch(); + void CreateImageCache(); /** - * Adds the texture set to the renderer, and the renderer to the + * @brief Adds the texture set to the renderer, and the renderer to the * placement actor, and starts the frame timer - * @param[in] textureSet The texture set to apply + * @param[in] textureSet The texture set to apply + * @param[in] firstInterval frame interval(ms) for the first frame. */ - void StartFirstFrame(TextureSet& textureSet); + void StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval); /** - * Prepares the texture set for displaying + * @brief Prepares the texture set for displaying */ - TextureSet PrepareTextureSet(); + void PrepareTextureSet(); /** - * Set the image size from the texture set + * @brief Set the image size from the texture set * @param[in] textureSet The texture set to get the size from */ void SetImageSize(TextureSet& textureSet); /** - * Called when the next frame is ready. + * @brief Called when the next frame is ready. * @param[in] textureSet the texture set to apply + * @param[in] interval interval(ms) for the frame */ - void FrameReady(TextureSet textureSet) override; + void FrameReady(TextureSet textureSet, uint32_t interval) override; /** - * Display the next frame. It is called when the mFrameDelayTimer ticks. - * Returns true to ensure the timer continues running. + * @brief Display the next frame. It is called when the mFrameDelayTimer ticks. + * @return true to ensure the timer continues running. */ bool DisplayNextFrame(); /** - * Initialize the animated image variables. - * @param[in] imageUrl The url of the animated image - */ - void InitializeAnimatedImage(const VisualUrl& imageUrl); - - /** - * Set the state of loading fail of an image or a frame. - * Returns TextureSet of broken image. + * @brief Set the state of loading fail of an image or a frame. + * @return TextureSet of broken image. */ TextureSet SetLoadingFailed(); @@ -260,6 +257,7 @@ private: VisualUrl mImageUrl; Dali::AnimatedImageLoading mAnimatedImageLoading; // Only needed for animated image uint32_t mFrameIndexForJumpTo; // Frame index into textureRects + uint32_t mCurrentFrameIndex; // Variables for Multi-Image player ImageCache::UrlList* mImageUrls; @@ -269,7 +267,10 @@ private: uint16_t mFrameDelay; int16_t mLoopCount; int16_t mCurrentLoopIndex; - uint16_t mUrlIndex; + + // Variables for image visual policy. + Dali::Toolkit::ImageVisual::LoadPolicy::Type mLoadPolicy; + Dali::Toolkit::ImageVisual::ReleasePolicy::Type mReleasePolicy; // Shared variables uint32_t mFrameCount; // Number of frames diff --git a/dali-toolkit/internal/visuals/animated-image/fixed-image-cache.cpp b/dali-toolkit/internal/visuals/animated-image/fixed-image-cache.cpp index dcb6621..192a3c4 100644 --- a/dali-toolkit/internal/visuals/animated-image/fixed-image-cache.cpp +++ b/dali-toolkit/internal/visuals/animated-image/fixed-image-cache.cpp @@ -20,6 +20,9 @@ // INTERNAL HEADERS #include // For ImageAtlasManagerPtr +// EXTERNAL HEADERS +#include + namespace Dali { namespace Toolkit @@ -28,79 +31,60 @@ namespace Internal { namespace { -const bool ENABLE_ORIENTATION_CORRECTION(true); +constexpr bool ENABLE_ORIENTATION_CORRECTION(true); +constexpr uint32_t FIRST_FRAME_INDEX = 0u; } // namespace FixedImageCache::FixedImageCache( - TextureManager& textureManager, UrlList& urlList, ImageCache::FrameReadyObserver& observer, unsigned int batchSize) -: ImageCache(textureManager, observer, batchSize), + TextureManager& textureManager, UrlList& urlList, ImageCache::FrameReadyObserver& observer, uint32_t batchSize, uint32_t interval) +: ImageCache(textureManager, observer, batchSize, interval), mImageUrls(urlList), - mFront(0u) + mFront(FIRST_FRAME_INDEX) { mReadyFlags.reserve(mImageUrls.size()); - LoadBatch(); } FixedImageCache::~FixedImageCache() { - if(mTextureManagerAlive) - { - for(std::size_t i = 0; i < mImageUrls.size(); ++i) - { - mTextureManager.Remove(mImageUrls[i].mTextureId, this); - } - } + ClearCache(); } TextureSet FixedImageCache::Frame(uint32_t frameIndex) { - while(frameIndex > mFront) + TextureSet textureSet; + if(frameIndex >= mImageUrls.size()) + { + DALI_LOG_ERROR("Wrong frameIndex requested.\n"); + return textureSet; + } + + while(mReadyFlags.size() < mImageUrls.size() && + (frameIndex > mFront || mReadyFlags.empty())) { ++mFront; - if(mFront >= mImageUrls.size()) - { - mFront = 0; - } LoadBatch(); } mFront = frameIndex; - TextureSet textureSet; - if(IsFrontReady() == true) + if(IsFrontReady() && mLoadState != TextureManager::LoadState::LOAD_FAILED) { textureSet = GetFrontTextureSet(); } - else - { - mWaitingForReadyFrame = true; - } return textureSet; } TextureSet FixedImageCache::FirstFrame() { - TextureSet textureSet = GetFrontTextureSet(); - - if(!textureSet) - { - mWaitingForReadyFrame = true; - } - - return textureSet; -} - -TextureSet FixedImageCache::NextFrame() -{ - TextureSet textureSet = Frame((mFront + 1) % mImageUrls.size()); + TextureSet textureSet = Frame(FIRST_FRAME_INDEX); return textureSet; } uint32_t FixedImageCache::GetFrameInterval(uint32_t frameIndex) const { - return 0u; + return mInterval; } int32_t FixedImageCache::GetCurrentFrameIndex() const @@ -121,13 +105,11 @@ bool FixedImageCache::IsFrontReady() const void FixedImageCache::LoadBatch() { // Try and load up to mBatchSize images, until the cache is filled. - // Once the cache is filled, mUrlIndex exceeds mImageUrls size and - // no more images are loaded. - bool frontFrameReady = IsFrontReady(); - - for(unsigned int i = 0; i < mBatchSize && mUrlIndex < mImageUrls.size(); ++i) + // Once the cache is filled, no more images are loaded. + for(unsigned int i = 0; i < mBatchSize && mReadyFlags.size() < mImageUrls.size(); ++i) { - std::string& url = mImageUrls[mUrlIndex].mUrl; + uint32_t frameIndex = mReadyFlags.size(); + std::string& url = mImageUrls[frameIndex].mUrl; mReadyFlags.push_back(false); @@ -135,6 +117,7 @@ void FixedImageCache::LoadBatch() // from within this method. This means it won't yet have a texture id, so we // need to account for this inside the LoadComplete method using mRequestingLoad. mRequestingLoad = true; + mLoadState = TextureManager::LoadState::LOADING; bool synchronousLoading = false; bool atlasingStatus = false; @@ -147,28 +130,9 @@ void FixedImageCache::LoadBatch() auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; mTextureManager.LoadTexture( - url, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, maskInfo, synchronousLoading, mImageUrls[mUrlIndex].mTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiply); + url, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, maskInfo, synchronousLoading, mImageUrls[frameIndex].mTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiply); - if(loadingStatus == false) // not loading, means it's already ready. - { - SetImageFrameReady(mImageUrls[mUrlIndex].mTextureId); - } mRequestingLoad = false; - ++mUrlIndex; - } - - CheckFrontFrame(frontFrameReady); -} - -void FixedImageCache::SetImageFrameReady(TextureManager::TextureId textureId) -{ - for(std::size_t i = 0; i < mImageUrls.size(); ++i) - { - if(mImageUrls[i].mTextureId == textureId) - { - mReadyFlags[i] = true; - break; - } } } @@ -179,29 +143,53 @@ TextureSet FixedImageCache::GetFrontTextureSet() const void FixedImageCache::CheckFrontFrame(bool wasReady) { - if(mWaitingForReadyFrame && wasReady == false && IsFrontReady()) + if(wasReady == false && IsFrontReady()) { - mWaitingForReadyFrame = false; - mObserver.FrameReady(GetFrontTextureSet()); + mObserver.FrameReady(GetFrontTextureSet(), mInterval); } } -void FixedImageCache::LoadComplete(bool loadSuccess, TextureInformation textureInformation) +void FixedImageCache::ClearCache() { - bool frontFrameReady = IsFrontReady(); - - if(!mRequestingLoad) + if(mTextureManagerAlive) { - SetImageFrameReady(textureInformation.textureId); + for(std::size_t i = 0; i < mImageUrls.size(); ++i) + { + mTextureManager.Remove(mImageUrls[i].mTextureId, this); + mImageUrls[i].mTextureId = TextureManager::INVALID_TEXTURE_ID; + } + } + mReadyFlags.clear(); + mLoadState = TextureManager::LoadState::NOT_STARTED; +} +void FixedImageCache::LoadComplete(bool loadSuccess, TextureInformation textureInformation) +{ + if(loadSuccess) + { + mLoadState = TextureManager::LoadState::LOAD_FINISHED; + bool frontFrameReady = IsFrontReady(); + if(!mRequestingLoad) + { + for(std::size_t i = 0; i < mImageUrls.size(); ++i) + { + if(mImageUrls[i].mTextureId == textureInformation.textureId) + { + mReadyFlags[i] = true; + break; + } + } + } + else + { + mReadyFlags.back() = true; + } CheckFrontFrame(frontFrameReady); } else { - // LoadComplete has been called from within RequestLoad. TextureManager must - // therefore already have the texture cached, so make the texture ready. - // (Use the last texture, as the texture id hasn't been assigned yet) - mReadyFlags.back() = true; + mLoadState = TextureManager::LoadState::LOAD_FAILED; + mObserver.FrameReady(TextureSet(), 0); } } diff --git a/dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h b/dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h index 6a2394a..2d446f0 100644 --- a/dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h +++ b/dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h @@ -36,6 +36,7 @@ public: * @param[in] urlList List of urls to cache * @param[in] observer FrameReady observer * @param[in] batchSize The size of a batch to load + * @param[in] interval Time interval between each frame * * This will start loading textures immediately, according to the * batch and cache sizes. The cache is as large as the number of urls. @@ -43,79 +44,79 @@ public: FixedImageCache(TextureManager& textureManager, UrlList& urlList, ImageCache::FrameReadyObserver& observer, - unsigned int batchSize); + uint32_t batchSize, + uint32_t interval); ~FixedImageCache() override; /** - * Get the Nth frame. If it's not ready, this will trigger the - * sending of FrameReady() when the image becomes ready. + * @copydoc Internal::ImageCache::Frame() */ TextureSet Frame(uint32_t frameIndex) override; /** - * Get the first frame. If it's not ready, this will trigger the - * sending of FrameReady() when the image becomes ready. + * @copydoc Internal::ImageCache::FirstFrame() */ TextureSet FirstFrame() override; /** - * Get the next frame. If it's not ready, this will trigger the - * sending of FrameReady() when the image becomes ready. - */ - TextureSet NextFrame() override; - - /** - * Get the interval of Nth frame. + * @copydoc Internal::ImageCache::GetFrameInterval() */ uint32_t GetFrameInterval(uint32_t frameIndex) const override; /** - * Get the current rendered frame index. - * If there isn't any loaded frame, returns -1. + * @copydoc Internal::ImageCache::GetCurrentFrameIndex() */ int32_t GetCurrentFrameIndex() const override; /** - * Get total frame count of the animated image file. + * @copydoc Internal::ImageCache::GetTotalFrameCount() */ int32_t GetTotalFrameCount() const override; + /** + * @copydoc Internal::ImageCache::ClearCache() + */ + void ClearCache() override; + private: /** + * @brief Check whether the front frame is ready or not. + * * @return true if the front frame is ready */ bool IsFrontReady() const; /** - * Load the next batch of images + * @brief Load the next batch of images */ void LoadBatch(); /** - * Find the matching image frame, and set it to ready - */ - void SetImageFrameReady(TextureManager::TextureId textureId); - - /** - * Get the texture set of the front frame. - * @return the texture set + * @brief Get the texture set of the front frame. + * + * @return the texture set of the front of Cache. */ TextureSet GetFrontTextureSet() const; /** - * Check if the front frame has become ready - if so, inform observer + * @brief Check if the front frame has become ready - if so, inform observer + * * @param[in] wasReady Readiness before call. */ void CheckFrontFrame(bool wasReady); protected: + /** + * @copydoc Toolkit::TextureUploadObserver::LoadComplete() + */ void LoadComplete(bool loadSuccess, TextureInformation textureInformation) override; private: - std::vector& mImageUrls; - std::vector mReadyFlags; - unsigned int mFront; + std::vector& mImageUrls; + std::vector mReadyFlags; + std::vector mLoadStates; + uint32_t mFront; }; } //namespace Internal diff --git a/dali-toolkit/internal/visuals/animated-image/image-cache.cpp b/dali-toolkit/internal/visuals/animated-image/image-cache.cpp index cccb085..27c3ef2 100644 --- a/dali-toolkit/internal/visuals/animated-image/image-cache.cpp +++ b/dali-toolkit/internal/visuals/animated-image/image-cache.cpp @@ -24,12 +24,13 @@ namespace Internal { ImageCache::ImageCache(TextureManager& textureManager, ImageCache::FrameReadyObserver& observer, - unsigned int batchSize) + uint32_t batchSize, + uint32_t interval) : mTextureManager(textureManager), mObserver(observer), mBatchSize(batchSize), - mUrlIndex(0u), - mWaitingForReadyFrame(false), + mInterval(interval), + mLoadState(TextureManager::LoadState::NOT_STARTED), mRequestingLoad(false), mTextureManagerAlive(true) { @@ -49,6 +50,11 @@ void ImageCache::TextureManagerDestroyed() mTextureManagerAlive = false; } +void ImageCache::SetInterval(uint32_t interval) +{ + mInterval = interval; +} + } //namespace Internal } //namespace Toolkit } //namespace Dali diff --git a/dali-toolkit/internal/visuals/animated-image/image-cache.h b/dali-toolkit/internal/visuals/animated-image/image-cache.h index 3200902..19a8f52 100644 --- a/dali-toolkit/internal/visuals/animated-image/image-cache.h +++ b/dali-toolkit/internal/visuals/animated-image/image-cache.h @@ -37,10 +37,11 @@ public: { public: /** - * Informs observer when the next texture set is ready to display + * @brief Informs observer when the next texture set is ready to display * @param[in] textureSet The ready texture set + * @param[in] interval interval(ms) for the frame */ - virtual void FrameReady(TextureSet textureSet) = 0; + virtual void FrameReady(TextureSet textureSet, uint32_t interval) = 0; }; struct UrlStore @@ -56,69 +57,89 @@ public: public: /** - * Constructor. + * @brief Constructor. * @param[in] textureManager The texture manager * @param[in] urlList List of urls to cache * @param[in] observer FrameReady observer * @param[in] batchSize The size of a batch to load + * @param[in] interval Time interval(ms) between each frame * * This will start loading textures immediately, according to the * batch and cache sizes. The cache is as large as the number of urls. */ ImageCache(TextureManager& textureManager, ImageCache::FrameReadyObserver& observer, - unsigned int batchSize); + uint32_t batchSize, + uint32_t interval); virtual ~ImageCache(); /** - * Get the first frame. If it's not ready, this will trigger the + * @brief Get the first frame. If it's not ready, this will trigger the * sending of FrameReady() when the image becomes ready. + * + * @return TextureSet of the first frame. */ virtual TextureSet FirstFrame() = 0; /** - * Get the next frame. If it's not ready, this will trigger the - * sending of FrameReady() when the image becomes ready. - */ - virtual TextureSet NextFrame() = 0; - - /** - * Get the Nth frame. If it's not ready, this will trigger the + * @brief Get the Nth frame. If it's not ready, this will trigger the * sending of FrameReady() when the image becomes ready. + * + * @param[in] frameIndex required frame index to be returned. + * @return TextureSet of the frame index. */ virtual TextureSet Frame(uint32_t frameIndex) = 0; /** - * Get the interval of Nth frame. + * @brief Get the interval(ms) of Nth frame. + * + * @param[in] frameIndex frame index to get frame interval. + * @return Time interval in millisecond between frames of frameIndex and frameIndex + 1. */ virtual uint32_t GetFrameInterval(uint32_t frameIndex) const = 0; /** - * Get the current rendered frame index. + * @brief Get the current rendered frame index. * If there isn't any loaded frame, returns -1. + * + * @return Frame index of currently showing frame. */ virtual int32_t GetCurrentFrameIndex() const = 0; /** - * Get total frame count of the animated image file. + * @brief Get total frame count of the animated image file. + * + * @return Total frame count of the animated image file. */ virtual int32_t GetTotalFrameCount() const = 0; + /** + * @brief Clears animated image cache and remove loaded textures. + */ + virtual void ClearCache() = 0; + + /** + * @brief Set default interval(ms) between each frame. + * + * @param[in] interval time interval in millisecond to be used as default interval. + */ + virtual void SetInterval(uint32_t interval); + private: /** - * Called before the texture manager is destroyed. + * @brief Called before the texture manager is destroyed. */ void TextureManagerDestroyed() final; protected: - TextureManager& mTextureManager; - FrameReadyObserver& mObserver; - unsigned int mBatchSize; - unsigned int mUrlIndex; - bool mWaitingForReadyFrame : 1; - bool mRequestingLoad : 1; - bool mTextureManagerAlive : 1; + TextureManager& mTextureManager; + FrameReadyObserver& mObserver; + uint32_t mBatchSize; + uint32_t mInterval; + TextureManager::LoadState mLoadState; + bool mRequestingLoad : 1; + bool mTextureManagerAlive : 1; }; } //namespace Internal diff --git a/dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.cpp b/dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.cpp index 490c3be..473f276 100644 --- a/dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.cpp +++ b/dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.cpp @@ -17,8 +17,6 @@ // CLASS HEADER #include "rolling-animated-image-cache.h" -// EXTERNAL HEADERS - // INTERNAL HEADERS #include #include // For ImageAtlasManagerPtr @@ -45,7 +43,7 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, " #define LOG_CACHE #endif -const bool ENABLE_ORIENTATION_CORRECTION(true); +static constexpr bool ENABLE_ORIENTATION_CORRECTION(true); } // namespace @@ -55,32 +53,30 @@ namespace Toolkit { namespace Internal { +namespace +{ +static constexpr uint32_t SINGLE_IMAGE_COUNT = 1u; +static constexpr uint32_t FIRST_FRAME_INDEX = 0u; +} // namespace + RollingAnimatedImageCache::RollingAnimatedImageCache( - TextureManager& textureManager, AnimatedImageLoading& animatedImageLoading, uint32_t frameCount, ImageCache::FrameReadyObserver& observer, uint16_t cacheSize, uint16_t batchSize, bool isSynchronousLoading) -: ImageCache(textureManager, observer, batchSize), + TextureManager& textureManager, AnimatedImageLoading& animatedImageLoading, ImageCache::FrameReadyObserver& observer, uint16_t cacheSize, uint16_t batchSize, bool isSynchronousLoading) +: ImageCache(textureManager, observer, batchSize, 0u), mAnimatedImageLoading(animatedImageLoading), - mFrameCount(frameCount), - mFrameIndex(0), + mFrameCount(SINGLE_IMAGE_COUNT), + mFrameIndex(FIRST_FRAME_INDEX), mCacheSize(cacheSize), mQueue(cacheSize), - mIsSynchronousLoading(isSynchronousLoading), - mOnLoading(false) + mIsSynchronousLoading(isSynchronousLoading) { mImageUrls.resize(mFrameCount); mIntervals.assign(mFrameCount, 0); - LoadBatch(); } RollingAnimatedImageCache::~RollingAnimatedImageCache() { - if(mTextureManagerAlive) - { - while(!mQueue.IsEmpty()) - { - ImageFrame imageFrame = mQueue.PopFront(); - mTextureManager.Remove(mImageUrls[imageFrame.mFrameNumber].mTextureId, this); - } - } + ClearCache(); + mAnimatedImageLoading.Reset(); } TextureSet RollingAnimatedImageCache::Frame(uint32_t frameIndex) @@ -95,53 +91,53 @@ TextureSet RollingAnimatedImageCache::Frame(uint32_t frameIndex) } TextureSet textureSet; + uint32_t batchFrameIndex = frameIndex; // If we need to load new frame that are not stored in queue. // Load the frame synchronously. + bool synchronouslyLoaded = false; if(mIsSynchronousLoading && mQueue.IsEmpty()) { - bool synchronousLoading = true; - textureSet = mTextureManager.LoadAnimatedImageTexture(mAnimatedImageLoading, frameIndex, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, mImageUrls[frameIndex].mTextureId, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this); - mFrameIndex = (frameIndex + 1) % mFrameCount; + textureSet = RequestFrameLoading(frameIndex, frameIndex == FIRST_FRAME_INDEX, true); + batchFrameIndex = (frameIndex + 1) % mFrameCount; + uint32_t interval = 0u; + if(textureSet) + { + synchronouslyLoaded = true; + interval = mAnimatedImageLoading.GetFrameInterval(mQueue.Back().mFrameNumber); + } + MakeFrameReady(synchronouslyLoaded, textureSet, interval); } - if(popExist || mQueue.IsEmpty()) + if(popExist || mQueue.IsEmpty() || synchronouslyLoaded) { // If the frame of frameIndex was already loaded, load batch from the last frame of queue if(!mQueue.IsEmpty()) { if(!mLoadWaitingQueue.empty()) { - mFrameIndex = (mLoadWaitingQueue.back() + 1) % mFrameCount; + batchFrameIndex = (mLoadWaitingQueue.back() + 1) % mFrameCount; } else { - mFrameIndex = (mQueue.Back().mFrameNumber + 1) % mFrameCount; + batchFrameIndex = (mQueue.Back().mFrameNumber + 1) % mFrameCount; } } else { - mOnLoading = false; // If the request is for the first frame or a jumped frame(JUMP_TO) remove current waiting queue. mLoadWaitingQueue.clear(); // If the queue is empty, and the frame of frameIndex is not loaded synchronously. load batch from the frame of frameIndex if(!textureSet) { - mFrameIndex = frameIndex; + batchFrameIndex = frameIndex; } } - LoadBatch(); + LoadBatch(batchFrameIndex); } - if(!textureSet) + if(!textureSet && mLoadState != TextureManager::LoadState::LOAD_FAILED && IsFrontReady() == true) { - if(IsFrontReady() == true) - { - textureSet = GetFrontTextureSet(); - } - else - { - mWaitingForReadyFrame = true; - } + textureSet = GetFrontTextureSet(); } return textureSet; @@ -149,32 +145,17 @@ TextureSet RollingAnimatedImageCache::Frame(uint32_t frameIndex) TextureSet RollingAnimatedImageCache::FirstFrame() { - return Frame(0u); -} - -TextureSet RollingAnimatedImageCache::NextFrame() -{ - TextureSet textureSet; - if(!mQueue.IsEmpty()) - { - uint32_t frameIndex = mQueue.Front().mFrameNumber; - if(IsFrontReady()) - { - frameIndex = (frameIndex + 1) % mFrameCount; - } - textureSet = Frame(frameIndex); - } - else - { - DALI_LOG_ERROR("Cache is empty."); - } - + TextureSet textureSet = Frame(FIRST_FRAME_INDEX); return textureSet; } uint32_t RollingAnimatedImageCache::GetFrameInterval(uint32_t frameIndex) const { - return mAnimatedImageLoading.GetFrameInterval(frameIndex); + if(frameIndex >= mIntervals.size()) + { + return 0u; + } + return mIntervals[frameIndex]; } int32_t RollingAnimatedImageCache::GetCurrentFrameIndex() const @@ -196,7 +177,7 @@ bool RollingAnimatedImageCache::IsFrontReady() const return (!mQueue.IsEmpty() && mQueue.Front().mReady); } -void RollingAnimatedImageCache::RequestFrameLoading(uint32_t frameIndex) +TextureSet RollingAnimatedImageCache::RequestFrameLoading(uint32_t frameIndex, bool useCache, bool synchronousLoading) { ImageFrame imageFrame; imageFrame.mFrameNumber = frameIndex; @@ -204,39 +185,45 @@ void RollingAnimatedImageCache::RequestFrameLoading(uint32_t frameIndex) mQueue.PushBack(imageFrame); - mRequestingLoad = true; + mLoadState = TextureManager::LoadState::LOADING; + + TextureManager::TextureId loadTextureId = TextureManager::INVALID_TEXTURE_ID; + TextureSet textureSet = mTextureManager.LoadAnimatedImageTexture(mAnimatedImageLoading, + frameIndex, + loadTextureId, + SamplingMode::BOX_THEN_LINEAR, + Dali::WrapMode::Type::DEFAULT, + Dali::WrapMode::Type::DEFAULT, + synchronousLoading, + useCache, + this); - bool synchronousLoading = false; - mTextureManager.LoadAnimatedImageTexture(mAnimatedImageLoading, frameIndex, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, mImageUrls[frameIndex].mTextureId, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this); + mImageUrls[frameIndex].mTextureId = loadTextureId; - mRequestingLoad = false; + return textureSet; } -void RollingAnimatedImageCache::LoadBatch() +void RollingAnimatedImageCache::LoadBatch(uint32_t frameIndex) { // Try and load up to mBatchSize images, until the cache is filled. // Once the cache is filled, as frames progress, the old frame is // removed, and another frame is loaded - - bool frontFrameReady = IsFrontReady(); - for(unsigned int i = 0; i < mBatchSize && mQueue.Count() + mLoadWaitingQueue.size() < static_cast(mCacheSize) && !mQueue.IsFull(); ++i) + uint32_t minimumSize = std::min(mCacheSize, mFrameCount); + for(uint32_t i = 0; i < mBatchSize && (mQueue.Count() + mLoadWaitingQueue.size()) < minimumSize; ++i) { - if(!mOnLoading) + if(mLoadState != TextureManager::LoadState::LOADING) { - mOnLoading = true; - RequestFrameLoading(mFrameIndex); + RequestFrameLoading(frameIndex, frameIndex == FIRST_FRAME_INDEX, false); } else { - mLoadWaitingQueue.push_back(mFrameIndex); + mLoadWaitingQueue.push_back(frameIndex); } - mFrameIndex++; - mFrameIndex %= mFrameCount; + frameIndex++; + frameIndex %= mFrameCount; } - CheckFrontFrame(frontFrameReady); - LOG_CACHE; } @@ -265,46 +252,84 @@ TextureManager::TextureId RollingAnimatedImageCache::GetCachedTextureId(int inde return mImageUrls[mQueue[index].mFrameNumber].mTextureId; } -void RollingAnimatedImageCache::CheckFrontFrame(bool wasReady) +void RollingAnimatedImageCache::ClearCache() { - if(mWaitingForReadyFrame && wasReady == false && IsFrontReady()) + while(mTextureManagerAlive && !mQueue.IsEmpty()) { - mWaitingForReadyFrame = false; - mObserver.FrameReady(GetFrontTextureSet()); + ImageFrame imageFrame = mQueue.PopFront(); + mTextureManager.Remove(mImageUrls[imageFrame.mFrameNumber].mTextureId, this); + mImageUrls[imageFrame.mFrameNumber].mTextureId = TextureManager::INVALID_TEXTURE_ID; } + mLoadWaitingQueue.clear(); + mLoadState = TextureManager::LoadState::NOT_STARTED; } -void RollingAnimatedImageCache::LoadComplete(bool loadSuccess, TextureInformation textureInformation) +void RollingAnimatedImageCache::MakeFrameReady(bool loadSuccess, TextureSet textureSet, uint32_t interval) { - DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::LoadComplete(textureId:%d) start\n", textureInformation.textureId); - LOG_CACHE; - - bool frontFrameReady = IsFrontReady(); - - if(!mRequestingLoad) + if(!loadSuccess) { - SetImageFrameReady(textureInformation.textureId); - - CheckFrontFrame(frontFrameReady); + mLoadState = TextureManager::LoadState::LOAD_FAILED; + mObserver.FrameReady(TextureSet(), 0); } else { - // LoadComplete has been called from within RequestLoad. TextureManager must - // therefore already have the texture cached, so make the texture ready. - // (Use the last texture, as the texture id hasn't been assigned yet) - mQueue.Back().mReady = true; + mLoadState = TextureManager::LoadState::LOAD_FINISHED; + + // Reset size of Queue according to the real frame count. + if(mFrameCount != mAnimatedImageLoading.GetImageCount()) + { + mFrameCount = mAnimatedImageLoading.GetImageCount(); + mImageUrls.resize(mFrameCount); + mIntervals.assign(mFrameCount, 0u); + } + + bool frontFrameReady = IsFrontReady(); + // Because only one frame is on loading and the others are in mLoadWaitingQueue, + // mQueue.Back() is always the frame currently loaded. + mQueue.Back().mReady = true; + mIntervals[mQueue.Back().mFrameNumber] = interval; + // Check whether currently loaded frame is front of queue or not. + // If it is, notify frame ready to observer. + if(frontFrameReady == false && IsFrontReady()) + { + mObserver.FrameReady(textureSet, interval); + } } +} - mOnLoading = false; - // The frames of a single animated image can not be loaded parallelly. - // Therefore, a frame is now loading, other orders are waiting. - // And, after the frame is loaded, requests load of next order. - if(!mLoadWaitingQueue.empty()) +void RollingAnimatedImageCache::LoadComplete(bool loadSuccess, TextureInformation textureInformation) +{ + DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::LoadComplete(textureId:%d) start\n", textureInformation.textureId); + LOG_CACHE; + + MakeFrameReady(loadSuccess, mTextureManager.GetTextureSet(textureInformation.textureId), textureInformation.interval); + + if(loadSuccess) { - uint32_t loadingIndex = mLoadWaitingQueue.front(); - mLoadWaitingQueue.erase(mLoadWaitingQueue.begin()); - mOnLoading = true; - RequestFrameLoading(loadingIndex); + // The frames of a single animated image can not be loaded parallelly. + // Therefore, a frame is now loading, other orders are waiting. + // And, after the frame is loaded, requests load of next order. + if(!mLoadWaitingQueue.empty()) + { + uint32_t loadingIndex = mLoadWaitingQueue.front(); + mLoadWaitingQueue.erase(mLoadWaitingQueue.begin()); + RequestFrameLoading(loadingIndex, loadingIndex == FIRST_FRAME_INDEX, false); + } + else if(mQueue.Count() == 1u && textureInformation.frameCount > SINGLE_IMAGE_COUNT) + { + // There is only an image in queue and no waiting queue. + // Request to load batch once again. + uint32_t batchFrameIndex = 0u; + if(!mLoadWaitingQueue.empty()) + { + batchFrameIndex = (mLoadWaitingQueue.back() + 1) % mFrameCount; + } + else + { + batchFrameIndex = (mQueue.Back().mFrameNumber + 1) % mFrameCount; + } + LoadBatch(batchFrameIndex); + } } LOG_CACHE; diff --git a/dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h b/dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h index 08aec52..605fbb9 100644 --- a/dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h +++ b/dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h @@ -40,10 +40,9 @@ class RollingAnimatedImageCache : public ImageCache, public TextureUploadObserve { public: /** - * Constructor. + * @brief Constructor. * @param[in] textureManager The texture manager * @param[in] animatedImageLoader The loaded animated image - * @param[in] frameCount The number of frames in the animated image * @param[in] observer FrameReady observer * @param[in] cacheSize The size of the cache * @param[in] batchSize The size of a batch to load @@ -54,112 +53,128 @@ public: */ RollingAnimatedImageCache(TextureManager& textureManager, AnimatedImageLoading& animatedImageLoader, - uint32_t frameCount, ImageCache::FrameReadyObserver& observer, uint16_t cacheSize, uint16_t batchSize, bool isSynchronousLoading); /** - * Destructor + * @brief Destructor */ ~RollingAnimatedImageCache() override; /** - * Get the Nth frame. If it's not ready, this will trigger the - * sending of FrameReady() when the image becomes ready. + * @copydoc Internal::ImageCache::Frame() */ TextureSet Frame(uint32_t frameIndex) override; /** - * Get the first frame. If it's not ready, this will trigger the - * sending of FrameReady() when the image becomes ready. + * @copydoc Internal::ImageCache::FirstFrame() */ TextureSet FirstFrame() override; /** - * Get the next frame. If it's not ready, this will trigger the - * sending of FrameReady() when the image becomes ready. - */ - TextureSet NextFrame() override; - - /** - * Get the interval of Nth frame. + * @copydoc Internal::ImageCache::GetFrameInterval() */ uint32_t GetFrameInterval(uint32_t frameIndex) const override; /** - * Get the current rendered frame index. - * If there isn't any loaded frame, returns -1. + * @copydoc Internal::ImageCache::GetCurrentFrameIndex() */ int32_t GetCurrentFrameIndex() const override; /** - * Get total frame count of the animated image file. + * @copydoc Internal::ImageCache::GetTotalFrameCount() */ int32_t GetTotalFrameCount() const override; + /** + * @copydoc Internal::ImageCache::ClearCache() + */ + void ClearCache() override; + private: /** + * @brief Check whether the front frame is ready or not. + * * @return true if the front frame is ready */ bool IsFrontReady() const; /** - * Request to Load a frame + * @brief Request to Load a frame + * + * @param[in] frameIndex index of frame to be loaded. + * @param[in] useCache true if this frame loading uses cache. + * @param[in] synchronousLoading true if the frame should be loaded synchronously + * + * @return the texture set currently loaded. */ - void RequestFrameLoading(uint32_t frameIndex); + TextureSet RequestFrameLoading(uint32_t frameIndex, bool useCache, bool synchronousLoading); /** - * Load the next batch of images + * @brief Load the next batch of images + * + * @param[in] frameIndex starting frame index of batch to be loaded. */ - void LoadBatch(); + void LoadBatch(uint32_t frameIndex); /** - * Find the matching image frame, and set it to ready + * @brief Find the matching image frame, and set it to ready + * + * @param[in] textureId texture id to be marked as ready. */ void SetImageFrameReady(TextureManager::TextureId textureId); /** - * Get the texture set of the front frame. - * @return the texture set + * @brief Get the texture set of the front frame. + * + * @return the texture set of the front of Cache. */ TextureSet GetFrontTextureSet() const; /** - * Get the texture id of the given index + * @brief Get the texture id of the given index + * + * @param[in] index index of the queue. */ TextureManager::TextureId GetCachedTextureId(int index) const; /** - * Check if the front frame has become ready - if so, inform observer - * @param[in] wasReady Readiness before call. + * @brief Make the loaded frame ready and notify it to the texture upload observer + * + * @param[in] loadSuccess whether the loading is succeded or not. + * @param[in] textureSet textureSet for this frame. + * @param[in] interval interval between this frame and next frame. */ - void CheckFrontFrame(bool wasReady); + void MakeFrameReady(bool loadSuccess, TextureSet textureSet, uint32_t interval); protected: + /** + * @copydoc Toolkit::TextureUploadObserver::LoadComplete() + */ void LoadComplete(bool loadSuccess, TextureInformation textureInformation) override; private: + /** * Secondary class to hold readiness and index into url */ struct ImageFrame { - unsigned int mFrameNumber = 0u; - bool mReady = false; + uint32_t mFrameNumber = 0u; + bool mReady = false; }; Dali::AnimatedImageLoading mAnimatedImageLoading; uint32_t mFrameCount; - int mFrameIndex; - int mCacheSize; + uint32_t mFrameIndex; + uint32_t mCacheSize; std::vector mImageUrls; std::vector mIntervals; std::vector mLoadWaitingQueue; CircularQueue mQueue; bool mIsSynchronousLoading; - bool mOnLoading; }; } // namespace Internal diff --git a/dali-toolkit/internal/visuals/animated-image/rolling-image-cache.cpp b/dali-toolkit/internal/visuals/animated-image/rolling-image-cache.cpp index caecf39..08fe0d0 100644 --- a/dali-toolkit/internal/visuals/animated-image/rolling-image-cache.cpp +++ b/dali-toolkit/internal/visuals/animated-image/rolling-image-cache.cpp @@ -44,7 +44,9 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, " #define LOG_CACHE #endif -const bool ENABLE_ORIENTATION_CORRECTION(true); +static constexpr bool ENABLE_ORIENTATION_CORRECTION(true); + +static constexpr uint32_t FIRST_FRAME_INDEX = 0u; } // namespace @@ -55,100 +57,60 @@ namespace Toolkit namespace Internal { RollingImageCache::RollingImageCache( - TextureManager& textureManager, UrlList& urlList, ImageCache::FrameReadyObserver& observer, uint16_t cacheSize, uint16_t batchSize) -: ImageCache(textureManager, observer, batchSize), + TextureManager& textureManager, UrlList& urlList, ImageCache::FrameReadyObserver& observer, uint16_t cacheSize, uint16_t batchSize, uint32_t interval) +: ImageCache(textureManager, observer, batchSize, interval), mImageUrls(urlList), mQueue(cacheSize) { - LoadBatch(); } RollingImageCache::~RollingImageCache() { - if(mTextureManagerAlive) - { - while(!mQueue.IsEmpty()) - { - ImageFrame imageFrame = mQueue.PopFront(); - mTextureManager.Remove(mImageUrls[imageFrame.mUrlIndex].mTextureId, this); - } - } + ClearCache(); } TextureSet RollingImageCache::Frame(uint32_t frameIndex) { - // If a frame of frameIndex is not loaded, clear the queue and remove all loaded textures. - if(mImageUrls[frameIndex].mTextureId == TextureManager::INVALID_TEXTURE_ID) + // Pop frames until the frame of frameIndex become front frame. + bool popExist = false; + while(!mQueue.IsEmpty() && mQueue.Front().mUrlIndex != frameIndex) { - mUrlIndex = frameIndex; - while(!mQueue.IsEmpty()) - { - ImageFrame imageFrame = mQueue.PopFront(); - mTextureManager.Remove(mImageUrls[imageFrame.mUrlIndex].mTextureId, this); - mImageUrls[imageFrame.mUrlIndex].mTextureId = TextureManager::INVALID_TEXTURE_ID; - } - LoadBatch(); + ImageFrame imageFrame = mQueue.PopFront(); + mTextureManager.Remove(mImageUrls[imageFrame.mUrlIndex].mTextureId, this); + mImageUrls[imageFrame.mUrlIndex].mTextureId = TextureManager::INVALID_TEXTURE_ID; + popExist = true; } - // If the frame is already loaded, remove previous frames of the frame in the queue - // and load new frames amount of removed frames. - else + + // TODO: synchronous loading of first frame. + if(popExist || mQueue.IsEmpty()) { - bool popExist = false; - while(!mQueue.IsEmpty() && mQueue.Front().mUrlIndex != frameIndex) - { - ImageFrame imageFrame = mQueue.PopFront(); - mTextureManager.Remove(mImageUrls[imageFrame.mUrlIndex].mTextureId, this); - mImageUrls[imageFrame.mUrlIndex].mTextureId = TextureManager::INVALID_TEXTURE_ID; - popExist = true; - } - if(popExist) + uint32_t batchFrameIndex = frameIndex; + // If the frame of frameIndex was already loaded, load batch from the last frame of queue + if(!mQueue.IsEmpty()) { - mUrlIndex = (mQueue.Back().mUrlIndex + 1) % mImageUrls.size(); - LoadBatch(); + batchFrameIndex = (mQueue.Back().mUrlIndex + 1) % mImageUrls.size(); } + LoadBatch(batchFrameIndex); } TextureSet textureSet; - if(IsFrontReady() == true) + if(IsFrontReady() == true && mLoadState != TextureManager::LoadState::LOAD_FAILED) { textureSet = GetFrontTextureSet(); } - else - { - mWaitingForReadyFrame = true; - } return textureSet; } TextureSet RollingImageCache::FirstFrame() { - return Frame(0u); -} - -TextureSet RollingImageCache::NextFrame() -{ - TextureSet textureSet; - if(!mQueue.IsEmpty()) - { - uint32_t frameIndex = mQueue.Front().mUrlIndex; - if(IsFrontReady()) - { - frameIndex = (frameIndex + 1) % mImageUrls.size(); - } - textureSet = Frame(frameIndex); - } - else - { - DALI_LOG_ERROR("Cache is empty."); - } - + TextureSet textureSet = Frame(FIRST_FRAME_INDEX); return textureSet; } uint32_t RollingImageCache::GetFrameInterval(uint32_t frameIndex) const { - return 0u; + return mInterval; } int32_t RollingImageCache::GetCurrentFrameIndex() const @@ -170,30 +132,26 @@ bool RollingImageCache::IsFrontReady() const return (!mQueue.IsEmpty() && mQueue.Front().mReady); } -void RollingImageCache::LoadBatch() +void RollingImageCache::LoadBatch(uint32_t frameIndex) { // Try and load up to mBatchSize images, until the cache is filled. // Once the cache is filled, as frames progress, the old frame is // cleared, but not erased, and another image is loaded - bool frontFrameReady = IsFrontReady(); - for(unsigned int i = 0; i < mBatchSize && !mQueue.IsFull(); ++i) { ImageFrame imageFrame; - std::string& url = mImageUrls[mUrlIndex].mUrl; - imageFrame.mUrlIndex = mUrlIndex; + std::string& url = mImageUrls[frameIndex].mUrl; + imageFrame.mUrlIndex = frameIndex; imageFrame.mReady = false; - ++mUrlIndex; - mUrlIndex %= mImageUrls.size(); - mQueue.PushBack(imageFrame); // Note, if the image is already loaded, then LoadComplete will get called // from within this method. This means it won't yet have a texture id, so we // need to account for this inside the LoadComplete method using mRequestingLoad. mRequestingLoad = true; + mLoadState = TextureManager::LoadState::LOADING; bool synchronousLoading = false; bool atlasingStatus = false; @@ -205,24 +163,15 @@ void RollingImageCache::LoadBatch() Dali::ImageDimensions textureRectSize; auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - mTextureManager.LoadTexture( - url, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, maskInfo, synchronousLoading, mImageUrls[imageFrame.mUrlIndex].mTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiply); + TextureManager::TextureId loadTextureId = TextureManager::INVALID_TEXTURE_ID; + TextureSet textureSet = mTextureManager.LoadTexture( + url, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, maskInfo, synchronousLoading, loadTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiply); + mImageUrls[imageFrame.mUrlIndex].mTextureId = loadTextureId; mRequestingLoad = false; - } - CheckFrontFrame(frontFrameReady); -} - -void RollingImageCache::SetImageFrameReady(TextureManager::TextureId textureId) -{ - for(std::size_t i = 0; i < mQueue.Count(); ++i) - { - if(GetCachedTextureId(i) == textureId) - { - mQueue[i].mReady = true; - break; - } + ++frameIndex; + frameIndex %= mImageUrls.size(); } } @@ -237,13 +186,15 @@ TextureManager::TextureId RollingImageCache::GetCachedTextureId(int index) const return mImageUrls[mQueue[index].mUrlIndex].mTextureId; } -void RollingImageCache::CheckFrontFrame(bool wasReady) +void RollingImageCache::ClearCache() { - if(mWaitingForReadyFrame && wasReady == false && IsFrontReady()) + while(mTextureManagerAlive && !mQueue.IsEmpty()) { - mWaitingForReadyFrame = false; - mObserver.FrameReady(GetFrontTextureSet()); + ImageFrame imageFrame = mQueue.PopFront(); + mTextureManager.Remove(mImageUrls[imageFrame.mUrlIndex].mTextureId, this); + mImageUrls[imageFrame.mUrlIndex].mTextureId = TextureManager::INVALID_TEXTURE_ID; } + mLoadState = TextureManager::LoadState::NOT_STARTED; } void RollingImageCache::LoadComplete(bool loadSuccess, TextureInformation textureInformation) @@ -251,20 +202,38 @@ void RollingImageCache::LoadComplete(bool loadSuccess, TextureInformation textur DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::LoadComplete(textureId:%d) start\n", textureInformation.textureId); LOG_CACHE; - bool frontFrameReady = IsFrontReady(); - - if(!mRequestingLoad) + if(loadSuccess) { - SetImageFrameReady(textureInformation.textureId); + mLoadState = TextureManager::LoadState::LOAD_FINISHED; + bool frontFrameReady = IsFrontReady(); + if(!mRequestingLoad) + { + for(std::size_t i = 0; i < mQueue.Count(); ++i) + { + if(GetCachedTextureId(i) == textureInformation.textureId) + { + mQueue[i].mReady = true; + break; + } + } + } + else + { + // LoadComplete has been called from within RequestLoad. TextureManager must + // therefore already have the texture cached, so make the texture ready. + // (Use the last texture, as the texture id hasn't been assigned yet) + mQueue.Back().mReady = true; + } - CheckFrontFrame(frontFrameReady); + if(!frontFrameReady && IsFrontReady()) + { + mObserver.FrameReady(mTextureManager.GetTextureSet(textureInformation.textureId), mInterval); + } } else { - // LoadComplete has been called from within RequestLoad. TextureManager must - // therefore already have the texture cached, so make the texture ready. - // (Use the last texture, as the texture id hasn't been assigned yet) - mQueue.Back().mReady = true; + mLoadState = TextureManager::LoadState::LOAD_FAILED; + mObserver.FrameReady(TextureSet(), 0); } LOG_CACHE; diff --git a/dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h b/dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h index 7647e31..98ba45c 100644 --- a/dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h +++ b/dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h @@ -42,6 +42,7 @@ public: * @param[in] observer FrameReady observer * @param[in] cacheSize The size of the cache * @param[in] batchSize The size of a batch to load + * @param[in] interval Time interval between each frame * * This will start loading textures immediately, according to the * batch and cache sizes. @@ -50,7 +51,8 @@ public: UrlList& urlList, ImageCache::FrameReadyObserver& observer, uint16_t cacheSize, - uint16_t batchSize); + uint16_t batchSize, + uint32_t interval); /** * Destructor @@ -58,73 +60,75 @@ public: ~RollingImageCache() override; /** - * Get the Nth frame. If it's not ready, this will trigger the - * sending of FrameReady() when the image becomes ready. + * @copydoc Internal::ImageCache::Frame() */ TextureSet Frame(uint32_t frameIndex) override; /** - * Get the first frame. If it's not ready, this will trigger the - * sending of FrameReady() when the image becomes ready. + * @copydoc Internal::ImageCache::FirstFrame() */ TextureSet FirstFrame() override; /** - * Get the next frame. If it's not ready, this will trigger the - * sending of FrameReady() when the image becomes ready. - */ - TextureSet NextFrame() override; - - /** - * Get the interval of Nth frame. + * @copydoc Internal::ImageCache::GetFrameInterval() */ uint32_t GetFrameInterval(uint32_t frameIndex) const override; /** - * Get the current rendered frame index. - * If there isn't any loaded frame, returns -1. + * @copydoc Internal::ImageCache::GetCurrentFrameIndex() */ int32_t GetCurrentFrameIndex() const override; /** - * Get total frame count of the animated image file. + * @copydoc Internal::ImageCache::GetTotalFrameCount() */ int32_t GetTotalFrameCount() const override; -private: /** - * @return true if the front frame is ready + * @copydoc Internal::ImageCache::ClearCache() */ - bool IsFrontReady() const; + void ClearCache() override; +private: /** - * Load the next batch of images + * @brief Check whether the front frame is ready or not. + * + * @return true if the front frame is ready */ - void LoadBatch(); + bool IsFrontReady() const; /** - * Find the matching image frame, and set it to ready + * @brief Load the next batch of images + * + * @param[in] frameIndex starting frame index of batch to be loaded. */ - void SetImageFrameReady(TextureManager::TextureId textureId); + void LoadBatch(uint32_t frameIndex); /** - * Get the texture set of the front frame. - * @return the texture set + * @brief Get the texture set of the front frame. + * + * @return the texture set of the front of Cache. */ TextureSet GetFrontTextureSet() const; /** - * Get the texture id of the given index + * @brief Get the texture id of the given index + * + * @param[in] index index of the queue. */ TextureManager::TextureId GetCachedTextureId(int index) const; /** - * Check if the front frame has become ready - if so, inform observer + * @brief Check if the front frame has become ready - if so, inform observer + * * @param[in] wasReady Readiness before call. */ void CheckFrontFrame(bool wasReady); protected: + /** + * @copydoc Toolkit::TextureUploadObserver::LoadComplete() + */ void LoadComplete(bool loadSuccess, TextureInformation textureInformation) override; private: @@ -137,8 +141,8 @@ private: bool mReady = false; }; - std::vector& mImageUrls; - CircularQueue mQueue; + std::vector& mImageUrls; + CircularQueue mQueue; }; } // namespace Internal -- 2.7.4