60c0579d8340b923e2db3eeffbd1444f6617c0ee
[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   // This returns -1 until the loading is finished.
349   int32_t frameCount = mFrameCount;
350   if(mImageCache && frameCount == 0)
351   {
352     frameCount = mImageCache->GetTotalFrameCount();
353
354     if(frameCount <= SINGLE_IMAGE_COUNT && mAnimatedImageLoading && mAnimatedImageLoading.HasLoadingSucceeded())
355     {
356       frameCount = mAnimatedImageLoading.GetImageCount();
357     }
358     else
359     {
360       frameCount = -1;
361     }
362   }
363
364   map.Insert(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, static_cast<int>(frameCount));
365
366   map.Insert(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mStopBehavior);
367
368   if(mMaskingData != nullptr)
369   {
370     map.Insert(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, mMaskingData->mAlphaMaskUrl.GetUrl());
371     map.Insert(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, mMaskingData->mContentScaleFactor);
372     map.Insert(Toolkit::ImageVisual::Property::CROP_TO_MASK, mMaskingData->mCropToMask);
373     map.Insert(Toolkit::DevelImageVisual::Property::MASKING_TYPE, mMaskingData->mPreappliedMasking ? DevelImageVisual::MaskingType::MASKING_ON_LOADING : DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
374   }
375
376   map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
377   map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
378   map.Insert(Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode);
379   map.Insert(Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode);
380   map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
381   map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
382 }
383
384 void AnimatedImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
385 {
386   map.Clear();
387   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE);
388   if(mImageUrl.IsValid())
389   {
390     map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
391     map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
392   }
393 }
394
395 void AnimatedImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
396 {
397   // Make not set any action when the resource status is already failed.
398   if(mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED)
399   {
400     return;
401   }
402
403   // Check if action is valid for this visual type and perform action if possible
404   switch(actionId)
405   {
406     case DevelAnimatedImageVisual::Action::PAUSE:
407     {
408       // Pause will be executed on next timer tick
409       mActionStatus = DevelAnimatedImageVisual::Action::PAUSE;
410       break;
411     }
412     case DevelAnimatedImageVisual::Action::PLAY:
413     {
414       if(mFrameDelayTimer && IsOnScene() && mActionStatus != DevelAnimatedImageVisual::Action::PLAY)
415       {
416         mFrameDelayTimer.Start();
417       }
418       mActionStatus = DevelAnimatedImageVisual::Action::PLAY;
419       break;
420     }
421     case DevelAnimatedImageVisual::Action::STOP:
422     {
423       // STOP reset functionality will actually be done in a future change
424       // Stop will be executed on next timer tick
425       mActionStatus     = DevelAnimatedImageVisual::Action::STOP;
426       mCurrentLoopIndex = FIRST_LOOP;
427       if(IsOnScene())
428       {
429         DisplayNextFrame();
430       }
431       break;
432     }
433     case DevelAnimatedImageVisual::Action::JUMP_TO:
434     {
435       int32_t frameNumber;
436       if(attributes.Get(frameNumber))
437       {
438         if(frameNumber < 0 || frameNumber >= static_cast<int32_t>(mFrameCount))
439         {
440           DALI_LOG_ERROR("Invalid frame index used.\n");
441         }
442         else
443         {
444           mIsJumpTo            = true;
445           mFrameIndexForJumpTo = frameNumber;
446           if(IsOnScene())
447           {
448             DisplayNextFrame();
449           }
450         }
451       }
452       break;
453     }
454   }
455 }
456
457 void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap)
458 {
459   // url[s] already passed in from constructor
460   for(Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter)
461   {
462     KeyValuePair keyValue = propertyMap.GetKeyValue(iter);
463     if(keyValue.first.type == Property::Key::INDEX)
464     {
465       DoSetProperty(keyValue.first.indexKey, keyValue.second);
466     }
467     else
468     {
469       if(keyValue.first == PIXEL_AREA_UNIFORM_NAME)
470       {
471         DoSetProperty(Toolkit::ImageVisual::Property::PIXEL_AREA, keyValue.second);
472       }
473       else if(keyValue.first == IMAGE_WRAP_MODE_U)
474       {
475         DoSetProperty(Toolkit::ImageVisual::Property::WRAP_MODE_U, keyValue.second);
476       }
477       else if(keyValue.first == IMAGE_WRAP_MODE_V)
478       {
479         DoSetProperty(Toolkit::ImageVisual::Property::WRAP_MODE_V, keyValue.second);
480       }
481       else if(keyValue.first == BATCH_SIZE_NAME)
482       {
483         DoSetProperty(Toolkit::ImageVisual::Property::BATCH_SIZE, keyValue.second);
484       }
485       else if(keyValue.first == CACHE_SIZE_NAME)
486       {
487         DoSetProperty(Toolkit::ImageVisual::Property::CACHE_SIZE, keyValue.second);
488       }
489       else if(keyValue.first == FRAME_DELAY_NAME)
490       {
491         DoSetProperty(Toolkit::ImageVisual::Property::FRAME_DELAY, keyValue.second);
492       }
493       else if(keyValue.first == LOOP_COUNT_NAME)
494       {
495         DoSetProperty(Toolkit::DevelImageVisual::Property::LOOP_COUNT, keyValue.second);
496       }
497       else if(keyValue.first == STOP_BEHAVIOR_NAME)
498       {
499         DoSetProperty(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, keyValue.second);
500       }
501       else if(keyValue.first == ALPHA_MASK_URL)
502       {
503         DoSetProperty(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, keyValue.second);
504       }
505       else if(keyValue.first == MASK_CONTENT_SCALE_NAME)
506       {
507         DoSetProperty(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, keyValue.second);
508       }
509       else if(keyValue.first == CROP_TO_MASK_NAME)
510       {
511         DoSetProperty(Toolkit::ImageVisual::Property::CROP_TO_MASK, keyValue.second);
512       }
513       else if(keyValue.first == MASKING_TYPE_NAME)
514       {
515         DoSetProperty(Toolkit::DevelImageVisual::Property::MASKING_TYPE, keyValue.second);
516       }
517       else if(keyValue.first == LOAD_POLICY_NAME)
518       {
519         DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second);
520       }
521       else if(keyValue.first == RELEASE_POLICY_NAME)
522       {
523         DoSetProperty(Toolkit::ImageVisual::Property::RELEASE_POLICY, keyValue.second);
524       }
525       else if(keyValue.first == SYNCHRONOUS_LOADING)
526       {
527         DoSetProperty(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second);
528       }
529       else if(keyValue.first == IMAGE_FITTING_MODE)
530       {
531         DoSetProperty(Toolkit::ImageVisual::Property::FITTING_MODE, keyValue.second);
532       }
533       else if(keyValue.first == IMAGE_SAMPLING_MODE)
534       {
535         DoSetProperty(Toolkit::ImageVisual::Property::SAMPLING_MODE, keyValue.second);
536       }
537       else if(keyValue.first == IMAGE_DESIRED_WIDTH)
538       {
539         DoSetProperty(Toolkit::ImageVisual::Property::DESIRED_WIDTH, keyValue.second);
540       }
541       else if(keyValue.first == IMAGE_DESIRED_HEIGHT)
542       {
543         DoSetProperty(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, keyValue.second);
544       }
545     }
546   }
547   // Load image immediately if LOAD_POLICY requires it
548   if(mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::IMMEDIATE)
549   {
550     PrepareTextureSet();
551   }
552 }
553
554 void AnimatedImageVisual::DoSetProperty(Property::Index        index,
555                                         const Property::Value& value)
556 {
557   switch(index)
558   {
559     case Toolkit::ImageVisual::Property::PIXEL_AREA:
560     {
561       value.Get(mPixelArea);
562       break;
563     }
564     case Toolkit::ImageVisual::Property::WRAP_MODE_U:
565     {
566       int wrapMode = 0;
567       if(Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode))
568       {
569         mWrapModeU = Dali::WrapMode::Type(wrapMode);
570       }
571       else
572       {
573         mWrapModeU = Dali::WrapMode::Type::DEFAULT;
574       }
575       break;
576     }
577     case Toolkit::ImageVisual::Property::WRAP_MODE_V:
578     {
579       int wrapMode = 0;
580       if(Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode))
581       {
582         mWrapModeV = Dali::WrapMode::Type(wrapMode);
583       }
584       else
585       {
586         mWrapModeV = Dali::WrapMode::Type::DEFAULT;
587       }
588       break;
589     }
590
591     case Toolkit::ImageVisual::Property::BATCH_SIZE:
592     {
593       int batchSize;
594       if(value.Get(batchSize))
595       {
596         if(batchSize < 2)
597         {
598           DALI_LOG_ERROR("The minimum value of batch size is 2.");
599         }
600         else
601         {
602           mBatchSize = batchSize;
603         }
604       }
605       break;
606     }
607
608     case Toolkit::ImageVisual::Property::CACHE_SIZE:
609     {
610       int cacheSize;
611       if(value.Get(cacheSize))
612       {
613         if(cacheSize < 2)
614         {
615           DALI_LOG_ERROR("The minimum value of cache size is 2.");
616         }
617         else
618         {
619           mCacheSize = cacheSize;
620         }
621       }
622       break;
623     }
624
625     case Toolkit::ImageVisual::Property::FRAME_DELAY:
626     {
627       int frameDelay;
628       if(value.Get(frameDelay))
629       {
630         mFrameDelay = frameDelay;
631         if(mImageCache)
632         {
633           mImageCache->SetInterval(static_cast<uint32_t>(mFrameDelay));
634         }
635       }
636       break;
637     }
638
639     case Toolkit::DevelImageVisual::Property::LOOP_COUNT:
640     {
641       int loopCount;
642       if(value.Get(loopCount))
643       {
644         mLoopCount = loopCount;
645       }
646       break;
647     }
648
649     case Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR:
650     {
651       int32_t stopBehavior = mStopBehavior;
652       if(Scripting::GetEnumerationProperty(value, STOP_BEHAVIOR_TABLE, STOP_BEHAVIOR_TABLE_COUNT, stopBehavior))
653       {
654         mStopBehavior = DevelImageVisual::StopBehavior::Type(stopBehavior);
655       }
656       break;
657     }
658
659     case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
660     {
661       bool sync = false;
662       value.Get(sync);
663       if(sync)
664       {
665         mImpl->mFlags |= Visual::Base::Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
666       }
667       else
668       {
669         mImpl->mFlags &= ~Visual::Base::Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
670       }
671       break;
672     }
673
674     case Toolkit::ImageVisual::Property::ALPHA_MASK_URL:
675     {
676       std::string alphaUrl = "";
677       if(value.Get(alphaUrl))
678       {
679         AllocateMaskData();
680         mMaskingData->mAlphaMaskUrl = alphaUrl;
681       }
682       break;
683     }
684
685     case Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE:
686     {
687       float scale = 1.0f;
688       if(value.Get(scale))
689       {
690         AllocateMaskData();
691         mMaskingData->mContentScaleFactor = scale;
692       }
693       break;
694     }
695
696     case Toolkit::ImageVisual::Property::CROP_TO_MASK:
697     {
698       bool crop = false;
699       if(value.Get(crop))
700       {
701         AllocateMaskData();
702         mMaskingData->mCropToMask = crop;
703       }
704       break;
705     }
706
707     case Toolkit::DevelImageVisual::Property::MASKING_TYPE:
708     {
709       int maskingType = 0;
710       if(value.Get(maskingType))
711       {
712         AllocateMaskData();
713         mMaskingData->mPreappliedMasking = Toolkit::DevelImageVisual::MaskingType::Type(maskingType) == Toolkit::DevelImageVisual::MaskingType::MASKING_ON_LOADING ? true : false;
714       }
715       break;
716     }
717
718     case Toolkit::ImageVisual::Property::RELEASE_POLICY:
719     {
720       int releasePolicy = 0;
721       Scripting::GetEnumerationProperty(value, RELEASE_POLICY_TABLE, RELEASE_POLICY_TABLE_COUNT, releasePolicy);
722       mReleasePolicy = Toolkit::ImageVisual::ReleasePolicy::Type(releasePolicy);
723       break;
724     }
725
726     case Toolkit::ImageVisual::Property::LOAD_POLICY:
727     {
728       int loadPolicy = 0;
729       Scripting::GetEnumerationProperty(value, LOAD_POLICY_TABLE, LOAD_POLICY_TABLE_COUNT, loadPolicy);
730       mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type(loadPolicy);
731       break;
732     }
733
734     case Toolkit::ImageVisual::Property::FITTING_MODE:
735     {
736       int fittingMode = 0;
737       Scripting::GetEnumerationProperty(value, FITTING_MODE_TABLE, FITTING_MODE_TABLE_COUNT, fittingMode);
738       mFittingMode = Dali::FittingMode::Type(fittingMode);
739       break;
740     }
741
742     case Toolkit::ImageVisual::Property::SAMPLING_MODE:
743     {
744       int samplingMode = 0;
745       Scripting::GetEnumerationProperty(value, SAMPLING_MODE_TABLE, SAMPLING_MODE_TABLE_COUNT, samplingMode);
746       mSamplingMode = Dali::SamplingMode::Type(samplingMode);
747       break;
748     }
749
750     case Toolkit::ImageVisual::Property::DESIRED_WIDTH:
751     {
752       float desiredWidth = 0.0f;
753       if(value.Get(desiredWidth))
754       {
755         mDesiredSize.SetWidth(desiredWidth);
756       }
757       else
758       {
759         DALI_LOG_ERROR("AnimatedImageVisual: desiredWidth property has incorrect type\n");
760       }
761       break;
762     }
763
764     case Toolkit::ImageVisual::Property::DESIRED_HEIGHT:
765     {
766       float desiredHeight = 0.0f;
767       if(value.Get(desiredHeight))
768       {
769         mDesiredSize.SetHeight(desiredHeight);
770       }
771       else
772       {
773         DALI_LOG_ERROR("AnimatedImageVisual: desiredHeight property has incorrect type\n");
774       }
775       break;
776     }
777   }
778 }
779
780 void AnimatedImageVisual::DoSetOnScene(Actor& actor)
781 {
782   mStartFirstFrame = true;
783   mPlacementActor  = actor;
784   PrepareTextureSet();
785
786   actor.InheritedVisibilityChangedSignal().Connect(this, &AnimatedImageVisual::OnControlInheritedVisibilityChanged);
787
788   Window window = DevelWindow::Get(actor);
789   if(window)
790   {
791     mPlacementWindow = window;
792     DevelWindow::VisibilityChangedSignal(window).Connect(this, &AnimatedImageVisual::OnWindowVisibilityChanged);
793   }
794 }
795
796 void AnimatedImageVisual::DoSetOffScene(Actor& actor)
797 {
798   DALI_ASSERT_DEBUG((bool)mImpl->mRenderer && "There should always be a renderer whilst on stage");
799
800   if(mFrameDelayTimer)
801   {
802     mFrameDelayTimer.Stop();
803     mFrameDelayTimer.Reset();
804   }
805
806   actor.RemoveRenderer(mImpl->mRenderer);
807   if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
808   {
809     mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
810     mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
811
812     TextureSet textureSet = TextureSet::New();
813     mImpl->mRenderer.SetTextures(textureSet);
814   }
815
816   mPlacementActor.Reset();
817   mStartFirstFrame   = false;
818   mCurrentFrameIndex = FIRST_FRAME_INDEX;
819   mCurrentLoopIndex  = FIRST_LOOP;
820
821   actor.InheritedVisibilityChangedSignal().Disconnect(this, &AnimatedImageVisual::OnControlInheritedVisibilityChanged);
822
823   Window window = mPlacementWindow.GetHandle();
824   if(window)
825   {
826     DevelWindow::VisibilityChangedSignal(window).Disconnect(this, &AnimatedImageVisual::OnWindowVisibilityChanged);
827     mPlacementWindow.Reset();
828   }
829 }
830
831 void AnimatedImageVisual::OnSetTransform()
832 {
833   if(mImpl->mRenderer)
834   {
835     mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
836   }
837 }
838
839 void AnimatedImageVisual::UpdateShader()
840 {
841   if(mImpl->mRenderer)
842   {
843     Shader shader = GenerateShader();
844     mImpl->mRenderer.SetShader(shader);
845   }
846 }
847
848 Shader AnimatedImageVisual::GenerateShader() const
849 {
850   bool   defaultWrapMode                 = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
851   bool   requiredAlphaMaskingOnRendering = (mMaskingData && !mMaskingData->mMaskImageLoadingFailed) ? !mMaskingData->mPreappliedMasking : false;
852   Shader shader;
853   shader = mImageVisualShaderFactory.GetShader(
854     mFactoryCache,
855     ImageVisualShaderFeatureBuilder()
856       .ApplyDefaultTextureWrapMode(defaultWrapMode)
857       .EnableRoundedCorner(IsRoundedCornerRequired())
858       .EnableBorderline(IsBorderlineRequired())
859       .EnableAlphaMaskingOnRendering(requiredAlphaMaskingOnRendering));
860   return shader;
861 }
862
863 void AnimatedImageVisual::OnInitialize()
864 {
865   CreateImageCache();
866
867   bool   defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
868   Shader shader          = GenerateShader();
869
870   Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
871
872   mImpl->mRenderer = DecoratedVisualRenderer::New(geometry, shader);
873   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
874
875   // Register transform properties
876   mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
877
878   if(!defaultWrapMode) // custom wrap mode
879   {
880     Vector2 wrapMode(mWrapModeU - WrapMode::CLAMP_TO_EDGE, mWrapModeV - WrapMode::CLAMP_TO_EDGE);
881     wrapMode.Clamp(Vector2::ZERO, Vector2(2.f, 2.f));
882     mImpl->mRenderer.RegisterProperty(WRAP_MODE_UNIFORM_NAME, wrapMode);
883   }
884
885   if(mPixelArea != FULL_TEXTURE_RECT)
886   {
887     mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea);
888   }
889
890   if(mMaskingData)
891   {
892     mImpl->mRenderer.RegisterProperty(CROP_TO_MASK_NAME, static_cast<float>(mMaskingData->mCropToMask));
893   }
894
895   // Enable PreMultipliedAlpha if it need premultiplied
896   auto preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader
897                              ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
898                              : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
899   EnablePreMultipliedAlpha(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
900 }
901
902 void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval)
903 {
904   DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::StartFirstFrame()\n");
905
906   mStartFirstFrame = false;
907   if(mImpl->mRenderer)
908   {
909     mImpl->mRenderer.SetTextures(textureSet);
910     CheckMaskTexture();
911
912     Actor actor = mPlacementActor.GetHandle();
913     if(actor)
914     {
915       actor.AddRenderer(mImpl->mRenderer);
916       mPlacementActor.Reset();
917     }
918   }
919
920   if(mImpl->mResourceStatus != Toolkit::Visual::ResourceStatus::FAILED)
921   {
922     if(mFrameCount > SINGLE_IMAGE_COUNT)
923     {
924       mFrameDelayTimer = Timer::New(firstInterval);
925       mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame);
926       mFrameDelayTimer.Start();
927     }
928
929     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::READY)\n");
930     ResourceReady(Toolkit::Visual::ResourceStatus::READY);
931   }
932
933   mCurrentFrameIndex = FIRST_FRAME_INDEX;
934 }
935
936 void AnimatedImageVisual::PrepareTextureSet()
937 {
938   TextureSet textureSet;
939   if(mImageCache)
940   {
941     textureSet = mImageCache->FirstFrame();
942   }
943
944   // Check whether synchronous loading is true or false for the first frame.
945   if(textureSet)
946   {
947     SetImageSize(textureSet);
948   }
949 }
950
951 void AnimatedImageVisual::SetImageSize(TextureSet& textureSet)
952 {
953   if(textureSet)
954   {
955     Texture texture = textureSet.GetTexture(0);
956     if(texture)
957     {
958       mImageSize.SetWidth(texture.GetWidth());
959       mImageSize.SetHeight(texture.GetHeight());
960     }
961
962     if(textureSet.GetTextureCount() > 1u && mMaskingData && mMaskingData->mCropToMask)
963     {
964       Texture maskTexture = textureSet.GetTexture(1);
965       if(maskTexture)
966       {
967         mImageSize.SetWidth(std::min(static_cast<uint32_t>(mImageSize.GetWidth() * mMaskingData->mContentScaleFactor), maskTexture.GetWidth()));
968         mImageSize.SetHeight(std::min(static_cast<uint32_t>(mImageSize.GetHeight() * mMaskingData->mContentScaleFactor), maskTexture.GetHeight()));
969
970         float   textureWidth  = std::max(static_cast<float>(texture.GetWidth() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1);
971         float   textureHeight = std::max(static_cast<float>(texture.GetHeight() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1);
972         Vector2 textureRatio(std::min(static_cast<float>(maskTexture.GetWidth()), textureWidth) / textureWidth,
973                              std::min(static_cast<float>(maskTexture.GetHeight()), textureHeight) / textureHeight);
974         mImpl->mRenderer.RegisterProperty(MASK_TEXTURE_RATIO_NAME, textureRatio);
975       }
976     }
977   }
978 }
979
980 void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval, bool preMultiplied)
981 {
982   EnablePreMultipliedAlpha(preMultiplied);
983
984   // When image visual requested to load new frame to mImageCache and it is failed.
985   if(!mImageCache || !textureSet)
986   {
987     textureSet = SetLoadingFailed();
988   }
989   SetImageSize(textureSet);
990
991   if(mStartFirstFrame)
992   {
993     mFrameCount = mImageCache->GetTotalFrameCount();
994     StartFirstFrame(textureSet, interval);
995   }
996   else
997   {
998     if(mImpl->mRenderer)
999     {
1000       if(mFrameDelayTimer && interval > 0u)
1001       {
1002         mFrameDelayTimer.SetInterval(interval);
1003       }
1004       mImpl->mRenderer.SetTextures(textureSet);
1005       CheckMaskTexture();
1006     }
1007   }
1008 }
1009
1010 bool AnimatedImageVisual::DisplayNextFrame()
1011 {
1012   TextureSet textureSet;
1013   bool       continueTimer = false;
1014
1015   if(mImageCache)
1016   {
1017     uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
1018
1019     if(mIsJumpTo)
1020     {
1021       mIsJumpTo  = false;
1022       frameIndex = mFrameIndexForJumpTo;
1023     }
1024     else if(mActionStatus == DevelAnimatedImageVisual::Action::PAUSE)
1025     {
1026       return false;
1027     }
1028     else if(mActionStatus == DevelAnimatedImageVisual::Action::STOP)
1029     {
1030       mCurrentLoopIndex = FIRST_LOOP;
1031       if(mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME)
1032       {
1033         frameIndex = FIRST_FRAME_INDEX;
1034       }
1035       else if(mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME)
1036       {
1037         frameIndex = mFrameCount - 1;
1038       }
1039       else
1040       {
1041         return false; // Do not draw already rendered scene twice.
1042       }
1043     }
1044     else
1045     {
1046       if(mFrameCount > SINGLE_IMAGE_COUNT)
1047       {
1048         frameIndex++;
1049         if(frameIndex >= mFrameCount)
1050         {
1051           frameIndex = FIRST_FRAME_INDEX;
1052           ++mCurrentLoopIndex;
1053         }
1054
1055         if(mLoopCount >= 0 && mCurrentLoopIndex >= mLoopCount)
1056         {
1057           // This will stop timer
1058           mActionStatus = DevelAnimatedImageVisual::Action::STOP;
1059           return DisplayNextFrame();
1060         }
1061       }
1062     }
1063
1064     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex);
1065
1066     textureSet = mImageCache->Frame(frameIndex);
1067
1068     if(textureSet)
1069     {
1070       SetImageSize(textureSet);
1071       if(mImpl->mRenderer)
1072       {
1073         mImpl->mRenderer.SetTextures(textureSet);
1074         CheckMaskTexture();
1075       }
1076       mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex));
1077     }
1078
1079     mCurrentFrameIndex = frameIndex;
1080     continueTimer      = (mActionStatus == DevelAnimatedImageVisual::Action::PLAY && textureSet) ? true : false;
1081   }
1082
1083   return continueTimer;
1084 }
1085
1086 TextureSet AnimatedImageVisual::SetLoadingFailed()
1087 {
1088   DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n");
1089   ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
1090
1091   Actor   actor     = mPlacementActor.GetHandle();
1092   Vector2 imageSize = Vector2::ZERO;
1093   if(actor)
1094   {
1095     imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
1096   }
1097   mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
1098   TextureSet textureSet = mImpl->mRenderer.GetTextures();
1099
1100   if(mFrameDelayTimer)
1101   {
1102     mFrameDelayTimer.Stop();
1103     mFrameDelayTimer.Reset();
1104   }
1105
1106   SetImageSize(textureSet);
1107
1108   return textureSet;
1109 }
1110
1111 void AnimatedImageVisual::AllocateMaskData()
1112 {
1113   if(!mMaskingData)
1114   {
1115     mMaskingData.reset(new TextureManager::MaskingData());
1116   }
1117 }
1118
1119 void AnimatedImageVisual::CheckMaskTexture()
1120 {
1121   if(mMaskingData && !mMaskingData->mPreappliedMasking)
1122   {
1123     bool       maskLoadFailed = true;
1124     TextureSet textures       = mImpl->mRenderer.GetTextures();
1125     if(textures && textures.GetTextureCount() >= TEXTURE_COUNT_FOR_GPU_ALPHA_MASK)
1126     {
1127       maskLoadFailed = false;
1128     }
1129     if(mMaskingData->mMaskImageLoadingFailed != maskLoadFailed)
1130     {
1131       mMaskingData->mMaskImageLoadingFailed = maskLoadFailed;
1132       UpdateShader();
1133     }
1134   }
1135 }
1136
1137 void AnimatedImageVisual::OnControlInheritedVisibilityChanged(Actor actor, bool visible)
1138 {
1139   if(!visible && mActionStatus != DevelAnimatedImageVisual::Action::STOP)
1140   {
1141     mActionStatus = DevelAnimatedImageVisual::Action::STOP;
1142     DisplayNextFrame();
1143     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Verbose, "AnimatedImageVisual::OnControlInheritedVisibilityChanged: invisibile. Pause animation [%p]\n", this);
1144   }
1145 }
1146
1147 void AnimatedImageVisual::OnWindowVisibilityChanged(Window window, bool visible)
1148 {
1149   if(!visible && mActionStatus != DevelAnimatedImageVisual::Action::STOP)
1150   {
1151     mActionStatus = DevelAnimatedImageVisual::Action::STOP;
1152     DisplayNextFrame();
1153     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Verbose, "AnimatedImageVisual::OnWindowVisibilityChanged: invisibile. Pause animation [%p]\n", this);
1154   }
1155 }
1156
1157 } // namespace Internal
1158
1159 } // namespace Toolkit
1160
1161 } // namespace Dali