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