[dali_2.3.22] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / npatch-data.cpp
index 1097395..28c3ebd 100644 (file)
@@ -1,5 +1,5 @@
- /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+/*
+ * Copyright (c) 2023 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.
 
 namespace Dali
 {
-
 namespace Toolkit
 {
-
 namespace Internal
 {
-
 NPatchData::NPatchData()
 : mId(INVALID_NPATCH_DATA_ID),
   mUrl(),
@@ -41,18 +38,22 @@ NPatchData::NPatchData()
   mCroppedWidth(0),
   mCroppedHeight(0),
   mBorder(0, 0, 0, 0),
-  mLoadingState(LoadingState::LOADING),
-  mRenderingMap{nullptr}
+  mLoadingState(LoadingState::NOT_STARTED),
+  mRenderingMap{nullptr},
+  mPreMultiplyOnLoad(false),
+  mObserverNotifying(false)
 {
 }
 
 NPatchData::~NPatchData()
 {
   // If there is an opacity map, it has to be destroyed using addon call
-  if( mRenderingMap )
+  if(mRenderingMap)
   {
-    RenderingAddOn::Get().DestroyNPatch( mRenderingMap );
+    RenderingAddOn::Get().DestroyNPatch(mRenderingMap);
   }
+  mObserverList.Clear();
+  mQueuedObservers.Clear();
 }
 
 void NPatchData::SetId(const NPatchDataId id)
@@ -67,17 +68,33 @@ NPatchData::NPatchDataId NPatchData::GetId() const
 
 void NPatchData::AddObserver(TextureUploadObserver* textureObserver)
 {
-  mObserverList.PushBack( textureObserver );
+  if(textureObserver)
+  {
+    if(mObserverNotifying)
+    {
+      // Do not add it into observer list during observer notifying.
+      mQueuedObservers.PushBack(textureObserver);
+    }
+    else
+    {
+      mObserverList.PushBack(textureObserver);
+    }
+    textureObserver->DestructionSignal().Connect(this, &NPatchData::ObserverDestroyed);
+  }
 }
 
 void NPatchData::RemoveObserver(TextureUploadObserver* textureObserver)
 {
-  for(uint32_t index = 0; index < mObserverList.Count(); ++index )
+  if(textureObserver)
   {
-    if(textureObserver == mObserverList[index])
+    for(uint32_t index = 0; index < mObserverList.Count(); ++index)
     {
-      mObserverList.Erase( mObserverList.begin() + index );
-      break;
+      if(textureObserver == mObserverList[index])
+      {
+        textureObserver->DestructionSignal().Disconnect(this, &NPatchData::ObserverDestroyed);
+        mObserverList.Erase(mObserverList.begin() + index);
+        break;
+      }
     }
   }
 }
@@ -87,12 +104,12 @@ uint32_t NPatchData::GetObserverCount() const
   return mObserverList.Count();
 }
 
-void NPatchData::SetUrl(const std::string url)
+void NPatchData::SetUrl(const VisualUrl& url)
 {
   mUrl = url;
 }
 
-std::string NPatchData::GetUrl() const
+VisualUrl NPatchData::GetUrl() const
 {
   return mUrl;
 }
@@ -192,55 +209,127 @@ void* NPatchData::GetRenderingMap() const
   return mRenderingMap;
 }
 
-void NPatchData::SetLoadedNPatchData( Devel::PixelBuffer& pixelBuffer, bool preMultiplied )
+void NPatchData::SetLoadedNPatchData(Devel::PixelBuffer& pixelBuffer, bool preMultiplied)
 {
-  if( mBorder == Rect< int >( 0, 0, 0, 0 ) )
+  if(mBorder == Rect<int>(0, 0, 0, 0))
   {
-    NPatchUtility::ParseBorders( pixelBuffer, mStretchPixelsX, mStretchPixelsY );
+    NPatchUtility::ParseBorders(pixelBuffer, mStretchPixelsX, mStretchPixelsY);
 
     // Crop the image
-    pixelBuffer.Crop( 1, 1, pixelBuffer.GetWidth() - 2, pixelBuffer.GetHeight() - 2 );
+    pixelBuffer.Crop(1, 1, pixelBuffer.GetWidth() - 2, pixelBuffer.GetHeight() - 2);
   }
   else
   {
-    mStretchPixelsX.PushBack( Uint16Pair( mBorder.left, ( (pixelBuffer.GetWidth() >= static_cast< unsigned int >( mBorder.right )) ? pixelBuffer.GetWidth() - mBorder.right : 0 ) ) );
-    mStretchPixelsY.PushBack( Uint16Pair( mBorder.top, ( (pixelBuffer.GetHeight() >= static_cast< unsigned int >( mBorder.bottom )) ? pixelBuffer.GetHeight() - mBorder.bottom : 0 ) ) );
+    mStretchPixelsX.PushBack(Uint16Pair(mBorder.left, ((pixelBuffer.GetWidth() >= static_cast<unsigned int>(mBorder.right)) ? pixelBuffer.GetWidth() - mBorder.right : 0)));
+    mStretchPixelsY.PushBack(Uint16Pair(mBorder.top, ((pixelBuffer.GetHeight() >= static_cast<unsigned int>(mBorder.bottom)) ? pixelBuffer.GetHeight() - mBorder.bottom : 0)));
   }
 
-  mCroppedWidth = pixelBuffer.GetWidth();
+  mCroppedWidth  = pixelBuffer.GetWidth();
   mCroppedHeight = pixelBuffer.GetHeight();
 
   // Create opacity map
   mRenderingMap = RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().BuildNPatch(pixelBuffer, this) : nullptr;
 
-  PixelData pixels = Devel::PixelBuffer::Convert( pixelBuffer ); // takes ownership of buffer
+  PixelData pixels = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
 
-  Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() );
-  texture.Upload( pixels );
+  Texture texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
+  texture.Upload(pixels);
 
   mTextureSet = TextureSet::New();
-  mTextureSet.SetTexture( 0u, texture );
+  mTextureSet.SetTexture(0u, texture);
 
   mPreMultiplyOnLoad = preMultiplied;
 
   mLoadingState = LoadingState::LOAD_COMPLETE;
 }
 
-void NPatchData::LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied )
+void NPatchData::NotifyObserver(TextureUploadObserver* observer, const bool& loadSuccess)
 {
+  observer->LoadComplete(
+    loadSuccess,
+    TextureUploadObserver::TextureInformation(
+      TextureUploadObserver::ReturnType::TEXTURE,
+      static_cast<TextureManager::TextureId>(mId), ///< Note : until end of NPatchLoader::Load, npatch-visual don't know the id of data.
+      mTextureSet,
+      mUrl.GetUrl(),
+      mPreMultiplyOnLoad));
+}
+
+void NPatchData::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
+{
+  NPatchDataPtr self = this; // Keep reference until this API finished
+
   if(loadSuccess)
   {
-    SetLoadedNPatchData( pixelBuffer, preMultiplied );
+    if(mLoadingState != LoadingState::LOAD_COMPLETE)
+    {
+      // If mLoadingState is LOAD_FAILED, just re-set (It can be happened when sync loading is failed, but async loading is succeeded).
+      SetLoadedNPatchData(textureInformation.pixelBuffer, textureInformation.preMultiplied);
+    }
   }
   else
   {
-    mLoadingState = LoadingState::LOAD_FAILED;
+    if(mLoadingState == LoadingState::LOADING)
+    {
+      mLoadingState = LoadingState::LOAD_FAILED;
+    }
+    // If mLoadingState is already LOAD_COMPLETE, we can use uploaded texture (It can be happened when sync loading is succeeded, but async loading is failed).
+    else if(mLoadingState == LoadingState::LOAD_COMPLETE)
+    {
+      loadSuccess = true;
+    }
+  }
+
+  mObserverNotifying = true;
+
+  // Reverse observer list that we can pop_back the observer.
+  std::reverse(mObserverList.Begin(), mObserverList.End());
+
+  while(mObserverList.Count() > 0u)
+  {
+    TextureUploadObserver* observer = *(mObserverList.End() - 1u);
+    mObserverList.Erase(mObserverList.End() - 1u);
+
+    observer->DestructionSignal().Disconnect(this, &NPatchData::ObserverDestroyed);
+
+    NotifyObserver(observer, loadSuccess);
   }
 
-  for(uint32_t index = 0; index < mObserverList.Count(); ++index)
+  mObserverNotifying = false;
+
+  // Swap observer list what we queued during notify observer.
+  // If mQueuedObserver is not empty, it mean mLoadingState was LOAD_FAILED, and we try to re-load for this data.
+  // (If mLoadingState was LOAD_COMPLETE, NotifyObserver will be called directly. @todo : Should we fix this logic, matched with texture manager?)
+  // So LoadComplete will be called.
+  mObserverList.Swap(mQueuedObservers);
+}
+
+void NPatchData::ObserverDestroyed(TextureUploadObserver* observer)
+{
+  for(auto iter = mObserverList.Begin(); iter != mObserverList.End();)
   {
-    TextureUploadObserver* observer = mObserverList[index];
-    observer->UploadComplete(loadSuccess, TextureManager::INVALID_TEXTURE_ID, mTextureSet, false, Vector4(), preMultiplied);
+    if(observer == (*iter))
+    {
+      iter = mObserverList.Erase(iter);
+    }
+    else
+    {
+      ++iter;
+    }
+  }
+  if(mObserverNotifying)
+  {
+    for(auto iter = mQueuedObservers.Begin(); iter != mQueuedObservers.End();)
+    {
+      if(observer == (*iter))
+      {
+        iter = mQueuedObservers.Erase(iter);
+      }
+      else
+      {
+        ++iter;
+      }
+    }
   }
 }