It is possible that mImageCache is NULL if url suffix is not gif or webp.
In this case, mImageCache become null, so we should show broken image.
But also, we 'might' need to show non-animatable image even if we use
non-animatable image (like jpg), and set it to animated image visual forcibly.
To resolve general cases, let we make AnimatedImageVisual with non-animatable format image
just use image sequence with length 1.
===
Also, there was several bugs when we use fixed image cache, with cached texture manager image.
Before, we don't consider full-scenario when LoadComplete callback comes
during TextureManager.Load.
Change-Id: I173020e42d6447ff43e56e19f25ea8e06c7bbfc1
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
/*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
END_TEST;
}
+int UtcDaliAnimatedImageVisualImageLoadingFail02(void)
+{
+ ToolkitTestApplication application;
+
+ tet_infoline("Test with non-animated single image. We should show broken image than.");
+
+ for(int isSynchronousLoading = 0; isSynchronousLoading < 2; ++isSynchronousLoading)
+ {
+ tet_printf("Test to load non-animatable image %s\n", (isSynchronousLoading == 1) ? "Synchronously" : "Asynchronously");
+
+ Property::Map propertyMap;
+ propertyMap.Insert(Visual::Property::TYPE, Visual::ANIMATED_IMAGE);
+ propertyMap.Insert(ImageVisual::Property::URL, "dummy");
+ propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, (isSynchronousLoading == 1));
+
+ VisualFactory factory = VisualFactory::Get();
+ Visual::Base visual = factory.CreateVisual(propertyMap);
+
+ DummyControl dummyControl = DummyControl::New(true);
+ Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+ dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+
+ dummyControl.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
+
+ DALI_TEST_EQUALS(dummyControl.GetRendererCount(), 0u, TEST_LOCATION);
+
+ application.GetScene().Add(dummyControl);
+
+ application.SendNotification();
+ application.Render(20);
+
+ // TODO : Since fixed-image-cache didn't support synchronous loading now, we need to wait for a while.
+ // We have to remove it in future!
+ //if(!(isSynchronousLoading == 1))
+ {
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ }
+
+ application.SendNotification();
+ application.Render(20);
+
+ // Check broken image uploaded.
+ DALI_TEST_EQUALS(dummyControl.GetRendererCount(), 1u, TEST_LOCATION);
+
+ dummyControl.Unparent();
+
+ // Remove cached image at TextureManager.
+ application.SendNotification();
+ application.Render(20);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliAnimatedImageVisualImageLoadingFail03(void)
+{
+ ToolkitTestApplication application;
+
+ tet_infoline("Test with invalid image that suffix is .gif, and AnimatedImageLoading not supported. We should show broken image than.");
+
+ for(int isSynchronousLoading = 0; isSynchronousLoading < 2; ++isSynchronousLoading)
+ {
+ tet_printf("Test to load non-animatable image %s\n", (isSynchronousLoading == 1) ? "Synchronously" : "Asynchronously");
+
+ Property::Map propertyMap;
+ propertyMap.Insert(Visual::Property::TYPE, Visual::ANIMATED_IMAGE);
+ propertyMap.Insert(ImageVisual::Property::URL, "dummy.Gif"); ///< Suffix is gif so visual become AnimatedImageVisual. But AnimatedImageLoading become null.
+ propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, (isSynchronousLoading == 1));
+
+ VisualFactory factory = VisualFactory::Get();
+ Visual::Base visual = factory.CreateVisual(propertyMap);
+
+ DummyControl dummyControl = DummyControl::New(true);
+ Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+ dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+
+ dummyControl.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
+
+ DALI_TEST_EQUALS(dummyControl.GetRendererCount(), 0u, TEST_LOCATION);
+
+ application.GetScene().Add(dummyControl);
+
+ application.SendNotification();
+ application.Render(20);
+
+ // TODO : Since fixed-image-cache didn't support synchronous loading now, we need to wait for a while.
+ // We have to remove it in future!
+ //if(!(isSynchronousLoading == 1))
+ {
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ }
+
+ application.SendNotification();
+ application.Render(20);
+
+ // Check broken image uploaded.
+ DALI_TEST_EQUALS(dummyControl.GetRendererCount(), 1u, TEST_LOCATION);
+
+ dummyControl.Unparent();
+
+ // Remove cached image at TextureManager.
+ application.SendNotification();
+ application.Render(20);
+ }
+
+ END_TEST;
+}
+
int UtcDaliAnimatedImageVisualSynchronousLoading(void)
{
ToolkitTestApplication application;
DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedImageVisual::Action::JUMP_TO, 6);
- DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(6), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(4), true, TEST_LOCATION);
DALI_TEST_EQUALS(gl.GetNumGeneratedTextures(), 4, TEST_LOCATION);
dummyControl.Unparent();
END_TEST;
}
+namespace
+{
void TestLoopCount(ToolkitTestApplication& application, DummyControl& dummyControl, uint16_t frameCount, uint16_t loopCount, const char* location)
{
TestGlAbstraction& gl = application.GetGlAbstraction();
dummyControl.Unparent();
}
+} // namespace
int UtcDaliAnimatedImageVisualLoopCount(void)
{
{
mImageUrl = imageUrl;
mAnimatedImageLoading = AnimatedImageLoading::New(imageUrl.GetUrl(), imageUrl.IsLocalResource());
+
+ // If we fail to load the animated image, we will try to load as a normal image.
+ if(!mAnimatedImageLoading)
+ {
+ mImageUrls = new ImageCache::UrlList();
+ mImageUrls->reserve(SINGLE_IMAGE_COUNT);
+
+ for(unsigned int i = 0; i < SINGLE_IMAGE_COUNT; ++i)
+ {
+ ImageCache::UrlStore urlStore;
+ urlStore.mTextureId = TextureManager::INVALID_TEXTURE_ID;
+ urlStore.mUrl = imageUrl;
+ mImageUrls->push_back(urlStore);
+ }
+ mFrameCount = SINGLE_IMAGE_COUNT;
+ }
}
void AnimatedImageVisual::CreateImageCache()
}
}
- if(!mImageCache)
+ if(DALI_UNLIKELY(!mImageCache))
{
DALI_LOG_ERROR("mImageCache is null\n");
}
// 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();
+ if(DALI_LIKELY(mImageCache))
+ {
+ mImageCache->ClearCache();
+ }
}
delete mImageCache;
delete mImageUrls;
}
}
- if(mImageUrl.IsValid())
+ if(mImageUrl.IsValid() && mAnimatedImageLoading)
{
mImageSize = mAnimatedImageLoading.GetImageSize();
}
{
map.Clear();
map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE);
- if(mImageUrl.IsValid())
- {
- map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
- map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
- }
+ map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
+ map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
}
void AnimatedImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
if(value.Get(frameDelay))
{
mFrameDelay = frameDelay;
- if(mImageCache)
+ if(DALI_LIKELY(mImageCache))
{
mImageCache->SetInterval(static_cast<uint32_t>(mFrameDelay));
}
actor.RemoveRenderer(mImpl->mRenderer);
if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
{
- mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
+ if(DALI_LIKELY(mImageCache))
+ {
+ mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
+ }
mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
TextureSet textureSet = TextureSet::New();
void AnimatedImageVisual::PrepareTextureSet()
{
TextureSet textureSet;
- if(mImageCache)
+ if(DALI_LIKELY(mImageCache))
{
textureSet = mImageCache->FirstFrame();
}
+ else
+ {
+ // preMultiplied should be false because broken image don't premultiply alpha on load
+ FrameReady(TextureSet(), 0, false);
+ }
// Check whether synchronous loading is true or false for the first frame.
if(textureSet)
if(mStartFirstFrame)
{
- mFrameCount = mImageCache->GetTotalFrameCount();
+ if(DALI_LIKELY(mImageCache))
+ {
+ mFrameCount = mImageCache->GetTotalFrameCount();
+ }
StartFirstFrame(textureSet, interval);
}
else
TextureSet textureSet;
bool continueTimer = false;
- if(mImageCache)
+ if(DALI_LIKELY(mImageCache))
{
uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
{
imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
}
- mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
- TextureSet textureSet = mImpl->mRenderer.GetTextures();
+
+ TextureSet textureSet;
+ if(DALI_LIKELY(mImpl->mRenderer))
+ {
+ mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
+ textureSet = mImpl->mRenderer.GetTextures();
+ }
if(mFrameDelayTimer)
{
return textureSet;
}
- while(mReadyFlags.size() < mImageUrls.size() &&
- (frameIndex > mCurrentFrameIndex || mReadyFlags.empty()))
+ mCurrentFrameIndex = frameIndex;
+
+ bool batchRequested = false;
+
+ // Make ensure that current frameIndex load requested.
+ while(mReadyFlags.size() <= frameIndex)
{
- ++mCurrentFrameIndex;
+ batchRequested = true;
LoadBatch();
}
- mCurrentFrameIndex = frameIndex;
+ // Request batch only 1 times for this function.
+ if(!batchRequested && mReadyFlags.size() < mImageUrls.size())
+ {
+ LoadBatch();
+ }
if(IsFrameReady(mCurrentFrameIndex) && mLoadState != TextureManager::LoadState::LOAD_FAILED)
{
bool FixedImageCache::IsFrameReady(uint32_t frameIndex) const
{
- return (mReadyFlags.size() > 0 && mReadyFlags[frameIndex] == true);
+ return ((mReadyFlags.size() > 0) && (mReadyFlags[frameIndex] == true));
}
void FixedImageCache::LoadBatch()
auto preMultiplyOnLoading = mPreMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
: TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
- mTextureManager.LoadTexture(url, mDesiredSize, mFittingMode, mSamplingMode, mMaskingData, synchronousLoading, mImageUrls[frameIndex].mTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiplyOnLoading);
- mRequestingLoad = false;
+ TextureManager::TextureId loadTextureId = TextureManager::INVALID_TEXTURE_ID;
+ mTextureManager.LoadTexture(url, mDesiredSize, mFittingMode, mSamplingMode, mMaskingData, synchronousLoading, loadTextureId, textureRect, textureRectSize, atlasingStatus, loadingStatus, this, atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiplyOnLoading);
+ mImageUrls[frameIndex].mTextureId = loadTextureId;
+ mRequestingLoad = false;
}
}
{
if(loadSuccess)
{
- mLoadState = TextureManager::LoadState::LOAD_FINISHED;
- bool isCurrentFrameReady = IsFrameReady(mCurrentFrameIndex);
+ mLoadState = TextureManager::LoadState::LOAD_FINISHED;
+ bool wasCurrentFrameReady = IsFrameReady(mCurrentFrameIndex);
if(!mRequestingLoad)
{
for(std::size_t i = 0; i < mImageUrls.size(); ++i)
}
else
{
- mReadyFlags.back() = true;
+ DALI_ASSERT_ALWAYS(mReadyFlags.size() > 0u && "Some FixedImageCache::LoadBatch() called mismatched!");
+ size_t i = mReadyFlags.size() - 1u;
+
+ // texture id might not setup yet. Update it now.
+ mImageUrls[i].mTextureId = textureInformation.textureId;
+ mReadyFlags[i] = true;
}
- MakeReady(isCurrentFrameReady, mCurrentFrameIndex, textureInformation.preMultiplied);
+ MakeReady(wasCurrentFrameReady, mCurrentFrameIndex, textureInformation.preMultiplied);
}
else
{