[dali_2.3.22] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / animated-image / animated-image-visual.cpp
1 /*
2  * Copyright (c) 2024 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 <dali-toolkit/internal/visuals/animated-image/animated-image-visual.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/image-loading.h>
23 #include <dali/devel-api/adaptor-framework/window-devel.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/rendering/decorated-visual-renderer.h>
26 #include <memory>
27
28 // INTERNAL INCLUDES
29 #include <dali-toolkit/devel-api/image-loader/image-atlas.h>
30 #include <dali-toolkit/devel-api/image-loader/texture-manager.h>
31 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
32 #include <dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h>
33 #include <dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h>
34 #include <dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h>
35 #include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
36 #include <dali-toolkit/internal/visuals/image-visual-shader-feature-builder.h>
37 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
38 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
39 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
40 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
41 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
42 #include <dali-toolkit/public-api/visuals/visual-properties.h>
43
44 namespace Dali
45 {
46 namespace Toolkit
47 {
48 namespace Internal
49 {
50 namespace
51 {
52 const int CUSTOM_PROPERTY_COUNT(5); // ltr, wrap, pixel area, crop to mask, mask texture ratio
53
54 // fitting modes
55 DALI_ENUM_TO_STRING_TABLE_BEGIN(FITTING_MODE)
56   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, SHRINK_TO_FIT)
57   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, SCALE_TO_FILL)
58   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, FIT_WIDTH)
59   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, FIT_HEIGHT)
60   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, DEFAULT)
61 DALI_ENUM_TO_STRING_TABLE_END(FITTING_MODE)
62
63 // sampling modes
64 DALI_ENUM_TO_STRING_TABLE_BEGIN(SAMPLING_MODE)
65   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, BOX)
66   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, NEAREST)
67   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, LINEAR)
68   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, BOX_THEN_NEAREST)
69   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, BOX_THEN_LINEAR)
70   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, NO_FILTER)
71   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, DONT_CARE)
72 DALI_ENUM_TO_STRING_TABLE_END(SAMPLING_MODE)
73
74 // stop behavior
75 DALI_ENUM_TO_STRING_TABLE_BEGIN(STOP_BEHAVIOR)
76   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::DevelImageVisual::StopBehavior, CURRENT_FRAME)
77   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::DevelImageVisual::StopBehavior, FIRST_FRAME)
78   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::DevelImageVisual::StopBehavior, LAST_FRAME)
79 DALI_ENUM_TO_STRING_TABLE_END(STOP_BEHAVIOR)
80
81 // wrap modes
82 DALI_ENUM_TO_STRING_TABLE_BEGIN(WRAP_MODE)
83   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, DEFAULT)
84   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, CLAMP_TO_EDGE)
85   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, REPEAT)
86   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, MIRRORED_REPEAT)
87 DALI_ENUM_TO_STRING_TABLE_END(WRAP_MODE)
88
89 // load policies
90 DALI_ENUM_TO_STRING_TABLE_BEGIN(LOAD_POLICY)
91   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::LoadPolicy, IMMEDIATE)
92   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::LoadPolicy, ATTACHED)
93 DALI_ENUM_TO_STRING_TABLE_END(LOAD_POLICY)
94
95 // release policies
96 DALI_ENUM_TO_STRING_TABLE_BEGIN(RELEASE_POLICY)
97   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, DETACHED)
98   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, DESTROYED)
99   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, NEVER)
100 DALI_ENUM_TO_STRING_TABLE_END(RELEASE_POLICY)
101
102 static constexpr uint32_t SINGLE_IMAGE_COUNT = 1u;
103 static constexpr uint32_t FIRST_FRAME_INDEX  = 0u;
104 static constexpr uint16_t MINIMUM_CACHESIZE  = 1;
105 static constexpr Vector4  FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
106 static constexpr auto     LOOP_FOREVER = -1;
107 static constexpr auto     FIRST_LOOP   = 0u;
108
109 constexpr uint32_t TEXTURE_COUNT_FOR_GPU_ALPHA_MASK = 2u;
110
111 #if defined(DEBUG_ENABLED)
112 Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
113 #endif
114 } // namespace
115
116 /**
117  * Multi-image  Flow of execution
118  *
119  *   | New
120  *   |   DoSetProperties()
121  *   |   OnInitialize()
122  *   |     CreateImageCache()
123  *   |
124  *   | DoSetOnScene()
125  *   |   PrepareTextureSet()
126  *   |     cache->FirstFrame()
127  *   |
128  *   | FrameReady(textureSet)
129  *   |   StartFirstFrame:
130  *   |     actor.AddRenderer
131  *   |     start timer
132  *   |   mRenderer.SetTextures(textureSet)
133  *   |
134  *   | Timer ticks
135  *   |   DisplayNextFrame()
136  *   |     if front frame is ready,
137  *   |       mRenderer.SetTextures( front frame's texture )
138  *   |     else
139  *   |       Waiting for frame ready.
140  *   |
141  *   | FrameReady(textureSet)
142  *   |   mRenderer.SetTextures(textureSet)
143  *   V
144  *  Time
145  */
146
147 AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, const Property::Map& properties)
148 {
149   AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory, ImageDimensions()));
150   visual->InitializeAnimatedImage(imageUrl);
151   visual->SetProperties(properties);
152
153   visual->Initialize();
154
155   return visual;
156 }
157
158 AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const Property::Array& imageUrls, const Property::Map& properties)
159 {
160   AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory, ImageDimensions()));
161   visual->mImageUrls = new ImageCache::UrlList();
162   visual->mImageUrls->reserve(imageUrls.Count());
163
164   for(unsigned int i = 0; i < imageUrls.Count(); ++i)
165   {
166     ImageCache::UrlStore urlStore;
167     urlStore.mTextureId = TextureManager::INVALID_TEXTURE_ID;
168     urlStore.mUrl       = imageUrls[i].Get<std::string>();
169     visual->mImageUrls->push_back(urlStore);
170   }
171   visual->mFrameCount = imageUrls.Count();
172   visual->SetProperties(properties);
173
174   visual->Initialize();
175
176   return visual;
177 }
178
179 AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, ImageDimensions size)
180 {
181   AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory, size));
182   visual->InitializeAnimatedImage(imageUrl);
183
184   visual->Initialize();
185
186   return visual;
187 }
188
189 void AnimatedImageVisual::InitializeAnimatedImage(const VisualUrl& imageUrl)
190 {
191   mImageUrl             = imageUrl;
192   mAnimatedImageLoading = AnimatedImageLoading::New(imageUrl.GetUrl(), imageUrl.IsLocalResource());
193 }
194
195 void AnimatedImageVisual::CreateImageCache()
196 {
197   DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::CreateImageCache()  batchSize:%d  cacheSize:%d\n", mBatchSize, mCacheSize);
198
199   TextureManager& textureManager = mFactoryCache.GetTextureManager();
200
201   if(mAnimatedImageLoading)
202   {
203     mImageCache = new RollingAnimatedImageCache(textureManager, mDesiredSize, mFittingMode, mSamplingMode, mAnimatedImageLoading, mMaskingData, *this, mCacheSize, mBatchSize, mWrapModeU, mWrapModeV, IsSynchronousLoadingRequired(), IsPreMultipliedAlphaEnabled());
204   }
205   else if(mImageUrls)
206   {
207     // Ensure the batch size and cache size are no bigger than the number of URLs,
208     // and that the cache is at least as big as the batch size.
209     uint16_t numUrls   = mImageUrls->size();
210     uint16_t batchSize = std::max(std::min(mBatchSize, numUrls), MINIMUM_CACHESIZE);
211     uint16_t cacheSize = std::max(std::min(std::max(batchSize, mCacheSize), numUrls), MINIMUM_CACHESIZE);
212     if(cacheSize < numUrls)
213     {
214       mImageCache = new RollingImageCache(textureManager, mDesiredSize, mFittingMode, mSamplingMode, *mImageUrls, mMaskingData, *this, cacheSize, batchSize, mFrameDelay, IsPreMultipliedAlphaEnabled());
215     }
216     else
217     {
218       mImageCache = new FixedImageCache(textureManager, mDesiredSize, mFittingMode, mSamplingMode, *mImageUrls, mMaskingData, *this, batchSize, mFrameDelay, IsPreMultipliedAlphaEnabled());
219     }
220   }
221
222   if(!mImageCache)
223   {
224     DALI_LOG_ERROR("mImageCache is null\n");
225   }
226 }
227
228 AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, ImageDimensions desiredSize)
229 : Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::ANIMATED_IMAGE),
230   mFrameDelayTimer(),
231   mPlacementActor(),
232   mImageVisualShaderFactory(shaderFactory),
233   mPixelArea(FULL_TEXTURE_RECT),
234   mImageUrl(),
235   mAnimatedImageLoading(),
236   mFrameIndexForJumpTo(0),
237   mCurrentFrameIndex(FIRST_FRAME_INDEX),
238   mImageUrls(NULL),
239   mImageCache(NULL),
240   mCacheSize(2),
241   mBatchSize(2),
242   mFrameDelay(100),
243   mLoopCount(LOOP_FOREVER),
244   mCurrentLoopIndex(FIRST_LOOP),
245   mLoadPolicy(Toolkit::ImageVisual::LoadPolicy::ATTACHED),
246   mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED),
247   mMaskingData(),
248   mDesiredSize(desiredSize),
249   mFrameCount(0),
250   mImageSize(),
251   mActionStatus(DevelAnimatedImageVisual::Action::PLAY),
252   mWrapModeU(WrapMode::DEFAULT),
253   mWrapModeV(WrapMode::DEFAULT),
254   mFittingMode(FittingMode::SCALE_TO_FILL),
255   mSamplingMode(SamplingMode::BOX_THEN_LINEAR),
256   mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME),
257   mStartFirstFrame(false),
258   mIsJumpTo(false)
259 {
260   EnablePreMultipliedAlpha(mFactoryCache.GetPreMultiplyOnLoad());
261 }
262
263 AnimatedImageVisual::~AnimatedImageVisual()
264 {
265   // AnimatedImageVisual destroyed so remove texture unless ReleasePolicy is set to never release
266   // If this is animated image, clear cache. Else if this is single frame image, this is affected be release policy.
267   if(mFrameCount > SINGLE_IMAGE_COUNT || mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER)
268   {
269     mImageCache->ClearCache();
270   }
271   delete mImageCache;
272   delete mImageUrls;
273 }
274
275 void AnimatedImageVisual::GetNaturalSize(Vector2& naturalSize)
276 {
277   if(mDesiredSize.GetWidth() > 0 && mDesiredSize.GetHeight() > 0)
278   {
279     naturalSize.x = mDesiredSize.GetWidth();
280     naturalSize.y = mDesiredSize.GetHeight();
281     return;
282   }
283
284   naturalSize = Vector2::ZERO;
285   if(mImageSize.GetWidth() == 0 && mImageSize.GetHeight() == 0)
286   {
287     if(mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid() &&
288        mMaskingData->mCropToMask)
289     {
290       ImageDimensions dimensions = Dali::GetClosestImageSize(mMaskingData->mAlphaMaskUrl.GetUrl());
291       if(dimensions != ImageDimensions(0, 0))
292       {
293         mImageSize    = dimensions;
294         naturalSize.x = dimensions.GetWidth();
295         naturalSize.y = dimensions.GetHeight();
296         return;
297       }
298     }
299
300     if(mImageUrl.IsValid())
301     {
302       mImageSize = mAnimatedImageLoading.GetImageSize();
303     }
304     else if(mImageUrls && mImageUrls->size() > 0)
305     {
306       mImageSize = Dali::GetClosestImageSize((*mImageUrls)[0].mUrl.GetUrl());
307     }
308   }
309
310   naturalSize.width  = mImageSize.GetWidth();
311   naturalSize.height = mImageSize.GetHeight();
312 }
313
314 void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const
315 {
316   map.Clear();
317
318   bool sync = IsSynchronousLoadingRequired();
319   map.Insert(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync);
320
321   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE);
322
323   if(mImageUrl.IsValid())
324   {
325     map.Insert(Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl());
326   }
327   if(mImageUrls != NULL && !mImageUrls->empty())
328   {
329     Property::Array urls;
330     for(unsigned int i = 0; i < mImageUrls->size(); ++i)
331     {
332       urls.Add((*mImageUrls)[i].mUrl.GetUrl());
333     }
334     Property::Value value(const_cast<Property::Array&>(urls));
335     map.Insert(Toolkit::ImageVisual::Property::URL, value);
336   }
337
338   map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea);
339   map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU);
340   map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV);
341
342   map.Insert(Toolkit::ImageVisual::Property::BATCH_SIZE, static_cast<int>(mBatchSize));
343   map.Insert(Toolkit::ImageVisual::Property::CACHE_SIZE, static_cast<int>(mCacheSize));
344   map.Insert(Toolkit::ImageVisual::Property::FRAME_DELAY, static_cast<int>(mFrameDelay));
345   map.Insert(Toolkit::DevelImageVisual::Property::LOOP_COUNT, static_cast<int>(mLoopCount));
346   map.Insert(Toolkit::DevelImageVisual::Property::CURRENT_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>(mImageCache->GetCurrentFrameIndex()) : -1);
347
348   // 1. Get cached mFrameCount if mFrameCount != 0.
349   // 2. If we are not using animated image loading, ask to image cache.
350   // 2-1. If image cache return SINGLE_IMAGE_COUNT or less, It might not a valid value
351   //      (since default frameCount of image cache is SINGLE_IMAGE_COUNT)
352   //      So, let we ask to animated image loader again.
353   // 2-1-1. If animated image loader return 0, it means that it is not a valid animated image.
354   // 2-1-2. Otherwise, we can assume that it is valid frame count.
355   // 2-2. Otherwise, we can assume that it is valid frame count.
356   uint32_t frameCount = mFrameCount;
357   if(mImageCache && frameCount == 0)
358   {
359     frameCount = mImageCache->GetTotalFrameCount();
360
361     if(frameCount <= SINGLE_IMAGE_COUNT && mAnimatedImageLoading)
362     {
363       frameCount = mAnimatedImageLoading.GetImageCount();
364     }
365   }
366
367   map.Insert(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, (frameCount >= SINGLE_IMAGE_COUNT) ? static_cast<int>(frameCount) : -1);
368
369   map.Insert(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mStopBehavior);
370
371   if(mMaskingData != nullptr)
372   {
373     map.Insert(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, mMaskingData->mAlphaMaskUrl.GetUrl());
374     map.Insert(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, mMaskingData->mContentScaleFactor);
375     map.Insert(Toolkit::ImageVisual::Property::CROP_TO_MASK, mMaskingData->mCropToMask);
376     map.Insert(Toolkit::DevelImageVisual::Property::MASKING_TYPE, mMaskingData->mPreappliedMasking ? DevelImageVisual::MaskingType::MASKING_ON_LOADING : DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
377   }
378
379   map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
380   map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
381   map.Insert(Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode);
382   map.Insert(Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode);
383   map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
384   map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
385 }
386
387 void AnimatedImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
388 {
389   map.Clear();
390   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE);
391   if(mImageUrl.IsValid())
392   {
393     map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
394     map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
395   }
396 }
397
398 void AnimatedImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
399 {
400   // Make not set any action when the resource status is already failed.
401   if(mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED)
402   {
403     return;
404   }
405
406   // Check if action is valid for this visual type and perform action if possible
407   switch(actionId)
408   {
409     case DevelAnimatedImageVisual::Action::PAUSE:
410     {
411       // Pause will be executed on next timer tick
412       mActionStatus = DevelAnimatedImageVisual::Action::PAUSE;
413       break;
414     }
415     case DevelAnimatedImageVisual::Action::PLAY:
416     {
417       if(mFrameDelayTimer && IsOnScene() && mActionStatus != DevelAnimatedImageVisual::Action::PLAY)
418       {
419         mFrameDelayTimer.Start();
420       }
421       mActionStatus = DevelAnimatedImageVisual::Action::PLAY;
422       break;
423     }
424     case DevelAnimatedImageVisual::Action::STOP:
425     {
426       // STOP reset functionality will actually be done in a future change
427       // Stop will be executed on next timer tick
428       mActionStatus     = DevelAnimatedImageVisual::Action::STOP;
429       mCurrentLoopIndex = FIRST_LOOP;
430       if(IsOnScene())
431       {
432         DisplayNextFrame();
433       }
434       break;
435     }
436     case DevelAnimatedImageVisual::Action::JUMP_TO:
437     {
438       int32_t frameNumber;
439       if(attributes.Get(frameNumber))
440       {
441         if(frameNumber < 0 || frameNumber >= static_cast<int32_t>(mFrameCount))
442         {
443           DALI_LOG_ERROR("Invalid frame index used.\n");
444         }
445         else
446         {
447           mIsJumpTo            = true;
448           mFrameIndexForJumpTo = frameNumber;
449           if(IsOnScene())
450           {
451             DisplayNextFrame();
452           }
453         }
454       }
455       break;
456     }
457   }
458 }
459
460 void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap)
461 {
462   // url[s] already passed in from constructor
463   for(Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter)
464   {
465     KeyValuePair keyValue = propertyMap.GetKeyValue(iter);
466     if(keyValue.first.type == Property::Key::INDEX)
467     {
468       DoSetProperty(keyValue.first.indexKey, keyValue.second);
469     }
470     else
471     {
472       if(keyValue.first == PIXEL_AREA_UNIFORM_NAME)
473       {
474         DoSetProperty(Toolkit::ImageVisual::Property::PIXEL_AREA, keyValue.second);
475       }
476       else if(keyValue.first == IMAGE_WRAP_MODE_U)
477       {
478         DoSetProperty(Toolkit::ImageVisual::Property::WRAP_MODE_U, keyValue.second);
479       }
480       else if(keyValue.first == IMAGE_WRAP_MODE_V)
481       {
482         DoSetProperty(Toolkit::ImageVisual::Property::WRAP_MODE_V, keyValue.second);
483       }
484       else if(keyValue.first == BATCH_SIZE_NAME)
485       {
486         DoSetProperty(Toolkit::ImageVisual::Property::BATCH_SIZE, keyValue.second);
487       }
488       else if(keyValue.first == CACHE_SIZE_NAME)
489       {
490         DoSetProperty(Toolkit::ImageVisual::Property::CACHE_SIZE, keyValue.second);
491       }
492       else if(keyValue.first == FRAME_DELAY_NAME)
493       {
494         DoSetProperty(Toolkit::ImageVisual::Property::FRAME_DELAY, keyValue.second);
495       }
496       else if(keyValue.first == LOOP_COUNT_NAME)
497       {
498         DoSetProperty(Toolkit::DevelImageVisual::Property::LOOP_COUNT, keyValue.second);
499       }
500       else if(keyValue.first == STOP_BEHAVIOR_NAME)
501       {
502         DoSetProperty(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, keyValue.second);
503       }
504       else if(keyValue.first == ALPHA_MASK_URL)
505       {
506         DoSetProperty(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, keyValue.second);
507       }
508       else if(keyValue.first == MASK_CONTENT_SCALE_NAME)
509       {
510         DoSetProperty(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, keyValue.second);
511       }
512       else if(keyValue.first == CROP_TO_MASK_NAME)
513       {
514         DoSetProperty(Toolkit::ImageVisual::Property::CROP_TO_MASK, keyValue.second);
515       }
516       else if(keyValue.first == MASKING_TYPE_NAME)
517       {
518         DoSetProperty(Toolkit::DevelImageVisual::Property::MASKING_TYPE, keyValue.second);
519       }
520       else if(keyValue.first == LOAD_POLICY_NAME)
521       {
522         DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second);
523       }
524       else if(keyValue.first == RELEASE_POLICY_NAME)
525       {
526         DoSetProperty(Toolkit::ImageVisual::Property::RELEASE_POLICY, keyValue.second);
527       }
528       else if(keyValue.first == SYNCHRONOUS_LOADING)
529       {
530         DoSetProperty(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second);
531       }
532       else if(keyValue.first == IMAGE_FITTING_MODE)
533       {
534         DoSetProperty(Toolkit::ImageVisual::Property::FITTING_MODE, keyValue.second);
535       }
536       else if(keyValue.first == IMAGE_SAMPLING_MODE)
537       {
538         DoSetProperty(Toolkit::ImageVisual::Property::SAMPLING_MODE, keyValue.second);
539       }
540       else if(keyValue.first == IMAGE_DESIRED_WIDTH)
541       {
542         DoSetProperty(Toolkit::ImageVisual::Property::DESIRED_WIDTH, keyValue.second);
543       }
544       else if(keyValue.first == IMAGE_DESIRED_HEIGHT)
545       {
546         DoSetProperty(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, keyValue.second);
547       }
548     }
549   }
550   // Load image immediately if LOAD_POLICY requires it
551   if(mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::IMMEDIATE)
552   {
553     PrepareTextureSet();
554   }
555 }
556
557 void AnimatedImageVisual::DoSetProperty(Property::Index        index,
558                                         const Property::Value& value)
559 {
560   switch(index)
561   {
562     case Toolkit::ImageVisual::Property::PIXEL_AREA:
563     {
564       value.Get(mPixelArea);
565       break;
566     }
567     case Toolkit::ImageVisual::Property::WRAP_MODE_U:
568     {
569       int wrapMode = 0;
570       if(Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode))
571       {
572         mWrapModeU = Dali::WrapMode::Type(wrapMode);
573       }
574       else
575       {
576         mWrapModeU = Dali::WrapMode::Type::DEFAULT;
577       }
578       break;
579     }
580     case Toolkit::ImageVisual::Property::WRAP_MODE_V:
581     {
582       int wrapMode = 0;
583       if(Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode))
584       {
585         mWrapModeV = Dali::WrapMode::Type(wrapMode);
586       }
587       else
588       {
589         mWrapModeV = Dali::WrapMode::Type::DEFAULT;
590       }
591       break;
592     }
593
594     case Toolkit::ImageVisual::Property::BATCH_SIZE:
595     {
596       int batchSize;
597       if(value.Get(batchSize))
598       {
599         if(batchSize < 2)
600         {
601           DALI_LOG_ERROR("The minimum value of batch size is 2.");
602         }
603         else
604         {
605           mBatchSize = batchSize;
606         }
607       }
608       break;
609     }
610
611     case Toolkit::ImageVisual::Property::CACHE_SIZE:
612     {
613       int cacheSize;
614       if(value.Get(cacheSize))
615       {
616         if(cacheSize < 2)
617         {
618           DALI_LOG_ERROR("The minimum value of cache size is 2.");
619         }
620         else
621         {
622           mCacheSize = cacheSize;
623         }
624       }
625       break;
626     }
627
628     case Toolkit::ImageVisual::Property::FRAME_DELAY:
629     {
630       int frameDelay;
631       if(value.Get(frameDelay))
632       {
633         mFrameDelay = frameDelay;
634         if(mImageCache)
635         {
636           mImageCache->SetInterval(static_cast<uint32_t>(mFrameDelay));
637         }
638       }
639       break;
640     }
641
642     case Toolkit::DevelImageVisual::Property::LOOP_COUNT:
643     {
644       int loopCount;
645       if(value.Get(loopCount))
646       {
647         mLoopCount = loopCount;
648       }
649       break;
650     }
651
652     case Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR:
653     {
654       int32_t stopBehavior = mStopBehavior;
655       if(Scripting::GetEnumerationProperty(value, STOP_BEHAVIOR_TABLE, STOP_BEHAVIOR_TABLE_COUNT, stopBehavior))
656       {
657         mStopBehavior = DevelImageVisual::StopBehavior::Type(stopBehavior);
658       }
659       break;
660     }
661
662     case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
663     {
664       bool sync = false;
665       value.Get(sync);
666       if(sync)
667       {
668         mImpl->mFlags |= Visual::Base::Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
669       }
670       else
671       {
672         mImpl->mFlags &= ~Visual::Base::Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
673       }
674       break;
675     }
676
677     case Toolkit::ImageVisual::Property::ALPHA_MASK_URL:
678     {
679       std::string alphaUrl = "";
680       if(value.Get(alphaUrl))
681       {
682         AllocateMaskData();
683         mMaskingData->mAlphaMaskUrl = alphaUrl;
684       }
685       break;
686     }
687
688     case Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE:
689     {
690       float scale = 1.0f;
691       if(value.Get(scale))
692       {
693         AllocateMaskData();
694         mMaskingData->mContentScaleFactor = scale;
695       }
696       break;
697     }
698
699     case Toolkit::ImageVisual::Property::CROP_TO_MASK:
700     {
701       bool crop = false;
702       if(value.Get(crop))
703       {
704         AllocateMaskData();
705         mMaskingData->mCropToMask = crop;
706       }
707       break;
708     }
709
710     case Toolkit::DevelImageVisual::Property::MASKING_TYPE:
711     {
712       int maskingType = 0;
713       if(value.Get(maskingType))
714       {
715         AllocateMaskData();
716         mMaskingData->mPreappliedMasking = Toolkit::DevelImageVisual::MaskingType::Type(maskingType) == Toolkit::DevelImageVisual::MaskingType::MASKING_ON_LOADING ? true : false;
717       }
718       break;
719     }
720
721     case Toolkit::ImageVisual::Property::RELEASE_POLICY:
722     {
723       int releasePolicy = 0;
724       Scripting::GetEnumerationProperty(value, RELEASE_POLICY_TABLE, RELEASE_POLICY_TABLE_COUNT, releasePolicy);
725       mReleasePolicy = Toolkit::ImageVisual::ReleasePolicy::Type(releasePolicy);
726       break;
727     }
728
729     case Toolkit::ImageVisual::Property::LOAD_POLICY:
730     {
731       int loadPolicy = 0;
732       Scripting::GetEnumerationProperty(value, LOAD_POLICY_TABLE, LOAD_POLICY_TABLE_COUNT, loadPolicy);
733       mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type(loadPolicy);
734       break;
735     }
736
737     case Toolkit::ImageVisual::Property::FITTING_MODE:
738     {
739       int fittingMode = 0;
740       Scripting::GetEnumerationProperty(value, FITTING_MODE_TABLE, FITTING_MODE_TABLE_COUNT, fittingMode);
741       mFittingMode = Dali::FittingMode::Type(fittingMode);
742       break;
743     }
744
745     case Toolkit::ImageVisual::Property::SAMPLING_MODE:
746     {
747       int samplingMode = 0;
748       Scripting::GetEnumerationProperty(value, SAMPLING_MODE_TABLE, SAMPLING_MODE_TABLE_COUNT, samplingMode);
749       mSamplingMode = Dali::SamplingMode::Type(samplingMode);
750       break;
751     }
752
753     case Toolkit::ImageVisual::Property::DESIRED_WIDTH:
754     {
755       float desiredWidth = 0.0f;
756       if(value.Get(desiredWidth))
757       {
758         mDesiredSize.SetWidth(desiredWidth);
759       }
760       else
761       {
762         DALI_LOG_ERROR("AnimatedImageVisual: desiredWidth property has incorrect type\n");
763       }
764       break;
765     }
766
767     case Toolkit::ImageVisual::Property::DESIRED_HEIGHT:
768     {
769       float desiredHeight = 0.0f;
770       if(value.Get(desiredHeight))
771       {
772         mDesiredSize.SetHeight(desiredHeight);
773       }
774       else
775       {
776         DALI_LOG_ERROR("AnimatedImageVisual: desiredHeight property has incorrect type\n");
777       }
778       break;
779     }
780   }
781 }
782
783 void AnimatedImageVisual::DoSetOnScene(Actor& actor)
784 {
785   mStartFirstFrame = true;
786   mPlacementActor  = actor;
787   PrepareTextureSet();
788
789   DevelActor::VisibilityChangedSignal(actor).Connect(this, &AnimatedImageVisual::OnControlVisibilityChanged);
790
791   Window window = DevelWindow::Get(actor);
792   if(window)
793   {
794     mPlacementWindow = window;
795     DevelWindow::VisibilityChangedSignal(window).Connect(this, &AnimatedImageVisual::OnWindowVisibilityChanged);
796   }
797 }
798
799 void AnimatedImageVisual::DoSetOffScene(Actor& actor)
800 {
801   DALI_ASSERT_DEBUG((bool)mImpl->mRenderer && "There should always be a renderer whilst on stage");
802
803   if(mFrameDelayTimer)
804   {
805     mFrameDelayTimer.Stop();
806     mFrameDelayTimer.Reset();
807   }
808
809   actor.RemoveRenderer(mImpl->mRenderer);
810   if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
811   {
812     mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
813     mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
814
815     TextureSet textureSet = TextureSet::New();
816     mImpl->mRenderer.SetTextures(textureSet);
817   }
818
819   mPlacementActor.Reset();
820   mStartFirstFrame   = false;
821   mCurrentFrameIndex = FIRST_FRAME_INDEX;
822   mCurrentLoopIndex  = FIRST_LOOP;
823
824   DevelActor::VisibilityChangedSignal(actor).Disconnect(this, &AnimatedImageVisual::OnControlVisibilityChanged);
825
826   Window window = mPlacementWindow.GetHandle();
827   if(window)
828   {
829     DevelWindow::VisibilityChangedSignal(window).Disconnect(this, &AnimatedImageVisual::OnWindowVisibilityChanged);
830     mPlacementWindow.Reset();
831   }
832 }
833
834 void AnimatedImageVisual::OnSetTransform()
835 {
836   if(mImpl->mRenderer)
837   {
838     mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
839   }
840 }
841
842 void AnimatedImageVisual::UpdateShader()
843 {
844   if(mImpl->mRenderer)
845   {
846     Shader shader = GenerateShader();
847     mImpl->mRenderer.SetShader(shader);
848   }
849 }
850
851 Shader AnimatedImageVisual::GenerateShader() const
852 {
853   bool   defaultWrapMode                 = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
854   bool   requiredAlphaMaskingOnRendering = (mMaskingData && !mMaskingData->mMaskImageLoadingFailed) ? !mMaskingData->mPreappliedMasking : false;
855   Shader shader;
856   shader = mImageVisualShaderFactory.GetShader(
857     mFactoryCache,
858     ImageVisualShaderFeatureBuilder()
859       .ApplyDefaultTextureWrapMode(defaultWrapMode)
860       .EnableRoundedCorner(IsRoundedCornerRequired())
861       .EnableBorderline(IsBorderlineRequired())
862       .EnableAlphaMaskingOnRendering(requiredAlphaMaskingOnRendering));
863   return shader;
864 }
865
866 void AnimatedImageVisual::OnInitialize()
867 {
868   CreateImageCache();
869
870   bool   defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
871   Shader shader          = GenerateShader();
872
873   Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
874
875   mImpl->mRenderer = DecoratedVisualRenderer::New(geometry, shader);
876   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
877
878   // Register transform properties
879   mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
880
881   if(!defaultWrapMode) // custom wrap mode
882   {
883     Vector2 wrapMode(mWrapModeU - WrapMode::CLAMP_TO_EDGE, mWrapModeV - WrapMode::CLAMP_TO_EDGE);
884     wrapMode.Clamp(Vector2::ZERO, Vector2(2.f, 2.f));
885     mImpl->mRenderer.RegisterProperty(WRAP_MODE_UNIFORM_NAME, wrapMode);
886   }
887
888   if(mPixelArea != FULL_TEXTURE_RECT)
889   {
890     mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea);
891   }
892
893   if(mMaskingData)
894   {
895     mImpl->mRenderer.RegisterProperty(CROP_TO_MASK_NAME, static_cast<float>(mMaskingData->mCropToMask));
896   }
897
898   // Enable PreMultipliedAlpha if it need premultiplied
899   auto preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader
900                              ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
901                              : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
902   EnablePreMultipliedAlpha(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
903 }
904
905 void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval)
906 {
907   DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::StartFirstFrame()\n");
908
909   mStartFirstFrame = false;
910   if(mImpl->mRenderer)
911   {
912     mImpl->mRenderer.SetTextures(textureSet);
913     CheckMaskTexture();
914
915     Actor actor = mPlacementActor.GetHandle();
916     if(actor)
917     {
918       actor.AddRenderer(mImpl->mRenderer);
919       mPlacementActor.Reset();
920     }
921   }
922
923   if(mImpl->mResourceStatus != Toolkit::Visual::ResourceStatus::FAILED)
924   {
925     if(mFrameCount > SINGLE_IMAGE_COUNT)
926     {
927       mFrameDelayTimer = Timer::New(firstInterval);
928       mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame);
929       mFrameDelayTimer.Start();
930     }
931
932     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::READY)\n");
933     ResourceReady(Toolkit::Visual::ResourceStatus::READY);
934   }
935
936   mCurrentFrameIndex = FIRST_FRAME_INDEX;
937 }
938
939 void AnimatedImageVisual::PrepareTextureSet()
940 {
941   TextureSet textureSet;
942   if(mImageCache)
943   {
944     textureSet = mImageCache->FirstFrame();
945   }
946
947   // Check whether synchronous loading is true or false for the first frame.
948   if(textureSet)
949   {
950     SetImageSize(textureSet);
951   }
952 }
953
954 void AnimatedImageVisual::SetImageSize(TextureSet& textureSet)
955 {
956   if(textureSet)
957   {
958     Texture texture = textureSet.GetTexture(0);
959     if(texture)
960     {
961       mImageSize.SetWidth(texture.GetWidth());
962       mImageSize.SetHeight(texture.GetHeight());
963     }
964
965     if(textureSet.GetTextureCount() > 1u && mMaskingData && mMaskingData->mCropToMask)
966     {
967       Texture maskTexture = textureSet.GetTexture(1);
968       if(maskTexture)
969       {
970         mImageSize.SetWidth(std::min(static_cast<uint32_t>(mImageSize.GetWidth() * mMaskingData->mContentScaleFactor), maskTexture.GetWidth()));
971         mImageSize.SetHeight(std::min(static_cast<uint32_t>(mImageSize.GetHeight() * mMaskingData->mContentScaleFactor), maskTexture.GetHeight()));
972
973         float   textureWidth  = std::max(static_cast<float>(texture.GetWidth() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1);
974         float   textureHeight = std::max(static_cast<float>(texture.GetHeight() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1);
975         Vector2 textureRatio(std::min(static_cast<float>(maskTexture.GetWidth()), textureWidth) / textureWidth,
976                              std::min(static_cast<float>(maskTexture.GetHeight()), textureHeight) / textureHeight);
977         mImpl->mRenderer.RegisterProperty(MASK_TEXTURE_RATIO_NAME, textureRatio);
978       }
979     }
980   }
981 }
982
983 void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval, bool preMultiplied)
984 {
985   EnablePreMultipliedAlpha(preMultiplied);
986
987   // When image visual requested to load new frame to mImageCache and it is failed.
988   if(!mImageCache || !textureSet)
989   {
990     textureSet = SetLoadingFailed();
991   }
992   SetImageSize(textureSet);
993
994   if(mStartFirstFrame)
995   {
996     mFrameCount = mImageCache->GetTotalFrameCount();
997     StartFirstFrame(textureSet, interval);
998   }
999   else
1000   {
1001     if(mImpl->mRenderer)
1002     {
1003       if(mFrameDelayTimer && interval > 0u)
1004       {
1005         mFrameDelayTimer.SetInterval(interval);
1006       }
1007       mImpl->mRenderer.SetTextures(textureSet);
1008       CheckMaskTexture();
1009     }
1010   }
1011 }
1012
1013 bool AnimatedImageVisual::DisplayNextFrame()
1014 {
1015   TextureSet textureSet;
1016   bool       continueTimer = false;
1017
1018   if(mImageCache)
1019   {
1020     uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
1021
1022     if(mIsJumpTo)
1023     {
1024       mIsJumpTo  = false;
1025       frameIndex = mFrameIndexForJumpTo;
1026     }
1027     else if(mActionStatus == DevelAnimatedImageVisual::Action::PAUSE)
1028     {
1029       return false;
1030     }
1031     else if(mActionStatus == DevelAnimatedImageVisual::Action::STOP)
1032     {
1033       mCurrentLoopIndex = FIRST_LOOP;
1034       if(mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME)
1035       {
1036         frameIndex = FIRST_FRAME_INDEX;
1037       }
1038       else if(mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME)
1039       {
1040         frameIndex = mFrameCount - 1;
1041       }
1042       else
1043       {
1044         return false; // Do not draw already rendered scene twice.
1045       }
1046     }
1047     else
1048     {
1049       if(mFrameCount > SINGLE_IMAGE_COUNT)
1050       {
1051         frameIndex++;
1052         if(frameIndex >= mFrameCount)
1053         {
1054           frameIndex = FIRST_FRAME_INDEX;
1055           ++mCurrentLoopIndex;
1056         }
1057
1058         if(mLoopCount >= 0 && mCurrentLoopIndex >= mLoopCount)
1059         {
1060           // This will stop timer
1061           mActionStatus = DevelAnimatedImageVisual::Action::STOP;
1062           return DisplayNextFrame();
1063         }
1064       }
1065     }
1066
1067     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex);
1068
1069     textureSet = mImageCache->Frame(frameIndex);
1070
1071     if(textureSet)
1072     {
1073       SetImageSize(textureSet);
1074       if(mImpl->mRenderer)
1075       {
1076         mImpl->mRenderer.SetTextures(textureSet);
1077         CheckMaskTexture();
1078       }
1079       mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex));
1080     }
1081
1082     mCurrentFrameIndex = frameIndex;
1083     continueTimer      = (mActionStatus == DevelAnimatedImageVisual::Action::PLAY && textureSet) ? true : false;
1084   }
1085
1086   return continueTimer;
1087 }
1088
1089 TextureSet AnimatedImageVisual::SetLoadingFailed()
1090 {
1091   DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n");
1092   ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
1093
1094   Actor   actor     = mPlacementActor.GetHandle();
1095   Vector2 imageSize = Vector2::ZERO;
1096   if(actor)
1097   {
1098     imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
1099   }
1100   mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
1101   TextureSet textureSet = mImpl->mRenderer.GetTextures();
1102
1103   if(mFrameDelayTimer)
1104   {
1105     mFrameDelayTimer.Stop();
1106     mFrameDelayTimer.Reset();
1107   }
1108
1109   SetImageSize(textureSet);
1110
1111   return textureSet;
1112 }
1113
1114 void AnimatedImageVisual::AllocateMaskData()
1115 {
1116   if(!mMaskingData)
1117   {
1118     mMaskingData.reset(new TextureManager::MaskingData());
1119   }
1120 }
1121
1122 void AnimatedImageVisual::CheckMaskTexture()
1123 {
1124   if(mMaskingData && !mMaskingData->mPreappliedMasking)
1125   {
1126     bool       maskLoadFailed = true;
1127     TextureSet textures       = mImpl->mRenderer.GetTextures();
1128     if(textures && textures.GetTextureCount() >= TEXTURE_COUNT_FOR_GPU_ALPHA_MASK)
1129     {
1130       maskLoadFailed = false;
1131     }
1132     if(mMaskingData->mMaskImageLoadingFailed != maskLoadFailed)
1133     {
1134       mMaskingData->mMaskImageLoadingFailed = maskLoadFailed;
1135       UpdateShader();
1136     }
1137   }
1138 }
1139
1140 void AnimatedImageVisual::OnControlVisibilityChanged(Actor actor, bool visible, DevelActor::VisibilityChange::Type type)
1141 {
1142   if(!visible && mActionStatus != DevelAnimatedImageVisual::Action::STOP)
1143   {
1144     mActionStatus = DevelAnimatedImageVisual::Action::STOP;
1145     DisplayNextFrame();
1146     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Verbose, "AnimatedImageVisual::OnControlVisibilityChanged: invisibile. Pause animation [%p]\n", this);
1147   }
1148 }
1149
1150 void AnimatedImageVisual::OnWindowVisibilityChanged(Window window, bool visible)
1151 {
1152   if(!visible && mActionStatus != DevelAnimatedImageVisual::Action::STOP)
1153   {
1154     mActionStatus = DevelAnimatedImageVisual::Action::STOP;
1155     DisplayNextFrame();
1156     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Verbose, "AnimatedImageVisual::OnWindowVisibilityChanged: invisibile. Pause animation [%p]\n", this);
1157   }
1158 }
1159
1160 } // namespace Internal
1161
1162 } // namespace Toolkit
1163
1164 } // namespace Dali