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