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