}
+int UtcDaliAnimatedImageVisualStopBehavior02(void)
+{
+ ToolkitTestApplication application;
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+
+ Property::Array urls;
+ CopyUrlsIntoArray( urls );
+
+ {
+ Property::Map propertyMap;
+ propertyMap.Insert( Visual::Property::TYPE, Visual::IMAGE );
+ propertyMap.Insert( ImageVisual::Property::URL, Property::Value(urls) );
+ propertyMap.Insert( DevelImageVisual::Property::STOP_BEHAVIOR, DevelImageVisual::StopBehavior::LAST_FRAME);
+ propertyMap.Insert( ImageVisual::Property::BATCH_SIZE, 2);
+ propertyMap.Insert( ImageVisual::Property::CACHE_SIZE, 2);
+ propertyMap.Insert( ImageVisual::Property::FRAME_DELAY, 20);
+
+ VisualFactory factory = VisualFactory::Get();
+ Visual::Base visual = factory.CreateVisual( propertyMap );
+
+ // Expect that a batch of 4 textures has been requested. These will be serially loaded
+ // below.
+
+ 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 );
+ application.GetScene().Add( dummyControl );
+
+ tet_infoline( "Ready the visual after the visual is on stage" );
+ DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+ TraceCallStack& textureTrace = gl.GetTextureTrace();
+ textureTrace.Enable(true);
+
+ application.SendNotification();
+ application.Render(20);
+
+ DALI_TEST_EQUALS( gl.GetLastGenTextureId(), 2, TEST_LOCATION );
+
+ Test::EmitGlobalTimerSignal();
+
+ DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+ application.SendNotification();
+ application.Render(20);
+
+ DALI_TEST_EQUALS( gl.GetNumGeneratedTextures(), 2, TEST_LOCATION );
+
+ DevelControl::DoAction( dummyControl, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedImageVisual::Action::STOP, Property::Map() );
+
+ tet_infoline( "Ready the visual after the visual is on stage" );
+ DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+ application.SendNotification();
+ application.Render(20);
+
+ DALI_TEST_EQUALS( gl.GetNumGeneratedTextures(), 2, TEST_LOCATION );
+
+ dummyControl.Unparent();
+ }
+ tet_infoline("Test that removing the visual from stage deletes all textures");
+ application.SendNotification();
+ application.Render(16);
+ DALI_TEST_EQUALS( gl.GetNumGeneratedTextures(), 0, TEST_LOCATION );
+
+ END_TEST;
+}
+
+
int UtcDaliAnimatedImageVisualAnimatedImage01(void)
{
ToolkitTestApplication application;
mPixelArea( FULL_TEXTURE_RECT ),
mImageUrl(),
mAnimatedImageLoading(),
- mCurrentFrameIndex( 0 ),
+ mFrameIndexForJumpTo( 0 ),
mImageUrls( NULL ),
mImageCache( NULL ),
mCacheSize( 2 ),
else
{
mIsJumpTo = true;
- mCurrentFrameIndex = frameNumber;
+ mFrameIndexForJumpTo = frameNumber;
if( IsOnScene() )
{
DisplayNextFrame();
{
mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
}
-
- mCurrentFrameIndex = 0;
}
void AnimatedImageVisual::LoadFirstBatch()
mPlacementActor.Reset();
}
- mCurrentFrameIndex = 0;
-
if( mFrameCount > 1 )
{
- int frameDelay = mFrameDelay; // from URL array
- if( mAnimatedImageLoading && mImageCache )
+ int frameDelay = mImageCache->GetFrameInterval( 0 );
+ if( frameDelay == 0u )
{
- frameDelay = mImageCache->GetFrameInterval( 0 );
+ frameDelay = mFrameDelay; // from URL array
}
mFrameDelayTimer = Timer::New( frameDelay );
mFrameDelayTimer.TickSignal().Connect( this, &AnimatedImageVisual::DisplayNextFrame );
{
textureSet = mImageCache->FirstFrame();
}
+
if( textureSet )
{
SetImageSize( textureSet );
}
- else
- {
- DALI_LOG_INFO( gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n" );
- ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
- }
return textureSet;
}
void AnimatedImageVisual::FrameReady( TextureSet textureSet )
{
- SetImageSize( textureSet );
-
- if( mStartFirstFrame )
+ if(textureSet)
{
- StartFirstFrame( textureSet );
+ SetImageSize(textureSet);
+
+ if(mStartFirstFrame)
+ {
+ StartFirstFrame(textureSet);
+ }
+ else
+ {
+ if(mImpl->mRenderer)
+ {
+ mImpl->mRenderer.SetTextures(textureSet);
+ }
+ }
}
else
{
- if( mImpl->mRenderer )
- {
- mImpl->mRenderer.SetTextures( textureSet );
- }
+ DALI_LOG_INFO( gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n" );
+ ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
}
}
bool AnimatedImageVisual::DisplayNextFrame()
{
+ bool nextFrame = false;
+ uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
+
if( mIsJumpTo )
{
mIsJumpTo = false;
+ frameIndex = mFrameIndexForJumpTo;
}
else if( mActionStatus == DevelAnimatedImageVisual::Action::PAUSE )
{
}
else if( mActionStatus == DevelAnimatedImageVisual::Action::STOP )
{
- mCurrentLoopIndex = 0;
+ frameIndex = 0;
if( mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME )
{
- mCurrentFrameIndex = 0;
+ frameIndex = 0;
}
else if( mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME )
{
- mCurrentFrameIndex = mFrameCount - 1;
+ frameIndex = mFrameCount - 1;
}
else
{
{
if( mFrameCount > 1 )
{
- // Wrap the frame index
- bool finished = false;
- ++mCurrentFrameIndex;
- if( mCurrentFrameIndex >= mFrameCount )
+ nextFrame = true;
+ frameIndex++;
+ if( frameIndex >= mFrameCount )
{
+ frameIndex %= mFrameCount;
++mCurrentLoopIndex;
- finished = true;
}
- if( mLoopCount < 0 || mCurrentLoopIndex < mLoopCount)
- {
- if( finished )
- {
- mCurrentFrameIndex = 0; // Back to the first frame
- }
- }
- else
+ if(mLoopCount >= 0 && mCurrentLoopIndex >= mLoopCount)
{
// This will stop timer
mActionStatus = DevelAnimatedImageVisual::Action::STOP;
return DisplayNextFrame();
}
}
- // TODO : newly added one.
- if( mAnimatedImageLoading && mImageCache )
+
+ unsigned int delay = mImageCache->GetFrameInterval( frameIndex );
+ if( delay > 0u )
{
- unsigned int delay = mImageCache->GetFrameInterval( mCurrentFrameIndex );
if( mFrameDelayTimer.GetInterval() != delay )
{
mFrameDelayTimer.SetInterval( delay );
}
}
- DALI_LOG_INFO( gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, mCurrentFrameIndex);
+ DALI_LOG_INFO( gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex);
TextureSet textureSet;
if( mImageCache )
{
- textureSet = mImageCache->Frame( mCurrentFrameIndex );
+ if(nextFrame)
+ {
+ textureSet = mImageCache->NextFrame();
+ }
+ else
+ {
+ textureSet = mImageCache->Frame( frameIndex );
+ }
+
if( textureSet )
{
SetImageSize( textureSet );
Vector4 mPixelArea;
VisualUrl mImageUrl;
Dali::AnimatedImageLoading mAnimatedImageLoading; // Only needed for animated image
- uint32_t mCurrentFrameIndex; // Frame index into textureRects
+ uint32_t mFrameIndexForJumpTo; // Frame index into textureRects
// Variables for Multi-Image player
ImageCache::UrlList* mImageUrls;
return textureSet;
}
-uint32_t FixedImageCache::GetFrameInterval( uint32_t frameIndex )
+TextureSet FixedImageCache::NextFrame()
+{
+ TextureSet textureSet = Frame((mFront + 1) % mImageUrls.size());
+
+ return textureSet;
+}
+
+uint32_t FixedImageCache::GetFrameInterval( uint32_t frameIndex ) const
{
return 0u;
}
+int32_t FixedImageCache::GetCurrentFrameIndex() const
+{
+ return static_cast<int32_t>(mFront);
+}
+
bool FixedImageCache::IsFrontReady() const
{
return ( mReadyFlags.size() > 0 && mReadyFlags[mFront] == true );
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.
*/
- uint32_t GetFrameInterval( uint32_t frameIndex ) override;
+ uint32_t GetFrameInterval( uint32_t frameIndex ) const override;
+
+ /**
+ * Get the current rendered frame index.
+ * If there isn't any loaded frame, returns -1.
+ */
+ int32_t GetCurrentFrameIndex() const override;
private:
/**
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
* sending of FrameReady() when the image becomes ready.
*/
/**
* Get the interval of Nth frame.
*/
- virtual uint32_t GetFrameInterval( uint32_t frameIndex ) = 0;
+ virtual uint32_t GetFrameInterval( uint32_t frameIndex ) const = 0;
+
+ /**
+ * Get the current rendered frame index.
+ * If there isn't any loaded frame, returns -1.
+ */
+ virtual int32_t GetCurrentFrameIndex() const = 0;
private:
mAnimatedImageLoading( animatedImageLoading ),
mFrameCount( frameCount ),
mFrameIndex( 0 ),
+ mCacheSize( cacheSize ),
mQueue( cacheSize ),
mIsSynchronousLoading( isSynchronousLoading ),
mOnLoading( false )
// If the frame of frameIndex was already loaded, load batch from the last frame of queue
if( !mQueue.IsEmpty() )
{
- mFrameIndex = ( mQueue.Back().mFrameNumber + 1 ) % mFrameCount;
+ if(!mLoadWaitingQueue.empty())
+ {
+ mFrameIndex = ( mLoadWaitingQueue.back() + 1 ) % mFrameCount;
+ }
+ else
+ {
+ mFrameIndex = ( 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
return Frame( 0u );
}
-uint32_t RollingAnimatedImageCache::GetFrameInterval( uint32_t frameIndex )
+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.");
+ }
+
+ return textureSet;
+}
+
+uint32_t RollingAnimatedImageCache::GetFrameInterval( uint32_t frameIndex ) const
{
return mAnimatedImageLoading.GetFrameInterval( frameIndex );
}
+int32_t RollingAnimatedImageCache::GetCurrentFrameIndex() const
+{
+ if(mQueue.IsEmpty())
+ {
+ return -1;
+ }
+ return mQueue.Front().mFrameNumber;
+}
+
bool RollingAnimatedImageCache::IsFrontReady() const
{
return ( !mQueue.IsEmpty() && mQueue.Front().mReady );
void RollingAnimatedImageCache::RequestFrameLoading( uint32_t frameIndex )
{
+ ImageFrame imageFrame;
+ imageFrame.mFrameNumber = frameIndex;
+ imageFrame.mReady = false;
+
+ mQueue.PushBack(imageFrame);
+
mRequestingLoad = true;
bool synchronousLoading = false;
// removed, and another frame is loaded
bool frontFrameReady = IsFrontReady();
- for( unsigned int i=0; i< mBatchSize && !mQueue.IsFull(); ++i )
+ for( unsigned int i=0; i< mBatchSize && mQueue.Count() + mLoadWaitingQueue.size() < static_cast<uint32_t>(mCacheSize) && !mQueue.IsFull(); ++i )
{
- ImageFrame imageFrame;
- imageFrame.mFrameNumber = mFrameIndex;
- imageFrame.mReady = false;
-
- mQueue.PushBack( imageFrame );
-
if( !mOnLoading )
{
mOnLoading = true;
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.
*/
- uint32_t GetFrameInterval( uint32_t frameIndex ) override;
+ uint32_t GetFrameInterval( uint32_t frameIndex ) const override;
+
+ /**
+ * Get the current rendered frame index.
+ * If there isn't any loaded frame, returns -1.
+ */
+ int32_t GetCurrentFrameIndex() const override;
private:
/**
Dali::AnimatedImageLoading mAnimatedImageLoading;
uint32_t mFrameCount;
int mFrameIndex;
+ int mCacheSize;
std::vector<UrlStore> mImageUrls;
std::vector<int32_t> mIntervals;
std::vector<uint32_t> mLoadWaitingQueue;
return Frame( 0u );
}
-uint32_t RollingImageCache::GetFrameInterval( uint32_t frameIndex )
+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.");
+ }
+
+ return textureSet;
+}
+
+uint32_t RollingImageCache::GetFrameInterval( uint32_t frameIndex ) const
{
return 0u;
}
+int32_t RollingImageCache::GetCurrentFrameIndex() const
+{
+ if(mQueue.IsEmpty())
+ {
+ return -1;
+ }
+ return mQueue.Front().mUrlIndex;
+}
+
bool RollingImageCache::IsFrontReady() const
{
return ( !mQueue.IsEmpty() && mQueue.Front().mReady );
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.
*/
- uint32_t GetFrameInterval( uint32_t frameIndex ) override;
+ uint32_t GetFrameInterval( uint32_t frameIndex ) const override;
+
+ /**
+ * Get the current rendered frame index.
+ * If there isn't any loaded frame, returns -1.
+ */
+ int32_t GetCurrentFrameIndex() const override;
private:
/**
TextureHash textureHash = INITIAL_CACHE_NUMBER;
int cacheIndex = INVALID_CACHE_INDEX;
- if(storageType != StorageType::RETURN_PIXEL_BUFFER)
+ if(storageType != StorageType::RETURN_PIXEL_BUFFER && !isAnimatedImage)
{
- textureHash = GenerateHash(url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, isAnimatedImage, frameIndex);
+ 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, isAnimatedImage, frameIndex);
+ cacheIndex = FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad);
}
TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
-
// Check if the requested Texture exists in the cache.
if( cacheIndex != INVALID_CACHE_INDEX )
{
const FittingMode::Type fittingMode,
const Dali::SamplingMode::Type samplingMode,
const UseAtlas useAtlas,
- TextureId maskTextureId,
- bool isAnimationImage,
- uint32_t frameIndex )
+ TextureId maskTextureId)
{
std::string hashTarget( url );
const size_t urlLength = hashTarget.length();
}
}
- if( isAnimationImage )
- {
- auto textureIdIndex = hashTarget.length();
- hashTarget.resize( hashTarget.length() + sizeof( uint32_t ) );
- char* hashTargetPtr = &( hashTarget[ textureIdIndex ] );
-
- for( size_t byteIter = 0; byteIter < sizeof( uint32_t ); ++byteIter )
- {
- *hashTargetPtr++ = frameIndex & 0xff;
- frameIndex >>= 8u;
- }
- }
-
if( maskTextureId != INVALID_TEXTURE_ID )
{
auto textureIdIndex = hashTarget.length();
const Dali::SamplingMode::Type samplingMode,
const bool useAtlas,
TextureId maskTextureId,
- TextureManager::MultiplyOnLoad preMultiplyOnLoad,
- bool isAnimatedImage,
- uint32_t frameIndex )
+ TextureManager::MultiplyOnLoad preMultiplyOnLoad)
{
// Default to an invalid ID, in case we do not find a match.
int cacheIndex = INVALID_CACHE_INDEX;
( size == textureInfo.desiredSize ) &&
( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
( fittingMode == textureInfo.fittingMode &&
- samplingMode == textureInfo.samplingMode ) ) &&
- ( isAnimatedImage == ( ( textureInfo.animatedImageLoading ) ? true : false ) ) &&
- ( frameIndex == textureInfo.frameIndex ) )
+ samplingMode == textureInfo.samplingMode ) ) )
{
// 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different.
// 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false.
* @param[in] samplingMode The SamplingMode to use
* @param[in] useAtlas True if atlased
* @param[in] maskTextureId The masking texture id (or INVALID_TEXTURE_ID)
- * @param[in] isAnimatedImage The boolean value to know whether the request is for animated image or not
- * @param[in] frameIndex The frame index of a frame to be loaded frame
* @return A hash of the provided data for caching.
*/
TextureHash GenerateHash( const std::string& url, const ImageDimensions size,
const FittingMode::Type fittingMode,
const Dali::SamplingMode::Type samplingMode, const UseAtlas useAtlas,
- TextureId maskTextureId, bool isAnimatedImage, uint32_t frameIndex );
+ TextureId maskTextureId );
/**
* @brief Looks up a cached texture by its hash.
* @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] isAnimatedImage The boolean value to know whether the request is for animated image or not
- * @param[in] frameIndex The frame index of a frame to be loaded frame
* @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,
- bool isAnimatedImage,
- uint32_t frameIndex );
+ MultiplyOnLoad preMultiplyOnLoad);
private: