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