CPU Alpha Masking for Animated Image Visual
[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() : 
311                                                                                                                                      mImageCache->GetTotalFrameCount()) : -1);
312
313   map.Insert(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mStopBehavior);
314
315   if(mMaskingData != nullptr)
316   {
317     map.Insert(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, mMaskingData->mAlphaMaskUrl.GetUrl());
318     map.Insert(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, mMaskingData->mContentScaleFactor);
319     map.Insert(Toolkit::ImageVisual::Property::CROP_TO_MASK, mMaskingData->mCropToMask);
320   }
321
322   map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
323   map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
324 }
325
326 void AnimatedImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
327 {
328   // Do nothing
329 }
330
331 void AnimatedImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
332 {
333   // Make not set any action when the resource status is already failed.
334   if(mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED)
335   {
336     return;
337   }
338
339   // Check if action is valid for this visual type and perform action if possible
340   switch(actionId)
341   {
342     case DevelAnimatedImageVisual::Action::PAUSE:
343     {
344       // Pause will be executed on next timer tick
345       mActionStatus = DevelAnimatedImageVisual::Action::PAUSE;
346       break;
347     }
348     case DevelAnimatedImageVisual::Action::PLAY:
349     {
350       if(mFrameDelayTimer && IsOnScene() && mActionStatus != DevelAnimatedImageVisual::Action::PLAY)
351       {
352         mFrameDelayTimer.Start();
353       }
354       mActionStatus = DevelAnimatedImageVisual::Action::PLAY;
355       break;
356     }
357     case DevelAnimatedImageVisual::Action::STOP:
358     {
359       // STOP reset functionality will actually be done in a future change
360       // Stop will be executed on next timer tick
361       mActionStatus = DevelAnimatedImageVisual::Action::STOP;
362       mCurrentLoopIndex = FIRST_LOOP;
363       if(IsOnScene())
364       {
365         DisplayNextFrame();
366       }
367       break;
368     }
369     case DevelAnimatedImageVisual::Action::JUMP_TO:
370     {
371       int32_t frameNumber;
372       if(attributes.Get(frameNumber))
373       {
374         if(frameNumber < 0 || frameNumber >= static_cast<int32_t>(mFrameCount))
375         {
376           DALI_LOG_ERROR("Invalid frame index used.\n");
377         }
378         else
379         {
380           mIsJumpTo            = true;
381           mFrameIndexForJumpTo = frameNumber;
382           if(IsOnScene())
383           {
384             DisplayNextFrame();
385           }
386         }
387       }
388       break;
389     }
390   }
391 }
392
393 void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap)
394 {
395   // url[s] already passed in from constructor
396   for(Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter)
397   {
398     KeyValuePair keyValue = propertyMap.GetKeyValue(iter);
399     if(keyValue.first.type == Property::Key::INDEX)
400     {
401       DoSetProperty(keyValue.first.indexKey, keyValue.second);
402     }
403     else
404     {
405       if(keyValue.first == PIXEL_AREA_UNIFORM_NAME)
406       {
407         DoSetProperty(Toolkit::ImageVisual::Property::PIXEL_AREA, keyValue.second);
408       }
409       else if(keyValue.first == IMAGE_WRAP_MODE_U)
410       {
411         DoSetProperty(Toolkit::ImageVisual::Property::WRAP_MODE_U, keyValue.second);
412       }
413       else if(keyValue.first == IMAGE_WRAP_MODE_V)
414       {
415         DoSetProperty(Toolkit::ImageVisual::Property::WRAP_MODE_V, keyValue.second);
416       }
417       else if(keyValue.first == BATCH_SIZE_NAME)
418       {
419         DoSetProperty(Toolkit::ImageVisual::Property::BATCH_SIZE, keyValue.second);
420       }
421       else if(keyValue.first == CACHE_SIZE_NAME)
422       {
423         DoSetProperty(Toolkit::ImageVisual::Property::CACHE_SIZE, keyValue.second);
424       }
425       else if(keyValue.first == FRAME_DELAY_NAME)
426       {
427         DoSetProperty(Toolkit::ImageVisual::Property::FRAME_DELAY, keyValue.second);
428       }
429       else if(keyValue.first == LOOP_COUNT_NAME)
430       {
431         DoSetProperty(Toolkit::DevelImageVisual::Property::LOOP_COUNT, keyValue.second);
432       }
433       else if(keyValue.first == STOP_BEHAVIOR_NAME)
434       {
435         DoSetProperty(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, keyValue.second);
436       }
437       else if(keyValue.first == ALPHA_MASK_URL)
438       {
439         DoSetProperty(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, keyValue.second);
440       }
441       else if(keyValue.first == MASK_CONTENT_SCALE_NAME)
442       {
443         DoSetProperty(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, keyValue.second);
444       }
445       else if(keyValue.first == CROP_TO_MASK_NAME)
446       {
447         DoSetProperty(Toolkit::ImageVisual::Property::CROP_TO_MASK, keyValue.second);
448       }
449       else if(keyValue.first == LOAD_POLICY_NAME)
450       {
451         DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second);
452       }
453       else if(keyValue.first == RELEASE_POLICY_NAME)
454       {
455         DoSetProperty(Toolkit::ImageVisual::Property::RELEASE_POLICY, keyValue.second);
456       }
457       else if(keyValue.first == SYNCHRONOUS_LOADING)
458       {
459         DoSetProperty(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second);
460       }
461     }
462   }
463   // Load image immediately if LOAD_POLICY requires it
464   if(mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::IMMEDIATE)
465   {
466     PrepareTextureSet();
467   }
468 }
469
470 void AnimatedImageVisual::DoSetProperty(Property::Index        index,
471                                         const Property::Value& value)
472 {
473   switch(index)
474   {
475     case Toolkit::ImageVisual::Property::PIXEL_AREA:
476     {
477       value.Get(mPixelArea);
478       break;
479     }
480     case Toolkit::ImageVisual::Property::WRAP_MODE_U:
481     {
482       int wrapMode = 0;
483       if(Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode))
484       {
485         mWrapModeU = Dali::WrapMode::Type(wrapMode);
486       }
487       else
488       {
489         mWrapModeU = Dali::WrapMode::Type::DEFAULT;
490       }
491       break;
492     }
493     case Toolkit::ImageVisual::Property::WRAP_MODE_V:
494     {
495       int wrapMode = 0;
496       if(Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode))
497       {
498         mWrapModeV = Dali::WrapMode::Type(wrapMode);
499       }
500       else
501       {
502         mWrapModeV = Dali::WrapMode::Type::DEFAULT;
503       }
504       break;
505     }
506
507     case Toolkit::ImageVisual::Property::BATCH_SIZE:
508     {
509       int batchSize;
510       if(value.Get(batchSize))
511       {
512         if(batchSize < 2)
513         {
514           DALI_LOG_ERROR("The minimum value of batch size is 2.");
515         }
516         else
517         {
518           mBatchSize = batchSize;
519         }
520       }
521       break;
522     }
523
524     case Toolkit::ImageVisual::Property::CACHE_SIZE:
525     {
526       int cacheSize;
527       if(value.Get(cacheSize))
528       {
529         if(cacheSize < 2)
530         {
531           DALI_LOG_ERROR("The minimum value of cache size is 2.");
532         }
533         else
534         {
535           mCacheSize = cacheSize;
536         }
537       }
538       break;
539     }
540
541     case Toolkit::ImageVisual::Property::FRAME_DELAY:
542     {
543       int frameDelay;
544       if(value.Get(frameDelay))
545       {
546         mFrameDelay = frameDelay;
547         if(mImageCache)
548         {
549           mImageCache->SetInterval(static_cast<uint32_t>(mFrameDelay));
550         }
551       }
552       break;
553     }
554
555     case Toolkit::DevelImageVisual::Property::LOOP_COUNT:
556     {
557       int loopCount;
558       if(value.Get(loopCount))
559       {
560         mLoopCount = loopCount;
561       }
562       break;
563     }
564
565     case Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR:
566     {
567       int32_t stopBehavior = mStopBehavior;
568       if(Scripting::GetEnumerationProperty(value, STOP_BEHAVIOR_TABLE, STOP_BEHAVIOR_TABLE_COUNT, stopBehavior))
569       {
570         mStopBehavior = DevelImageVisual::StopBehavior::Type(stopBehavior);
571       }
572       break;
573     }
574
575     case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
576     {
577       bool sync = false;
578       value.Get(sync);
579       if(sync)
580       {
581         mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
582       }
583       else
584       {
585         mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
586       }
587       break;
588     }
589
590     case Toolkit::ImageVisual::Property::ALPHA_MASK_URL:
591     {
592       std::string alphaUrl = "";
593       if(value.Get(alphaUrl))
594       {
595         AllocateMaskData();
596         mMaskingData->mAlphaMaskUrl = alphaUrl;
597       }
598       break;
599     }
600
601     case Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE:
602     {
603       float scale = 1.0f;
604       if(value.Get(scale))
605       {
606         AllocateMaskData();
607         mMaskingData->mContentScaleFactor = scale;
608       }
609       break;
610     }
611
612     case Toolkit::ImageVisual::Property::CROP_TO_MASK:
613     {
614       bool crop = false;
615       if(value.Get(crop))
616       {
617         AllocateMaskData();
618         mMaskingData->mCropToMask = crop;
619       }
620       break;
621     }
622
623     case Toolkit::ImageVisual::Property::RELEASE_POLICY:
624     {
625       int releasePolicy = 0;
626       Scripting::GetEnumerationProperty(value, RELEASE_POLICY_TABLE, RELEASE_POLICY_TABLE_COUNT, releasePolicy);
627       mReleasePolicy = Toolkit::ImageVisual::ReleasePolicy::Type(releasePolicy);
628       break;
629     }
630
631     case Toolkit::ImageVisual::Property::LOAD_POLICY:
632     {
633       int loadPolicy = 0;
634       Scripting::GetEnumerationProperty(value, LOAD_POLICY_TABLE, LOAD_POLICY_TABLE_COUNT, loadPolicy);
635       mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type(loadPolicy);
636       break;
637     }
638   }
639 }
640
641 void AnimatedImageVisual::DoSetOnScene(Actor& actor)
642 {
643   mStartFirstFrame      = true;
644   mPlacementActor       = actor;
645   PrepareTextureSet();
646 }
647
648 void AnimatedImageVisual::DoSetOffScene(Actor& actor)
649 {
650   DALI_ASSERT_DEBUG((bool)mImpl->mRenderer && "There should always be a renderer whilst on stage");
651
652   if(mFrameDelayTimer)
653   {
654     mFrameDelayTimer.Stop();
655     mFrameDelayTimer.Reset();
656   }
657
658   actor.RemoveRenderer(mImpl->mRenderer);
659   if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
660   {
661     mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
662     mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
663
664     TextureSet textureSet = TextureSet::New();
665     mImpl->mRenderer.SetTextures(textureSet);
666   }
667
668   mPlacementActor.Reset();
669   mStartFirstFrame = false;
670   mCurrentFrameIndex = FIRST_FRAME_INDEX;
671   mCurrentLoopIndex = FIRST_LOOP;
672 }
673
674 void AnimatedImageVisual::OnSetTransform()
675 {
676   if(mImpl->mRenderer)
677   {
678     mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
679   }
680 }
681
682 void AnimatedImageVisual::UpdateShader()
683 {
684   if(mImpl->mRenderer)
685   {
686     Shader shader = GenerateShader();
687     mImpl->mRenderer.SetShader(shader);
688   }
689 }
690
691 Shader AnimatedImageVisual::GenerateShader() const
692 {
693   bool   defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
694   Shader shader;
695   shader = mImageVisualShaderFactory.GetShader(
696     mFactoryCache,
697     ImageVisualShaderFeature::FeatureBuilder()
698       .ApplyDefaultTextureWrapMode(defaultWrapMode)
699       .EnableRoundedCorner(IsRoundedCornerRequired())
700       .EnableBorderline(IsBorderlineRequired()));
701   return shader;
702 }
703
704 void AnimatedImageVisual::OnInitialize()
705 {
706   CreateImageCache();
707
708   bool   defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
709   Shader shader          = GenerateShader();
710
711   Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
712
713   mImpl->mRenderer = VisualRenderer::New(geometry, shader);
714   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
715
716   // Register transform properties
717   mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
718
719   if(!defaultWrapMode) // custom wrap mode
720   {
721     Vector2 wrapMode(mWrapModeU - WrapMode::CLAMP_TO_EDGE, mWrapModeV - WrapMode::CLAMP_TO_EDGE);
722     wrapMode.Clamp(Vector2::ZERO, Vector2(2.f, 2.f));
723     mImpl->mRenderer.RegisterProperty(WRAP_MODE_UNIFORM_NAME, wrapMode);
724   }
725
726   if(mPixelArea != FULL_TEXTURE_RECT)
727   {
728     mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea);
729   }
730 }
731
732 void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval)
733 {
734   DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::StartFirstFrame()\n");
735
736   mStartFirstFrame = false;
737   if(mImpl->mRenderer)
738   {
739     mImpl->mRenderer.SetTextures(textureSet);
740
741     Actor actor = mPlacementActor.GetHandle();
742     if(actor)
743     {
744       actor.AddRenderer(mImpl->mRenderer);
745       mPlacementActor.Reset();
746     }
747   }
748
749   if(mImpl->mResourceStatus != Toolkit::Visual::ResourceStatus::FAILED)
750   {
751     if(mFrameCount > SINGLE_IMAGE_COUNT)
752     {
753       mFrameDelayTimer = Timer::New(firstInterval);
754       mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame);
755       mFrameDelayTimer.Start();
756     }
757
758     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::READY)\n");
759     ResourceReady(Toolkit::Visual::ResourceStatus::READY);
760   }
761
762   mCurrentFrameIndex = FIRST_FRAME_INDEX;
763 }
764
765 void AnimatedImageVisual::PrepareTextureSet()
766 {
767   TextureSet textureSet;
768   if(mImageCache)
769   {
770     textureSet = mImageCache->FirstFrame();
771   }
772
773   // Check whether synchronous loading is true or false for the first frame.
774   if(textureSet)
775   {
776     SetImageSize(textureSet);
777   }
778 }
779
780 void AnimatedImageVisual::SetImageSize(TextureSet& textureSet)
781 {
782   if(textureSet)
783   {
784     Texture texture = textureSet.GetTexture(0);
785     if(texture)
786     {
787       mImageSize.SetWidth(texture.GetWidth());
788       mImageSize.SetHeight(texture.GetHeight());
789     }
790   }
791 }
792
793 void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval)
794 {
795   // When image visual requested to load new frame to mImageCache and it is failed.
796   if(!mImageCache || !textureSet)
797   {
798     textureSet = SetLoadingFailed();
799   }
800   SetImageSize(textureSet);
801
802   if(mStartFirstFrame)
803   {
804     mFrameCount = mImageCache->GetTotalFrameCount();
805     StartFirstFrame(textureSet, interval);
806   }
807   else
808   {
809     if(mImpl->mRenderer)
810     {
811       if(mFrameDelayTimer && interval > 0u)
812       {
813         mFrameDelayTimer.SetInterval(interval);
814       }
815       mImpl->mRenderer.SetTextures(textureSet);
816     }
817   }
818 }
819
820 bool AnimatedImageVisual::DisplayNextFrame()
821 {
822   TextureSet textureSet;
823   bool       continueTimer = false;
824
825   if(mImageCache)
826   {
827     uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
828
829     if(mIsJumpTo)
830     {
831       mIsJumpTo  = false;
832       frameIndex = mFrameIndexForJumpTo;
833     }
834     else if(mActionStatus == DevelAnimatedImageVisual::Action::PAUSE)
835     {
836       return false;
837     }
838     else if(mActionStatus == DevelAnimatedImageVisual::Action::STOP)
839     {
840       mCurrentLoopIndex = FIRST_LOOP;
841       if(mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME)
842       {
843         frameIndex = FIRST_FRAME_INDEX;
844       }
845       else if(mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME)
846       {
847         frameIndex = mFrameCount - 1;
848       }
849       else
850       {
851         return false; // Do not draw already rendered scene twice.
852       }
853     }
854     else
855     {
856       if(mFrameCount > SINGLE_IMAGE_COUNT)
857       {
858         frameIndex++;
859         if(frameIndex >= mFrameCount)
860         {
861           frameIndex = FIRST_FRAME_INDEX;
862           ++mCurrentLoopIndex;
863         }
864
865         if(mLoopCount >= 0 && mCurrentLoopIndex >= mLoopCount)
866         {
867           // This will stop timer
868           mActionStatus = DevelAnimatedImageVisual::Action::STOP;
869           return DisplayNextFrame();
870         }
871       }
872     }
873
874     DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex);
875
876     textureSet = mImageCache->Frame(frameIndex);
877
878     if(textureSet)
879     {
880       SetImageSize(textureSet);
881       if(mImpl->mRenderer)
882       {
883         mImpl->mRenderer.SetTextures(textureSet);
884       }
885       mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex));
886     }
887
888     mCurrentFrameIndex = frameIndex;
889     continueTimer      = (mActionStatus == DevelAnimatedImageVisual::Action::PLAY && textureSet) ? true : false;
890   }
891
892   return continueTimer;
893 }
894
895 TextureSet AnimatedImageVisual::SetLoadingFailed()
896 {
897   DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n");
898   ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
899
900   Actor   actor     = mPlacementActor.GetHandle();
901   Vector2 imageSize = Vector2::ZERO;
902   if(actor)
903   {
904     imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
905   }
906   mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
907   TextureSet textureSet = mImpl->mRenderer.GetTextures();
908
909   if(mFrameDelayTimer)
910   {
911     mFrameDelayTimer.Stop();
912     mFrameDelayTimer.Reset();
913   }
914
915   SetImageSize(textureSet);
916
917   return textureSet;
918 }
919
920 void AnimatedImageVisual::AllocateMaskData()
921 {
922   if(!mMaskingData)
923   {
924     mMaskingData.reset(new TextureManager::MaskingData());
925   }
926 }
927
928 } // namespace Internal
929
930 } // namespace Toolkit
931
932 } // namespace Dali