Replace registered visuals only when replacement is ready 76/135976/11
authorAgnelo Vaz <agnelo.vaz@samsung.com>
Tue, 27 Jun 2017 18:40:15 +0000 (19:40 +0100)
committerAgnelo Vaz <agnelo.vaz@samsung.com>
Mon, 3 Jul 2017 14:06:57 +0000 (15:06 +0100)
Replacing an image before the replacement has loaded was causing a flicker
Control now only sets the visual of stage when the replacement is resource ready

Change-Id: I24d04102c2f945ba2659d4f35e2eaf257b71c11b
Signed-off-by: Agnelo Vaz <agnelo.vaz@samsung.com>
automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp
dali-toolkit/devel-api/controls/control-devel.cpp
dali-toolkit/internal/controls/control/control-data-impl.cpp
dali-toolkit/internal/controls/control/control-data-impl.h
dali-toolkit/internal/controls/image-view/image-view-impl.cpp
dali-toolkit/internal/controls/image-view/image-view-impl.h
dali-toolkit/internal/visuals/svg/svg-visual.cpp
dali-toolkit/internal/visuals/text/text-visual.cpp

index 29b7a40f3d4638e091babfcd8f443dab2c8b5196..e8198cacab4c84d12d1261c15a013c4029d923fa 100644 (file)
@@ -77,6 +77,9 @@ const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
 const char* TEST_IMAGE_FILE_NAME =  "gallery_image_01.jpg";
 const char* TEST_IMAGE_FILE_NAME2 =  "gallery_image_02.jpg";
 
+const char* TEST_IMAGE_1 = TEST_RESOURCE_DIR "/TB-gloss.png";
+const char* TEST_IMAGE_2 = TEST_RESOURCE_DIR "/tb-norm.png";
+
 // resolution: 34*34, pixel format: RGBA8888
 static const char* gImage_34_RGBA = TEST_RESOURCE_DIR "/icon-edit.png";
 // resolution: 600*600, pixel format: RGB888
@@ -1302,3 +1305,52 @@ int UtcDaliImageViewGetImageN(void)
 
   END_TEST;
 }
+
+
+int UtcDaliImageViewReplaceImage(void)
+{
+  ToolkitTestApplication application;
+
+  gResourceReadySignalFired = false;
+
+  int width = 100;
+  int height = 200;
+  Image image = CreateBufferImage( width, height, Vector4(1.f, 1.f, 1.f, 1.f) );
+
+  // Check ImageView with background and main image, to ensure both visuals are marked as loaded
+  ImageView imageView = ImageView::New( TEST_IMAGE_1 );
+
+  DALI_TEST_EQUALS( Toolkit::DevelControl::IsResourceReady( imageView ), false, TEST_LOCATION );
+
+  Toolkit::DevelControl::ResourceReadySignal( imageView ).Connect( &ResourceReadySignal);
+
+  Stage::GetCurrent().Add( imageView );
+
+  application.SendNotification();
+  application.Render(16);
+
+  // loading started, this waits for the loader thread for max 30 seconds
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( imageView.GetRendererCount(), 1u, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( gResourceReadySignalFired, true, TEST_LOCATION );
+
+  gResourceReadySignalFired = false;
+
+  imageView.SetImage(TEST_IMAGE_2);
+
+  application.SendNotification();
+  application.Render(16);
+
+  // loading started, this waits for the loader thread for max 30 seconds
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( imageView.GetRendererCount(), 1u, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( Toolkit::DevelControl::IsResourceReady( imageView ), true, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( gResourceReadySignalFired, true, TEST_LOCATION );
+
+  END_TEST;
+}
index 59fd144c48282463c068fd74f14ced2c23159dda..58b76f3a8af18dd00529eb9449f7978f185071fd 100644 (file)
@@ -46,63 +46,63 @@ ResourceReadySignalType&  ResourceReadySignal( Control& control )
 bool IsResourceReady( const Control& control )
 {
   const Internal::Control& internalControl = Toolkit::Internal::GetImplementation(control);
-  const Internal::Control::Impl& controlImpl = Internal::Control::Impl::Get( internalControl );
+  const Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( internalControl );
 
-  return controlImpl.IsResourceReady();
+  return controlDataImpl.IsResourceReady();
 }
 
 void RegisterVisual( Internal::Control& control, Dali::Property::Index index, Toolkit::Visual::Base& visual )
 {
-  Internal::Control::Impl& controlImpl = Internal::Control::Impl::Get( control );
-  controlImpl.RegisterVisual( index, visual );
+  Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( control );
+  controlDataImpl.RegisterVisual( index, visual );
 }
 
 void RegisterVisual( Internal::Control& control, Dali::Property::Index index, Toolkit::Visual::Base& visual, int depthIndex )
 {
-  Internal::Control::Impl& controlImpl = Internal::Control::Impl::Get( control );
-  controlImpl.RegisterVisual( index, visual, depthIndex );
+  Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( control );
+  controlDataImpl.RegisterVisual( index, visual, depthIndex );
 }
 
 void RegisterVisual( Internal::Control& control, Dali::Property::Index index, Toolkit::Visual::Base& visual, bool enabled )
 {
-  Internal::Control::Impl& controlImpl = Internal::Control::Impl::Get( control );
-  controlImpl.RegisterVisual( index, visual, enabled );
+  Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( control );
+  controlDataImpl.RegisterVisual( index, visual, enabled );
 }
 
 void RegisterVisual( Internal::Control& control, Dali::Property::Index index, Toolkit::Visual::Base& visual, bool enabled, int depthIndex )
 {
-  Internal::Control::Impl& controlImpl = Internal::Control::Impl::Get( control );
-  controlImpl.RegisterVisual( index, visual, enabled, depthIndex );
+  Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( control );
+  controlDataImpl.RegisterVisual( index, visual, enabled, depthIndex );
 }
 
 void UnregisterVisual( Internal::Control& control, Dali::Property::Index index )
 {
-  Internal::Control::Impl& controlImpl = Internal::Control::Impl::Get( control );
-  controlImpl.UnregisterVisual( index );
+  Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( control );
+  controlDataImpl.UnregisterVisual( index );
 }
 
 Toolkit::Visual::Base GetVisual( const Internal::Control& control, Dali::Property::Index index )
 {
-  const Internal::Control::Impl& controlImpl = Internal::Control::Impl::Get( control );
-  return controlImpl.GetVisual( index );
+  const Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( control );
+  return controlDataImpl.GetVisual( index );
 }
 
 void EnableVisual( Internal::Control& control, Dali::Property::Index index, bool enable )
 {
-  Internal::Control::Impl& controlImpl = Internal::Control::Impl::Get( control );
-  controlImpl.EnableVisual( index, enable );
+  Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( control );
+  controlDataImpl.EnableVisual( index, enable );
 }
 
 bool IsVisualEnabled( const Internal::Control& control, Dali::Property::Index index )
 {
-  const Internal::Control::Impl& controlImpl = Internal::Control::Impl::Get( control );
-  return controlImpl.IsVisualEnabled( index );
+  const Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( control );
+  return controlDataImpl.IsVisualEnabled( index );
 }
 
 Dali::Animation CreateTransition( Internal::Control& control, const Toolkit::TransitionData& handle )
 {
-  Internal::Control::Impl& controlImpl = Internal::Control::Impl::Get( control );
-  return controlImpl.CreateTransition( handle );
+  Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get( control );
+  return controlDataImpl.CreateTransition( handle );
 }
 
 
index 81fa0358011622831f9e84da17e3c72c3169827d..e1d82e39f97b11bad91889ec9db0d36f7e1aabf3 100644 (file)
@@ -150,6 +150,19 @@ Toolkit::Visual::Base GetVisualByName(
   return visualHandle;
 }
 
+/**
+ * Move visual from source to destination container
+ */
+void MoveVisual( RegisteredVisualContainer::Iterator sourceIter, RegisteredVisualContainer& source, RegisteredVisualContainer& destination )
+{
+   Toolkit::Visual::Base visual = (*sourceIter)->visual;
+   if( visual )
+   {
+     RegisteredVisual* rv = source.Release( sourceIter );
+     destination.PushBack( rv );
+   }
+}
+
 /**
  * Performs actions as requested using the action name.
  * @param[in] object The object on which to perform the action.
@@ -344,9 +357,51 @@ void Control::Impl::LongPressDetected(Actor actor, const LongPressGesture& longP
 // Called by a Visual when it's resource is ready
 void Control::Impl::ResourceReady( Visual::Base& object)
 {
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "ResourceReady \n");
+
+  // A resource is ready, check if is in the replacement visual container
+  // Iterate through all visuals in replacement container and store indexes of ready visuals
+  Dali::Vector <Property::Index> readyVisuals;
+  Actor self = mControlImpl.Self();
+
+  for( auto replacementVisualIter = mReplacementVisuals.Begin();
+        replacementVisualIter < mReplacementVisuals.End(); ++replacementVisualIter )
+  {
+    const Toolkit::Visual::Base replacementVisual = (*replacementVisualIter)->visual;
+    const Internal::Visual::Base& replacementVisualImpl = Toolkit::GetImplementation( replacementVisual );
+
+    if( replacementVisualImpl.IsResourceReady() )
+    {
+      // Check if new replacement visual (index) is already queued for replacement and swap old for new.
+      RegisteredVisualContainer::Iterator registeredVisualsIter;
+      if( FindVisual( (*replacementVisualIter)->index, mVisuals, registeredVisualsIter ) )
+      {
+        Property::Index readyVisualIndex = (*replacementVisualIter)->index;
+        DALI_LOG_INFO( gLogFilter, Debug::Verbose, "ResourceReady: %d Ready to replace\n", readyVisualIndex );
+        readyVisuals.PushBack( readyVisualIndex );
+        // Remove current shown visual from stage and from registered visuals container
+        Toolkit::GetImplementation((*registeredVisualsIter)->visual).SetOffStage( self );
+        mVisuals.Erase( registeredVisualsIter );
+      }
+    }
+  }
+
+  for( auto readyVisualsIter = readyVisuals.Begin(); readyVisualsIter != readyVisuals.End(); readyVisualsIter++ )
+  {
+    DALI_LOG_INFO( gLogFilter, Debug::Verbose, "ResourceReady: %d Matched\n", (*readyVisualsIter) );
+    // Move new visual to be shown from replacement container into the control's registered visuals container
+    // Replacement visual has already been set on stage when it was added to replacement container
+    RegisteredVisualContainer::Iterator readyReplacementVisual;
+    if( FindVisual( (*readyVisualsIter) , mReplacementVisuals, readyReplacementVisual ) )
+    {
+      MoveVisual( readyReplacementVisual, mReplacementVisuals, mVisuals ); // Erases visual from replacement queue
+    }
+    // A visual has been replaced so control will most likely need relayouting
+    mControlImpl.RelayoutRequest();
+  }
 
   // go through and check if all the visuals are ready, if they are emit a signal
-  for ( RegisteredVisualContainer::ConstIterator visualIter = mVisuals.Begin();
+  for( auto visualIter = mVisuals.Begin();
         visualIter != mVisuals.End(); ++visualIter )
   {
     const Toolkit::Visual::Base visual = (*visualIter)->visual;
@@ -362,7 +417,6 @@ void Control::Impl::ResourceReady( Visual::Base& object)
   // all the visuals are ready
   Dali::Toolkit::Control handle( mControlImpl.GetOwner() );
   mResourceReadySignal.Emit( handle );
-
 }
 
 bool Control::Impl::IsResourceReady() const
@@ -405,33 +459,56 @@ void Control::Impl::RegisterVisual( Property::Index index, Toolkit::Visual::Base
 
 void Control::Impl::RegisterVisual( Property::Index index, Toolkit::Visual::Base& visual, VisualState::Type enabled, DepthIndexValue::Type depthIndexValueSet, int depthIndex )
 {
+  DALI_LOG_INFO( gLogFilter, Debug::Concise, "RegisterVisual:%d \n", index );
+
   bool visualReplaced ( false );
   Actor self = mControlImpl.Self();
 
   if( !mVisuals.Empty() )
   {
-    RegisteredVisualContainer::Iterator iter;
+    RegisteredVisualContainer::Iterator registeredVisualsiter;
     // Check if visual (index) is already registered.  Replace if so.
-    if ( FindVisual( index, mVisuals, iter ) )
+    if( FindVisual( index, mVisuals, registeredVisualsiter ) )
     {
-      if( (*iter)->visual && self.OnStage() )
+      if( (*registeredVisualsiter)->visual )
       {
-        Toolkit::GetImplementation((*iter)->visual).SetOffStage( self );
-      }
+        // Store current visual depth index as may need to set the replacement visual to same depth
+        const int currentDepthIndex = (*registeredVisualsiter)->visual.GetDepthIndex();
 
-      // If we've not set the depth-index value and the new visual does not have a depth index applied to it, then use the previously set depth-index for this index
-      if( ( depthIndexValueSet == DepthIndexValue::NOT_SET ) &&
-          ( visual.GetDepthIndex() == 0 ) )
-      {
-        const int currentDepthIndex = (*iter)->visual.GetDepthIndex();
-        visual.SetDepthIndex( currentDepthIndex );
-      }
+        // Monitor when the visuals resources are ready
+        StopObservingVisual( (*registeredVisualsiter)->visual );
+        StartObservingVisual( visual );
+
+        if(  self.OnStage() )
+        {
+          DALI_LOG_INFO( gLogFilter, Debug::Verbose, "RegisterVisual Adding visual to replacement Queue: %d \n", index );
+          // Check if visual is currently in the process of being replaced
+          RegisteredVisualContainer::Iterator queuedReplacementVisual;
+          if ( FindVisual( index, mReplacementVisuals, queuedReplacementVisual ) )
+          {
+            // If visual on replacement queue is going to be replaced before it's ready then will be removed from queue (and stage)
+            // Only the the last requested visual will be queued and then displayed.
+            Toolkit::GetImplementation( (*queuedReplacementVisual)->visual ).SetOffStage( self );
+            mReplacementVisuals.Erase(queuedReplacementVisual);
+          }
+          // Add to replacement list
+          mReplacementVisuals.PushBack( new RegisteredVisual( index, visual, ( enabled == VisualState::ENABLED ? true : false ) ) );
+        }
+        else
+        {
+          // Not staged so can just replace registered visual
+          (*registeredVisualsiter)->visual = visual;
+          (*registeredVisualsiter)->enabled = ( enabled == VisualState::ENABLED ) ? true : false;
+        }
 
-      StopObservingVisual( (*iter)->visual );
-      StartObservingVisual( visual );
+        // If we've not set the depth-index value and the new visual does not have a depth index applied to it, then use the previously set depth-index for this index
+        if( ( depthIndexValueSet == DepthIndexValue::NOT_SET ) &&
+            ( visual.GetDepthIndex() == 0 ) )
+        {
+          visual.SetDepthIndex( currentDepthIndex );
+        }
+      }
 
-      (*iter)->visual = visual;
-      (*iter)->enabled = ( enabled == VisualState::ENABLED ) ? true : false;
       visualReplaced = true;
     }
   }
@@ -458,6 +535,7 @@ void Control::Impl::RegisterVisual( Property::Index index, Toolkit::Visual::Base
 
   if( !visualReplaced ) // New registration entry
   {
+    DALI_LOG_INFO( gLogFilter, Debug::Concise, "New Visual registration %d\n", index);
     mVisuals.PushBack( new RegisteredVisual( index, visual, ( enabled == VisualState::ENABLED ? true : false ) ) );
 
     // monitor when the visuals resources are ready
@@ -497,6 +575,7 @@ void Control::Impl::RegisterVisual( Property::Index index, Toolkit::Visual::Base
     // Put on stage if enabled and the control is already on the stage
     if( ( enabled == VisualState::ENABLED ) && self.OnStage() )
     {
+      // Visual must be set on stage for the renderer to be created and the ResourceReady triggered.
       Toolkit::GetImplementation(visual).SetOnStage( self );
     }
   }
@@ -532,6 +611,8 @@ Toolkit::Visual::Base Control::Impl::GetVisual( Property::Index index ) const
 
 void Control::Impl::EnableVisual( Property::Index index, bool enable )
 {
+  DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Control::EnableVisual Visual (%d)\n", index);
+
   RegisteredVisualContainer::Iterator iter;
   if ( FindVisual( index, mVisuals, iter ) )
   {
index eceaa31960b147119685a3a74a2e7cd1562ca800..91b831486d4a3b00bfcd1c59b18ceb9059ecd079 100644 (file)
@@ -288,10 +288,13 @@ private:
   /**
    * @brief Adds the visual to the list of registered visuals.
    * @param[in] index The Property index of the visual, used to reference visual
-   * @param[in] visual The visual to register
+   * @param[in,out] visual The visual to register, which can be altered in this function
    * @param[in] enabled false if derived class wants to control when visual is set on stage
    * @param[in] depthIndexValueSet Set to true if the depthIndex has actually been set manually
    * @param[in] depthIndex The visual's depth-index is set to this
+   *
+   * @note Registering a visual with an index that already has a registered visual will replace it. The replacement will
+   *       occur once the replacement visual is ready (loaded).
    */
   void RegisterVisual( Property::Index index, Toolkit::Visual::Base& visual, VisualState::Type enabled, DepthIndexValue::Type depthIndexValueSet, int depthIndex = 0 );
 
@@ -329,6 +332,8 @@ public:
   bool mIsKeyboardNavigationSupported :1;  ///< Stores whether keyboard navigation is supported by the control.
   bool mIsKeyboardFocusGroup :1;           ///< Stores whether the control is a focus group.
 
+  RegisteredVisualContainer mReplacementVisuals;         ///< List of visuals that will be used for replacing current visuals.
+
   // Properties - these need to be members of Internal::Control::Impl as they access private methods/data of Internal::Control and Internal::Control::Impl.
   static const PropertyRegistration PROPERTY_1;
   static const PropertyRegistration PROPERTY_2;
index 65e043ebf74f198190061c7eac76ff2434dd2ea3..8ced30e8ff4ed2fb239ec367e1d462caebf8e246 100644 (file)
@@ -87,6 +87,13 @@ Toolkit::ImageView ImageView::New()
 
 /////////////////////////////////////////////////////////////
 
+void ImageView::OnInitialize()
+{
+  // ImageView can relayout in the OnImageReady, alternative to a signal would be to have a upcall from the Control to ImageView
+  Dali::Toolkit::Control handle( GetOwner() );
+  Toolkit::DevelControl::ResourceReadySignal( handle ).Connect( this, &ImageView::OnResourceReady );
+}
+
 void ImageView::SetImage( Image image )
 {
   // Don't bother comparing if we had a visual previously, just drop old visual and create new one
@@ -94,10 +101,13 @@ void ImageView::SetImage( Image image )
   mUrl.clear();
   mPropertyMap.Clear();
 
-  mVisual =  Toolkit::VisualFactory::Get().CreateVisual( image );
-  DevelControl::RegisterVisual( *this, Toolkit::ImageView::Property::IMAGE, mVisual  );
+  Toolkit::Visual::Base visual =  Toolkit::VisualFactory::Get().CreateVisual( image );
+  if (!mVisual)
+  {
+    mVisual = visual;
+  }
 
-  RelayoutRequest();
+  DevelControl::RegisterVisual( *this, Toolkit::ImageView::Property::IMAGE, visual  );
 }
 
 void ImageView::SetImage( const Property::Map& map )
@@ -106,11 +116,14 @@ void ImageView::SetImage( const Property::Map& map )
   mPropertyMap = map;
   mUrl.clear();
   mImage.Reset();
+  Toolkit::Visual::Base visual =  Toolkit::VisualFactory::Get().CreateVisual( mPropertyMap );
+  // Don't set mVisual until it is ready and shown. Getters will still use current visual.
+  if (!mVisual)
+  {
+    mVisual = visual;
+  }
 
-  mVisual =  Toolkit::VisualFactory::Get().CreateVisual( mPropertyMap );
-  DevelControl::RegisterVisual( *this, Toolkit::ImageView::Property::IMAGE, mVisual  );
-
-  RelayoutRequest();
+  DevelControl::RegisterVisual( *this, Toolkit::ImageView::Property::IMAGE, visual  );
 }
 
 void ImageView::SetImage( const std::string& url, ImageDimensions size )
@@ -120,10 +133,14 @@ void ImageView::SetImage( const std::string& url, ImageDimensions size )
   mImage.Reset();
   mPropertyMap.Clear();
 
-  mVisual =  Toolkit::VisualFactory::Get().CreateVisual( url, size );
-  DevelControl::RegisterVisual( *this, Toolkit::ImageView::Property::IMAGE, mVisual );
+  // Don't set mVisual until it is ready and shown. Getters will still use current visual.
+  Toolkit::Visual::Base visual =  Toolkit::VisualFactory::Get().CreateVisual( url, size );
+  if (!mVisual)
+  {
+    mVisual = visual;
+  }
 
-  RelayoutRequest();
+  DevelControl::RegisterVisual( *this, Toolkit::ImageView::Property::IMAGE, visual );
 }
 
 Image ImageView::GetImage() const
@@ -205,6 +222,11 @@ void ImageView::OnRelayout( const Vector2& size, RelayoutContainer& container )
   }
 }
 
+void ImageView::OnResourceReady( Toolkit::Control control )
+{
+  mVisual = DevelControl::GetVisual( *this, Toolkit::ImageView::Property::IMAGE );
+}
+
 ///////////////////////////////////////////////////////////
 //
 // Properties
index ac76e7805960f1f3bad9057f17f2e1764afcce73..f335ce7a28d62b3aafc18afdf246437486b688ff 100644 (file)
@@ -122,6 +122,11 @@ public:
 
 private: // From Control
 
+  /**
+   * @copydoc Toolkit::Control::OnInitialize
+   */
+  void OnInitialize();
+
   /**
    * @copydoc Toolkit::Control::GetNaturalSize
    */
@@ -142,6 +147,14 @@ private: // From Control
    */
   virtual void OnRelayout( const Vector2& size, RelayoutContainer& container );
 
+private:
+
+  /**
+   * @brief Callback for ResourceReadySignal
+   * param[in] control signal prototype
+   */
+  void OnResourceReady( Toolkit::Control control );
+
 private:
   // Undefined
   ImageView( const ImageView& );
index a1da87f48c1275bb14a1e5520bd0fd56ec6e5e6f..1e9522349c44830fbe34d7a92f91fdb453e60b0a 100644 (file)
@@ -110,6 +110,9 @@ void SvgVisual::DoSetOnStage( Actor& actor )
 
   // Hold the weak handle of the placement actor and delay the adding of renderer until the svg rasterization is finished.
   mPlacementActor = actor;
+
+  // SVG visual needs it's size set before it can be rasterized hence set ResourceReady once on stage
+  ResourceReady();
 }
 
 void SvgVisual::DoSetOffStage( Actor& actor )
index 3ff4ce87cd071542a81a09c538ec305b69d240d2..2b5215ac49e337d5390ce3cb67b96d2da59c4680 100644 (file)
@@ -457,6 +457,7 @@ void TextVisual::UpdateRenderer( bool initializeRendererAndTexture )
     }
 
     // Nothing else to do if the relayout size is zero.
+    ResourceReady();
     return;
   }