- Animated image file will be opened when the visual is on scene.
- Cache first frame only to cache single frame image as like image visual.
- 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: I86548fc0d1952a70959f710a7005eb1d4b66624b
Signed-off-by: seungho <sbsh.baek@samsung.com>
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");
*
* | New
* | DoSetProperties()
- * | LoadFirstBatch()
* | new cache
- * | cache->LoadBatch()
* |
* | DoSetOnScene()
* | PrepareTextureSet()
* | StartFirstFrame()
* |
* | FrameReady(textureSet)
- * | start first frame:
+ * | StartFirstFrame:
* | actor.AddRenderer
* | start timer
* | mRenderer.SetTextures(textureSet)
* | 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)
visual->InitializeAnimatedImage(imageUrl);
visual->SetProperties(properties);
- if(visual->mFrameCount > 0)
- {
- visual->LoadFirstBatch();
- }
-
visual->Initialize();
return visual;
visual->mFrameCount = imageUrls.Count();
visual->SetProperties(properties);
- if(visual->mFrameCount > 0)
- {
- visual->LoadFirstBatch();
- }
-
visual->Initialize();
return visual;
AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory));
visual->InitializeAnimatedImage(imageUrl);
- if(visual->mFrameCount > 0)
- {
- visual->LoadFirstBatch();
- }
-
visual->Initialize();
return visual;
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)
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),
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;
}
{
if(mImageUrl.IsValid())
{
- mImageSize = mAnimatedImageLoading.GetImageSize();
+ if(mAnimatedImageLoading.HasLoadingSucceeded())
+ {
+ mImageSize = mAnimatedImageLoading.GetImageSize();
+ }
+ else
+ {
+ mImageSize = Dali::GetClosestImageSize(mImageUrl.GetUrl());
+ }
}
else if(mImageUrls && mImageUrls->size() > 0)
{
map.Insert(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>(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
// 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();
{
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,
if(value.Get(frameDelay))
{
mFrameDelay = frameDelay;
+ if(mImageCache)
+ {
+ mImageCache->SetInterval(static_cast<uint32_t>(mFrameDelay));
+ }
}
break;
}
}
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()))
+ if(!mImageCache || mImageCache->GetLoadState() == TextureManager::LoadState::LOAD_FAILED)
{
textureSet = SetLoadingFailed();
}
- if(textureSet) // if the image loading is successful
+ // If textureSet is prepared and first frame still not started,
+ // make first frame start.
+ if(textureSet && mStartFirstFrame)
{
- StartFirstFrame(textureSet);
- }
- else
- {
- mStartFirstFrame = true;
+ StartFirstFrame(textureSet, mImageCache->GetFrameInterval(FIRST_FRAME_INDEX));
}
}
}
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()
void AnimatedImageVisual::OnInitialize()
{
+ CreateImageCache();
+
bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
Shader shader = GenerateShader();
}
}
-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");
}
}
- 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()
textureSet = mImageCache->FirstFrame();
}
+ // Check whether synchronous loading is true or false for the first frame.
if(textureSet)
{
SetImageSize(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)
{
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);
}
}
{
TextureSet textureSet;
bool continueTimer = false;
-
if(mImageCache)
{
- bool nextFrame = false;
uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
if(mIsJumpTo)
}
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)
{
}
else
{
- if(mFrameCount > 1)
+ if(mFrameCount > SINGLE_IMAGE_COUNT)
{
- nextFrame = true;
frameIndex++;
if(frameIndex >= mFrameCount)
{
- frameIndex %= mFrameCount;
+ frameIndex = FIRST_FRAME_INDEX;
++mCurrentLoopIndex;
}
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;
private:
/**
- * Creates the renderer for the animated image
+ * @brief Creates the renderer for the animated image
*/
void CreateRenderer();
/**
- * Starts the Load of the first batch of URLs
- */
- void LoadFirstBatch();
-
- /**
- * 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 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
+ * @return Prepared texture set if it is loaded.
*/
TextureSet 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 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.
+ * @brief 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 Create image cache for animated image or image array.
+ */
+ void CreateImageCache();
+
+ /**
+ * @brief Set the state of loading fail of an image or a frame.
+ * @return TextureSet of broken image.
*/
TextureSet SetLoadingFailed();
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;
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
// INTERNAL HEADERS
#include <dali-toolkit/internal/visuals/image-atlas-manager.h> // For ImageAtlasManagerPtr
+// EXTERNAL HEADERS
+#include <dali/integration-api/debug.h>
+
namespace Dali
{
namespace Toolkit
{
namespace
{
-const bool ENABLE_ORIENTATION_CORRECTION(true);
+static constexpr bool ENABLE_ORIENTATION_CORRECTION(true);
+
+static 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)
{
+ mLoadStates.assign(mImageUrls.size(), TextureManager::LoadState::NOT_STARTED);
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)
+ while(frameIndex > mFront || mReadyFlags.empty() ||
+ (frameIndex == FIRST_FRAME_INDEX && mFront != FIRST_FRAME_INDEX))
{
++mFront;
if(mFront >= mImageUrls.size())
mFront = frameIndex;
TextureSet textureSet;
- if(IsFrontReady() == true)
+ if(IsFrontReady())
{
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
return mImageUrls.size();
}
+TextureManager::LoadState FixedImageCache::GetLoadState() const
+{
+ return mLoadStates[mFront];
+}
+
bool FixedImageCache::IsFrontReady() const
{
return (mReadyFlags.size() > 0 && mReadyFlags[mFront] == true);
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);
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[mUrlIndex].mTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiply);
+ TextureSet textureSet = mTextureManager.LoadTexture(
+ 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.
+ // If textureSet is returned but loadingState is false than load state is LOAD_FINISHED. (Notification is not comming yet.)
+ // If textureSet is null and the request is synchronous, load state is LOAD_FAILED.
+ // If textureSet is null but the request is asynchronous, the frame is still loading so load state is LOADING.
+ mLoadStates[frameIndex] = TextureManager::LoadState::LOADING;
+ if(textureSet)
+ {
+ if(!loadingStatus)
+ {
+ SetImageFrameReady(mImageUrls[frameIndex].mTextureId, true);
+ }
+ }
+ else if(synchronousLoading)
{
- SetImageFrameReady(mImageUrls[mUrlIndex].mTextureId);
+ // Synchronous loading is failed
+ mLoadStates[frameIndex] = TextureManager::LoadState::LOAD_FAILED;
}
+
mRequestingLoad = false;
- ++mUrlIndex;
}
-
- CheckFrontFrame(frontFrameReady);
}
-void FixedImageCache::SetImageFrameReady(TextureManager::TextureId textureId)
+void FixedImageCache::SetImageFrameReady(TextureManager::TextureId textureId, bool loadSuccess)
{
for(std::size_t i = 0; i < mImageUrls.size(); ++i)
{
if(mImageUrls[i].mTextureId == textureId)
{
+ if(loadSuccess)
+ {
+ mLoadStates[i] = TextureManager::LoadState::LOAD_FINISHED;
+ }
+ else
+ {
+ mLoadStates[i] = TextureManager::LoadState::LOAD_FAILED;
+ }
mReadyFlags[i] = true;
break;
}
void FixedImageCache::CheckFrontFrame(bool wasReady)
{
- if(mWaitingForReadyFrame && wasReady == false && IsFrontReady())
+ if(wasReady == false && IsFrontReady())
+ {
+ mObserver.FrameReady(GetFrontTextureSet(), mInterval);
+ }
+}
+
+void FixedImageCache::ClearCache()
+{
+ if(mTextureManagerAlive)
{
- mWaitingForReadyFrame = false;
- mObserver.FrameReady(GetFrontTextureSet());
+ 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();
+ mLoadStates.assign(mImageUrls.size(), TextureManager::LoadState::NOT_STARTED);
}
void FixedImageCache::UploadComplete(
bool preMultiplied)
{
bool frontFrameReady = IsFrontReady();
-
+ SetImageFrameReady(textureId, loadSuccess);
if(!mRequestingLoad)
{
- SetImageFrameReady(textureId);
-
CheckFrontFrame(frontFrameReady);
}
- else
- {
- // UploadComplete 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;
- }
-}
-
-void FixedImageCache::LoadComplete(
- bool loadSuccess,
- Devel::PixelBuffer pixelBuffer,
- const VisualUrl& url,
- bool preMultiplied)
-{
- // LoadComplete is called if this TextureUploadObserver requested to load
- // an image that will be returned as a type of PixelBuffer by using a method
- // TextureManager::LoadPixelBuffer.
}
} //namespace Internal
* @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.
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::GetLoadState()
+ */
+ TextureManager::LoadState GetLoadState() 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
+ * @brief Find the matching image frame, and set it to ready
+ *
+ * @param[in] textureId texture id to be marked as ready.
+ * @param[in] loadSuccess true if the frame loading is succeeded.
*/
- void SetImageFrameReady(TextureManager::TextureId textureId);
+ void SetImageFrameReady(TextureManager::TextureId textureId, bool loadSuccess);
/**
- * 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::UploadComplete()
+ */
void UploadComplete(
bool loadSuccess,
int32_t textureId,
const Vector4& atlasRect,
bool premultiplied) override;
- void LoadComplete(
- bool loadSuccess,
- Devel::PixelBuffer pixelBuffer,
- const VisualUrl& url,
- bool preMultiplied) override;
-
private:
- std::vector<UrlStore>& mImageUrls;
- std::vector<bool> mReadyFlags;
- unsigned int mFront;
+ std::vector<UrlStore>& mImageUrls;
+ std::vector<bool> mReadyFlags;
+ std::vector<TextureManager::LoadState> mLoadStates;
+ uint32_t mFront;
};
} //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),
mRequestingLoad(false),
mTextureManagerAlive(true)
{
mTextureManagerAlive = false;
}
+void ImageCache::SetInterval(uint32_t interval)
+{
+ mInterval = interval;
+}
+
} //namespace Internal
} //namespace Toolkit
} //namespace Dali
{
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 for the frame
*/
- virtual void FrameReady(TextureSet textureSet) = 0;
+ virtual void FrameReady(TextureSet textureSet, uint32_t interval) = 0;
};
struct UrlStore
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 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 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 Get Loading state of the animated image file.
+ *
+ * @return LoadState of currently requested frame.
+ */
+ virtual TextureManager::LoadState GetLoadState() const = 0;
+
+ /**
+ * @brief Clears animated image cache and remove loaded textures.
+ */
+ virtual void ClearCache() = 0;
+
+ /**
+ * @brief Set default interval 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;
+ uint32_t mBatchSize;
+ uint32_t mInterval;
bool mRequestingLoad : 1;
bool mTextureManagerAlive : 1;
};
// CLASS HEADER
#include "rolling-animated-image-cache.h"
-// EXTERNAL HEADERS
-
// INTERNAL HEADERS
#include <dali-toolkit/devel-api/image-loader/texture-manager.h>
#include <dali-toolkit/internal/visuals/image-atlas-manager.h> // For ImageAtlasManagerPtr
#define LOG_CACHE
#endif
-const bool ENABLE_ORIENTATION_CORRECTION(true);
+static constexpr bool ENABLE_ORIENTATION_CORRECTION(true);
} // namespace
{
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)
+ mLoadState(TextureManager::LoadState::NOT_STARTED),
+ 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)
}
TextureSet textureSet;
+ uint32_t batchFrameIndex = frameIndex;
// If we need to load new frame that are not stored in queue.
// Load the frame synchronously.
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;
}
if(popExist || 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)
{
textureSet = GetFrontTextureSet();
}
- else
- {
- mWaitingForReadyFrame = true;
- }
}
return textureSet;
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
return mFrameCount;
}
+TextureManager::LoadState RollingAnimatedImageCache::GetLoadState() const
+{
+ return mLoadState;
+}
+
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;
mQueue.PushBack(imageFrame);
- mRequestingLoad = true;
-
- bool synchronousLoading = false;
- mTextureManager.LoadAnimatedImageTexture(mAnimatedImageLoading, frameIndex, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, mImageUrls[frameIndex].mTextureId, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this);
+ bool loadingStatus = false;
+ TextureSet textureSet = mTextureManager.LoadAnimatedImageTexture(mAnimatedImageLoading,
+ frameIndex,
+ loadingStatus,
+ mImageUrls[frameIndex].mTextureId,
+ SamplingMode::BOX_THEN_LINEAR,
+ Dali::WrapMode::Type::DEFAULT,
+ Dali::WrapMode::Type::DEFAULT,
+ synchronousLoading,
+ useCache,
+ this);
+
+ // If textureSet is returned but loadingState is false than load state is LOAD_FINISHED. (Notification is not comming yet.)
+ // If textureSet is null and the request is synchronous, load state is LOAD_FAILED.
+ // If textureSet is null but the request is asynchronous, the frame is still loading so load state is LOADING.
+ mLoadState = TextureManager::LoadState::LOADING;
+ if(textureSet)
+ {
+ if(!loadingStatus)
+ {
+ mLoadState = TextureManager::LoadState::LOAD_FINISHED;
+ }
+ }
+ else if(synchronousLoading)
+ {
+ // Synchronous loading is failed
+ mLoadState = TextureManager::LoadState::LOAD_FAILED;
+ }
- 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<uint32_t>(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;
}
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::UploadComplete(
- bool loadSuccess,
- int32_t textureId,
- TextureSet textureSet,
- bool useAtlasing,
- const Vector4& atlasRect,
- bool preMultiplied)
+void RollingAnimatedImageCache::AnimatedImageUploadComplete(bool loadSuccess, int32_t textureId, uint32_t frameCount, uint32_t interval)
{
DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::UploadComplete(textureId:%d) start\n", textureId);
LOG_CACHE;
- bool frontFrameReady = IsFrontReady();
-
- if(!mRequestingLoad)
+ // Reset size of Queue according to the real frame count.
+ if(mFrameCount != frameCount)
{
- SetImageFrameReady(textureId);
+ mFrameCount = frameCount;
+ mImageUrls.resize(mFrameCount);
+ mIntervals.assign(mFrameCount, 0u);
+ }
- CheckFrontFrame(frontFrameReady);
+ if(loadSuccess)
+ {
+ mLoadState = TextureManager::LoadState::LOAD_FINISHED;
}
else
{
- // UploadComplete 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;
+ }
+
+ 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(GetFrontTextureSet(), 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.
{
uint32_t loadingIndex = mLoadWaitingQueue.front();
mLoadWaitingQueue.erase(mLoadWaitingQueue.begin());
- mOnLoading = true;
- RequestFrameLoading(loadingIndex);
+ RequestFrameLoading(loadingIndex, loadingIndex == FIRST_FRAME_INDEX, false);
+ }
+ else if(mQueue.Count() == 1u && 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;
}
-void RollingAnimatedImageCache::LoadComplete(
- bool loadSuccess,
- Devel::PixelBuffer pixelBuffer,
- const VisualUrl& url,
- bool preMultiplied)
-{
- // LoadComplete is called if this TextureUploadObserver requested to load
- // an image that will be returned as a type of PixelBuffer by using a method
- // TextureManager::LoadPixelBuffer.
-}
-
-} //namespace Internal
+} // namespace Internal
} //namespace Toolkit
} //namespace Dali
{
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
*/
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::GetLoadState()
+ */
+ TextureManager::LoadState GetLoadState() 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
*/
- 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;
+protected:
+
/**
- * Check if the front frame has become ready - if so, inform observer
- * @param[in] wasReady Readiness before call.
+ * @copydoc Toolkit::TextureUploadObserver::AnimatedImageUploadComplete()
*/
- void CheckFrontFrame(bool wasReady);
-
-protected:
- void UploadComplete(
- bool loadSuccess,
- int32_t textureId,
- TextureSet textureSet,
- bool useAtlasing,
- const Vector4& atlasRect,
- bool preMultiplied) override;
-
- void LoadComplete(
- bool loadSuccess,
- Devel::PixelBuffer pixelBuffer,
- const VisualUrl& url,
- bool preMultiplied) override;
+ void AnimatedImageUploadComplete(bool loadSuccess, int32_t textureId, uint32_t frameCount, uint32_t interval) 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<UrlStore> mImageUrls;
std::vector<int32_t> mIntervals;
std::vector<uint32_t> mLoadWaitingQueue;
CircularQueue<ImageFrame> mQueue;
+ TextureManager::LoadState mLoadState; ///< The texture loading state
bool mIsSynchronousLoading;
- bool mOnLoading;
};
} // namespace Internal
#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
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();
+ mLoadStates.assign(mImageUrls.size(), TextureManager::LoadState::NOT_STARTED);
}
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
+ 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;
{
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
return mImageUrls.size();
}
+TextureManager::LoadState RollingImageCache::GetLoadState() const
+{
+ return mLoadStates[mQueue.Front().mUrlIndex];
+}
+
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 UploadComplete will get called
Dali::ImageDimensions textureRectSize;
auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
- mTextureManager.LoadTexture(
+ TextureSet textureSet = 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);
+ // If textureSet is returned but loadingState is false than load state is LOAD_FINISHED. (Notification is not comming yet.)
+ // If textureSet is null and the request is synchronous, load state is LOAD_FAILED.
+ // If textureSet is null but the request is asynchronous, the frame is still loading so load state is LOADING.
+ mLoadStates[frameIndex] = TextureManager::LoadState::LOADING;
+ if(textureSet)
+ {
+ if(!loadingStatus)
+ {
+ mLoadStates[frameIndex] = TextureManager::LoadState::LOAD_FINISHED;
+ }
+ }
+ else if(synchronousLoading)
+ {
+ // Synchronous loading is failed
+ mLoadStates[frameIndex] = TextureManager::LoadState::LOAD_FAILED;
+ }
+
mRequestingLoad = false;
- }
- CheckFrontFrame(frontFrameReady);
+ ++frameIndex;
+ frameIndex %= mImageUrls.size();
+ }
}
-void RollingImageCache::SetImageFrameReady(TextureManager::TextureId textureId)
+void RollingImageCache::SetImageFrameReady(TextureManager::TextureId textureId, bool loadSuccess)
{
for(std::size_t i = 0; i < mQueue.Count(); ++i)
{
if(GetCachedTextureId(i) == textureId)
{
+ if(loadSuccess)
+ {
+ mLoadStates[i] = TextureManager::LoadState::LOAD_FINISHED;
+ }
+ else
+ {
+ mLoadStates[i] = TextureManager::LoadState::LOAD_FAILED;
+ }
mQueue[i].mReady = true;
break;
}
void RollingImageCache::CheckFrontFrame(bool wasReady)
{
- if(mWaitingForReadyFrame && wasReady == false && IsFrontReady())
+ if(wasReady == false && IsFrontReady())
{
- mWaitingForReadyFrame = false;
- mObserver.FrameReady(GetFrontTextureSet());
+ mObserver.FrameReady(GetFrontTextureSet(), mInterval);
}
}
+void RollingImageCache::ClearCache()
+{
+ while(mTextureManagerAlive && !mQueue.IsEmpty())
+ {
+ ImageFrame imageFrame = mQueue.PopFront();
+ mTextureManager.Remove(mImageUrls[imageFrame.mUrlIndex].mTextureId, this);
+ mImageUrls[imageFrame.mUrlIndex].mTextureId = TextureManager::INVALID_TEXTURE_ID;
+ }
+ mLoadStates.assign(mImageUrls.size(), TextureManager::LoadState::NOT_STARTED);
+}
+
void RollingImageCache::UploadComplete(
bool loadSuccess,
int32_t textureId,
LOG_CACHE;
bool frontFrameReady = IsFrontReady();
-
+ SetImageFrameReady(textureId, loadSuccess);
if(!mRequestingLoad)
{
- SetImageFrameReady(textureId);
-
CheckFrontFrame(frontFrameReady);
}
- else
- {
- // UploadComplete 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;
- }
LOG_CACHE;
}
-void RollingImageCache::LoadComplete(
- bool loadSuccess,
- Devel::PixelBuffer pixelBuffer,
- const VisualUrl& url,
- bool preMultiplied)
-{
- // LoadComplete is called if this TextureUploadObserver requested to load
- // an image that will be returned as a type of PixelBuffer by using a method
- // TextureManager::LoadPixelBuffer.
-}
-
} //namespace Internal
} //namespace Toolkit
} //namespace Dali
* @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.
UrlList& urlList,
ImageCache::FrameReadyObserver& observer,
uint16_t cacheSize,
- uint16_t batchSize);
+ uint16_t batchSize,
+ uint32_t interval);
/**
* Destructor
~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;
+ /**
+ * @copydoc Internal::ImageCache::GetLoadState()
+ */
+ TextureManager::LoadState GetLoadState() 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
+ *
+ * @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.
+ * @param[in] loadSuccess true if the frame loading is succeeded.
*/
- void SetImageFrameReady(TextureManager::TextureId textureId);
+ void SetImageFrameReady(TextureManager::TextureId textureId, bool loadSuccess);
/**
- * 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::UploadComplete()
+ */
void UploadComplete(
bool loadSuccess,
int32_t textureId,
const Vector4& atlasRect,
bool preMultiplied) override;
- void LoadComplete(
- bool loadSuccess,
- Devel::PixelBuffer pixelBuffer,
- const VisualUrl& url,
- bool preMultiplied) override;
-
private:
/**
* Secondary class to hold readiness and index into url
bool mReady = false;
};
- std::vector<UrlStore>& mImageUrls;
- CircularQueue<ImageFrame> mQueue;
+ std::vector<UrlStore>& mImageUrls;
+ std::vector<TextureManager::LoadState> mLoadStates;
+ CircularQueue<ImageFrame> mQueue;
};
} // namespace Internal
namespace
{
-constexpr auto INITIAL_CACHE_NUMBER = size_t{0u};
-constexpr auto DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS = size_t{4u};
-constexpr auto DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS = size_t{8u};
+constexpr auto INITIAL_CACHE_NUMBER = size_t{0u};
+constexpr auto DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS = size_t{4u};
+constexpr auto DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS = size_t{8u};
constexpr auto NUMBER_OF_LOCAL_LOADER_THREADS_ENV = "DALI_TEXTURE_LOCAL_THREADS";
constexpr auto NUMBER_OF_REMOTE_LOADER_THREADS_ENV = "DALI_TEXTURE_REMOTE_THREADS";
}
}
-TextureSet TextureManager::LoadAnimatedImageTexture(
- Dali::AnimatedImageLoading animatedImageLoading, uint32_t frameIndex, Dali::SamplingMode::Type samplingMode, bool synchronousLoading, TextureManager::TextureId& textureId, Dali::WrapMode::Type wrapModeU, Dali::WrapMode::Type wrapModeV, TextureUploadObserver* textureObserver)
+TextureSet TextureManager::LoadAnimatedImageTexture(Dali::AnimatedImageLoading animatedImageLoading,
+ uint32_t frameIndex,
+ bool& loadingStatus,
+ TextureManager::TextureId& textureId,
+ Dali::SamplingMode::Type samplingMode,
+ Dali::WrapMode::Type wrapModeU,
+ Dali::WrapMode::Type wrapModeV,
+ bool synchronousLoading,
+ bool useCache,
+ TextureUploadObserver* textureObserver)
{
+ loadingStatus = false;
TextureSet textureSet;
-
if(synchronousLoading)
{
Devel::PixelBuffer pixelBuffer;
}
else
{
+ loadingStatus = true;
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, TextureManager::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex);
+ textureId = RequestLoadInternal(animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, TextureManager::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex, useCache);
TextureManager::LoadState loadState = GetTextureStateInternal(textureId);
if(loadState == TextureManager::LoadState::UPLOADED)
{
// UploadComplete has already been called - keep the same texture set
textureSet = GetTextureSet(textureId);
}
+
+ // If we are loading the texture, or waiting for the ready signal handler to complete, inform
+ // caller that they need to wait.
+ loadingStatus = (loadState == TextureManager::LoadState::LOADING ||
+ loadState == TextureManager::LoadState::WAITING_FOR_MASK ||
+ loadState == TextureManager::LoadState::MASK_APPLYING ||
+ loadState == TextureManager::LoadState::MASK_APPLIED ||
+ loadState == TextureManager::LoadState::NOT_STARTED ||
+ mQueueLoadFlag);
}
if(textureSet)
}
else
{
- RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u);
+ RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false);
}
return pixelBuffer;
TextureManager::ReloadPolicy reloadPolicy,
TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
{
- return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u);
+ return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, true);
}
TextureManager::TextureId TextureManager::RequestLoad(
TextureManager::ReloadPolicy reloadPolicy,
TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
{
- return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u);
+ return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, true);
}
TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl)
{
// 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, NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u);
+ return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, true);
}
TextureManager::TextureId TextureManager::RequestLoadInternal(
TextureManager::ReloadPolicy reloadPolicy,
TextureManager::MultiplyOnLoad& preMultiplyOnLoad,
Dali::AnimatedImageLoading animatedImageLoading,
- uint32_t frameIndex)
+ uint32_t frameIndex,
+ bool useCache)
{
// First check if the requested Texture is cached.
- bool isAnimatedImage = (animatedImageLoading) ? true : false;
-
TextureHash textureHash = INITIAL_CACHE_NUMBER;
int cacheIndex = INVALID_CACHE_INDEX;
- if(storageType != StorageType::RETURN_PIXEL_BUFFER && !isAnimatedImage)
+ if(storageType != StorageType::RETURN_PIXEL_BUFFER && useCache)
{
textureHash = 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 = FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad);
+ cacheIndex = FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad, (animatedImageLoading) ? true : false);
}
TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
if(textureInfoIndex != INVALID_INDEX)
{
TextureInfo& textureInfo(mTextureInfoContainer[textureInfoIndex]);
-
DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::Remove(%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.
{
// The Texture has already loaded. The other observers have already been notified.
// We need to send a "late" loaded notification for this observer.
- observer->UploadComplete(true, textureInfo.textureId, textureInfo.textureSet, textureInfo.useAtlas, textureInfo.atlasRect, textureInfo.preMultiplied);
+ if(textureInfo.isAnimatedImageFormat)
+ {
+ observer->AnimatedImageUploadComplete(true, textureInfo.textureId, textureInfo.frameCount, textureInfo.frameInterval);
+ }
+ else
+ {
+ observer->UploadComplete(true, textureInfo.textureId, textureInfo.textureSet, textureInfo.useAtlas, textureInfo.atlasRect, textureInfo.preMultiplied);
+ }
}
break;
}
TextureInfo& textureInfo(mTextureInfoContainer[cacheIndex]);
if(textureInfo.loadState == LoadState::UPLOADED)
{
- element.mObserver->UploadComplete(true, textureInfo.textureId, textureInfo.textureSet, textureInfo.useAtlas, textureInfo.atlasRect, textureInfo.preMultiplied);
+ if(textureInfo.isAnimatedImageFormat)
+ {
+ element.mObserver->AnimatedImageUploadComplete(true, textureInfo.textureId, textureInfo.frameCount, textureInfo.frameInterval);
+ }
+ else
+ {
+ element.mObserver->UploadComplete(true, textureInfo.textureId, textureInfo.textureSet, textureInfo.useAtlas, textureInfo.atlasRect, textureInfo.preMultiplied);
+ }
}
else if(textureInfo.loadState == LoadState::LOAD_FINISHED && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER)
{
// and erase it from the list
TextureInfo* info = &textureInfo;
+ if(info->animatedImageLoading)
+ {
+ info->frameCount = info->animatedImageLoading.GetImageCount();
+ info->frameInterval = info->animatedImageLoading.GetFrameInterval(info->frameIndex);
+ info->animatedImageLoading.Reset();
+ }
+
mQueueLoadFlag = true;
while(info->observerList.Count())
{
observer->LoadComplete(success, info->pixelBuffer, info->url, info->preMultiplied);
}
+ else if(info->isAnimatedImageFormat)
+ {
+ observer->AnimatedImageUploadComplete(success, info->textureId, info->frameCount, info->frameInterval);
+ }
else
{
observer->UploadComplete(success, info->textureId, info->textureSet, info->useAtlas, info->atlasRect, info->preMultiplied);
const Dali::SamplingMode::Type samplingMode,
const bool useAtlas,
TextureId maskTextureId,
- TextureManager::MultiplyOnLoad preMultiplyOnLoad)
+ TextureManager::MultiplyOnLoad preMultiplyOnLoad,
+ bool isAnimatedImage)
{
// Default to an invalid ID, in case we do not find a match.
int cacheIndex = INVALID_CACHE_INDEX;
(useAtlas == textureInfo.useAtlas) &&
(maskTextureId == textureInfo.maskTextureId) &&
(size == textureInfo.desiredSize) &&
+ (isAnimatedImage == textureInfo.isAnimatedImageFormat) &&
((size.GetWidth() == 0 && size.GetHeight() == 0) ||
(fittingMode == textureInfo.fittingMode &&
samplingMode == textureInfo.samplingMode)))
* 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] loadingStatus The loading status of the texture
+ * @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 "UploadCompleted" 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,
uint32_t frameIndex,
- Dali::SamplingMode::Type samplingMode,
- bool synchronousLoading,
+ bool& loadingStatus,
TextureManager::TextureId& textureId,
+ Dali::SamplingMode::Type samplingMode,
Dali::WrapMode::Type wrapModeU,
Dali::WrapMode::Type wrapModeV,
+ bool synchronousLoading,
+ bool useCache,
TextureUploadObserver* textureObserver);
/**
TextureManager::ReloadPolicy reloadPolicy,
MultiplyOnLoad& preMultiplyOnLoad,
Dali::AnimatedImageLoading animatedImageLoading,
- uint32_t frameIndex);
+ uint32_t frameIndex,
+ bool useCache);
/**
* @brief Get the current state of a texture
storageType(StorageType::UPLOAD_TO_TEXTURE),
animatedImageLoading(animatedImageLoading),
frameIndex(frameIndex),
+ frameCount(0u),
+ frameInterval(0u),
loadSynchronously(loadSynchronously),
useAtlas(useAtlas),
cropToMask(cropToMask),
preMultiplyOnLoad(preMultiplyOnLoad),
preMultiplied(false)
{
+ isAnimatedImageFormat = (animatedImageLoading) ? true : false;
}
/**
Dali::SamplingMode::Type samplingMode : 3; ///< The requested SamplingMode
StorageType storageType; ///< CPU storage / GPU upload;
Dali::AnimatedImageLoading animatedImageLoading; ///< AnimatedImageLoading that contains animated image information.
- 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.
bool loadSynchronously : 1; ///< True if synchronous loading was requested
UseAtlas useAtlas : 2; ///< USE_ATLAS if an atlas was requested.
///< This is updated to false if atlas is not used
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.
};
/**
* @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 TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found.
*/
TextureManager::TextureId FindCachedTexture(
const Dali::SamplingMode::Type samplingMode,
const bool useAtlas,
TextureId maskTextureId,
- MultiplyOnLoad preMultiplyOnLoad);
+ MultiplyOnLoad preMultiplyOnLoad,
+ bool isAnimatedImage);
private:
/**
// EXTERNAL INCLUDES
#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
#include <dali/public-api/signals/dali-signal.h>
+#include <dali/public-api/rendering/texture-set.h>
// INTERNAL INCLUDES
#include <dali-toolkit/internal/visuals/visual-url.h>
namespace Dali
{
-class TextureSet;
namespace Toolkit
{
virtual ~TextureUploadObserver();
/**
- * The action to be taken once the async load has finished and the upload to GPU is completed.
+ * @brief The action to be taken once the async load has finished and the upload to GPU is completed.
* This should be overridden by the deriving class.
*
* @param[in] loadSuccess True if the texture load was successful (i.e. the resource is available). If false, then the resource failed to load. In future, this will automatically upload a "broken" image.
* @param[in] atlasRect If using atlasing, this is the rectangle within the atlas to use.
* @param[in] preMultiplied True if the image had pre-multiplied alpha applied
*/
- virtual void UploadComplete(bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied) = 0;
+ virtual void UploadComplete(bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied)
+ {
+ }
/**
- * The action to be taken once the async load has finished.
+ * @brief The action to be taken once the async load of animated image has finished and the upload to GPU is completed.
+ * This should be overridden by the deriving class.
+ *
+ * @param[in] loadSuccess True if the texture load was successful (i.e. the resource is available). If false, then the resource failed to load. In future, this will automatically upload a "broken" image.
+ * @param[in] textureId The textureId of the loaded texture in the TextureManager
+ * @param[in] frameCount The frameCount of the animated image
+ * @param[in] interval Time interval between currently loaded frame and next frame.
+ */
+ virtual void AnimatedImageUploadComplete(bool loadSuccess, int32_t textureId, uint32_t frameCount, uint32_t interval)
+ {
+ }
+
+ /**
+ * @brief The action to be taken once the async load has finished.
* This should be overridden by the deriving class.
*
* @param[in] loadSuccess True if the image load was successful (i.e. the resource is available). If false, then the resource failed to load.
* @param[in] url The url address of the loaded image.
* @param[in] preMultiplied True if the image had pre-multiplied alpha applied
*/
- virtual void LoadComplete(bool loadSuccess, Devel::PixelBuffer pixelBuffer, const Internal::VisualUrl& url, bool preMultiplied) = 0;
+ virtual void LoadComplete(bool loadSuccess, Devel::PixelBuffer pixelBuffer, const Internal::VisualUrl& url, bool preMultiplied)
+ {
+ }
/**
* @brief Returns the destruction signal.