Create Renderer when the Visual is created
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / animated-image / animated-image-visual.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "animated-image-visual.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/image-loading.h>
23 #include <dali/integration-api/debug.h>
24 #include <memory>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
28 #include <dali-toolkit/public-api/visuals/visual-properties.h>
29 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
30 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
31 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
32 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
33 #include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
34 #include <dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h>
35 #include <dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h>
36 #include <dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h>
37 #include <dali-toolkit/devel-api/image-loader/image-atlas.h>
38 #include <dali-toolkit/devel-api/image-loader/texture-manager.h>
39 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
40
41 namespace Dali
42 {
43
44 namespace Toolkit
45 {
46
47 namespace Internal
48 {
49
50 namespace
51 {
52 // stop behavior
53 DALI_ENUM_TO_STRING_TABLE_BEGIN( STOP_BEHAVIOR )
54 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::Toolkit::DevelImageVisual::StopBehavior, CURRENT_FRAME )
55 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::Toolkit::DevelImageVisual::StopBehavior, FIRST_FRAME )
56 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::Toolkit::DevelImageVisual::StopBehavior, LAST_FRAME )
57 DALI_ENUM_TO_STRING_TABLE_END( STOP_BEHAVIOR )
58
59 // wrap modes
60 DALI_ENUM_TO_STRING_TABLE_BEGIN( WRAP_MODE )
61 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, DEFAULT )
62 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, CLAMP_TO_EDGE )
63 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, REPEAT )
64 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, MIRRORED_REPEAT )
65 DALI_ENUM_TO_STRING_TABLE_END( WRAP_MODE )
66
67 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
68 constexpr auto LOOP_FOREVER = -1;
69
70 #if defined(DEBUG_ENABLED)
71 Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
72 #endif
73 }
74
75
76 /**
77  * Multi-image  Flow of execution
78  *
79  *   | New
80  *   |   DoSetProperties()
81  *   |   LoadFirstBatch()
82  *   |     new cache
83  *   |       cache->LoadBatch()
84  *   |
85  *   | DoSetOnScene()
86  *   |   PrepareTextureSet()
87  *   |     cache->FirstFrame()
88  *   |   CreateRenderer()    (Doesn't become ready until first frame loads)
89  *   |   StartFirstFrame()
90  *   |
91  *   | FrameReady(textureSet)
92  *   |   start first frame:
93  *   |     actor.AddRenderer
94  *   |     start timer
95  *   |   mRenderer.SetTextures(textureSet)
96  *   |
97  *   | Timer ticks
98  *   |   DisplayNextFrame()
99  *   |     if front frame is ready,
100  *   |       mRenderer.SetTextures( front frame's texture )
101  *   |     else
102  *   |       mWaitingForTexture=true
103  *   |     cache->LoadBatch()
104  *   |
105  *   | FrameReady(textureSet)
106  *   |   mRenderer.SetTextures(textureSet)
107  *   V
108  *  Time
109  */
110
111 AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, const Property::Map& properties )
112 {
113   AnimatedImageVisualPtr visual( new AnimatedImageVisual( factoryCache, shaderFactory ) );
114   visual->InitializeAnimatedImage( imageUrl );
115   visual->SetProperties( properties );
116
117   if( visual->mFrameCount > 0 )
118   {
119     visual->LoadFirstBatch();
120   }
121
122   visual->Initialize();
123
124   return visual;
125 }
126
127 AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const Property::Array& imageUrls, const Property::Map& properties )
128 {
129   AnimatedImageVisualPtr visual( new AnimatedImageVisual( factoryCache, shaderFactory ) );
130   visual->mImageUrls = new ImageCache::UrlList();
131   visual->mImageUrls->reserve( imageUrls.Count() );
132
133   for( unsigned int i=0; i < imageUrls.Count(); ++i)
134   {
135     ImageCache::UrlStore urlStore;
136     urlStore.mTextureId = TextureManager::INVALID_TEXTURE_ID;
137     urlStore.mUrl = imageUrls[i].Get<std::string>();
138     visual->mImageUrls->push_back( urlStore );
139   }
140   visual->mFrameCount = imageUrls.Count();
141   visual->SetProperties( properties );
142
143   if( visual->mFrameCount > 0 )
144   {
145     visual->LoadFirstBatch();
146   }
147
148   visual->Initialize();
149
150   return visual;
151 }
152
153 AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl )
154 {
155   AnimatedImageVisualPtr visual( new AnimatedImageVisual( factoryCache, shaderFactory ) );
156   visual->InitializeAnimatedImage( imageUrl );
157
158   if( visual->mFrameCount > 0 )
159   {
160     visual->LoadFirstBatch();
161   }
162
163   visual->Initialize();
164
165   return visual;
166 }
167
168 void AnimatedImageVisual::InitializeAnimatedImage( const VisualUrl& imageUrl )
169 {
170   mImageUrl = imageUrl;
171   mAnimatedImageLoading = AnimatedImageLoading::New( imageUrl.GetUrl(), imageUrl.IsLocalResource() );
172   mFrameCount = mAnimatedImageLoading.GetImageCount();
173 }
174
175 AnimatedImageVisual::AnimatedImageVisual( VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory )
176 : Visual::Base( factoryCache, Visual::FittingMode::FIT_KEEP_ASPECT_RATIO, Toolkit::Visual::ANIMATED_IMAGE ),
177   mFrameDelayTimer(),
178   mPlacementActor(),
179   mImageVisualShaderFactory( shaderFactory ),
180   mPixelArea( FULL_TEXTURE_RECT ),
181   mImageUrl(),
182   mAnimatedImageLoading(),
183   mFrameIndexForJumpTo( 0 ),
184   mImageUrls( NULL ),
185   mImageCache( NULL ),
186   mCacheSize( 2 ),
187   mBatchSize( 2 ),
188   mFrameDelay( 100 ),
189   mLoopCount( LOOP_FOREVER ),
190   mCurrentLoopIndex( 0 ),
191   mUrlIndex( 0 ),
192   mFrameCount( 0 ),
193   mImageSize(),
194   mWrapModeU( WrapMode::DEFAULT ),
195   mWrapModeV( WrapMode::DEFAULT ),
196   mActionStatus( DevelAnimatedImageVisual::Action::PLAY ),
197   mStopBehavior( DevelImageVisual::StopBehavior::CURRENT_FRAME ),
198   mStartFirstFrame(false),
199   mIsJumpTo( false )
200 {}
201
202 AnimatedImageVisual::~AnimatedImageVisual()
203 {
204   delete mImageCache;
205   delete mImageUrls;
206 }
207
208 void AnimatedImageVisual::GetNaturalSize( Vector2& naturalSize )
209 {
210   if( mImageSize.GetWidth() == 0 &&  mImageSize.GetHeight() == 0)
211   {
212     if( mImageUrl.IsValid() )
213     {
214       mImageSize = mAnimatedImageLoading.GetImageSize();
215     }
216     else if( mImageUrls && mImageUrls->size() > 0 )
217     {
218       mImageSize = Dali::GetClosestImageSize( (*mImageUrls)[0].mUrl );
219     }
220   }
221
222   naturalSize.width = mImageSize.GetWidth();
223   naturalSize.height = mImageSize.GetHeight();
224 }
225
226 void AnimatedImageVisual::DoCreatePropertyMap( Property::Map& map ) const
227 {
228   map.Clear();
229
230   bool sync = IsSynchronousLoadingRequired();
231   map.Insert( Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync );
232
233   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE );
234
235   if( mImageUrl.IsValid() )
236   {
237     map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() );
238   }
239   if( mImageUrls != NULL && ! mImageUrls->empty() )
240   {
241     Property::Array urls;
242     for( unsigned int i=0; i<mImageUrls->size(); ++i)
243     {
244       urls.Add( (*mImageUrls)[i].mUrl );
245     }
246     Property::Value value( const_cast<Property::Array&>(urls) );
247     map.Insert( Toolkit::ImageVisual::Property::URL, value );
248   }
249
250   map.Insert( Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea );
251   map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU );
252   map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV );
253
254   map.Insert( Toolkit::ImageVisual::Property::BATCH_SIZE, static_cast<int>(mBatchSize) );
255   map.Insert( Toolkit::ImageVisual::Property::CACHE_SIZE, static_cast<int>(mCacheSize) );
256   map.Insert( Toolkit::ImageVisual::Property::FRAME_DELAY, static_cast<int>(mFrameDelay) );
257   map.Insert( Toolkit::DevelImageVisual::Property::LOOP_COUNT, static_cast<int>(mLoopCount) );
258   map.Insert( Toolkit::DevelImageVisual::Property::CURRENT_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>(mImageCache->GetCurrentFrameIndex()) : -1 );
259   map.Insert( Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>(mImageCache->GetTotalFrameCount()) : -1 );
260
261   map.Insert( Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mStopBehavior );
262 }
263
264 void AnimatedImageVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
265 {
266   // Do nothing
267 }
268
269 void AnimatedImageVisual::OnDoAction( const Dali::Property::Index actionId, const Dali::Property::Value& attributes )
270 {
271   // Check if action is valid for this visual type and perform action if possible
272
273   switch ( actionId )
274   {
275     case DevelAnimatedImageVisual::Action::PAUSE:
276     {
277       // Pause will be executed on next timer tick
278       mActionStatus = DevelAnimatedImageVisual::Action::PAUSE;
279       break;
280     }
281     case DevelAnimatedImageVisual::Action::PLAY:
282     {
283       if( mFrameDelayTimer && IsOnScene() && mActionStatus != DevelAnimatedImageVisual::Action::PLAY )
284       {
285         mFrameDelayTimer.Start();
286       }
287       mActionStatus = DevelAnimatedImageVisual::Action::PLAY;
288       break;
289     }
290     case DevelAnimatedImageVisual::Action::STOP:
291     {
292       // STOP reset functionality will actually be done in a future change
293       // Stop will be executed on next timer tick
294       mActionStatus = DevelAnimatedImageVisual::Action::STOP;
295       if( IsOnScene() )
296       {
297         DisplayNextFrame();
298       }
299       break;
300     }
301     case DevelAnimatedImageVisual::Action::JUMP_TO:
302     {
303       int32_t frameNumber;
304       if( attributes.Get( frameNumber ) )
305       {
306         if( frameNumber < 0 || frameNumber >= static_cast<int32_t>( mFrameCount ) )
307         {
308           DALI_LOG_ERROR( "Invalid frame index used.\n" );
309         }
310         else
311         {
312           mIsJumpTo = true;
313           mFrameIndexForJumpTo = frameNumber;
314           if( IsOnScene() )
315           {
316             DisplayNextFrame();
317           }
318         }
319       }
320       break;
321     }
322   }
323 }
324
325 void AnimatedImageVisual::DoSetProperties( const Property::Map& propertyMap )
326 {
327   // url[s] already passed in from constructor
328
329   for( Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter )
330   {
331     KeyValuePair keyValue = propertyMap.GetKeyValue( iter );
332     if( keyValue.first.type == Property::Key::INDEX )
333     {
334       DoSetProperty( keyValue.first.indexKey, keyValue.second );
335     }
336     else
337     {
338       if( keyValue.first == PIXEL_AREA_UNIFORM_NAME )
339       {
340         DoSetProperty( Toolkit::ImageVisual::Property::PIXEL_AREA, keyValue.second );
341       }
342       else if( keyValue.first == IMAGE_WRAP_MODE_U )
343       {
344         DoSetProperty( Toolkit::ImageVisual::Property::WRAP_MODE_U, keyValue.second );
345       }
346       else if( keyValue.first == IMAGE_WRAP_MODE_V )
347       {
348         DoSetProperty( Toolkit::ImageVisual::Property::WRAP_MODE_V, keyValue.second );
349       }
350       else if( keyValue.first == BATCH_SIZE_NAME )
351       {
352         DoSetProperty( Toolkit::ImageVisual::Property::BATCH_SIZE, keyValue.second );
353       }
354       else if( keyValue.first == CACHE_SIZE_NAME )
355       {
356         DoSetProperty( Toolkit::ImageVisual::Property::CACHE_SIZE, keyValue.second );
357       }
358       else if( keyValue.first == FRAME_DELAY_NAME )
359       {
360         DoSetProperty( Toolkit::ImageVisual::Property::FRAME_DELAY, keyValue.second );
361       }
362       else if( keyValue.first == LOOP_COUNT_NAME )
363       {
364         DoSetProperty( Toolkit::DevelImageVisual::Property::LOOP_COUNT, keyValue.second );
365       }
366       else if( keyValue.first == STOP_BEHAVIOR_NAME )
367       {
368          DoSetProperty( Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, keyValue.second );
369       }
370     }
371   }
372 }
373
374 void AnimatedImageVisual::DoSetProperty( Property::Index index,
375                                          const Property::Value& value )
376 {
377   switch(index)
378   {
379     case Toolkit::ImageVisual::Property::PIXEL_AREA:
380     {
381       value.Get( mPixelArea );
382       break;
383     }
384     case Toolkit::ImageVisual::Property::WRAP_MODE_U:
385     {
386       int wrapMode = 0;
387       if(Scripting::GetEnumerationProperty( value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode ))
388       {
389         mWrapModeU = Dali::WrapMode::Type(wrapMode);
390       }
391       else
392       {
393         mWrapModeU = Dali::WrapMode::Type::DEFAULT;
394       }
395       break;
396     }
397     case Toolkit::ImageVisual::Property::WRAP_MODE_V:
398     {
399       int wrapMode = 0;
400       if(Scripting::GetEnumerationProperty( value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode ))
401       {
402         mWrapModeV = Dali::WrapMode::Type(wrapMode);
403       }
404       else
405       {
406         mWrapModeV = Dali::WrapMode::Type::DEFAULT;
407       }
408       break;
409     }
410
411     case Toolkit::ImageVisual::Property::BATCH_SIZE:
412     {
413       int batchSize;
414       if( value.Get( batchSize ) )
415       {
416         if( batchSize < 2 )
417         {
418           DALI_LOG_ERROR( "The minimum value of batch size is 2." );
419         }
420         else
421         {
422           mBatchSize = batchSize;
423         }
424       }
425       break;
426     }
427
428     case Toolkit::ImageVisual::Property::CACHE_SIZE:
429     {
430       int cacheSize;
431       if( value.Get( cacheSize ) )
432       {
433         if( cacheSize < 2 )
434         {
435           DALI_LOG_ERROR( "The minimum value of cache size is 2." );
436         }
437         else
438         {
439           mCacheSize = cacheSize;
440         }
441       }
442       break;
443     }
444
445     case Toolkit::ImageVisual::Property::FRAME_DELAY:
446     {
447       int frameDelay;
448       if( value.Get( frameDelay ) )
449       {
450         mFrameDelay = frameDelay;
451       }
452       break;
453     }
454
455     case Toolkit::DevelImageVisual::Property::LOOP_COUNT:
456     {
457       int loopCount;
458       if( value.Get( loopCount ) )
459       {
460         mLoopCount = loopCount;
461       }
462       break;
463     }
464
465     case Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR:
466     {
467       int32_t stopBehavior = mStopBehavior;
468       if( Scripting::GetEnumerationProperty( value, STOP_BEHAVIOR_TABLE, STOP_BEHAVIOR_TABLE_COUNT, stopBehavior ) )
469       {
470         mStopBehavior = DevelImageVisual::StopBehavior::Type( stopBehavior );
471       }
472       break;
473     }
474
475     case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
476     {
477       bool sync = false;
478       value.Get( sync );
479       if( sync )
480       {
481         mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
482       }
483       else
484       {
485         mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
486       }
487       break;
488     }
489   }
490 }
491
492 void AnimatedImageVisual::DoSetOnScene( Actor& actor )
493 {
494   mPlacementActor = actor;
495   TextureSet textureSet = PrepareTextureSet();
496
497   if( textureSet ) // if the image loading is successful
498   {
499     StartFirstFrame( textureSet );
500   }
501   else
502   {
503     mStartFirstFrame = true;
504   }
505 }
506
507 void AnimatedImageVisual::DoSetOffScene( Actor& actor )
508 {
509   DALI_ASSERT_DEBUG( (bool)mImpl->mRenderer && "There should always be a renderer whilst on stage");
510
511   if( mFrameDelayTimer )
512   {
513     mFrameDelayTimer.Stop();
514     mFrameDelayTimer.Reset();
515   }
516
517   actor.RemoveRenderer( mImpl->mRenderer );
518   mPlacementActor.Reset();
519   mStartFirstFrame = false;
520 }
521
522 void AnimatedImageVisual::OnSetTransform()
523 {
524   if( mImpl->mRenderer )
525   {
526     mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
527   }
528 }
529
530 void AnimatedImageVisual::OnInitialize()
531 {
532   bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
533   bool atlasing = false;
534   Shader shader = mImageVisualShaderFactory.GetShader( mFactoryCache, atlasing, defaultWrapMode, IsRoundedCornerRequired() );
535
536   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
537
538   mImpl->mRenderer = Renderer::New( geometry, shader );
539
540   // Register transform properties
541   mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
542
543   if( !defaultWrapMode ) // custom wrap mode
544   {
545     Vector2 wrapMode(mWrapModeU-WrapMode::CLAMP_TO_EDGE, mWrapModeV-WrapMode::CLAMP_TO_EDGE);
546     wrapMode.Clamp( Vector2::ZERO, Vector2( 2.f, 2.f ) );
547     mImpl->mRenderer.RegisterProperty( WRAP_MODE_UNIFORM_NAME, wrapMode );
548   }
549
550   if( mPixelArea != FULL_TEXTURE_RECT )
551   {
552     mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
553   }
554 }
555
556 void AnimatedImageVisual::LoadFirstBatch()
557 {
558   // Ensure the batch size and cache size are no bigger than the number of URLs,
559   // and that the cache is at least as big as the batch size.
560   uint16_t numUrls = 0;
561   uint16_t batchSize = 1;
562   uint16_t cacheSize = 1;
563
564   if( mImageUrls )
565   {
566     numUrls = mImageUrls->size();
567   }
568   else
569   {
570     numUrls = mFrameCount;
571   }
572
573   batchSize = std::min( mBatchSize, numUrls );
574   cacheSize = std::min( std::max( batchSize, mCacheSize ), numUrls );
575
576   DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::LoadFirstBatch()  batchSize:%d  cacheSize:%d\n", batchSize, cacheSize);
577
578   mUrlIndex = 0;
579   TextureManager& textureManager = mFactoryCache.GetTextureManager();
580
581   if( mAnimatedImageLoading )
582   {
583     mImageCache = new RollingAnimatedImageCache( textureManager, mAnimatedImageLoading, mFrameCount, *this, cacheSize, batchSize, IsSynchronousLoadingRequired() );
584   }
585   else if( mImageUrls )
586   {
587     if( batchSize > 0 && cacheSize > 0 )
588     {
589       if( cacheSize < numUrls )
590       {
591         mImageCache = new RollingImageCache( textureManager, *mImageUrls, *this, cacheSize, batchSize );
592       }
593       else
594       {
595         mImageCache = new FixedImageCache( textureManager, *mImageUrls, *this, batchSize );
596       }
597     }
598     else
599     {
600       mImageCache = new RollingImageCache( textureManager, *mImageUrls, *this, 1, 1 );
601     }
602   }
603
604   if (!mImageCache)
605   {
606     DALI_LOG_ERROR("mImageCache is null\n");
607   }
608 }
609
610 void AnimatedImageVisual::StartFirstFrame( TextureSet& textureSet )
611 {
612   DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::StartFirstFrame()\n");
613
614   mStartFirstFrame = false;
615   if(mImpl->mRenderer)
616   {
617     mImpl->mRenderer.SetTextures( textureSet );
618
619     Actor actor = mPlacementActor.GetHandle();
620     if( actor )
621     {
622       actor.AddRenderer( mImpl->mRenderer );
623       mPlacementActor.Reset();
624     }
625   }
626
627   if( mFrameCount > 1 )
628   {
629     int frameDelay = mImageCache->GetFrameInterval( 0 );
630     if( frameDelay == 0u )
631     {
632       frameDelay = mFrameDelay; // from URL array
633     }
634     mFrameDelayTimer = Timer::New( frameDelay );
635     mFrameDelayTimer.TickSignal().Connect( this, &AnimatedImageVisual::DisplayNextFrame );
636     mFrameDelayTimer.Start();
637   }
638   DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"ResourceReady(ResourceStatus::READY)\n");
639   ResourceReady( Toolkit::Visual::ResourceStatus::READY );
640 }
641
642 TextureSet AnimatedImageVisual::PrepareTextureSet()
643 {
644   TextureSet textureSet;
645   if (mImageCache)
646   {
647     textureSet = mImageCache->FirstFrame();
648   }
649
650   if( textureSet )
651   {
652     SetImageSize( textureSet );
653   }
654
655   return textureSet;
656 }
657
658 void AnimatedImageVisual::SetImageSize( TextureSet& textureSet )
659 {
660   if( textureSet )
661   {
662     Texture texture = textureSet.GetTexture( 0 );
663     if( texture )
664     {
665       mImageSize.SetWidth( texture.GetWidth() );
666       mImageSize.SetHeight( texture.GetHeight() );
667     }
668   }
669 }
670
671 void AnimatedImageVisual::FrameReady( TextureSet textureSet )
672 {
673   if(textureSet)
674   {
675     SetImageSize(textureSet);
676
677     if(mStartFirstFrame)
678     {
679       StartFirstFrame(textureSet);
680     }
681     else
682     {
683       if(mImpl->mRenderer)
684       {
685         mImpl->mRenderer.SetTextures(textureSet);
686       }
687     }
688   }
689   else
690   {
691     DALI_LOG_INFO( gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n" );
692     ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
693   }
694 }
695
696 bool AnimatedImageVisual::DisplayNextFrame()
697 {
698   bool continueTimer = false;
699
700   if(mImageCache)
701   {
702     bool nextFrame = false;
703     uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
704
705     if( mIsJumpTo )
706     {
707       mIsJumpTo = false;
708       frameIndex = mFrameIndexForJumpTo;
709     }
710     else if( mActionStatus == DevelAnimatedImageVisual::Action::PAUSE )
711     {
712       return false;
713     }
714     else if( mActionStatus == DevelAnimatedImageVisual::Action::STOP )
715     {
716       frameIndex = 0;
717       if( mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME )
718       {
719         frameIndex = 0;
720       }
721       else if( mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME )
722       {
723         frameIndex = mFrameCount - 1;
724       }
725       else
726       {
727         return false; // Do not draw already rendered scene twice.
728       }
729     }
730     else
731     {
732       if( mFrameCount > 1 )
733       {
734         nextFrame = true;
735         frameIndex++;
736         if( frameIndex >= mFrameCount )
737         {
738           frameIndex %= mFrameCount;
739           ++mCurrentLoopIndex;
740         }
741
742         if(mLoopCount >= 0 && mCurrentLoopIndex >= mLoopCount)
743         {
744           // This will stop timer
745           mActionStatus = DevelAnimatedImageVisual::Action::STOP;
746           return DisplayNextFrame();
747         }
748       }
749
750       unsigned int delay = mImageCache->GetFrameInterval( frameIndex );
751       if( delay > 0u )
752       {
753         if( mFrameDelayTimer.GetInterval() != delay )
754         {
755           mFrameDelayTimer.SetInterval( delay );
756         }
757       }
758     }
759
760     DALI_LOG_INFO( gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex);
761
762     TextureSet textureSet;
763     if(nextFrame)
764     {
765       textureSet = mImageCache->NextFrame();
766     }
767     else
768     {
769       textureSet = mImageCache->Frame( frameIndex );
770     }
771
772     if( textureSet )
773     {
774       SetImageSize( textureSet );
775       if( mImpl->mRenderer )
776       {
777         mImpl->mRenderer.SetTextures( textureSet );
778       }
779     }
780
781     continueTimer = ( mActionStatus == DevelAnimatedImageVisual::Action::PLAY ) ? true : false;
782   }
783
784   return continueTimer;
785 }
786
787
788 } // namespace Internal
789
790 } // namespace Toolkit
791
792 } // namespace Dali