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