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