Merge "(Vector) Ensure not to add duplicated task" into devel/master
authorHeeyong Song <heeyong.song@samsung.com>
Mon, 13 Jul 2020 06:19:45 +0000 (06:19 +0000)
committerGerrit Code Review <gerrit@review>
Mon, 13 Jul 2020 06:19:45 +0000 (06:19 +0000)
24 files changed:
automated-tests/src/dali-toolkit/utc-Dali-AnimatedImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-ScrollBar.cpp
automated-tests/src/dali-toolkit/utc-Dali-VisualFactory.cpp
dali-toolkit/devel-api/image-loader/async-image-loader-devel.cpp
dali-toolkit/devel-api/image-loader/async-image-loader-devel.h
dali-toolkit/devel-api/text/text-utils-devel.cpp
dali-toolkit/internal/controls/scroll-bar/scroll-bar-impl.cpp
dali-toolkit/internal/controls/scroll-bar/scroll-bar-impl.h
dali-toolkit/internal/image-loader/async-image-loader-impl.cpp
dali-toolkit/internal/image-loader/async-image-loader-impl.h
dali-toolkit/internal/image-loader/image-load-thread.cpp
dali-toolkit/internal/image-loader/image-load-thread.h
dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp
dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.cpp
dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h
dali-toolkit/internal/visuals/image/image-visual.cpp
dali-toolkit/internal/visuals/npatch/npatch-visual.cpp
dali-toolkit/internal/visuals/texture-manager-impl.cpp
dali-toolkit/internal/visuals/texture-manager-impl.h
dali-toolkit/public-api/dali-toolkit-version.cpp
dali-toolkit/public-api/visuals/image-visual-properties.h
packaging/dali-toolkit.spec

index bad5ea0..59be4f2 100644 (file)
@@ -158,6 +158,105 @@ int UtcDaliAnimatedImageVisualGetPropertyMap02(void)
   END_TEST;
 }
 
+int UtcDaliAnimatedImageVisualGetPropertyMap03(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliAnimatedImageVisualGetPropertyMap" );
+
+  // request AnimatedImageVisual with a property map
+  VisualFactory factory = VisualFactory::Get();
+  Visual::Base animatedImageVisual = factory.CreateVisual(
+    Property::Map()
+    .Add( Toolkit::Visual::Property::TYPE, Visual::ANIMATED_IMAGE )
+    .Add( ImageVisual::Property::URL, TEST_GIF_FILE_NAME )
+    .Add( ImageVisual::Property::BATCH_SIZE, 1 )
+    .Add( ImageVisual::Property::CACHE_SIZE, 1 )
+    .Add( ImageVisual::Property::SYNCHRONOUS_LOADING, false ));
+
+  Property::Map resultMap;
+  animatedImageVisual.CreatePropertyMap( resultMap );
+
+  // check the property values from the returned map from a visual
+  Property::Value* value = resultMap.Find( Toolkit::Visual::Property::TYPE,  Property::INTEGER );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get<int>() == Visual::ANIMATED_IMAGE );
+
+  value = resultMap.Find( ImageVisual::Property::URL,  Property::STRING );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get<std::string>() == TEST_GIF_FILE_NAME );
+
+  value = resultMap.Find( ImageVisual::Property::BATCH_SIZE,  Property::INTEGER );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get<int>() == 2 );
+
+  value = resultMap.Find( ImageVisual::Property::CACHE_SIZE,  Property::INTEGER );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get<int>() == 2 );
+
+  END_TEST;
+}
+
+
+int UtcDaliAnimatedImageVisualSynchronousLoading(void)
+{
+  ToolkitTestApplication application;
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+
+  {
+    Property::Map propertyMap;
+    propertyMap.Insert(Visual::Property::TYPE, Visual::ANIMATED_IMAGE );
+    propertyMap.Insert(ImageVisual::Property::URL, TEST_GIF_FILE_NAME );
+    propertyMap.Insert( ImageVisual::Property::BATCH_SIZE, 2);
+    propertyMap.Insert( ImageVisual::Property::CACHE_SIZE, 2);
+    propertyMap.Insert( ImageVisual::Property::FRAME_DELAY, 20);
+    propertyMap.Insert( ImageVisual::Property::SYNCHRONOUS_LOADING, true);
+
+    VisualFactory factory = VisualFactory::Get();
+    Visual::Base visual = factory.CreateVisual( propertyMap );
+
+    DummyControl dummyControl = DummyControl::New(true);
+    Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+    dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
+
+    dummyControl.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
+    Stage::GetCurrent().Add( dummyControl );
+
+    TraceCallStack& textureTrace = gl.GetTextureTrace();
+    textureTrace.Enable(true);
+
+    application.SendNotification();
+    application.Render(20);
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render();
+
+    DALI_TEST_EQUALS( Test::GetTimerCount(), 1, TEST_LOCATION );
+    DALI_TEST_EQUALS( gl.GetNumGeneratedTextures(), 2, TEST_LOCATION );
+
+    DevelControl::DoAction( dummyControl, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedImageVisual::Action::JUMP_TO, 3 );
+
+    application.SendNotification();
+    application.Render(20);
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render();
+
+    DALI_TEST_EQUALS( gl.GetNumGeneratedTextures(), 3, TEST_LOCATION );
+
+    dummyControl.Unparent();
+  }
+  tet_infoline("Test that removing the visual from stage deletes all textures");
+  application.SendNotification();
+  application.Render(16);
+  DALI_TEST_EQUALS( gl.GetNumGeneratedTextures(), 0, TEST_LOCATION );
+
+  END_TEST;
+}
+
 
 int UtcDaliAnimatedImageVisualJumpToAction(void)
 {
@@ -318,6 +417,12 @@ int UtcDaliAnimatedImageVisualAnimatedImage01(void)
 
     dummyControl.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
     Stage::GetCurrent().Add( dummyControl );
+
+    application.SendNotification();
+    application.Render();
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
     application.SendNotification();
     application.Render(20);
 
@@ -331,6 +436,11 @@ int UtcDaliAnimatedImageVisualAnimatedImage01(void)
     Test::EmitGlobalTimerSignal();
 
     application.SendNotification();
+    application.Render();
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+    application.SendNotification();
     application.Render(20);
 
     DALI_TEST_EQUALS( gl.GetLastGenTextureId(), 4, TEST_LOCATION );
@@ -452,8 +562,8 @@ int UtcDaliAnimatedImageVisualMultiImage02(void)
     Property::Map propertyMap;
     propertyMap.Insert(Visual::Property::TYPE, Visual::IMAGE );
     propertyMap.Insert( ImageVisual::Property::URL, Property::Value(urls) );
-    propertyMap.Insert( ImageVisual::Property::BATCH_SIZE, 0);
-    propertyMap.Insert( ImageVisual::Property::CACHE_SIZE, 0);
+    propertyMap.Insert( ImageVisual::Property::BATCH_SIZE, 2);
+    propertyMap.Insert( ImageVisual::Property::CACHE_SIZE, 2);
     propertyMap.Insert( ImageVisual::Property::FRAME_DELAY, 100);
 
     VisualFactory factory = VisualFactory::Get();
@@ -749,9 +859,15 @@ void TestLoopCount( ToolkitTestApplication &application, DummyControl &dummyCont
 
   textureTrace.Enable(true);
   Stage::GetCurrent().Add( dummyControl );
+
   application.SendNotification();
   application.Render(16);
 
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_INNER_LOCATION( location ) );
+
+  application.SendNotification();
+  application.Render();
+
   tet_infoline( "Test that a timer has been created" );
   DALI_TEST_EQUALS( Test::GetTimerCount(), 1, TEST_INNER_LOCATION( location ) );
 
@@ -767,10 +883,15 @@ void TestLoopCount( ToolkitTestApplication &application, DummyControl &dummyCont
       Test::EmitGlobalTimerSignal();
       application.SendNotification();
       application.Render(16);
-      DALI_TEST_EQUALS( gl.GetNumGeneratedTextures(), 1, TEST_INNER_LOCATION( location ) );
+
+      DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_INNER_LOCATION( location ) );
+
+      application.SendNotification();
+      application.Render();
+      DALI_TEST_EQUALS( gl.GetNumGeneratedTextures(), 2, TEST_INNER_LOCATION( location ) );
       DALI_TEST_EQUALS( Test::AreTimersRunning(), true, TEST_INNER_LOCATION( location ) );
     }
-    tet_printf( "\nTest Loop %u \n", i );
+    tet_printf( "Test Loop %u \n\n", i + 1u );
   }
 
   tet_printf( "Test that after %u loops, and we have no frame. Timer should stop \n", loopCount );
@@ -808,6 +929,12 @@ int UtcDaliAnimatedImageVisualLoopCount(void)
 
     TestLoopCount( application, dummyControl, 4, 0, TEST_LOCATION );
 
+    dummyImpl.UnregisterVisual( DummyControl::Property::TEST_VISUAL );
+    animatedImageVisual.Reset();
+
+    application.SendNotification();
+    application.Render(16);
+
     // Test with no (1) loop count. Request AnimatedImageVisual with a property map
     animatedImageVisual = factory.CreateVisual(
       Property::Map()
@@ -822,6 +949,12 @@ int UtcDaliAnimatedImageVisualLoopCount(void)
 
     TestLoopCount( application, dummyControl, 4, 1, TEST_LOCATION );
 
+    dummyImpl.UnregisterVisual( DummyControl::Property::TEST_VISUAL );
+    animatedImageVisual.Reset();
+
+    application.SendNotification();
+    application.Render(16);
+
     // Test with no (100) loop count. Request AnimatedImageVisual with a property map
     animatedImageVisual = factory.CreateVisual(
       Property::Map()
@@ -836,7 +969,6 @@ int UtcDaliAnimatedImageVisualLoopCount(void)
 
     TestLoopCount( application, dummyControl, 4, 100, TEST_LOCATION );
   }
-
   END_TEST;
 }
 
@@ -871,12 +1003,22 @@ int UtcDaliAnimatedImageVisualPlayback(void)
     application.SendNotification();
     application.Render(16);
 
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render();
+
     tet_infoline( "Test that a timer has been created" );
     DALI_TEST_EQUALS( Test::GetTimerCount(), 1, TEST_LOCATION );
 
     Test::EmitGlobalTimerSignal();
     application.SendNotification();
     application.Render(16);
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render();
     DALI_TEST_EQUALS( Test::AreTimersRunning(), true, TEST_LOCATION );
 
     Property::Map attributes;
@@ -892,6 +1034,11 @@ int UtcDaliAnimatedImageVisualPlayback(void)
     Test::EmitGlobalTimerSignal();
     application.SendNotification();
     application.Render(16);
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render();
     DALI_TEST_EQUALS( Test::AreTimersRunning(), true, TEST_LOCATION );
 
     tet_infoline( "Test Stop action. Timer should stop after Stop action" );
@@ -906,6 +1053,11 @@ int UtcDaliAnimatedImageVisualPlayback(void)
     Test::EmitGlobalTimerSignal();
     application.SendNotification();
     application.Render(16);
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render();
     DALI_TEST_EQUALS( Test::AreTimersRunning(), true, TEST_LOCATION );
 
     dummyControl.Unparent();
index d7aa177..7ece9d3 100644 (file)
@@ -424,6 +424,11 @@ int UtcDaliImageViewPixelArea(void)
   // loading started
   application.SendNotification();
   application.Render(16);
+
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
   DALI_TEST_CHECK( gifView.GetRendererCount() == 1u );
 
   const Vector4 fullTextureRect( 0.f, 0.f, 1.f, 1.f );
@@ -861,6 +866,11 @@ int UtcDaliImageViewCheckResourceReady(void)
   application.SendNotification();
   application.Render(16);
 
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
   DALI_TEST_EQUALS( imageView.IsResourceReady(), true, TEST_LOCATION );
 
   DALI_TEST_EQUALS( gResourceReadySignalFired, true, TEST_LOCATION );
index 78792d9..d34f665 100644 (file)
@@ -1098,7 +1098,7 @@ int UtcDaliImageVisualTextureCancelRemoteLoad(void)
 int UtcDaliImageVisualTextureCancelAsyncLoad(void)
 {
   ToolkitTestApplication application;
-  tet_infoline( "Load image asynchronosly, cancel loading, then load again" );
+  tet_infoline( "Load image asynchronously, cancel loading, then load again" );
 
   VisualFactory factory = VisualFactory::Get();
   DALI_TEST_CHECK( factory );
@@ -1861,6 +1861,87 @@ int UtcDaliImageVisualReleasePolicy07(void)
   END_TEST;
 }
 
+int UtcDaliImageVisualReleasePolicy08(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliImageVisualReleasePolicy08 Ensure TextureSet is same after detach/attach on stage when texture used the DESTROYED release policy" );
+
+  tet_infoline( "Create first visual with DESTROYED release policy" );
+  Visual::Base imageVisualDestroyed = CreateVisualWithPolicy( TEST_IMAGE_FILE_NAME, ImageVisual::Property::RELEASE_POLICY, ImageVisual::ReleasePolicy::DESTROYED );
+
+  // Set up trace debug
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+  TraceCallStack& textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  tet_infoline( "Register visuals with control and ensure it has the only handles" );
+  DummyControl actor = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, imageVisualDestroyed );
+  imageVisualDestroyed.Reset(); // reduce ref count so only the control keeps the visual alive.
+
+  actor.SetProperty( Actor::Property::SIZE, Vector2(200.f, 200.f) );
+
+  // Test initially zero renderers
+  application.SendNotification();
+  application.Render(0);
+  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+  DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), false, TEST_LOCATION );
+  textureTrace.Reset();
+
+  Stage::GetCurrent().Add( actor );
+
+  // Wait for image to load
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render(0);
+  tet_infoline( "Ensure a texture is created" );
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION );
+  textureTrace.Reset();
+
+  // Ensure TextureSet is same after detach/attach on stage when texture used the DESTROYED release policy
+  // 1. Get TextureSet
+  TextureSet textureSetBefore = actor.GetRendererAt( 0u ).GetTextures();
+
+  // 2.Remove actor from stage. In this case, renderer also is deleted.
+  tet_infoline( "Remove actor from stage" );
+  Stage::GetCurrent().Remove( actor );
+  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+  application.SendNotification();
+  application.Render();
+
+  tet_infoline( "Ensure a texture is not deleted as visual used the DESTROYED release policy" );
+  DALI_TEST_EQUALS( textureTrace.CountMethod("DeleteTextures"), 0, TEST_LOCATION );
+  textureTrace.Reset();
+
+  // 3.Add actor in stage. In this case, renderer is created.
+  tet_infoline( "Add actor in stage" );
+  Stage::GetCurrent().Add( actor );
+  DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+  application.SendNotification();
+  application.Render();
+  tet_infoline( "Ensure a texture is not created again" );
+  DALI_TEST_EQUALS( textureTrace.CountMethod("GenTextures"), 0, TEST_LOCATION );
+  textureTrace.Reset();
+
+  // 4.Compare Texture with before and after. textureSet need to be same because release policy is the DESTROYED.
+  tet_infoline( "Ensure a textureSet is not deleted because it is used the DESTROYED release policy" );
+  TextureSet textureSetAfter = actor.GetRendererAt( 0u ).GetTextures();
+  DALI_TEST_CHECK( textureSetBefore == textureSetAfter );
+  textureSetBefore.Reset();
+  textureSetAfter.Reset();
+
+  dummyImpl.UnregisterVisual( DummyControl::Property::TEST_VISUAL );
+  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS( textureTrace.CountMethod("DeleteTextures"), 1, TEST_LOCATION );
+
+  END_TEST;
+}
+
 int UtcDaliImageVisualLoadPolicy01(void)
 {
   ToolkitTestApplication application;
index 1d32e33..609621a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 #include <string>
 #include <dali-toolkit-test-suite-utils.h>
+#include "dali-toolkit-test-utils/toolkit-timer.h"
 #include <dali-toolkit/dali-toolkit.h>
 #include <dali/devel-api/actors/actor-devel.h>
 #include <dali-toolkit/devel-api/controls/scroll-bar/scroll-bar.h>
@@ -116,6 +117,8 @@ static Vector2 PerformGestureSwipe(ToolkitTestApplication& application, Vector2
 
   for(int i = 0; i < frames; i++)
   {
+    Test::EmitGlobalTimerSignal();
+
     pos += direction; // Move in this direction
     time += RENDER_FRAME_INTERVAL;
     TestMovePan( application, pos, time);
index c78d090..bad210c 100644 (file)
@@ -2048,6 +2048,11 @@ int UtcDaliVisualFactoryGetAnimatedImageVisual1(void)
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
   // renderer is added to actor
   DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
   Renderer renderer = actor.GetRendererAt( 0u );
@@ -2061,14 +2066,23 @@ int UtcDaliVisualFactoryGetAnimatedImageVisual1(void)
   timer.MockEmitSignal();
   application.SendNotification();
   application.Render();
+
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
   DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION );
   textureTrace.Reset();
 
-
   // Force the timer used by the animatedImageVisual to tick,
   timer.MockEmitSignal();
   application.SendNotification();
   application.Render();
+
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
   DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION );
   textureTrace.Reset();
 
@@ -2076,6 +2090,11 @@ int UtcDaliVisualFactoryGetAnimatedImageVisual1(void)
   timer.MockEmitSignal();
   application.SendNotification();
   application.Render();
+
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
   DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION );
   textureTrace.Reset();
 
@@ -2117,6 +2136,11 @@ int UtcDaliVisualFactoryGetAnimatedImageVisual2(void)
   application.SendNotification();
   application.Render();
 
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
   DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
 
   DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
index 5fc985f..e9b7744 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,6 +24,13 @@ namespace Toolkit
 namespace DevelAsyncImageLoader
 {
 
+uint32_t LoadAnimatedImage( AsyncImageLoader asyncImageLoader,
+                            Dali::AnimatedImageLoading animatedImageLoading,
+                            uint32_t frameIndex )
+{
+  return GetImplementation( asyncImageLoader ).LoadAnimatedImage( animatedImageLoading, frameIndex );
+}
+
 uint32_t Load( AsyncImageLoader asyncImageLoader,
                const std::string& url,
                ImageDimensions dimensions,
index 99ca154..a6732df 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_DEVEL_API_IMAGE_LOADER_ASYNC_IMAGE_LOADER_DEVEL_H
 
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
+// EXTERNAL HEADER
 #include <dali/public-api/signals/dali-signal.h>
 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/devel-api/adaptor-framework/animated-image-loading.h>
+
+// INTERNAL HEADER
 #include <dali-toolkit/public-api/image-loader/async-image-loader.h>
 
 namespace Dali
@@ -40,6 +44,19 @@ enum class PreMultiplyOnLoad
 };
 
 /**
+ * @brief Starts an animated image loading task.
+ * @REMARK_INTERNET
+ * @REMARK_STORAGE
+ * @param[in] asyncImageLoader The ayncImageLoader
+ * @param[in] animatedImageLoading The AnimatedImageLoading to load animated image
+ * @param[in] frameIndex The frame index of a frame to be loaded frame
+ * @return The loading task id
+ */
+DALI_TOOLKIT_API uint32_t LoadAnimatedImage( AsyncImageLoader asyncImageLoader,
+                                             Dali::AnimatedImageLoading animatedImageLoading,
+                                             uint32_t frameIndex );
+
+/**
  * @brief Starts an image loading task.
  * @REMARK_INTERNET
  * @REMARK_STORAGE
index fc8afb9..ccea7e0 100755 (executable)
@@ -104,6 +104,36 @@ DALI_ENUM_TO_STRING_WITH_SCOPE( DevelText::CircularAlignment, CENTER )
 DALI_ENUM_TO_STRING_WITH_SCOPE( DevelText::CircularAlignment, END )
 DALI_ENUM_TO_STRING_TABLE_END( CIRCULAR_ALIGNMENT_TYPE )
 
+struct InternalDataModel
+{
+  InternalDataModel( FontClient& fontClient,
+                     MetricsPtr metrics,
+                     Text::ModelPtr textModel )
+  : fontClient( fontClient ),
+    metrics( metrics ),
+    textModel( textModel ),
+    numberOfCharacters{ 0u },
+    isTextMirrored{ false },
+    numberOfGlyphs{ 0u }
+  {
+      layoutEngine.SetMetrics( metrics );
+  }
+
+  FontClient& fontClient;
+  MetricsPtr metrics;
+  Text::Layout::Engine layoutEngine;             ///< The layout engine.
+  Text::ModelPtr textModel;                      ///< Pointer to the text's model.
+  Vector<ColorBlendingMode> blendingMode;        ///< How embedded items and bitmap font glyphs are blended with color text.
+  Vector<bool> isEmoji;                          ///< Whether the glyph is an emoji.
+
+  Vector<Character> mirroredUtf32Characters;               // The utf32Characters Characters but mirrored if there are RTL text.
+
+  Length numberOfCharacters;                               // The number of characters (not glyphs!).
+  bool isTextMirrored ;                                    // Whether the text has been mirrored.
+
+  Length numberOfGlyphs;
+};
+
 bool GetLayoutEnumeration(const Property::Value& propertyValue, DevelText::Layout::Type& layout)
 {
   return Scripting::GetEnumerationProperty(propertyValue, LAYOUT_TYPE_TABLE, LAYOUT_TYPE_TABLE_COUNT, layout);
@@ -114,80 +144,38 @@ bool GetCircularAlignmentEnumeration(const Property::Value& propertyValue, Devel
   return Scripting::GetEnumerationProperty(propertyValue, CIRCULAR_ALIGNMENT_TYPE_TABLE, CIRCULAR_ALIGNMENT_TYPE_TABLE_COUNT, circularAlignment);
 }
 
-Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout )
-{
-  if( textParameters.text.empty() )
-  {
-    Dali::Devel::PixelBuffer pixelBuffer = Dali::Devel::PixelBuffer::New( textParameters.textWidth,
-                                                                          textParameters.textHeight,
-                                                                          Dali::Pixel::RGBA8888 );
-
-    const unsigned int bufferSize = textParameters.textWidth * textParameters.textHeight * Dali::Pixel::GetBytesPerPixel(Dali::Pixel::RGBA8888);
-    unsigned char* buffer = pixelBuffer.GetBuffer();
-    memset(buffer, 0, bufferSize);
 
-    return pixelBuffer;
-  }
+void ShapeTextPreprocess( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters, InternalDataModel& internalDataModel )
+{
 
   MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
-  FontClient fontClient = FontClient::Get();
-  MetricsPtr metrics;
-  Text::Layout::Engine layoutEngine;             ///< The layout engine.
-  Text::ModelPtr textModel = Text::Model::New(); ///< Pointer to the text's model.
-  Vector<ColorBlendingMode> blendingMode;        ///< How embedded items and bitmap font glyphs are blended with color text.
-  Vector<bool> isEmoji;                          ///< Whether the glyph is an emoji.
-
-  // Use this to access FontClient i.e. to get down-scaled Emoji metrics.
-  metrics = Metrics::New( fontClient );
-  layoutEngine.SetMetrics( metrics );
-
-  TextAbstraction::TextRenderer::Parameters rendererParameters( textModel->mVisualModel->mGlyphs,
-                                                                textModel->mVisualModel->mGlyphPositions,
-                                                                textModel->mVisualModel->mColors,
-                                                                textModel->mVisualModel->mColorIndices,
-                                                                blendingMode,
-                                                                isEmoji );
+  const uint8_t* utf8 = NULL;                              // pointer to the first character of the text (encoded in utf8)
+  Length textSize = 0u;                                    // The length of the utf8 string.
 
-  rendererParameters.width = textParameters.textWidth;
-  rendererParameters.height = textParameters.textHeight;
-  rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888; // @note: At the moment all textures are generated RGBA8888
+  Length& numberOfCharacters = internalDataModel.numberOfCharacters;
+  Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
+  Text::ModelPtr& textModel = internalDataModel.textModel;
 
   Vector<Character>& utf32Characters = textModel->mLogicalModel->mText;                                             // Characters encoded in utf32.
-  Vector<Character> mirroredUtf32Characters;                                                                        // The utf32Characters Characters but mirrored if there are RTL text.
   Vector<LineBreakInfo>& lineBreakInfo = textModel->mLogicalModel->mLineBreakInfo;                                  // The line break info.
   Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns;                                               // Charactes's script.
   Vector<FontDescriptionRun>& fontDescriptionRuns = textModel->mLogicalModel->mFontDescriptionRuns;                 // Desired font descriptions.
   Vector<FontRun>& validFonts = textModel->mLogicalModel->mFontRuns;                                                // Validated fonts.
   Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = textModel->mLogicalModel->mBidirectionalParagraphInfo; // The bidirectional info per paragraph.
-  //Vector<BidirectionalLineInfoRun>& bidirectionalLineInfo = textModel->mLogicalModel->mBidirectionalLineInfo;     // The bidirectional info per line.
   Vector<CharacterDirection>& directions = textModel->mLogicalModel->mCharacterDirections;                          // Character's directions.
   Vector<ColorRun>& colorRuns = textModel->mLogicalModel->mColorRuns;                                               // colors of the text.
 
-  Vector<CharacterIndex>& glyphsToCharacters = textModel->mVisualModel->mGlyphsToCharacters;                        // Glyphs to character map.
-  Vector<GlyphIndex>& charactersToGlyph = textModel->mVisualModel->mCharactersToGlyph;                              // Characters to glyphs map.
-  Vector<Length>& charactersPerGlyph = textModel->mVisualModel->mCharactersPerGlyph;                                // Number of characters per glyph.
-  Vector<Length>& glyphsPerCharacter = textModel->mVisualModel->mGlyphsPerCharacter;                                // The number of glyphs that are shaped.
-  Vector<LineRun>& lines = textModel->mVisualModel->mLines;                                                         // The laid out lines.
-
-  Vector<GlyphIndex> newParagraphGlyphs;                   // Glyphs for the new paragraph characters.
-
   // the default font's description.
   FontDescription defaultFontDescription;
   PointSize26Dot6 defaultPointSize = FontClient::DEFAULT_POINT_SIZE;
 
-  Length numberOfCharacters = 0u;                          // The number of characters (not glyphs!).
-  bool isTextMirrored = false;                             // Whether the text has been mirrored.
-
-  const uint8_t* utf8 = NULL;                              // pointer to the first character of the text (encoded in utf8)
-  Length textSize = 0u;                                    // The length of the utf8 string.
-
   ////////////////////////////////////////////////////////////////////////////////
   // Process the markup string if the mark-up processor is enabled.
   ////////////////////////////////////////////////////////////////////////////////
 
   MarkupProcessData markupProcessData( colorRuns,
-                                       fontDescriptionRuns,
-                                       textModel->mLogicalModel->mEmbeddedItems );
+                                      fontDescriptionRuns,
+                                      textModel->mLogicalModel->mEmbeddedItems );
 
   if (textParameters.markupEnabled)
   {
@@ -232,9 +220,9 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
   ////////////////////////////////////////////////////////////////////////////////
 
   multilanguageSupport.SetScripts( utf32Characters,
-                                   0u,
-                                   numberOfCharacters,
-                                   scripts );
+                                  0u,
+                                  numberOfCharacters,
+                                  scripts );
 
   // Check if there are emojis.
   // If there are an RGBA8888 pixel format is needed.
@@ -330,13 +318,30 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
     // This paragraph has right to left text. Some characters may need to be mirrored.
     // TODO: consider if the mirrored string can be stored as well.
 
-    isTextMirrored = GetMirroredText( utf32Characters,
+    internalDataModel.isTextMirrored = GetMirroredText( utf32Characters,
                                       directions,
                                       bidirectionalInfo,
                                       0u,
                                       numberOfCharacters,
                                       mirroredUtf32Characters );
   }
+}
+
+void ShapeText( TextAbstraction::TextRenderer::Parameters& rendererParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel )
+{
+  Vector<GlyphIndex> newParagraphGlyphs;                   // Glyphs for the new paragraph characters.
+  const Length numberOfCharacters = internalDataModel.numberOfCharacters;
+  const bool isTextMirrored = internalDataModel.isTextMirrored;
+  Text::ModelPtr& textModel = internalDataModel.textModel;
+  const Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
+  FontClient& fontClient = internalDataModel.fontClient;
+  const Vector<Character>& utf32Characters = textModel->mLogicalModel->mText;                                             // Characters encoded in utf32.
+  const Vector<LineBreakInfo>& lineBreakInfo = textModel->mLogicalModel->mLineBreakInfo;                                  // The line break info.
+  const Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns;                                               // Charactes's script.
+  const Vector<FontRun>& validFonts = textModel->mLogicalModel->mFontRuns;                                                // Validated fonts.
+
+  Vector<CharacterIndex>& glyphsToCharacters = textModel->mVisualModel->mGlyphsToCharacters;                        // Glyphs to character map.
+  Vector<Length>& charactersPerGlyph = textModel->mVisualModel->mCharactersPerGlyph;                                // Number of characters per glyph.
 
   ////////////////////////////////////////////////////////////////////////////////
   // Retrieve the glyphs. Text shaping
@@ -363,7 +368,7 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
   textModel->mVisualModel->CreateGlyphsPerCharacterTable( 0u, 0u, numberOfCharacters );
   textModel->mVisualModel->CreateCharacterToGlyphTable( 0u, 0u, numberOfCharacters );
 
-  const Length numberOfGlyphs = rendererParameters.glyphs.Count();
+  internalDataModel.numberOfGlyphs = rendererParameters.glyphs.Count();
 
   // Once the text has been shaped and the glyphs created it's possible to replace the font id of those glyphs
   // that represent an image or an item and create the embedded item layout info.
@@ -402,12 +407,18 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
       embeddedItemLayout.PushBack( embeddedInfo );
     }
   }
+}
 
-  ////////////////////////////////////////////////////////////////////////////////
-  // Retrieve the glyph's metrics.
-  ////////////////////////////////////////////////////////////////////////////////
+void SetColorSegmentation( const RendererParameters& textParameters, InternalDataModel& internalDataModel )
+{
+
+  Text::ModelPtr& textModel = internalDataModel.textModel;
+  Vector<ColorBlendingMode>& blendingMode = internalDataModel.blendingMode;
+
+  Vector<ColorRun>& colorRuns = textModel->mLogicalModel->mColorRuns;                                               // colors of the text.
 
-  metrics->GetGlyphMetrics( rendererParameters.glyphs.Begin(), numberOfGlyphs );
+  Vector<GlyphIndex>& charactersToGlyph = textModel->mVisualModel->mCharactersToGlyph;                              // Characters to glyphs map.
+  Vector<Length>& glyphsPerCharacter = textModel->mVisualModel->mGlyphsPerCharacter;                                // The number of glyphs that are shaped.
 
   ////////////////////////////////////////////////////////////////////////////////
   // Set the color runs in glyphs.
@@ -418,7 +429,7 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
                             glyphsPerCharacter,
                             0u,
                             0u,
-                            numberOfCharacters,
+                            internalDataModel.numberOfCharacters,
                             textModel->mVisualModel->mColors,
                             textModel->mVisualModel->mColorIndices );
 
@@ -426,7 +437,7 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
   textModel->mVisualModel->mColors.Insert( textModel->mVisualModel->mColors.Begin(),textParameters.textColor );
 
   // Set how the embedded items are blended with text color.
-  blendingMode.Resize( numberOfGlyphs, textParameters.isTextColorSet ? ColorBlendingMode::MULTIPLY : ColorBlendingMode::NONE );
+  blendingMode.Resize( internalDataModel.numberOfGlyphs, textParameters.isTextColorSet ? ColorBlendingMode::MULTIPLY : ColorBlendingMode::NONE );
 
   if( !textParameters.isTextColorSet )
   {
@@ -450,7 +461,15 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
     const GlyphIndex glyphIndex = textModel->mVisualModel->mCharactersToGlyph[item.characterIndex];
     blendingMode[glyphIndex] = item.colorBlendingMode;
   }
+}
 
+void SetEmojiVector( InternalDataModel& internalDataModel )
+{
+  Vector<bool>& isEmoji = internalDataModel.isEmoji;
+  Text::ModelPtr& textModel = internalDataModel.textModel;
+  const Length numberOfGlyphs = internalDataModel.numberOfGlyphs;
+
+  const Vector<ScriptRun>& scripts = textModel->mLogicalModel->mScriptRuns;                                               // Charactes's script.
   ////////////////////////////////////////////////////////////////////////////////
   // Set the isEmoji Vector
   ////////////////////////////////////////////////////////////////////////////////
@@ -471,120 +490,21 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
       }
     }
   }
+}
 
-  ////////////////////////////////////////////////////////////////////////////////
-  // Layout the text.
-  ////////////////////////////////////////////////////////////////////////////////
-
-  // Sets the alignment
-  HorizontalAlignment::Type horizontalAlignment = Toolkit::HorizontalAlignment::CENTER;
-  HorizontalAlignment::Type horizontalCircularAlignment = Toolkit::HorizontalAlignment::CENTER;
-  VerticalAlignment::Type verticalAlignment = VerticalAlignment::CENTER;
-  Layout::Type layout = Layout::SINGLELINE;
-  CircularAlignment::Type circularAlignment = CircularAlignment::BEGIN;
-
-  Property::Value horizontalAlignmentStr( textParameters.horizontalAlignment );
-  GetHorizontalAlignmentEnumeration( horizontalAlignmentStr, horizontalAlignment );
-  horizontalCircularAlignment = horizontalAlignment;
-
-  Property::Value verticalAlignmentStr( textParameters.verticalAlignment );
-  GetVerticalAlignmentEnumeration( verticalAlignmentStr, verticalAlignment );
-
-  Property::Value layoutStr( textParameters.layout );
-  GetLayoutEnumeration( layoutStr, layout );
-
-  Property::Value circularAlignmentStr( textParameters.circularAlignment );
-  GetCircularAlignmentEnumeration( circularAlignmentStr, circularAlignment );
-
-  // Whether the layout is multi-line.
-  const Text::Layout::Engine::Type horizontalLayout = ( Layout::MULTILINE == layout ) ? Text::Layout::Engine::MULTI_LINE_BOX : Text::Layout::Engine::SINGLE_LINE_BOX;
-  layoutEngine.SetLayout( horizontalLayout ); // TODO: multi-line.
-
-
-  // Whether the layout is circular.
-  const bool isCircularTextLayout = (Layout::CIRCULAR == layout);
-  const bool isClockwise = isCircularTextLayout && ( 0.f < textParameters.incrementAngle );
-
-  // Calculates the max ascender or the max descender.
-  // Is used to calculate the radius of the base line of the text.
-  float maxAscenderDescender = 0.f;
-  if( isCircularTextLayout )
-  {
-    FontId currentFontId = 0u;
-    for( const auto& glyph : rendererParameters.glyphs )
-    {
-      if( currentFontId != glyph.fontId )
-      {
-        currentFontId = glyph.fontId;
-        FontMetrics metrics;
-        fontClient.GetFontMetrics(currentFontId, metrics);
-        maxAscenderDescender = std::max( maxAscenderDescender, isClockwise ? metrics.ascender : metrics.descender );
-      }
-    }
-  }
-  const unsigned int radius = textParameters.radius - static_cast<unsigned int>( maxAscenderDescender );
-
-  // Convert CircularAlignment to HorizontalAlignment.
-  if( isCircularTextLayout )
-  {
-    switch( circularAlignment )
-    {
-      case CircularAlignment::BEGIN:
-      {
-        horizontalCircularAlignment = Toolkit::HorizontalAlignment::BEGIN;
-        break;
-      }
-      case CircularAlignment::CENTER:
-      {
-        horizontalCircularAlignment = Toolkit::HorizontalAlignment::CENTER;
-        break;
-      }
-      case CircularAlignment::END:
-      {
-        horizontalCircularAlignment = Toolkit::HorizontalAlignment::END;
-        break;
-      }
-    }
-  }
-
-  // Set the layout parameters.
-  Size textLayoutArea( static_cast<float>( textParameters.textWidth ),
-                     static_cast<float>( textParameters.textHeight ) );
-
-  if( isCircularTextLayout )
-  {
-    // In a circular layout, the length of the text area depends on the radius.
-    rendererParameters.radius = radius;
-    textLayoutArea.width = fabs( Radian( Degree( textParameters.incrementAngle ) ) * static_cast<float>( rendererParameters.radius ) );
-  }
-
-  textModel->mHorizontalAlignment = isCircularTextLayout ? horizontalCircularAlignment : horizontalAlignment;
-  textModel->mLineWrapMode = LineWrap::WORD;
-  textModel->mIgnoreSpacesAfterText = false;
-  textModel->mMatchSystemLanguageDirection = false;
-  Text::Layout::Parameters layoutParameters( textLayoutArea,
-                                             textModel );
-
-  // Resize the vector of positions to have the same size than the vector of glyphs.
-  rendererParameters.positions.Resize( numberOfGlyphs );
-
-  // Whether the last character is a new paragraph character.
-  layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( textToShape[numberOfCharacters - 1u] );
 
-  // The initial glyph and the number of glyphs to layout.
-  layoutParameters.startGlyphIndex = 0u;
-  layoutParameters.numberOfGlyphs = numberOfGlyphs;
-  layoutParameters.startLineIndex = 0u;
-  layoutParameters.estimatedNumberOfLines = 1u;
-  layoutParameters.interGlyphExtraAdvance = 0.f;
+void Align( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters,
+            Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel,
+            Size& textLayoutArea, const Size& newLayoutSize,
+            const bool isCircularTextLayout, const bool isClockwise,
+            HorizontalAlignment::Type horizontalAlignment, VerticalAlignment::Type verticalAlignment, CircularAlignment::Type circularAlignment,
+            const unsigned int radius )
+{
+  Text::Layout::Engine& layoutEngine = internalDataModel.layoutEngine;
+  Text::ModelPtr& textModel = internalDataModel.textModel;
+  const Length numberOfCharacters = internalDataModel.numberOfCharacters;
 
-  // Update the visual model.
-  Size newLayoutSize;
-  bool isAutoScrollEnabled = false;
-  layoutEngine.LayoutText( layoutParameters,
-                           newLayoutSize,
-                           textParameters.ellipsisEnabled,
-                           isAutoScrollEnabled );
+  Vector<LineRun>& lines = textModel->mVisualModel->mLines;                                                         // The laid out lines.
 
   ////////////////////////////////////////////////////////////////////////////////
   // Align the text.
@@ -730,7 +650,7 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
     }
 
     // Calculate the horizontal offset according with the given alignment.
-  float alignmentOffset = 0.f;
+    float alignmentOffset = 0.f;
     layoutEngine.Align( textLayoutArea,
                       0u,
                       numberOfCharacters,
@@ -742,10 +662,10 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
 
     // Update the position of the glyphs with the calculated offsets.
     for( auto& position : rendererParameters.positions )
-  {
-    position.x += line.alignmentOffset;
-    position.y = penY;
-  }
+    {
+      position.x += line.alignmentOffset;
+      position.y = penY;
+    }
   }
 
   // Cairo adds the bearing to the position of the glyph
@@ -845,6 +765,17 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
     }
   }
 
+}
+
+void Ellipsis(  const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters,
+               Vector<EmbeddedItemInfo>& embeddedItemLayout, Size& textLayoutArea,  InternalDataModel& internalDataModel )
+{
+  Text::ModelPtr& textModel = internalDataModel.textModel;
+  FontClient& fontClient = internalDataModel.fontClient;
+
+  Vector<LineRun>& lines = textModel->mVisualModel->mLines;                              // The laid out lines.
+  Vector<bool>& isEmoji = internalDataModel.isEmoji;
+
   ////////////////////////////////////////////////////////////////////////////////
   // Ellipsis the text.
   ////////////////////////////////////////////////////////////////////////////////
@@ -995,7 +926,149 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
       }
     }
   }
+}
 
+Size LayoutText( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters,
+                 Vector<EmbeddedItemInfo>& embeddedItemLayout, InternalDataModel& internalDataModel )
+{
+  ////////////////////////////////////////////////////////////////////////////////
+  // Layout the text.
+  ////////////////////////////////////////////////////////////////////////////////
+  Text::ModelPtr& textModel = internalDataModel.textModel;
+  Text::Layout::Engine& layoutEngine = internalDataModel.layoutEngine;
+  FontClient& fontClient = internalDataModel.fontClient;
+  const Length numberOfGlyphs = internalDataModel.numberOfGlyphs;
+  const bool isTextMirrored = internalDataModel.isTextMirrored;
+  const Vector<Character>& mirroredUtf32Characters = internalDataModel.mirroredUtf32Characters;
+  const Length numberOfCharacters = internalDataModel.numberOfCharacters;
+
+  // Sets the alignment
+  HorizontalAlignment::Type horizontalAlignment = Toolkit::HorizontalAlignment::CENTER;
+  HorizontalAlignment::Type horizontalCircularAlignment = Toolkit::HorizontalAlignment::CENTER;
+  VerticalAlignment::Type verticalAlignment = VerticalAlignment::CENTER;
+  Layout::Type layout = Layout::SINGLELINE;
+  CircularAlignment::Type circularAlignment = CircularAlignment::BEGIN;
+
+  Property::Value horizontalAlignmentStr( textParameters.horizontalAlignment );
+  GetHorizontalAlignmentEnumeration( horizontalAlignmentStr, horizontalAlignment );
+  horizontalCircularAlignment = horizontalAlignment;
+
+  Property::Value verticalAlignmentStr( textParameters.verticalAlignment );
+  GetVerticalAlignmentEnumeration( verticalAlignmentStr, verticalAlignment );
+
+  Property::Value layoutStr( textParameters.layout );
+  GetLayoutEnumeration( layoutStr, layout );
+
+  Property::Value circularAlignmentStr( textParameters.circularAlignment );
+  GetCircularAlignmentEnumeration( circularAlignmentStr, circularAlignment );
+
+  // Whether the layout is multi-line.
+  const Text::Layout::Engine::Type horizontalLayout = ( Layout::MULTILINE == layout ) ? Text::Layout::Engine::MULTI_LINE_BOX : Text::Layout::Engine::SINGLE_LINE_BOX;
+  layoutEngine.SetLayout( horizontalLayout ); // TODO: multi-line.
+
+
+  // Whether the layout is circular.
+  const bool isCircularTextLayout = (Layout::CIRCULAR == layout);
+  const bool isClockwise = isCircularTextLayout && ( 0.f < textParameters.incrementAngle );
+
+  // Calculates the max ascender or the max descender.
+  // Is used to calculate the radius of the base line of the text.
+  float maxAscenderDescender = 0.f;
+  if( isCircularTextLayout )
+  {
+    FontId currentFontId = 0u;
+    for( const auto& glyph : rendererParameters.glyphs )
+    {
+      if( currentFontId != glyph.fontId )
+      {
+        currentFontId = glyph.fontId;
+        FontMetrics metrics;
+        fontClient.GetFontMetrics(currentFontId, metrics);
+        maxAscenderDescender = std::max( maxAscenderDescender, isClockwise ? metrics.ascender : metrics.descender );
+      }
+    }
+  }
+  const unsigned int radius = textParameters.radius - static_cast<unsigned int>( maxAscenderDescender );
+
+  // Convert CircularAlignment to HorizontalAlignment.
+  if( isCircularTextLayout )
+  {
+    switch( circularAlignment )
+    {
+      case CircularAlignment::BEGIN:
+      {
+        horizontalCircularAlignment = Toolkit::HorizontalAlignment::BEGIN;
+        break;
+      }
+      case CircularAlignment::CENTER:
+      {
+        horizontalCircularAlignment = Toolkit::HorizontalAlignment::CENTER;
+        break;
+      }
+      case CircularAlignment::END:
+      {
+        horizontalCircularAlignment = Toolkit::HorizontalAlignment::END;
+        break;
+      }
+    }
+  }
+
+  // Set the layout parameters.
+  Size textLayoutArea( static_cast<float>( textParameters.textWidth ),
+                     static_cast<float>( textParameters.textHeight ) );
+
+  if( isCircularTextLayout )
+  {
+    // In a circular layout, the length of the text area depends on the radius.
+    rendererParameters.radius = radius;
+    textLayoutArea.width = fabs( Radian( Degree( textParameters.incrementAngle ) ) * static_cast<float>( rendererParameters.radius ) );
+  }
+  // Resize the vector of positions to have the same size than the vector of glyphs.
+  rendererParameters.positions.Resize( numberOfGlyphs );
+
+
+
+  textModel->mHorizontalAlignment = isCircularTextLayout ? horizontalCircularAlignment : horizontalAlignment;
+  textModel->mLineWrapMode = LineWrap::WORD;
+  textModel->mIgnoreSpacesAfterText = false;
+  textModel->mMatchSystemLanguageDirection = false;
+  Text::Layout::Parameters layoutParameters( textLayoutArea,
+                                             textModel );
+
+
+  // Whether the last character is a new paragraph character.
+  const Vector<Character>& textToShape = isTextMirrored ? mirroredUtf32Characters : textModel->mLogicalModel->mText;
+  layoutParameters.isLastNewParagraph = TextAbstraction::IsNewParagraph( textToShape[numberOfCharacters - 1u] );
+
+  // The initial glyph and the number of glyphs to layout.
+  layoutParameters.startGlyphIndex = 0u;
+  layoutParameters.numberOfGlyphs = numberOfGlyphs;
+  layoutParameters.startLineIndex = 0u;
+  layoutParameters.estimatedNumberOfLines = 1u;
+  layoutParameters.interGlyphExtraAdvance = 0.f;
+
+  // Update the visual model.
+  Size newLayoutSize;
+  bool isAutoScrollEnabled = false;
+  layoutEngine.LayoutText( layoutParameters,
+                           newLayoutSize,
+                           textParameters.ellipsisEnabled,
+                           isAutoScrollEnabled );
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Align the text.
+  ////////////////////////////////////////////////////////////////////////////////
+  Align( textParameters, rendererParameters, embeddedItemLayout, internalDataModel,
+         textLayoutArea, newLayoutSize, isCircularTextLayout, isClockwise,
+         horizontalAlignment, verticalAlignment, circularAlignment, radius );
+
+  return textLayoutArea;
+
+}
+
+
+Devel::PixelBuffer RenderText( const RendererParameters& textParameters, TextAbstraction::TextRenderer::Parameters& rendererParameters )
+{
   ////////////////////////////////////////////////////////////////////////////////
   // Render the text.
   ////////////////////////////////////////////////////////////////////////////////
@@ -1007,6 +1080,88 @@ Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<Embe
   return renderer.Render( rendererParameters );
 }
 
+
+Devel::PixelBuffer Render( const RendererParameters& textParameters, Vector<EmbeddedItemInfo>& embeddedItemLayout )
+{
+  if( textParameters.text.empty() )
+  {
+    Dali::Devel::PixelBuffer pixelBuffer = Dali::Devel::PixelBuffer::New( textParameters.textWidth,
+                                                                          textParameters.textHeight,
+                                                                          Dali::Pixel::RGBA8888 );
+
+    const unsigned int bufferSize = textParameters.textWidth * textParameters.textHeight * Dali::Pixel::GetBytesPerPixel(Dali::Pixel::RGBA8888);
+    unsigned char* buffer = pixelBuffer.GetBuffer();
+    memset(buffer, 0, bufferSize);
+
+    return pixelBuffer;
+  }
+
+  FontClient fontClient = FontClient::Get();
+  MetricsPtr metrics;
+  // Use this to access FontClient i.e. to get down-scaled Emoji metrics.
+  metrics = Metrics::New( fontClient );
+
+  Text::ModelPtr textModel = Text::Model::New();
+  InternalDataModel internalData( fontClient, metrics, textModel );
+
+  TextAbstraction::TextRenderer::Parameters rendererParameters( internalData.textModel->mVisualModel->mGlyphs,
+                                                                internalData.textModel->mVisualModel->mGlyphPositions,
+                                                                internalData.textModel->mVisualModel->mColors,
+                                                                internalData.textModel->mVisualModel->mColorIndices,
+                                                                internalData.blendingMode,
+                                                                internalData.isEmoji );
+
+
+  rendererParameters.width = textParameters.textWidth;
+  rendererParameters.height = textParameters.textHeight;
+  rendererParameters.pixelFormat = TextAbstraction::TextRenderer::Parameters::RGBA8888; // @note: At the moment all textures are generated RGBA8888
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Process the markup string if the mark-up processor is enabled.
+  ////////////////////////////////////////////////////////////////////////////////
+  ShapeTextPreprocess( textParameters, rendererParameters, internalData );
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Retrieve the glyphs. Text shaping
+  ////////////////////////////////////////////////////////////////////////////////
+  ShapeText( rendererParameters, embeddedItemLayout, internalData );
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Retrieve the glyph's metrics.
+  ////////////////////////////////////////////////////////////////////////////////
+
+  metrics->GetGlyphMetrics( rendererParameters.glyphs.Begin(), internalData.numberOfGlyphs );
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Set the color runs in glyphs.
+  ////////////////////////////////////////////////////////////////////////////////
+  SetColorSegmentation( textParameters, internalData );
+
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Set the isEmoji Vector
+  ////////////////////////////////////////////////////////////////////////////////
+  SetEmojiVector( internalData );
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Layout the text and Align the text
+  ////////////////////////////////////////////////////////////////////////////////
+  Size textLayoutArea = LayoutText( textParameters, rendererParameters, embeddedItemLayout, internalData );
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Ellipsis the text.
+  ////////////////////////////////////////////////////////////////////////////////
+  Ellipsis( textParameters, rendererParameters, embeddedItemLayout, textLayoutArea, internalData );
+
+  ////////////////////////////////////////////////////////////////////////////////
+  // Render the text.
+  ////////////////////////////////////////////////////////////////////////////////
+  return RenderText( textParameters, rendererParameters );
+}
+
+
 Devel::PixelBuffer CreateShadow( const ShadowParameters& shadowParameters )
 {
   // The size of the pixel data.
index 711f323..7056b0c 100755 (executable)
@@ -186,7 +186,7 @@ ScrollBar::ScrollBar(Toolkit::ScrollBar::Direction direction)
 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
   mIndicatorShowAlpha(1.0f),
   mDirection(direction),
-  mScrollableObject(WeakHandleBase()),
+  mScrollableObject(WeakHandle<Handle>()),
   mPropertyScrollPosition(Property::INVALID_INDEX),
   mPropertyMinScrollPosition(Property::INVALID_INDEX),
   mPropertyMaxScrollPosition(Property::INVALID_INDEX),
@@ -225,7 +225,7 @@ void ScrollBar::SetScrollPropertySource( Handle handle, Property::Index property
       && propertyMaxScrollPosition != Property::INVALID_INDEX
       && propertyScrollContentSize != Property::INVALID_INDEX )
   {
-    mScrollableObject = WeakHandleBase(handle);
+    mScrollableObject = WeakHandle<Handle>(handle);
     mPropertyScrollPosition = propertyScrollPosition;
     mPropertyMinScrollPosition = propertyMinScrollPosition;
     mPropertyMaxScrollPosition = propertyMaxScrollPosition;
@@ -294,7 +294,7 @@ Actor ScrollBar::GetScrollIndicator()
 
 void ScrollBar::ApplyConstraints()
 {
-  Handle scrollableHandle = mScrollableObject.GetBaseHandle();
+  Handle scrollableHandle = mScrollableObject.GetHandle();
 
   if( scrollableHandle )
   {
@@ -337,7 +337,7 @@ void ScrollBar::SetScrollPositionIntervals( const Dali::Vector<float>& positions
 {
   mScrollPositionIntervals = positions;
 
-  Handle scrollableHandle = mScrollableObject.GetBaseHandle();
+  Handle scrollableHandle = mScrollableObject.GetHandle();
 
   if( scrollableHandle )
   {
@@ -359,7 +359,7 @@ Dali::Vector<float> ScrollBar::GetScrollPositionIntervals() const
 void ScrollBar::OnScrollPositionIntervalReached(PropertyNotification& source)
 {
   // Emit the signal to notify the scroll position crossing
-  Handle scrollableHandle = mScrollableObject.GetBaseHandle();
+  Handle scrollableHandle = mScrollableObject.GetHandle();
   if(scrollableHandle)
   {
     mScrollPositionIntervalReachedSignal.Emit( scrollableHandle.GetCurrentProperty< float >( mPropertyScrollPosition ) );
@@ -442,7 +442,7 @@ void ScrollBar::ShowTransientIndicator()
 bool ScrollBar::OnPanGestureProcessTick()
 {
   // Update the scroll position property.
-  Handle scrollableHandle = mScrollableObject.GetBaseHandle();
+  Handle scrollableHandle = mScrollableObject.GetHandle();
   if( scrollableHandle )
   {
     scrollableHandle.SetProperty(mPropertyScrollPosition, mCurrentScrollPosition);
@@ -453,7 +453,7 @@ bool ScrollBar::OnPanGestureProcessTick()
 
 void ScrollBar::OnPan( const PanGesture& gesture )
 {
-  Handle scrollableHandle = mScrollableObject.GetBaseHandle();
+  Handle scrollableHandle = mScrollableObject.GetHandle();
 
   if(scrollableHandle)
   {
index e808d83..f56b9a0 100755 (executable)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_INTERNAL_SCROLL_BAR_H
 
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -283,7 +283,7 @@ private:
 
   Toolkit::ScrollBar::Direction mDirection;                          ///< The direction of scroll bar (vertical or horizontal)
 
-  WeakHandleBase mScrollableObject;                                  ///< Object to be scrolled
+  WeakHandle<Handle> mScrollableObject;                              ///< Object to be scrolled
 
   Property::Index mPropertyScrollPosition;                           ///< Index of scroll position property owned by the object to be scrolled
   Property::Index mPropertyMinScrollPosition;                        ///< Index of minimum scroll position property owned by the object to be scrolled
index 6fe8eba..912a441 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -49,6 +49,19 @@ IntrusivePtr<AsyncImageLoader> AsyncImageLoader::New()
   return internal;
 }
 
+uint32_t AsyncImageLoader::LoadAnimatedImage( Dali::AnimatedImageLoading animatedImageLoading,
+                                              uint32_t frameIndex )
+{
+  if( !mIsLoadThreadStarted )
+  {
+    mLoadThread.Start();
+    mIsLoadThreadStarted = true;
+  }
+  mLoadThread.AddTask( new LoadingTask( ++mLoadTaskId, animatedImageLoading, frameIndex ) );
+
+  return mLoadTaskId;
+}
+
 uint32_t AsyncImageLoader::Load( const VisualUrl& url,
                                  ImageDimensions dimensions,
                                  FittingMode::Type fittingMode,
index a97088d..e49b146 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_ASYNC_IMAGE_LOADER_IMPL_H
 
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -50,6 +50,12 @@ public:
   static IntrusivePtr<AsyncImageLoader> New();
 
   /**
+   * @copydoc Toolkit::AsyncImageLoader::LoadAnimatedImage( Dali::AnimatedImageLoading animatedImageLoading, uint32_t frameIndex )
+   */
+  uint32_t LoadAnimatedImage( Dali::AnimatedImageLoading animatedImageLoading,
+                              uint32_t frameIndex );
+
+  /**
    * @copydoc Toolkit::AsyncImageLoader::Load( const std::string&, ImageDimensions, FittingMode::Type, SamplingMode::Type, bool , DevelAsyncImageLoader::PreMultiplyOnLoad )
    */
   uint32_t Load( const VisualUrl& url,
index 179734a..dae742d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -33,6 +33,24 @@ namespace Toolkit
 namespace Internal
 {
 
+LoadingTask::LoadingTask( uint32_t id, Dali::AnimatedImageLoading animatedImageLoading, uint32_t frameIndex )
+: pixelBuffer(),
+  url(),
+  id( id ),
+  dimensions(),
+  fittingMode(),
+  samplingMode(),
+  orientationCorrection(),
+  preMultiplyOnLoad( DevelAsyncImageLoader::PreMultiplyOnLoad::OFF ),
+  isMaskTask( false ),
+  maskPixelBuffer(),
+  contentScale( 1.0f ),
+  cropToMask( false ),
+  animatedImageLoading( animatedImageLoading ),
+  frameIndex( frameIndex )
+{
+}
+
 LoadingTask::LoadingTask( uint32_t id, const VisualUrl& url, ImageDimensions dimensions,
                           FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad )
 : pixelBuffer(),
@@ -46,7 +64,9 @@ LoadingTask::LoadingTask( uint32_t id, const VisualUrl& url, ImageDimensions dim
   isMaskTask( false ),
   maskPixelBuffer(),
   contentScale( 1.0f ),
-  cropToMask( false )
+  cropToMask( false ),
+  animatedImageLoading(),
+  frameIndex( 0u )
 {
 }
 
@@ -63,13 +83,19 @@ LoadingTask::LoadingTask( uint32_t id, Devel::PixelBuffer pixelBuffer, Devel::Pi
   isMaskTask( true ),
   maskPixelBuffer( maskPixelBuffer ),
   contentScale( contentScale ),
-  cropToMask( cropToMask )
+  cropToMask( cropToMask ),
+  animatedImageLoading(),
+  frameIndex( 0u )
 {
 }
 
 void LoadingTask::Load()
-{
-  if( url.IsLocalResource() )
+{;
+  if( animatedImageLoading )
+  {
+    pixelBuffer = animatedImageLoading.LoadFrame( frameIndex );
+  }
+  else if( url.IsLocalResource() )
   {
     pixelBuffer = Dali::LoadImageFromFile( url.GetUrl(), dimensions, fittingMode, samplingMode, orientationCorrection );
   }
@@ -140,7 +166,6 @@ void ImageLoadThread::Run()
 void ImageLoadThread::AddTask( LoadingTask* task )
 {
   bool wasEmpty = false;
-
   {
     // Lock while adding task to the queue
     ConditionalWait::ScopedLock lock( mConditionalWait );
index 779541a..b435611 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_IMAGE_LOAD_THREAD_H
 
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -47,6 +47,16 @@ struct LoadingTask
   /**
    * Constructor.
    * @param [in] id of the task
+   * @param [in] animatedImageLoading The AnimatedImageLoading to load animated image
+   * @param [in] frameIndex The frame index of a frame to be loaded frame
+   */
+  LoadingTask( uint32_t id,
+               Dali::AnimatedImageLoading animatedImageLoading,
+               uint32_t frameIndex );
+
+  /**
+   * Constructor.
+   * @param [in] id of the task
    * @param [in] url The URL of the image file to load.
    * @param [in] size The width and height to fit the loaded image to, 0.0 means whole image
    * @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
@@ -60,7 +70,7 @@ struct LoadingTask
                FittingMode::Type fittingMode,
                SamplingMode::Type samplingMode,
                bool orientationCorrection,
-               DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad);
+               DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad );
 
   /**
    * Constructor.
@@ -72,11 +82,11 @@ struct LoadingTask
    * @param [in] preMultiplyOnLoad ON if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha.
    */
   LoadingTask( uint32_t id,
-              Devel::PixelBuffer pixelBuffer,
-              Devel::PixelBuffer maskPixelBuffer,
-              float contentScale,
-              bool cropToMask,
-              DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad);
+               Devel::PixelBuffer pixelBuffer,
+               Devel::PixelBuffer maskPixelBuffer,
+               float contentScale,
+               bool cropToMask,
+               DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad );
 
   /**
    * Load the image
@@ -117,6 +127,8 @@ public:
   Devel::PixelBuffer maskPixelBuffer; ///< pixelBuffer of mask image
   float contentScale;               ///< The factor to scale the content
   bool cropToMask;                  ///< Whether to crop the content to the mask size
+  Dali::AnimatedImageLoading animatedImageLoading;
+  uint32_t frameIndex;
 };
 
 
index fdde260..6b7adf3 100755 (executable)
@@ -177,8 +177,8 @@ AnimatedImageVisual::AnimatedImageVisual( VisualFactoryCache& factoryCache, Imag
   mCurrentFrameIndex( 0 ),
   mImageUrls( NULL ),
   mImageCache( NULL ),
-  mCacheSize( 1 ),
-  mBatchSize( 1 ),
+  mCacheSize( 2 ),
+  mBatchSize( 2 ),
   mFrameDelay( 100 ),
   mLoopCount( LOOP_FOREVER ),
   mCurrentLoopIndex( 0 ),
@@ -221,6 +221,9 @@ void AnimatedImageVisual::DoCreatePropertyMap( Property::Map& map ) const
 {
   map.Clear();
 
+  bool sync = IsSynchronousLoadingRequired();
+  map.Insert( Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync );
+
   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE );
 
   if( mImageUrl.IsValid() )
@@ -402,7 +405,14 @@ void AnimatedImageVisual::DoSetProperty( Property::Index index,
       int batchSize;
       if( value.Get( batchSize ) )
       {
-        mBatchSize = batchSize;
+        if( batchSize < 2 )
+        {
+          DALI_LOG_ERROR( "The minimum value of batch size is 2." );
+        }
+        else
+        {
+          mBatchSize = batchSize;
+        }
       }
       break;
     }
@@ -412,7 +422,14 @@ void AnimatedImageVisual::DoSetProperty( Property::Index index,
       int cacheSize;
       if( value.Get( cacheSize ) )
       {
-        mCacheSize = cacheSize;
+        if( cacheSize < 2 )
+        {
+          DALI_LOG_ERROR( "The minimum value of cache size is 2." );
+        }
+        else
+        {
+          mCacheSize = cacheSize;
+        }
       }
       break;
     }
@@ -446,6 +463,21 @@ void AnimatedImageVisual::DoSetProperty( Property::Index index,
       }
       break;
     }
+
+    case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
+    {
+      bool sync = false;
+      value.Get( sync );
+      if( sync )
+      {
+        mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
+      }
+      else
+      {
+        mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
+      }
+      break;
+    }
   }
 }
 
@@ -543,7 +575,7 @@ void AnimatedImageVisual::LoadFirstBatch()
 
   if( mAnimatedImageLoading )
   {
-    mImageCache = new RollingAnimatedImageCache( textureManager, mAnimatedImageLoading, mFrameCount, *this, cacheSize, batchSize );
+    mImageCache = new RollingAnimatedImageCache( textureManager, mAnimatedImageLoading, mFrameCount, *this, cacheSize, batchSize, IsSynchronousLoadingRequired() );
   }
   else if( mImageUrls )
   {
@@ -607,14 +639,16 @@ TextureSet AnimatedImageVisual::PrepareTextureSet()
 {
   TextureSet textureSet;
   if (mImageCache)
+  {
     textureSet = mImageCache->FirstFrame();
+  }
   if( textureSet )
   {
     SetImageSize( textureSet );
   }
   else
   {
-    DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"ResourceReady(ResourceStatus::FAILED)\n");
+    DALI_LOG_INFO( gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n" );
     ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
   }
 
@@ -644,7 +678,7 @@ void AnimatedImageVisual::FrameReady( TextureSet textureSet )
   }
   else
   {
-    if(mImpl->mRenderer)
+    if( mImpl->mRenderer )
     {
       mImpl->mRenderer.SetTextures( textureSet );
     }
index c421678..3992b08 100644 (file)
@@ -60,15 +60,17 @@ namespace Internal
 
 RollingAnimatedImageCache::RollingAnimatedImageCache(
   TextureManager& textureManager, AnimatedImageLoading& animatedImageLoading, uint32_t frameCount, ImageCache::FrameReadyObserver& observer,
-  uint16_t cacheSize, uint16_t batchSize )
+  uint16_t cacheSize, uint16_t batchSize, bool isSynchronousLoading )
 : ImageCache( textureManager, observer, batchSize ),
   mAnimatedImageLoading( animatedImageLoading ),
   mFrameCount( frameCount ),
   mFrameIndex( 0 ),
-  mCacheSize( cacheSize ),
-  mQueue( cacheSize )
+  mQueue( cacheSize ),
+  mIsSynchronousLoading( isSynchronousLoading ),
+  mOnLoading( false )
 {
   mImageUrls.resize( mFrameCount );
+  mIntervals.assign( mFrameCount, 0 );
   LoadBatch();
 }
 
@@ -76,10 +78,10 @@ RollingAnimatedImageCache::~RollingAnimatedImageCache()
 {
   if( mTextureManagerAlive )
   {
-    while( IsFrontReady() )
+    while( !mQueue.IsEmpty() )
     {
       ImageFrame imageFrame = mQueue.PopFront();
-      Dali::Toolkit::TextureManager::RemoveTexture( mImageUrls[ imageFrame.mFrameNumber ].mUrl );
+      mTextureManager.Remove( mImageUrls[ imageFrame.mFrameNumber ].mTextureId, this );
     }
   }
 }
@@ -87,29 +89,59 @@ RollingAnimatedImageCache::~RollingAnimatedImageCache()
 TextureSet RollingAnimatedImageCache::Frame( uint32_t frameIndex )
 {
   bool popExist = false;
-  while( IsFrontReady() && mQueue.Front().mFrameNumber != frameIndex )
+  while( !mQueue.IsEmpty() && mQueue.Front().mFrameNumber != frameIndex )
   {
     ImageFrame imageFrame = mQueue.PopFront();
-    Dali::Toolkit::TextureManager::RemoveTexture( mImageUrls[ imageFrame.mFrameNumber ].mUrl );
+    mTextureManager.Remove( mImageUrls[ imageFrame.mFrameNumber ].mTextureId, this );
     mImageUrls[ imageFrame.mFrameNumber ].mTextureId = TextureManager::INVALID_TEXTURE_ID;
     popExist = true;
   }
-  if( popExist || mImageUrls[ frameIndex ].mTextureId == TextureManager::INVALID_TEXTURE_ID )
+
+  TextureSet textureSet;
+  // If we need to load new frame that are not stored in queue.
+  // Load the frame synchronously.
+  if( mIsSynchronousLoading && mQueue.IsEmpty() )
+  {
+    bool synchronousLoading = true;
+    textureSet = mTextureManager.LoadAnimatedImageTexture( mAnimatedImageLoading, frameIndex, SamplingMode::BOX_THEN_LINEAR,
+                                                           synchronousLoading, mImageUrls[ frameIndex ].mTextureId, Dali::WrapMode::Type::DEFAULT,
+                                                           Dali::WrapMode::Type::DEFAULT, this );
+    mFrameIndex = ( frameIndex + 1 ) % mFrameCount;
+  }
+
+  if( popExist || mQueue.IsEmpty() )
   {
     // If the frame of frameIndex was already loaded, load batch from the last frame of queue
-    if( IsFrontReady() )
+    if( !mQueue.IsEmpty() )
     {
       mFrameIndex = ( mQueue.Back().mFrameNumber + 1 ) % mFrameCount;
     }
-    // If the queue is empty, load batch from the frame of frameIndex
     else
     {
-      mFrameIndex = frameIndex;
+      // If the request is for the first frame or a jumped frame(JUMP_TO) remove current waiting queue.
+      mLoadWaitingQueue.clear();
+      // If the queue is empty, and the frame of frameIndex is not loaded synchronously. load batch from the frame of frameIndex
+      if( !textureSet )
+      {
+        mFrameIndex = frameIndex;
+      }
     }
     LoadBatch();
   }
 
-  return GetFrontTextureSet();
+  if( !textureSet )
+  {
+    if( IsFrontReady() == true )
+    {
+      textureSet = GetFrontTextureSet();
+    }
+    else
+    {
+      mWaitingForReadyFrame = true;
+    }
+  }
+
+  return textureSet;
 }
 
 TextureSet RollingAnimatedImageCache::FirstFrame()
@@ -119,13 +151,24 @@ TextureSet RollingAnimatedImageCache::FirstFrame()
 
 uint32_t RollingAnimatedImageCache::GetFrameInterval( uint32_t frameIndex )
 {
-  Frame( frameIndex );
   return mAnimatedImageLoading.GetFrameInterval( frameIndex );
 }
 
 bool RollingAnimatedImageCache::IsFrontReady() const
 {
-  return ( !mQueue.IsEmpty() );
+  return ( !mQueue.IsEmpty() && mQueue.Front().mReady );
+}
+
+void RollingAnimatedImageCache::RequestFrameLoading( uint32_t frameIndex )
+{
+  mRequestingLoad = true;
+
+  bool synchronousLoading = false;
+  mTextureManager.LoadAnimatedImageTexture( mAnimatedImageLoading, frameIndex, SamplingMode::BOX_THEN_LINEAR,
+                                            synchronousLoading, mImageUrls[ frameIndex ].mTextureId, Dali::WrapMode::Type::DEFAULT,
+                                            Dali::WrapMode::Type::DEFAULT, this );
+
+  mRequestingLoad = false;
 }
 
 void RollingAnimatedImageCache::LoadBatch()
@@ -134,61 +177,46 @@ void RollingAnimatedImageCache::LoadBatch()
   // Once the cache is filled, as frames progress, the old frame is
   // removed, and another frame is loaded
 
-  std::vector<Dali::PixelData> pixelDataList;
-
-  // Get the smallest number of frames we need to load
-  int batchSize = std::min( std::size_t(mBatchSize), mCacheSize - mQueue.Count() );
-  DALI_LOG_INFO( gAnimImgLogFilter, Debug::Concise, "RollingAnimatedImageCache::LoadBatch() mFrameIndex:%d  batchSize:%d\n", mFrameIndex, batchSize );
-  if( mAnimatedImageLoading.LoadNextNFrames( mFrameIndex, batchSize, pixelDataList) )
+  bool frontFrameReady = IsFrontReady();
+  for( unsigned int i=0; i< mBatchSize && !mQueue.IsFull(); ++i )
   {
-    unsigned int pixelDataListCount = pixelDataList.size();
+    ImageFrame imageFrame;
+    imageFrame.mFrameNumber = mFrameIndex;
+    imageFrame.mReady = false;
 
-    for( unsigned int i = 0; i < pixelDataListCount && !mQueue.IsFull(); ++i )
+    mQueue.PushBack( imageFrame );
+
+    if( !mOnLoading )
     {
-      ImageFrame imageFrame;
-
-      // create the texture for uploading the pixel data
-      Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D,
-                                      pixelDataList[i].GetPixelFormat(),
-                                      pixelDataList[i].GetWidth(),
-                                      pixelDataList[i].GetHeight() );
-
-      texture.Upload( pixelDataList[i] );
-
-      mImageUrls[ mUrlIndex ].mUrl = Dali::Toolkit::TextureManager::AddTexture(texture);
-      imageFrame.mFrameNumber = mUrlIndex;
-
-      ++mUrlIndex;
-      mUrlIndex %= mImageUrls.size();
-
-      mQueue.PushBack( imageFrame );
-
-      bool synchronousLoading = false;
-      bool atlasingStatus = false;
-      bool loadingStatus = false;
-      TextureManager::MaskingDataPointer maskInfo = nullptr;
-      AtlasUploadObserver* atlasObserver = nullptr;
-      ImageAtlasManagerPtr imageAtlasManager = nullptr;
-      Vector4 textureRect;
-      Dali::ImageDimensions textureRectSize;
-      auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-
-      mTextureManager.LoadTexture(
-        mImageUrls[ imageFrame.mFrameNumber ].mUrl, ImageDimensions(), FittingMode::SCALE_TO_FILL,
-        SamplingMode::BOX_THEN_LINEAR, maskInfo,
-        synchronousLoading, mImageUrls[ imageFrame.mFrameNumber ].mTextureId, textureRect, textureRectSize,
-        atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT,
-        Dali::WrapMode::Type::DEFAULT, NULL,
-        atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED, preMultiply );
+      mOnLoading = true;
+      RequestFrameLoading( mFrameIndex );
+    }
+    else
+    {
+      mLoadWaitingQueue.push_back( mFrameIndex );
     }
 
-    mFrameIndex += batchSize;
+    mFrameIndex++;
     mFrameIndex %= mFrameCount;
   }
 
+  CheckFrontFrame( frontFrameReady );
+
   LOG_CACHE;
 }
 
+void RollingAnimatedImageCache::SetImageFrameReady( TextureManager::TextureId textureId )
+{
+  for( std::size_t i = 0; i < mQueue.Count() ; ++i )
+  {
+    if( GetCachedTextureId( i ) == textureId )
+    {
+      mQueue[i].mReady = true;
+      break;
+    }
+  }
+}
+
 TextureSet RollingAnimatedImageCache::GetFrontTextureSet() const
 {
   DALI_LOG_INFO( gAnimImgLogFilter, Debug::Concise, "RollingAnimatedImageCache::GetFrontTextureSet() FrameNumber:%d\n", mQueue[ 0 ].mFrameNumber );
@@ -202,6 +230,68 @@ TextureManager::TextureId RollingAnimatedImageCache::GetCachedTextureId( int ind
   return mImageUrls[ mQueue[ index ].mFrameNumber ].mTextureId;
 }
 
+void RollingAnimatedImageCache::CheckFrontFrame( bool wasReady )
+{
+  if( mWaitingForReadyFrame && wasReady == false && IsFrontReady() )
+  {
+    mWaitingForReadyFrame = false;
+    mObserver.FrameReady( GetFrontTextureSet() );
+  }
+}
+
+void RollingAnimatedImageCache::UploadComplete(
+  bool           loadSuccess,
+  int32_t        textureId,
+  TextureSet     textureSet,
+  bool           useAtlasing,
+  const Vector4& atlasRect,
+  bool           preMultiplied )
+{
+  DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::UploadComplete(textureId:%d) start\n", textureId);
+  LOG_CACHE;
+
+  bool frontFrameReady = IsFrontReady();
+
+  if( !mRequestingLoad )
+  {
+    SetImageFrameReady( textureId );
+
+    CheckFrontFrame( frontFrameReady );
+  }
+  else
+  {
+    // UploadComplete has been called from within RequestLoad. TextureManager must
+    // therefore already have the texture cached, so make the texture ready.
+    // (Use the last texture, as the texture id hasn't been assigned yet)
+    mQueue.Back().mReady = true;
+  }
+
+  mOnLoading = false;
+  // The frames of a single animated image can not be loaded parallelly.
+  // Therefore, a frame is now loading, other orders are waiting.
+  // And, after the frame is loaded, requests load of next order.
+  if( !mLoadWaitingQueue.empty() )
+  {
+    uint32_t loadingIndex = mLoadWaitingQueue.front();
+    mLoadWaitingQueue.erase( mLoadWaitingQueue.begin() );
+    mOnLoading = true;
+    RequestFrameLoading( loadingIndex );
+  }
+
+  LOG_CACHE;
+}
+
+void RollingAnimatedImageCache::LoadComplete(
+  bool loadSuccess,
+  Devel::PixelBuffer pixelBuffer,
+  const VisualUrl& url,
+  bool preMultiplied )
+{
+  // LoadComplete is called if this TextureUploadObserver requested to load
+  // an image that will be returned as a type of PixelBuffer by using a method
+  // TextureManager::LoadPixelBuffer.
+}
+
 } //namespace Internal
 } //namespace Toolkit
 } //namespace Dali
index 503f65e..8281c32 100644 (file)
@@ -37,7 +37,7 @@ namespace Internal
  * Frames are always ready, so the observer.FrameReady callback is never triggered;
  * the FirstFrame and NextFrame APIs will always return a texture.
  */
-class RollingAnimatedImageCache : public ImageCache
+class RollingAnimatedImageCache : public ImageCache, public TextureUploadObserver
 {
 public:
   /**
@@ -48,16 +48,18 @@ public:
    * @param[in] observer FrameReady observer
    * @param[in] cacheSize The size of the cache
    * @param[in] batchSize The size of a batch to load
+   * @param[in] isSynchronousLoading The flag to define whether to load first frame synchronously
    *
    * This will start loading textures immediately, according to the
    * batch and cache sizes.
    */
   RollingAnimatedImageCache( TextureManager&                 textureManager,
-                        AnimatedImageLoading&           animatedImageLoader,
-                        uint32_t                        frameCount,
-                        ImageCache::FrameReadyObserver& observer,
-                        uint16_t                        cacheSize,
-                        uint16_t                        batchSize );
+                             AnimatedImageLoading&           animatedImageLoader,
+                             uint32_t                        frameCount,
+                             ImageCache::FrameReadyObserver& observer,
+                             uint16_t                        cacheSize,
+                             uint16_t                        batchSize,
+                             bool                            isSynchronousLoading );
 
   /**
    * Destructor
@@ -88,11 +90,21 @@ private:
   bool IsFrontReady() const;
 
   /**
+   * Request to Load a frame
+   */
+  void RequestFrameLoading( uint32_t frameIndex );
+
+  /**
    * Load the next batch of images
    */
   void LoadBatch();
 
   /**
+   * Find the matching image frame, and set it to ready
+   */
+  void SetImageFrameReady( TextureManager::TextureId textureId );
+
+  /**
    * Get the texture set of the front frame.
    * @return the texture set
    */
@@ -103,6 +115,27 @@ private:
    */
   TextureManager::TextureId GetCachedTextureId( int index ) const;
 
+  /**
+   * Check if the front frame has become ready - if so, inform observer
+   * @param[in] wasReady Readiness before call.
+   */
+  void CheckFrontFrame( bool wasReady );
+
+protected:
+  void UploadComplete(
+    bool           loadSuccess,
+    int32_t        textureId,
+    TextureSet     textureSet,
+    bool           useAtlasing,
+    const Vector4& atlasRect,
+    bool           preMultiplied ) override;
+
+  void LoadComplete(
+    bool loadSuccess,
+    Devel::PixelBuffer pixelBuffer,
+    const VisualUrl& url,
+    bool preMultiplied ) override;
+
 private:
   /**
    * Secondary class to hold readiness and index into url
@@ -110,14 +143,18 @@ private:
   struct ImageFrame
   {
     unsigned int mFrameNumber = 0u;
+    bool mReady = false;
   };
 
-  Dali::AnimatedImageLoading& mAnimatedImageLoading;
-  uint32_t                   mFrameCount;
-  int                        mFrameIndex;
-  std::vector<UrlStore>      mImageUrls;
-  uint16_t                   mCacheSize;
-  CircularQueue<ImageFrame>  mQueue;
+  Dali::AnimatedImageLoading  mAnimatedImageLoading;
+  uint32_t                    mFrameCount;
+  int                         mFrameIndex;
+  std::vector<UrlStore>       mImageUrls;
+  std::vector<int32_t>        mIntervals;
+  std::vector<uint32_t>       mLoadWaitingQueue;
+  CircularQueue<ImageFrame>   mQueue;
+  bool                        mIsSynchronousLoading;
+  bool                        mOnLoading;
 };
 
 } // namespace Internal
index 9823a92..194b9ba 100644 (file)
@@ -609,13 +609,27 @@ bool ImageVisual::AttemptAtlasing()
 void ImageVisual::InitializeRenderer()
 {
   auto attemptAtlasing = AttemptAtlasing();
-  // texture set has to be created first as we need to know if atlasing succeeded or not
-  // when selecting the shader
 
-  if( mTextureId == TextureManager::INVALID_TEXTURE_ID && ! mTextures ) // Only load the texture once
+  // Load Texture if mTextures is empty.
+  // mTextures is already set, the mTexture can be used to create Renderer.
+  // There are two cases mTextures is empty.
+  // 1. mTextureId == TextureManager::INVALID_TEXTURE_ID
+  //  - Visual is on stage with LoadPolicy::ATTACHED
+  // 2. mTextureId != TextureManager::INVALID_TEXTURE_ID
+  //  - If ReleasePolicy is DESTROYED, InitializeRenderer called every on stage called.
+  //  - Then every resources those contained in Visual are Reset but mTextureId is remained when the Off stage time,
+  //  - So, mTextures needed to be get from texture manager to created resources like mImpl->mRenderer.
+  if( ! mTextures )
   {
-    LoadTexture( attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection,
-                 TextureManager::ReloadPolicy::CACHED );
+    if( mTextureId == TextureManager::INVALID_TEXTURE_ID )
+    {
+      LoadTexture( attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection,
+                   TextureManager::ReloadPolicy::CACHED );
+    }
+    else
+    {
+      mTextures = mFactoryCache.GetTextureManager().GetTextureSet( mTextureId );
+    }
   }
 
   CreateRenderer( mTextures );
index 6ba1a99..147f88c 100644 (file)
@@ -431,6 +431,8 @@ void NPatchVisual::OnSetTransform()
 void NPatchVisual::DoCreatePropertyMap( Property::Map& map ) const
 {
   map.Clear();
+  bool sync = IsSynchronousLoadingRequired();
+  map.Insert( Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync );
   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::N_PATCH );
   map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() );
   map.Insert( Toolkit::ImageVisual::Property::BORDER_ONLY, mBorderOnly );
index 5c694d0..e23290b 100644 (file)
@@ -143,6 +143,71 @@ TextureManager::~TextureManager()
   }
 }
 
+TextureSet TextureManager::LoadAnimatedImageTexture(
+  Dali::AnimatedImageLoading animatedImageLoading, uint32_t frameIndex, Dali::SamplingMode::Type samplingMode,
+  bool synchronousLoading, TextureManager::TextureId& textureId, Dali::WrapMode::Type wrapModeU, Dali::WrapMode::Type wrapModeV, TextureUploadObserver* textureObserver )
+{
+  TextureSet textureSet;
+
+  if( synchronousLoading )
+  {
+    Devel::PixelBuffer pixelBuffer;
+    if( animatedImageLoading )
+    {
+      pixelBuffer = animatedImageLoading.LoadFrame( frameIndex );
+    }
+    if( !pixelBuffer )
+    {
+      // use broken image
+      pixelBuffer = LoadImageFromFile( mBrokenImageUrl );
+      PixelData pixelData;
+      if( pixelBuffer )
+      {
+        pixelData = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
+      }
+      Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(),
+                                      pixelData.GetWidth(), pixelData.GetHeight() );
+      texture.Upload( pixelData );
+      textureSet = TextureSet::New();
+      textureSet.SetTexture( 0u, texture );
+    }
+    else
+    {
+      PixelData pixelData = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
+      if( !textureSet )
+      {
+        Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(),
+                                        pixelData.GetWidth(), pixelData.GetHeight() );
+        texture.Upload( pixelData );
+        textureSet = TextureSet::New();
+        textureSet.SetTexture( 0u, texture );
+      }
+    }
+  }
+  else
+  {
+    auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+    textureId = RequestLoadInternal( animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL,
+                                     SamplingMode::BOX_THEN_LINEAR, TextureManager::NO_ATLAS, false, UPLOAD_TO_TEXTURE, textureObserver,
+                                     true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex );
+    TextureManager::LoadState loadState = GetTextureStateInternal( textureId );
+    if( loadState == TextureManager::UPLOADED )
+    {
+      // UploadComplete has already been called - keep the same texture set
+      textureSet = GetTextureSet( textureId );
+    }
+  }
+
+  if( textureSet )
+  {
+    Sampler sampler = Sampler::New();
+    sampler.SetWrapMode(  wrapModeU, wrapModeV  );
+    textureSet.SetSampler( 0u, sampler );
+  }
+
+  return textureSet;
+}
+
 Devel::PixelBuffer TextureManager::LoadPixelBuffer(
   const VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode, bool synchronousLoading, TextureUploadObserver* textureObserver, bool orientationCorrection, TextureManager::MultiplyOnLoad& preMultiplyOnLoad )
 {
@@ -163,7 +228,7 @@ Devel::PixelBuffer TextureManager::LoadPixelBuffer(
   {
     RequestLoadInternal( url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS,
                          false, RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED,
-                         preMultiplyOnLoad );
+                         preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u );
   }
 
   return pixelBuffer;
@@ -335,7 +400,7 @@ TextureManager::TextureId TextureManager::RequestLoad(
 {
   return RequestLoadInternal( url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas,
                               false, UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy,
-                              preMultiplyOnLoad );
+                              preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u );
 }
 
 TextureManager::TextureId TextureManager::RequestLoad(
@@ -354,7 +419,7 @@ TextureManager::TextureId TextureManager::RequestLoad(
 {
   return RequestLoadInternal( url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas,
                               cropToMask, UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy,
-                              preMultiplyOnLoad );
+                              preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u );
 }
 
 TextureManager::TextureId TextureManager::RequestMaskLoad( const VisualUrl& maskUrl )
@@ -363,7 +428,7 @@ TextureManager::TextureId TextureManager::RequestMaskLoad( const VisualUrl& mask
   auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
   return RequestLoadInternal( maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL,
                               SamplingMode::NO_FILTER, NO_ATLAS, false, KEEP_PIXEL_BUFFER, NULL, true,
-                              TextureManager::ReloadPolicy::CACHED, preMultiply );
+                              TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u );
 }
 
 TextureManager::TextureId TextureManager::RequestLoadInternal(
@@ -379,16 +444,19 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
   TextureUploadObserver*          observer,
   bool                            orientationCorrection,
   TextureManager::ReloadPolicy    reloadPolicy,
-  TextureManager::MultiplyOnLoad& preMultiplyOnLoad )
+  TextureManager::MultiplyOnLoad& preMultiplyOnLoad,
+  Dali::AnimatedImageLoading      animatedImageLoading,
+  uint32_t                        frameIndex )
 {
   // First check if the requested Texture is cached.
+  bool isAnimatedImage = ( animatedImageLoading ) ? true : false;
   const TextureHash textureHash = GenerateHash( url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas,
-                                                maskTextureId );
+                                                maskTextureId, storageType, isAnimatedImage, frameIndex );
 
   TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
   // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
   int cacheIndex = FindCachedTexture( textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas,
-                                      maskTextureId, preMultiplyOnLoad, storageType );
+                                      maskTextureId, preMultiplyOnLoad, storageType, isAnimatedImage, frameIndex );
 
   // Check if the requested Texture exists in the cache.
   if( cacheIndex != INVALID_CACHE_INDEX )
@@ -416,7 +484,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
     mTextureInfoContainer.push_back( TextureInfo( textureId, maskTextureId, url.GetUrl(),
                                                   desiredSize, contentScale, fittingMode, samplingMode,
                                                   false, cropToMask, useAtlas, textureHash, orientationCorrection,
-                                                  preMultiply ) );
+                                                  preMultiply, animatedImageLoading, frameIndex ) );
     cacheIndex = mTextureInfoContainer.size() - 1u;
 
     DALI_LOG_INFO( gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d\n",
@@ -675,7 +743,6 @@ TextureSet TextureManager::RemoveExternalTexture( const std::string& url )
   return TextureSet();
 }
 
-
 void TextureManager::AddObserver( TextureManager::LifecycleObserver& observer )
 {
   // make sure an observer doesn't observe the same object twice
@@ -763,10 +830,17 @@ void TextureManager::LoadTexture( TextureInfo& textureInfo, TextureUploadObserve
     auto premultiplyOnLoad = ( textureInfo.preMultiplyOnLoad && textureInfo.maskTextureId == INVALID_TEXTURE_ID ) ?
                                DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF;
     DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End());
-    loadingHelperIt->Load(textureInfo.textureId, textureInfo.url,
-                          textureInfo.desiredSize, textureInfo.fittingMode,
-                          textureInfo.samplingMode, textureInfo.orientationCorrection,
-                          premultiplyOnLoad );
+    if( textureInfo.animatedImageLoading )
+    {
+      loadingHelperIt->LoadAnimatedImage( textureInfo.textureId, textureInfo.animatedImageLoading, textureInfo.frameIndex );
+    }
+    else
+    {
+      loadingHelperIt->Load(textureInfo.textureId, textureInfo.url,
+                            textureInfo.desiredSize, textureInfo.fittingMode,
+                            textureInfo.samplingMode, textureInfo.orientationCorrection,
+                            premultiplyOnLoad );
+    }
   }
   ObserveTexture( textureInfo, observer );
 }
@@ -1078,7 +1152,10 @@ TextureManager::TextureHash TextureManager::GenerateHash(
   const FittingMode::Type        fittingMode,
   const Dali::SamplingMode::Type samplingMode,
   const UseAtlas                 useAtlas,
-  TextureId                      maskTextureId )
+  TextureId                      maskTextureId,
+  StorageType                    storageType,
+  bool                           isAnimationImage,
+  uint32_t                       frameIndex )
 {
   std::string hashTarget( url );
   const size_t urlLength = hashTarget.length();
@@ -1099,8 +1176,8 @@ TextureManager::TextureHash TextureManager::GenerateHash(
     *hashTargetPtr++ = ( size.GetHeight() >> 8u ) & 0xff;
 
     // Bit-pack the FittingMode, SamplingMode and atlasing.
-    // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit
-    *hashTargetPtr   = ( fittingMode << 4u ) | ( samplingMode << 1 ) | useAtlas;
+    // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit, storageType=2bits
+    *hashTargetPtr   = ( fittingMode << 6u ) | ( samplingMode << 3 ) | ( useAtlas << 2 ) | storageType;
   }
   else
   {
@@ -1123,6 +1200,19 @@ TextureManager::TextureHash TextureManager::GenerateHash(
     }
   }
 
+  if( isAnimationImage )
+  {
+    auto textureIdIndex = hashTarget.length();
+    hashTarget.resize( hashTarget.length() + sizeof( uint32_t ) );
+    char* hashTargetPtr = &( hashTarget[ textureIdIndex ] );
+
+    for( size_t byteIter = 0; byteIter < sizeof( uint32_t ); ++byteIter )
+    {
+      *hashTargetPtr++ = frameIndex & 0xff;
+      frameIndex >>= 8u;
+    }
+  }
+
   if( maskTextureId != INVALID_TEXTURE_ID )
   {
     auto textureIdIndex = hashTarget.length();
@@ -1150,7 +1240,9 @@ int TextureManager::FindCachedTexture(
   const bool                        useAtlas,
   TextureId                         maskTextureId,
   TextureManager::MultiplyOnLoad    preMultiplyOnLoad,
-  StorageType                       storageType )
+  StorageType                       storageType,
+  bool                              isAnimatedImage,
+  uint32_t                          frameIndex )
 {
   // Default to an invalid ID, in case we do not find a match.
   int cacheIndex = INVALID_CACHE_INDEX;
@@ -1171,7 +1263,9 @@ int TextureManager::FindCachedTexture(
           ( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
             ( fittingMode == textureInfo.fittingMode &&
               samplingMode == textureInfo.samplingMode ) ) &&
-          ( storageType == textureInfo.storageType ) )
+          ( storageType == textureInfo.storageType ) &&
+          ( isAnimatedImage == ( ( textureInfo.animatedImageLoading ) ? true : false ) ) &&
+          ( frameIndex == textureInfo.frameIndex ) )
       {
         // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different.
         // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false.
@@ -1217,27 +1311,36 @@ TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(TextureManager& textureMa
 {
 }
 
-void TextureManager::AsyncLoadingHelper::Load(TextureId          textureId,
-                                              const VisualUrl&   url,
-                                              ImageDimensions    desiredSize,
-                                              FittingMode::Type  fittingMode,
-                                              SamplingMode::Type samplingMode,
-                                              bool               orientationCorrection,
-                                              DevelAsyncImageLoader::PreMultiplyOnLoad  preMultiplyOnLoad)
+void TextureManager::AsyncLoadingHelper::LoadAnimatedImage( TextureId                   textureId,
+                                                            Dali::AnimatedImageLoading  animatedImageLoading,
+                                                            uint32_t                    frameIndex )
+{
+  mLoadingInfoContainer.push_back( AsyncLoadingInfo( textureId ) );
+  auto id = DevelAsyncImageLoader::LoadAnimatedImage( mLoader, animatedImageLoading, frameIndex );
+  mLoadingInfoContainer.back().loadId = id;
+}
+
+void TextureManager::AsyncLoadingHelper::Load( TextureId                                textureId,
+                                               const VisualUrl&                         url,
+                                               ImageDimensions                          desiredSize,
+                                               FittingMode::Type                        fittingMode,
+                                               SamplingMode::Type                       samplingMode,
+                                               bool                                     orientationCorrection,
+                                               DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad )
 {
-  mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId));
+  mLoadingInfoContainer.push_back( AsyncLoadingInfo( textureId ) );
   auto id = DevelAsyncImageLoader::Load( mLoader, url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad );
   mLoadingInfoContainer.back().loadId = id;
 }
 
-void TextureManager::AsyncLoadingHelper::ApplyMask( TextureId textureId,
-                                                    Devel::PixelBuffer pixelBuffer,
-                                                    Devel::PixelBuffer maskPixelBuffer,
-                                                    float contentScale,
-                                                    bool cropToMask,
+void TextureManager::AsyncLoadingHelper::ApplyMask( TextureId                                textureId,
+                                                    Devel::PixelBuffer                       pixelBuffer,
+                                                    Devel::PixelBuffer                       maskPixelBuffer,
+                                                    float                                    contentScale,
+                                                    bool                                     cropToMask,
                                                     DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad )
 {
-  mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId));
+  mLoadingInfoContainer.push_back( AsyncLoadingInfo( textureId ) );
   auto id = DevelAsyncImageLoader::ApplyMask( mLoader, pixelBuffer, maskPixelBuffer, contentScale, cropToMask, preMultiplyOnLoad );
   mLoadingInfoContainer.back().loadId = id;
 }
index cc5c0e4..c3cd015 100755 (executable)
@@ -164,6 +164,33 @@ public:
   // TextureManager Main API:
 
   /**
+   * @brief Requests an frame of animated image load.
+   *
+   * The parameters are used to specify how the animated image is loaded.
+   * The observer has the LoadComplete method called when the load is ready.
+   *
+   * @param[in] animatedImageLoading  The AnimatedImageLoading that contain the animated image information
+   * @param[in] frameIndex            The frame index to load.
+   * @param[in] samplingMode          The SamplingMode to use
+   * @param[in] synchronousLoading    true if the frame should be loaded synchronously
+   * @param[out] textureId            The textureId of the frame
+   * @param[in] wrapModeU             Horizontal Wrap mode
+   * @param[in] wrapModeV             Vertical Wrap mode
+   * @param[in] textureObserver       The client object should inherit from this and provide the "UploadCompleted" virtual.
+   *                                  This is called when an image load completes (or fails).
+   *
+   * @return                          The texture set containing the frame of animated image, or empty if still loading.
+   */
+
+  TextureSet LoadAnimatedImageTexture( Dali::AnimatedImageLoading animatedImageLoading,
+                                       uint32_t frameIndex,
+                                       Dali::SamplingMode::Type samplingMode,
+                                       bool synchronousLoading,
+                                       TextureManager::TextureId& textureId,
+                                       Dali::WrapMode::Type wrapModeU, Dali::WrapMode::Type wrapModeV,
+                                       TextureUploadObserver* textureObserver );
+
+  /**
    * @brief Requests an image load of the given URL to get PixelBuffer.
    *
    * The parameters are used to specify how the image is loaded.
@@ -431,6 +458,8 @@ private:
    * @param[in] reloadPolicy          Forces a reload of the texture even if already cached
    * @param[in] preMultiplyOnLoad     True if the image color should be multiplied by it's alpha. Set to false if
    *                                  there is no alpha
+   * @param[in] animatedImageLoading  The AnimatedImageLoading to load animated image
+   * @param[in] frameIndex            The frame index of a frame to be loaded frame
    * @return                          A TextureId to use as a handle to reference this Texture
    */
   TextureId RequestLoadInternal(
@@ -446,7 +475,9 @@ private:
     TextureUploadObserver*              observer,
     bool                                orientationCorrection,
     TextureManager::ReloadPolicy        reloadPolicy,
-    MultiplyOnLoad&                     preMultiplyOnLoad );
+    MultiplyOnLoad&                     preMultiplyOnLoad,
+    Dali::AnimatedImageLoading          animatedImageLoading,
+    uint32_t                            frameIndex );
 
   /**
    * @brief Get the current state of a texture
@@ -477,7 +508,9 @@ private:
                  UseAtlas useAtlas,
                  TextureManager::TextureHash hash,
                  bool orientationCorrection,
-                 bool preMultiplyOnLoad )
+                 bool preMultiplyOnLoad,
+                 Dali::AnimatedImageLoading animatedImageLoading,
+                 uint32_t frameIndex )
     : url( url ),
       desiredSize( desiredSize ),
       useSize( desiredSize ),
@@ -491,6 +524,8 @@ private:
       fittingMode( fittingMode ),
       samplingMode( samplingMode ),
       storageType( UPLOAD_TO_TEXTURE ),
+      animatedImageLoading( animatedImageLoading ),
+      frameIndex( frameIndex ),
       loadSynchronously( loadSynchronously ),
       useAtlas( useAtlas ),
       cropToMask( cropToMask ),
@@ -522,6 +557,8 @@ private:
     FittingMode::Type fittingMode:3; ///< The requested FittingMode
     Dali::SamplingMode::Type samplingMode:3; ///< The requested SamplingMode
     StorageType storageType:2;     ///< CPU storage / GPU upload;
+    Dali::AnimatedImageLoading animatedImageLoading; ///< AnimatedImageLoading that contains animated image information.
+    uint32_t frameIndex;           ///< frame index that be loaded, in case of animated image
     bool loadSynchronously:1;      ///< True if synchronous loading was requested
     UseAtlas useAtlas:2;           ///< USE_ATLAS if an atlas was requested.
                                    ///< This is updated to false if atlas is not used
@@ -685,18 +722,20 @@ private:
    * Only applies size, fitting mode andsampling mode if the size is specified.
    * Only applies maskTextureId if it isn't INVALID_TEXTURE_ID
    * Always applies useAtlas.
-   * @param[in] url          The URL of the image to load
-   * @param[in] size         The image size
-   * @param[in] fittingMode  The FittingMode to use
-   * @param[in] samplingMode The SamplingMode to use
-   * @param[in] useAtlas     True if atlased
-   * @param[in] maskTextureId The masking texture id (or INVALID_TEXTURE_ID)
-   * @return                 A hash of the provided data for caching.
+   * @param[in] url              The URL of the image to load
+   * @param[in] size             The image size
+   * @param[in] fittingMode      The FittingMode to use
+   * @param[in] samplingMode     The SamplingMode to use
+   * @param[in] useAtlas         True if atlased
+   * @param[in] maskTextureId    The masking texture id (or INVALID_TEXTURE_ID)
+   * @param[in] isAnimatedImage  The boolean value to know whether the request is for animated image or not
+   * @param[in] frameIndex       The frame index of a frame to be loaded frame
+   * @return                     A hash of the provided data for caching.
    */
   TextureHash GenerateHash( const std::string& url, const ImageDimensions size,
                             const FittingMode::Type fittingMode,
                             const Dali::SamplingMode::Type samplingMode, const UseAtlas useAtlas,
-                            TextureId maskTextureId );
+                            TextureId maskTextureId, StorageType storageType, bool isAnimatedImage, uint32_t frameIndex );
 
   /**
    * @brief Looks up a cached texture by its hash.
@@ -710,7 +749,9 @@ private:
    * @param[in] maskTextureId     Optional texture ID to use to mask this image
    * @param[in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha.
    * @param[in] storageType       Whether the pixel data is stored in the cache, returned with PixelBuffer or uploaded to the GPU
-   * @return A TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found.
+   * @param[in] isAnimatedImage   The boolean value to know whether the request is for animated image or not
+   * @param[in] frameIndex        The frame index of a frame to be loaded frame
+   * @return                      A TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found.
    */
   TextureManager::TextureId FindCachedTexture(
     const TextureManager::TextureHash hash,
@@ -721,7 +762,9 @@ private:
     const bool useAtlas,
     TextureId maskTextureId,
     MultiplyOnLoad preMultiplyOnLoad,
-    StorageType storageType );
+    StorageType storageType,
+    bool isAnimatedImage,
+    uint32_t frameIndex );
 
 private:
 
@@ -738,6 +781,16 @@ private:
     AsyncLoadingHelper(TextureManager& textureManager);
 
     /**
+     * @brief Load a new frame of animated image
+     * @param[in] textureId             TextureId to reference the texture that will be loaded
+     * @param[in] animatedImageLoading  The AnimatedImageLoading to load animated image
+     * @param[in] frameIndex            The frame index of a frame to be loaded frame
+     */
+    void LoadAnimatedImage( TextureId textureId,
+                            Dali::AnimatedImageLoading animatedImageLoading,
+                            uint32_t frameIndex);
+
+    /**
      * @brief Load a new texture.
      * @param[in] textureId             TextureId to reference the texture that will be loaded
      * @param[in] url                   The URL of the image to load
index 6e9fd15..6b99619 100644 (file)
@@ -31,7 +31,7 @@ namespace Toolkit
 
 const unsigned int TOOLKIT_MAJOR_VERSION = 1;
 const unsigned int TOOLKIT_MINOR_VERSION = 9;
-const unsigned int TOOLKIT_MICRO_VERSION = 19;
+const unsigned int TOOLKIT_MICRO_VERSION = 20;
 const char * const TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index 0f5d58b..c28b059 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_IMAGE_VISUAL_PROPERTIES_H
 
 /*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -184,19 +184,21 @@ enum
   /**
    * @brief Defines the batch size for pre-loading images in the AnimatedImageVisual
    * @details Name "batchSize", type Property::INTEGER, number of images to pre-load
-   * before starting to play. Default value: 1
+   * before starting to play. Default value: 2
    * @SINCE_1_2.60
    * @note Optional.
+   * @note Minimum supported value is 2.
    */
   BATCH_SIZE,
 
   /**
    * @brief Defines the cache size for loading images in the AnimatedImageVisual
    * @details Name "cacheSize", type Property::INTEGER, number of images to keep
-   * cached ahead during playback. Default value: 1
+   * cached ahead during playback. Default value: 2
    *
    * @SINCE_1_2.60
    * @note Optional.
+   * @note Minimum supported value is 2.
    * @note, cacheSize should be >= batchSize.
    * If it isn't, then the cache will automatically be changed to batchSize.
    * @note, because of the defaults, it is expected that the application developer
index 3864042..b2f11d8 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    1.9.19
+Version:    1.9.20
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT