Removed Texture Atlas for the GIF image.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / animated-image / animated-image-visual.cpp
1 /*
2  * Copyright (c) 2017 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/public-api/visuals/image-visual-properties.h>
28 #include <dali-toolkit/public-api/visuals/visual-properties.h>
29 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
30 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
31 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
32 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
33 #include <dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h>
34 #include <dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h>
35 #include <dali-toolkit/internal/visuals/animated-image/rolling-gif-image-cache.h>
36 #include <dali-toolkit/internal/visuals/image/image-visual.h>
37 #include <dali-toolkit/devel-api/image-loader/image-atlas.h>
38 #include <dali-toolkit/devel-api/image-loader/texture-manager.h>
39
40 namespace Dali
41 {
42
43 namespace Toolkit
44 {
45
46 namespace Internal
47 {
48
49 namespace
50 {
51 // wrap modes
52 DALI_ENUM_TO_STRING_TABLE_BEGIN( WRAP_MODE )
53 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, DEFAULT )
54 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, CLAMP_TO_EDGE )
55 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, REPEAT )
56 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, MIRRORED_REPEAT )
57 DALI_ENUM_TO_STRING_TABLE_END( WRAP_MODE )
58
59 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
60
61 #if defined(DEBUG_ENABLED)
62 Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
63 #endif
64 }
65
66
67 /**
68  * Multi-image  Flow of execution
69  *
70  *   | New
71  *   |   DoSetProperties()
72  *   |   LoadFirstBatch()
73  *   |     new cache
74  *   |       cache->LoadBatch()
75  *   |
76  *   | DoSetOnStage()
77  *   |   PrepareTextureSet()
78  *   |     cache->FirstFrame()
79  *   |   CreateRenderer()    (Doesn't become ready until first frame loads)
80  *   |   StartFirstFrame()
81  *   |
82  *   | FrameReady(textureSet)
83  *   |   start first frame:
84  *   |     actor.AddRenderer
85  *   |     start timer
86  *   |   mRenderer.SetTextures(textureSet)
87  *   |
88  *   | Timer ticks
89  *   |   DisplayNextFrame()
90  *   |     if front frame is ready,
91  *   |       mRenderer.SetTextures( front frame's texture )
92  *   |     else
93  *   |       mWaitingForTexture=true
94  *   |     cache->LoadBatch()
95  *   |
96  *   | FrameReady(textureSet)
97  *   |   mRenderer.SetTextures(textureSet)
98  *   V
99  *  Time
100  */
101
102 AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, const VisualUrl& imageUrl, const Property::Map& properties )
103 {
104   AnimatedImageVisualPtr visual( new AnimatedImageVisual( factoryCache ) );
105   visual->InitializeGif( imageUrl );
106   visual->SetProperties( properties );
107
108   if( visual->mFrameCount > 0 )
109   {
110     visual->LoadFirstBatch();
111   }
112
113   return visual;
114 }
115
116 AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, const Property::Array& imageUrls, const Property::Map& properties )
117 {
118   AnimatedImageVisualPtr visual( new AnimatedImageVisual( factoryCache ) );
119   visual->mImageUrls = new ImageCache::UrlList();
120   visual->mImageUrls->reserve( imageUrls.Count() );
121
122   for( unsigned int i=0; i < imageUrls.Count(); ++i)
123   {
124     ImageCache::UrlStore urlStore;
125     urlStore.mTextureId = TextureManager::INVALID_TEXTURE_ID;
126     urlStore.mUrl = imageUrls[i].Get<std::string>();
127     visual->mImageUrls->push_back( urlStore );
128   }
129   visual->mFrameCount = imageUrls.Count();
130   visual->SetProperties( properties );
131
132   if( visual->mFrameCount > 0 )
133   {
134     visual->LoadFirstBatch();
135   }
136
137   return visual;
138 }
139
140 AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, const VisualUrl& imageUrl )
141 {
142   AnimatedImageVisualPtr visual( new AnimatedImageVisual( factoryCache ) );
143   visual->InitializeGif( imageUrl );
144
145   if( visual->mFrameCount > 0 )
146   {
147     visual->LoadFirstBatch();
148   }
149
150   return visual;
151 }
152
153 void AnimatedImageVisual::InitializeGif( const VisualUrl& imageUrl )
154 {
155   mImageUrl = imageUrl;
156   mGifLoading = GifLoading::New( imageUrl.GetUrl() );
157   mFrameCount = mGifLoading->GetImageCount();
158   mGifLoading->LoadFrameDelays( mFrameDelayContainer );
159 }
160
161 AnimatedImageVisual::AnimatedImageVisual( VisualFactoryCache& factoryCache )
162 : Visual::Base( factoryCache ),
163   mFrameDelayTimer(),
164   mPlacementActor(),
165   mPixelArea( FULL_TEXTURE_RECT ),
166   mImageUrl(),
167   mGifLoading( nullptr ),
168   mCurrentFrameIndex( 0 ),
169   mImageUrls( NULL ),
170   mImageCache( NULL ),
171   mCacheSize( 1 ),
172   mBatchSize( 1 ),
173   mFrameDelay( 100 ),
174   mUrlIndex( 0 ),
175   mFrameCount( 0 ),
176   mImageSize(),
177   mWrapModeU( WrapMode::DEFAULT ),
178   mWrapModeV( WrapMode::DEFAULT ),
179   mStartFirstFrame(false)
180 {}
181
182 AnimatedImageVisual::~AnimatedImageVisual()
183 {
184   delete mImageCache;
185   delete mImageUrls;
186 }
187
188 void AnimatedImageVisual::GetNaturalSize( Vector2& naturalSize )
189 {
190   if( mImageSize.GetWidth() == 0 &&  mImageSize.GetHeight() == 0)
191   {
192     if( mImageUrl.IsValid() )
193     {
194       mImageSize = mGifLoading->GetImageSize();
195     }
196     else if( mImageUrls && mImageUrls->size() > 0 )
197     {
198       mImageSize = Dali::GetClosestImageSize( (*mImageUrls)[0].mUrl );
199     }
200   }
201
202   naturalSize.width = mImageSize.GetWidth();
203   naturalSize.height = mImageSize.GetHeight();
204 }
205
206 void AnimatedImageVisual::DoCreatePropertyMap( Property::Map& map ) const
207 {
208   map.Clear();
209
210   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE );
211
212   if( mImageUrl.IsValid() )
213   {
214     map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() );
215   }
216   if( mImageUrls != NULL && ! mImageUrls->empty() )
217   {
218     Property::Array urls;
219     for( unsigned int i=0; i<mImageUrls->size(); ++i)
220     {
221       urls.Add( (*mImageUrls)[i].mUrl );
222     }
223     Property::Value value( const_cast<Property::Array&>(urls) );
224     map.Insert( Toolkit::ImageVisual::Property::URL, value );
225   }
226
227   map.Insert( Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea );
228   map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU );
229   map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV );
230
231   map.Insert( Toolkit::ImageVisual::Property::BATCH_SIZE, static_cast<int>(mBatchSize) );
232   map.Insert( Toolkit::ImageVisual::Property::CACHE_SIZE, static_cast<int>(mCacheSize) );
233   map.Insert( Toolkit::ImageVisual::Property::FRAME_DELAY, static_cast<int>(mFrameDelay) );
234 }
235
236 void AnimatedImageVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
237 {
238   // Do nothing
239 }
240
241 void AnimatedImageVisual::DoSetProperties( const Property::Map& propertyMap )
242 {
243   // url[s] already passed in from constructor
244
245   for( Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter )
246   {
247     KeyValuePair keyValue = propertyMap.GetKeyValue( iter );
248     if( keyValue.first.type == Property::Key::INDEX )
249     {
250       DoSetProperty( keyValue.first.indexKey, keyValue.second );
251     }
252     else
253     {
254       if( keyValue.first == PIXEL_AREA_UNIFORM_NAME )
255       {
256         DoSetProperty( Toolkit::ImageVisual::Property::PIXEL_AREA, keyValue.second );
257       }
258       else if( keyValue.first == IMAGE_WRAP_MODE_U )
259       {
260         DoSetProperty( Toolkit::ImageVisual::Property::WRAP_MODE_U, keyValue.second );
261       }
262       else if( keyValue.first == IMAGE_WRAP_MODE_V )
263       {
264         DoSetProperty( Toolkit::ImageVisual::Property::WRAP_MODE_V, keyValue.second );
265       }
266       else if( keyValue.first == BATCH_SIZE_NAME )
267       {
268         DoSetProperty( Toolkit::ImageVisual::Property::BATCH_SIZE, keyValue.second );
269       }
270       else if( keyValue.first == CACHE_SIZE_NAME )
271       {
272         DoSetProperty( Toolkit::ImageVisual::Property::CACHE_SIZE, keyValue.second );
273       }
274       else if( keyValue.first == FRAME_DELAY_NAME )
275       {
276         DoSetProperty( Toolkit::ImageVisual::Property::FRAME_DELAY, keyValue.second );
277       }
278     }
279   }
280 }
281
282 void AnimatedImageVisual::DoSetProperty( Property::Index index,
283                                          const Property::Value& value )
284 {
285   switch(index)
286   {
287     case Toolkit::ImageVisual::Property::PIXEL_AREA:
288     {
289       value.Get( mPixelArea );
290       break;
291     }
292     case Toolkit::ImageVisual::Property::WRAP_MODE_U:
293     {
294       int wrapMode;
295       if(Scripting::GetEnumerationProperty( value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode ))
296       {
297         mWrapModeU = Dali::WrapMode::Type(wrapMode);
298       }
299       else
300       {
301         mWrapModeU = Dali::WrapMode::Type::DEFAULT;
302       }
303       break;
304     }
305     case Toolkit::ImageVisual::Property::WRAP_MODE_V:
306     {
307       int wrapMode;
308       if(Scripting::GetEnumerationProperty( value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode ))
309       {
310         mWrapModeV = Dali::WrapMode::Type(wrapMode);
311       }
312       else
313       {
314         mWrapModeV = Dali::WrapMode::Type::DEFAULT;
315       }
316       break;
317     }
318
319     case Toolkit::ImageVisual::Property::BATCH_SIZE:
320     {
321       int batchSize;
322       if( value.Get( batchSize ) )
323       {
324         mBatchSize = batchSize;
325       }
326       break;
327     }
328
329     case Toolkit::ImageVisual::Property::CACHE_SIZE:
330     {
331       int cacheSize;
332       if( value.Get( cacheSize ) )
333       {
334         mCacheSize = cacheSize;
335       }
336       break;
337     }
338
339     case Toolkit::ImageVisual::Property::FRAME_DELAY:
340     {
341       int frameDelay;
342       if( value.Get( frameDelay ) )
343       {
344         mFrameDelay = frameDelay;
345       }
346       break;
347     }
348   }
349 }
350
351 void AnimatedImageVisual::DoSetOnStage( Actor& actor )
352 {
353   mPlacementActor = actor;
354   TextureSet textureSet = PrepareTextureSet();
355   CreateRenderer(); // Always create a renderer when on stage
356
357   if( textureSet ) // if the image loading is successful
358   {
359     StartFirstFrame( textureSet );
360   }
361   else
362   {
363     mStartFirstFrame = true;
364   }
365 }
366
367 void AnimatedImageVisual::DoSetOffStage( Actor& actor )
368 {
369   DALI_ASSERT_DEBUG( (bool)mImpl->mRenderer && "There should always be a renderer whilst on stage");
370
371   if( mFrameDelayTimer )
372   {
373     mFrameDelayTimer.Stop();
374     mFrameDelayTimer.Reset();
375   }
376
377   actor.RemoveRenderer( mImpl->mRenderer );
378   mImpl->mRenderer.Reset();
379   mPlacementActor.Reset();
380 }
381
382 void AnimatedImageVisual::OnSetTransform()
383 {
384   if( mImpl->mRenderer )
385   {
386     mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
387   }
388 }
389
390 void AnimatedImageVisual::CreateRenderer()
391 {
392   bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
393   bool atlasing = false;
394   Shader shader = ImageVisual::GetImageShader( mFactoryCache, atlasing, defaultWrapMode );
395
396   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
397
398   mImpl->mRenderer = Renderer::New( geometry, shader );
399
400   // Register transform properties
401   mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
402
403   if( !defaultWrapMode ) // custom wrap mode
404   {
405     Vector2 wrapMode(mWrapModeU-WrapMode::CLAMP_TO_EDGE, mWrapModeV-WrapMode::CLAMP_TO_EDGE);
406     wrapMode.Clamp( Vector2::ZERO, Vector2( 2.f, 2.f ) );
407     mImpl->mRenderer.RegisterProperty( WRAP_MODE_UNIFORM_NAME, wrapMode );
408   }
409
410   if( mPixelArea != FULL_TEXTURE_RECT )
411   {
412     mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
413   }
414
415   mCurrentFrameIndex = 0;
416 }
417
418 void AnimatedImageVisual::LoadFirstBatch()
419 {
420   // Ensure the batch size and cache size are no bigger than the number of URLs,
421   // and that the cache is at least as big as the batch size.
422   uint16_t numUrls = 0;
423   uint16_t batchSize = 1;
424   uint16_t cacheSize = 1;
425
426   if( mImageUrls )
427   {
428     numUrls = mImageUrls->size();
429   }
430   else
431   {
432     numUrls = mFrameCount;
433   }
434
435   batchSize = std::min( mBatchSize, numUrls );
436   cacheSize = std::min( std::max( batchSize, mCacheSize ), numUrls );
437
438   DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::LoadFirstBatch()  batchSize:%d  cacheSize:%d\n", batchSize, cacheSize);
439
440   mUrlIndex = 0;
441   TextureManager& textureManager = mFactoryCache.GetTextureManager();
442
443   if( mGifLoading != nullptr )
444   {
445     mImageCache = new RollingGifImageCache( textureManager, *mGifLoading, mFrameCount, *this, cacheSize, batchSize );
446   }
447   else if( batchSize > 0 && cacheSize > 0 )
448   {
449     if( cacheSize < numUrls )
450     {
451       mImageCache = new RollingImageCache( textureManager, *mImageUrls, *this, cacheSize, batchSize );
452     }
453     else
454     {
455       mImageCache = new FixedImageCache( textureManager, *mImageUrls, *this, batchSize );
456     }
457   }
458   else
459   {
460     mImageCache = new RollingImageCache( textureManager, *mImageUrls, *this, 1, 1 );
461   }
462   if (!mImageCache)
463   {
464     DALI_LOG_ERROR("mImageCache is null");
465   }
466 }
467
468 void AnimatedImageVisual::StartFirstFrame( TextureSet& textureSet )
469 {
470   DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::StartFirstFrame()\n");
471
472   mStartFirstFrame = false;
473   mImpl->mRenderer.SetTextures( textureSet );
474   Actor actor = mPlacementActor.GetHandle();
475   if( actor )
476   {
477     actor.AddRenderer( mImpl->mRenderer );
478     mPlacementActor.Reset();
479   }
480
481   mCurrentFrameIndex = 0;
482
483   if( mFrameCount > 1 )
484   {
485     int frameDelay = mFrameDelay; // from URL array
486     if( mFrameDelayContainer.Count() > 0 ) // from GIF
487     {
488       frameDelay = mFrameDelayContainer[0];
489     }
490
491     mFrameDelayTimer = Timer::New( frameDelay );
492     mFrameDelayTimer.TickSignal().Connect( this, &AnimatedImageVisual::DisplayNextFrame );
493     mFrameDelayTimer.Start();
494   }
495   DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"ResourceReady(ResourceStatus::READY)\n");
496   ResourceReady( Toolkit::Visual::ResourceStatus::READY );
497 }
498
499 TextureSet AnimatedImageVisual::PrepareTextureSet()
500 {
501   TextureSet textureSet;
502   if (mImageCache)
503     textureSet = mImageCache->FirstFrame();
504   if( textureSet )
505   {
506     SetImageSize( textureSet );
507   }
508   else
509   {
510     DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"ResourceReady(ResourceStatus::FAILED)\n");
511     ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
512   }
513
514   return textureSet;
515 }
516
517 void AnimatedImageVisual::SetImageSize( TextureSet& textureSet )
518 {
519   if( textureSet )
520   {
521     Texture texture = textureSet.GetTexture( 0 );
522     if( texture )
523     {
524       mImageSize.SetWidth( texture.GetWidth() );
525       mImageSize.SetHeight( texture.GetHeight() );
526     }
527   }
528 }
529
530 void AnimatedImageVisual::FrameReady( TextureSet textureSet )
531 {
532   SetImageSize( textureSet );
533
534   if( mStartFirstFrame )
535   {
536     StartFirstFrame( textureSet );
537   }
538   else
539   {
540     mImpl->mRenderer.SetTextures( textureSet );
541   }
542 }
543
544 bool AnimatedImageVisual::DisplayNextFrame()
545 {
546   if( mFrameCount > 1 )
547   {
548     // Wrap the frame index
549     ++mCurrentFrameIndex;
550     mCurrentFrameIndex %= mFrameCount;
551   }
552   DALI_LOG_INFO( gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::DisplayNextFrame(this:%p) FrameCount:%d\n", this, mCurrentFrameIndex);
553
554   if( mFrameDelayContainer.Count() > 0 )
555   {
556     unsigned int delay = mFrameDelayContainer[mCurrentFrameIndex];
557
558     if( mFrameDelayTimer.GetInterval() != delay )
559     {
560       mFrameDelayTimer.SetInterval( delay );
561     }
562   }
563
564   TextureSet textureSet;
565   if (mImageCache)
566     textureSet = mImageCache->NextFrame();
567   if( textureSet )
568   {
569     SetImageSize( textureSet );
570     mImpl->mRenderer.SetTextures( textureSet );
571   }
572
573   // Keep timer ticking
574   return true;
575 }
576
577
578 } // namespace Internal
579
580 } // namespace Toolkit
581
582 } // namespace Dali