2b4a3d07266f8b2ba9acedd64c9f8050130eec2f
[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, mMaskingData, *this, mCacheSize, mBatchSize, IsSynchronousLoadingRequired(),mFactoryCache.GetPreMultiplyOnLoad());
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, mMaskingData, *this, cacheSize, batchSize, mFrameDelay);
190     }
191     else
192     {
193       mImageCache = new FixedImageCache(textureManager, *mImageUrls, mMaskingData, *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   mMaskingData(),
223   mFrameCount(0),
224   mImageSize(),
225   mActionStatus(DevelAnimatedImageVisual::Action::PLAY),
226   mWrapModeU(WrapMode::DEFAULT),
227   mWrapModeV(WrapMode::DEFAULT),
228   mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME),
229   mStartFirstFrame(false),
230   mIsJumpTo(false)
231 {
232   EnablePreMultipliedAlpha(mFactoryCache.GetPreMultiplyOnLoad());
233 }
234
235 AnimatedImageVisual::~AnimatedImageVisual()
236 {
237   // AnimatedImageVisual destroyed so remove texture unless ReleasePolicy is set to never release
238   // If this is animated image, clear cache. Else if this is single frame image, this is affected be release policy.
239   if(mFrameCount > SINGLE_IMAGE_COUNT || mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER)
240   {
241     mImageCache->ClearCache();
242   }
243   delete mImageCache;
244   delete mImageUrls;
245 }
246
247 void AnimatedImageVisual::GetNaturalSize(Vector2& naturalSize)
248 {
249   if(mImageSize.GetWidth() == 0 && mImageSize.GetHeight() == 0)
250   {
251     if(mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid() &&
252        mMaskingData->mCropToMask)
253     {
254       ImageDimensions dimensions = Dali::GetClosestImageSize(mMaskingData->mAlphaMaskUrl.GetUrl());
255       if(dimensions != ImageDimensions(0, 0))
256       {
257         mImageSize    = dimensions;
258         naturalSize.x = dimensions.GetWidth();
259         naturalSize.y = dimensions.GetHeight();
260         return;
261       }
262     }
263
264     if(mImageUrl.IsValid())
265     {
266       mImageSize = mAnimatedImageLoading.GetImageSize();
267     }
268     else if(mImageUrls && mImageUrls->size() > 0)
269     {
270       mImageSize = Dali::GetClosestImageSize((*mImageUrls)[0].mUrl.GetUrl());
271     }
272   }
273
274   naturalSize.width  = mImageSize.GetWidth();
275   naturalSize.height = mImageSize.GetHeight();
276 }
277
278 void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const
279 {
280   map.Clear();
281
282   bool sync = IsSynchronousLoadingRequired();
283   map.Insert(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync);
284
285   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE);
286
287   if(mImageUrl.IsValid())
288   {
289     map.Insert(Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl());
290   }
291   if(mImageUrls != NULL && !mImageUrls->empty())
292   {
293     Property::Array urls;
294     for(unsigned int i = 0; i < mImageUrls->size(); ++i)
295     {
296       urls.Add((*mImageUrls)[i].mUrl.GetUrl());
297     }
298     Property::Value value(const_cast<Property::Array&>(urls));
299     map.Insert(Toolkit::ImageVisual::Property::URL, value);
300   }
301
302   map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea);
303   map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU);
304   map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV);
305
306   map.Insert(Toolkit::ImageVisual::Property::BATCH_SIZE, static_cast<int>(mBatchSize));
307   map.Insert(Toolkit::ImageVisual::Property::CACHE_SIZE, static_cast<int>(mCacheSize));
308   map.Insert(Toolkit::ImageVisual::Property::FRAME_DELAY, static_cast<int>(mFrameDelay));
309   map.Insert(Toolkit::DevelImageVisual::Property::LOOP_COUNT, static_cast<int>(mLoopCount));
310   map.Insert(Toolkit::DevelImageVisual::Property::CURRENT_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>(mImageCache->GetCurrentFrameIndex()) : -1);
311   map.Insert(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>((mAnimatedImageLoading) ? mAnimatedImageLoading.GetImageCount() : mImageCache->GetTotalFrameCount()) : -1);
312
313   map.Insert(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mStopBehavior);
314
315   if(mMaskingData != nullptr)
316   {
317     map.Insert(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, mMaskingData->mAlphaMaskUrl.GetUrl());
318     map.Insert(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, mMaskingData->mContentScaleFactor);
319     map.Insert(Toolkit::ImageVisual::Property::CROP_TO_MASK, mMaskingData->mCropToMask);
320   }
321
322   map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
323   map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
324 }
325
326 void AnimatedImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
327 {
328   // Do nothing
329 }
330
331 void AnimatedImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
332 {
333   // Make not set any action when the resource status is already failed.
334   if(mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED)
335   {
336     return;
337   }
338
339   // Check if action is valid for this visual type and perform action if possible
340   switch(actionId)
341   {
342     case DevelAnimatedImageVisual::Action::PAUSE:
343     {
344       // Pause will be executed on next timer tick
345       mActionStatus = DevelAnimatedImageVisual::Action::PAUSE;
346       break;
347     }
348     case DevelAnimatedImageVisual::Action::PLAY:
349     {
350       if(mFrameDelayTimer && IsOnScene() && mActionStatus != DevelAnimatedImageVisual::Action::PLAY)
351       {
352         mFrameDelayTimer.Start();
353       }
354       mActionStatus = DevelAnimatedImageVisual::Action::PLAY;
355       break;
356     }
357     case DevelAnimatedImageVisual::Action::STOP:
358     {
359       // STOP reset functionality will actually be done in a future change
360       // Stop will be executed on next timer tick
361       mActionStatus     = DevelAnimatedImageVisual::Action::STOP;
362       mCurrentLoopIndex = FIRST_LOOP;
363       if(IsOnScene())
364       {
365         DisplayNextFrame();
366       }
367       break;
368     }
369     case DevelAnimatedImageVisual::Action::JUMP_TO:
370     {
371       int32_t frameNumber;
372       if(attributes.Get(frameNumber))
373       {
374         if(frameNumber < 0 || frameNumber >= static_cast<int32_t>(mFrameCount))
375         {
376           DALI_LOG_ERROR("Invalid frame index used.\n");
377         }
378         else
379         {
380           mIsJumpTo            = true;
381           mFrameIndexForJumpTo = frameNumber;
382           if(IsOnScene())
383           {
384             DisplayNextFrame();
385           }
386         }
387       }
388       break;
389     }
390   }
391 }
392
393 void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap)
394 {
395   // url[s] already passed in from constructor
396   for(Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter)
397   {
398     KeyValuePair keyValue = propertyMap.GetKeyValue(iter);
399     if(keyValue.first.type == Property::Key::INDEX)
400     {
401       DoSetProperty(keyValue.first.indexKey, keyValue.second);
402     }
403     else
404     {
405       if(keyValue.first == PIXEL_AREA_UNIFORM_NAME)
406       {
407         DoSetProperty(Toolkit::ImageVisual::Property::PIXEL_AREA, keyValue.second);
408       }
409       else if(keyValue.first == IMAGE_WRAP_MODE_U)
410       {
411         DoSetProperty(Toolkit::ImageVisual::Property::WRAP_MODE_U, keyValue.second);
412       }
413       else if(keyValue.first == IMAGE_WRAP_MODE_V)
414       {
415         DoSetProperty(Toolkit::ImageVisual::Property::WRAP_MODE_V, keyValue.second);
416       }
417       else if(keyValue.first == BATCH_SIZE_NAME)
418       {
419         DoSetProperty(Toolkit::ImageVisual::Property::BATCH_SIZE, keyValue.second);
420       }
421       else if(keyValue.first == CACHE_SIZE_NAME)
422       {
423         DoSetProperty(Toolkit::ImageVisual::Property::CACHE_SIZE, keyValue.second);
424       }
425       else if(keyValue.first == FRAME_DELAY_NAME)
426       {
427         DoSetProperty(Toolkit::ImageVisual::Property::FRAME_DELAY, keyValue.second);
428       }
429       else if(keyValue.first == LOOP_COUNT_NAME)
430       {
431         DoSetProperty(Toolkit::DevelImageVisual::Property::LOOP_COUNT, keyValue.second);
432       }
433       else if(keyValue.first == STOP_BEHAVIOR_NAME)
434       {
435         DoSetProperty(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, keyValue.second);
436       }
437       else if(keyValue.first == ALPHA_MASK_URL)
438       {
439         DoSetProperty(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, keyValue.second);
440       }
441       else if(keyValue.first == MASK_CONTENT_SCALE_NAME)
442       {
443         DoSetProperty(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, keyValue.second);
444       }
445       else if(keyValue.first == CROP_TO_MASK_NAME)
446       {
447         DoSetProperty(Toolkit::ImageVisual::Property::CROP_TO_MASK, keyValue.second);
448       }
449       else if(keyValue.first == LOAD_POLICY_NAME)
450       {
451         DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second);
452       }
453       else if(keyValue.first == RELEASE_POLICY_NAME)
454       {
455         DoSetProperty(Toolkit::ImageVisual::Property::RELEASE_POLICY, keyValue.second);
456       }
457       else if(keyValue.first == SYNCHRONOUS_LOADING)
458       {
459         DoSetProperty(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second);
460       }
461     }
462   }
463   // Load image immediately if LOAD_POLICY requires it
464   if(mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::IMMEDIATE)
465   {
466     PrepareTextureSet();
467   }
468 }
469
470 void AnimatedImageVisual::DoSetProperty(Property::Index        index,
471                                         const Property::Value& value)
472 {
473   switch(index)
474   {
475     case Toolkit::ImageVisual::Property::PIXEL_AREA:
476     {
477       value.Get(mPixelArea);
478       break;
479     }
480     case Toolkit::ImageVisual::Property::WRAP_MODE_U:
481     {
482       int wrapMode = 0;
483       if(Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode))
484       {
485         mWrapModeU = Dali::WrapMode::Type(wrapMode);
486       }
487       else
488       {
489         mWrapModeU = Dali::WrapMode::Type::DEFAULT;
490       }
491       break;
492     }
493     case Toolkit::ImageVisual::Property::WRAP_MODE_V:
494     {
495       int wrapMode = 0;
496       if(Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode))
497       {
498         mWrapModeV = Dali::WrapMode::Type(wrapMode);
499       }
500       else
501       {
502         mWrapModeV = Dali::WrapMode::Type::DEFAULT;
503       }
504       break;
505     }
506
507     case Toolkit::ImageVisual::Property::BATCH_SIZE:
508     {
509       int batchSize;
510       if(value.Get(batchSize))
511       {
512         if(batchSize < 2)
513         {
514           DALI_LOG_ERROR("The minimum value of batch size is 2.");
515         }
516         else
517         {
518           mBatchSize = batchSize;
519         }
520       }
521       break;
522     }
523
524     case Toolkit::ImageVisual::Property::CACHE_SIZE:
525     {
526       int cacheSize;
527       if(value.Get(cacheSize))
528       {
529         if(cacheSize < 2)
530         {
531           DALI_LOG_ERROR("The minimum value of cache size is 2.");
532         }
533         else
534         {
535           mCacheSize = cacheSize;
536         }
537       }
538       break;
539     }
540
541     case Toolkit::ImageVisual::Property::FRAME_DELAY:
542     {
543       int frameDelay;
544       if(value.Get(frameDelay))
545       {
546         mFrameDelay = frameDelay;
547         if(mImageCache)
548         {
549           mImageCache->SetInterval(static_cast<uint32_t>(mFrameDelay));
550         }
551       }
552       break;
553     }
554
555     case Toolkit::DevelImageVisual::Property::LOOP_COUNT:
556     {
557       int loopCount;
558       if(value.Get(loopCount))
559       {
560         mLoopCount = loopCount;
561       }
562       break;
563     }
564
565     case Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR:
566     {
567       int32_t stopBehavior = mStopBehavior;
568       if(Scripting::GetEnumerationProperty(value, STOP_BEHAVIOR_TABLE, STOP_BEHAVIOR_TABLE_COUNT, stopBehavior))
569       {
570         mStopBehavior = DevelImageVisual::StopBehavior::Type(stopBehavior);
571       }
572       break;
573     }
574
575     case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
576     {
577       bool sync = false;
578       value.Get(sync);
579       if(sync)
580       {
581         mImpl->mFlags |= Visual::Base::Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
582       }
583       else
584       {
585         mImpl->mFlags &= ~Visual::Base::Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
586       }
587       break;
588     }
589
590     case Toolkit::ImageVisual::Property::ALPHA_MASK_URL:
591     {
592       std::string alphaUrl = "";
593       if(value.Get(alphaUrl))
594       {
595         AllocateMaskData();
596         mMaskingData->mAlphaMaskUrl = alphaUrl;
597       }
598       break;
599     }
600
601     case Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE:
602     {
603       float scale = 1.0f;
604       if(value.Get(scale))
605       {
606         AllocateMaskData();
607         mMaskingData->mContentScaleFactor = scale;
608       }
609       break;
610     }
611
612     case Toolkit::ImageVisual::Property::CROP_TO_MASK:
613     {
614       bool crop = false;
615       if(value.Get(crop))
616       {
617         AllocateMaskData();
618         mMaskingData->mCropToMask = crop;
619       }
620       break;
621     }
622
623     case Toolkit::ImageVisual::Property::RELEASE_POLICY:
624     {
625       int releasePolicy = 0;
626       Scripting::GetEnumerationProperty(value, RELEASE_POLICY_TABLE, RELEASE_POLICY_TABLE_COUNT, releasePolicy);
627       mReleasePolicy = Toolkit::ImageVisual::ReleasePolicy::Type(releasePolicy);
628       break;
629     }
630
631     case Toolkit::ImageVisual::Property::LOAD_POLICY:
632     {
633       int loadPolicy = 0;
634       Scripting::GetEnumerationProperty(value, LOAD_POLICY_TABLE, LOAD_POLICY_TABLE_COUNT, loadPolicy);
635       mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type(loadPolicy);
636       break;
637     }
638   }
639 }
640
641 void AnimatedImageVisual::DoSetOnScene(Actor& actor)
642 {
643   mStartFirstFrame = true;
644   mPlacementActor  = actor;
645   PrepareTextureSet();
646 }
647
648 void AnimatedImageVisual::DoSetOffScene(Actor& actor)
649 {
650   DALI_ASSERT_DEBUG((bool)mImpl->mRenderer && "There should always be a renderer whilst on stage");
651
652   if(mFrameDelayTimer)
653   {
654     mFrameDelayTimer.Stop();
655     mFrameDelayTimer.Reset();
656   }
657
658   actor.RemoveRenderer(mImpl->mRenderer);
659   if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
660   {
661     mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
662     mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
663
664     TextureSet textureSet = TextureSet::New();
665     mImpl->mRenderer.SetTextures(textureSet);
666   }
667
668   mPlacementActor.Reset();
669   mStartFirstFrame   = false;
670   mCurrentFrameIndex = FIRST_FRAME_INDEX;
671   mCurrentLoopIndex  = FIRST_LOOP;
672 }
673
674 void AnimatedImageVisual::OnSetTransform()
675 {
676   if(mImpl->mRenderer)
677   {
678     mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
679   }
680 }
681
682 void AnimatedImageVisual::UpdateShader()
683 {
684   if(mImpl->mRenderer)
685   {
686     Shader shader = GenerateShader();
687     mImpl->mRenderer.SetShader(shader);
688   }
689 }
690
691 Shader AnimatedImageVisual::GenerateShader() const
692 {
693   bool   defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
694   Shader shader;
695   shader = mImageVisualShaderFactory.GetShader(
696     mFactoryCache,
697     ImageVisualShaderFeature::FeatureBuilder()
698       .ApplyDefaultTextureWrapMode(defaultWrapMode)
699       .EnableRoundedCorner(IsRoundedCornerRequired())
700       .EnableBorderline(IsBorderlineRequired()));
701   return shader;
702 }
703
704 void AnimatedImageVisual::OnInitialize()
705 {
706   CreateImageCache();
707
708   bool   defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
709   Shader shader          = GenerateShader();
710
711   Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
712
713   mImpl->mRenderer = VisualRenderer::New(geometry, shader);
714   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
715
716   // Register transform properties
717   mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
718
719   if(!defaultWrapMode) // custom wrap mode
720   {
721     Vector2 wrapMode(mWrapModeU - WrapMode::CLAMP_TO_EDGE, mWrapModeV - WrapMode::CLAMP_TO_EDGE);
722     wrapMode.Clamp(Vector2::ZERO, Vector2(2.f, 2.f));
723     mImpl->mRenderer.RegisterProperty(WRAP_MODE_UNIFORM_NAME, wrapMode);
724   }
725
726   if(mPixelArea != FULL_TEXTURE_RECT)
727   {
728     mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea);
729   }
730
731   // Enable PreMultipliedAlpha if it need premultiplied
732   auto preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader
733                              ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
734                              : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
735   EnablePreMultipliedAlpha(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
736 }
737
738 void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval)
739 {
740   DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::StartFirstFrame()\n");
741
742   mStartFirstFrame = false;
743   if(mImpl->mRenderer)
744   {
745     mImpl->mRenderer.SetTextures(textureSet);
746
747     Actor actor = mPlacementActor.GetHandle();
748     if(actor)
749     {
750       actor.AddRenderer(mImpl->mRenderer);
751       mPlacementActor.Reset();
752     }
753   }
754
755   if(mImpl->mResourceStatus != Toolkit::Visual::ResourceStatus::FAILED)
756   {
757     if(mFrameCount > SINGLE_IMAGE_COUNT)
758     {
759       mFrameDelayTimer = Timer::New(firstInterval);
760       mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame);
761       mFrameDelayTimer.Start();
762     }
763
764     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::READY)\n");
765     ResourceReady(Toolkit::Visual::ResourceStatus::READY);
766   }
767
768   mCurrentFrameIndex = FIRST_FRAME_INDEX;
769 }
770
771 void AnimatedImageVisual::PrepareTextureSet()
772 {
773   TextureSet textureSet;
774   if(mImageCache)
775   {
776     textureSet = mImageCache->FirstFrame();
777   }
778
779   // Check whether synchronous loading is true or false for the first frame.
780   if(textureSet)
781   {
782     SetImageSize(textureSet);
783   }
784 }
785
786 void AnimatedImageVisual::SetImageSize(TextureSet& textureSet)
787 {
788   if(textureSet)
789   {
790     Texture texture = textureSet.GetTexture(0);
791     if(texture)
792     {
793       mImageSize.SetWidth(texture.GetWidth());
794       mImageSize.SetHeight(texture.GetHeight());
795     }
796   }
797 }
798
799 void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval)
800 {
801   // When image visual requested to load new frame to mImageCache and it is failed.
802   if(!mImageCache || !textureSet)
803   {
804     textureSet = SetLoadingFailed();
805   }
806   SetImageSize(textureSet);
807
808   if(mStartFirstFrame)
809   {
810     mFrameCount = mImageCache->GetTotalFrameCount();
811     StartFirstFrame(textureSet, interval);
812   }
813   else
814   {
815     if(mImpl->mRenderer)
816     {
817       if(mFrameDelayTimer && interval > 0u)
818       {
819         mFrameDelayTimer.SetInterval(interval);
820       }
821       mImpl->mRenderer.SetTextures(textureSet);
822     }
823   }
824 }
825
826 bool AnimatedImageVisual::DisplayNextFrame()
827 {
828   TextureSet textureSet;
829   bool       continueTimer = false;
830
831   if(mImageCache)
832   {
833     uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
834
835     if(mIsJumpTo)
836     {
837       mIsJumpTo  = false;
838       frameIndex = mFrameIndexForJumpTo;
839     }
840     else if(mActionStatus == DevelAnimatedImageVisual::Action::PAUSE)
841     {
842       return false;
843     }
844     else if(mActionStatus == DevelAnimatedImageVisual::Action::STOP)
845     {
846       mCurrentLoopIndex = FIRST_LOOP;
847       if(mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME)
848       {
849         frameIndex = FIRST_FRAME_INDEX;
850       }
851       else if(mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME)
852       {
853         frameIndex = mFrameCount - 1;
854       }
855       else
856       {
857         return false; // Do not draw already rendered scene twice.
858       }
859     }
860     else
861     {
862       if(mFrameCount > SINGLE_IMAGE_COUNT)
863       {
864         frameIndex++;
865         if(frameIndex >= mFrameCount)
866         {
867           frameIndex = FIRST_FRAME_INDEX;
868           ++mCurrentLoopIndex;
869         }
870
871         if(mLoopCount >= 0 && mCurrentLoopIndex >= mLoopCount)
872         {
873           // This will stop timer
874           mActionStatus = DevelAnimatedImageVisual::Action::STOP;
875           return DisplayNextFrame();
876         }
877       }
878     }
879
880     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex);
881
882     textureSet = mImageCache->Frame(frameIndex);
883
884     if(textureSet)
885     {
886       SetImageSize(textureSet);
887       if(mImpl->mRenderer)
888       {
889         mImpl->mRenderer.SetTextures(textureSet);
890       }
891       mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex));
892     }
893
894     mCurrentFrameIndex = frameIndex;
895     continueTimer      = (mActionStatus == DevelAnimatedImageVisual::Action::PLAY && textureSet) ? true : false;
896   }
897
898   return continueTimer;
899 }
900
901 TextureSet AnimatedImageVisual::SetLoadingFailed()
902 {
903   DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n");
904   ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
905
906   Actor   actor     = mPlacementActor.GetHandle();
907   Vector2 imageSize = Vector2::ZERO;
908   if(actor)
909   {
910     imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
911   }
912   mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
913   TextureSet textureSet = mImpl->mRenderer.GetTextures();
914
915   if(mFrameDelayTimer)
916   {
917     mFrameDelayTimer.Stop();
918     mFrameDelayTimer.Reset();
919   }
920
921   SetImageSize(textureSet);
922
923   return textureSet;
924 }
925
926 void AnimatedImageVisual::AllocateMaskData()
927 {
928   if(!mMaskingData)
929   {
930     mMaskingData.reset(new TextureManager::MaskingData());
931   }
932 }
933
934 } // namespace Internal
935
936 } // namespace Toolkit
937
938 } // namespace Dali