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