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