[Tizen] Guard NPatchData removal case during signal emit 96/306396/1 accepted/tizen/7.0/unified/20240222.070145
authorEunki, Hong <eunkiki.hong@samsung.com>
Tue, 29 Aug 2023 06:52:47 +0000 (15:52 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Wed, 21 Feb 2024 05:18:01 +0000 (14:18 +0900)
This is a combination of 3 commits.

Guard NPatchData removal case during signal emit

It was possible that NPatchData removed during NotifyObserver.
For more safety, let we make NPatchData as shared_ptr, instead
of unique_ptr.

And also, let we don't touch observer list container during
NotifyObservers.

And also, let we remove NPatchInfo at post processing.
It will keep the life of NPatchData during NotifyObserver try to
remove the NPatchInfo.

Change-Id: Ieca33a2231df38d23966f96593506d67333cfcd0
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
Remove NPatchLoader observer when they request remove

Since we make remove NPatchData later, the observer was disconnected lately.
It might make some unusual behavior.

Change-Id: I794f22e21964962428f353ee84a9340687274808
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
Make NPatchData always use shared pointer

Let we keep NPatchData user always use shared_ptr instead of raw pointer.
In this case, NPatchData itself will be safe enought even if
NPatchData removed from NPatchLoader cache.

Change-Id: I27db855888bc21c7a7282a9e1114945f3f0e758d
Signed-off-by: Eunki Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp
dali-toolkit/devel-api/utility/npatch-helper.cpp
dali-toolkit/devel-api/utility/npatch-helper.h
dali-toolkit/internal/visuals/npatch-data.cpp
dali-toolkit/internal/visuals/npatch-data.h
dali-toolkit/internal/visuals/npatch-loader.cpp
dali-toolkit/internal/visuals/npatch-loader.h
dali-toolkit/internal/visuals/npatch/npatch-visual.cpp
dali-toolkit/internal/visuals/visual-factory-cache.cpp

index 6d16933..4831d7f 100644 (file)
@@ -71,6 +71,8 @@ const char* TEST_BROKEN_IMAGE_L       = TEST_RESOURCE_DIR "/broken_l.9.png";
 const char* TEST_BROKEN_IMAGE_01      = TEST_RESOURCE_DIR "/button-up.9.png";
 const char* TEST_BROKEN_IMAGE_02      = TEST_RESOURCE_DIR "/heartsframe.9.png";
 
+const char* TEST_INVALID_NPATCH_FILE_NAME_01 = "invalid1.9.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
@@ -4359,6 +4361,11 @@ int UtcDaliImageViewNpatchImageCacheTest01(void)
     {
       imageView[index].Unparent();
     }
+
+    // Ensure remove npatch cache if required.
+    application.SendNotification();
+    application.Render();
+
     imageView[index] = ImageView::New(nPatchImageUrl);
     imageView[index].SetProperty(Actor::Property::SIZE, Vector2(100.0f, 200.0f));
     application.GetScene().Add(imageView[index]);
index 53c6d4d..929dedb 100644 (file)
@@ -250,7 +250,7 @@ void RegisterStretchProperties(Renderer& renderer, const char* uniformName, cons
   }
 }
 
-void ApplyTextureAndUniforms(Renderer& renderer, const Internal::NPatchData* data)
+void ApplyTextureAndUniforms(Renderer& renderer, const std::shared_ptr<const Internal::NPatchData> data)
 {
   TextureSet        textureSet;
   textureSet = data->GetTextures();
index 7a198fe..2427063 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali/public-api/math/uint-16-pair.h>
 #include <dali/public-api/rendering/geometry.h>
 #include <dali/devel-api/rendering/renderer-devel.h>
+#include <memory> ///< for std::shared_ptr
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/public-api/dali-toolkit-common.h>
@@ -86,7 +87,7 @@ void RegisterStretchProperties(Renderer& renderer, const char* uniformName, cons
  * @param[in,out] renderer The renderer for broken image
  * @param[in] data The pointer of npatch-data
  */
-void ApplyTextureAndUniforms(Renderer& renderer, const Internal::NPatchData* data);
+void ApplyTextureAndUniforms(Renderer& renderer, const std::shared_ptr<const Internal::NPatchData> data);
 
 } // namespace NPatchUtility
 
index dc0a055..5c0c474 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 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.
@@ -39,8 +39,9 @@ NPatchData::NPatchData()
   mCroppedHeight(0),
   mBorder(0, 0, 0, 0),
   mLoadingState(LoadingState::NOT_STARTED),
+  mRenderingMap{nullptr},
   mPreMultiplyOnLoad(false),
-  mRenderingMap{nullptr}
+  mObserverNotifying(false)
 {
 }
 
@@ -51,6 +52,8 @@ NPatchData::~NPatchData()
   {
     RenderingAddOn::Get().DestroyNPatch(mRenderingMap);
   }
+  mObserverList.Clear();
+  mQueuedObservers.Clear();
 }
 
 void NPatchData::SetId(const NPatchDataId id)
@@ -65,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;
+      }
     }
   }
 }
@@ -259,13 +278,57 @@ void NPatchData::LoadComplete(bool loadSuccess, TextureInformation textureInform
     }
   }
 
-  for(uint32_t index = 0; index < mObserverList.Count(); ++index)
+  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[index];
+    TextureUploadObserver* observer = *(mObserverList.End() - 1u);
+    mObserverList.Erase(mObserverList.End() - 1u);
+
+    observer->DestructionSignal().Disconnect(this, &NPatchData::ObserverDestroyed);
+
     NotifyObserver(observer, loadSuccess);
   }
 
-  mObserverList.Clear();
+  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();)
+  {
+    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;
+      }
+    }
+  }
 }
 
 } // namespace Internal
index cc520d8..3df4b29 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_NPATCH_DATA_H
 
 /*
- * Copyright (c) 2022 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.
@@ -33,11 +33,11 @@ namespace Toolkit
 {
 namespace Internal
 {
-class NPatchData : public Dali::Toolkit::TextureUploadObserver
+class NPatchData : public ConnectionTracker, public Dali::Toolkit::TextureUploadObserver
 {
 public:
-  typedef int32_t  NPatchDataId;                ///< The NPatchDataId type. This is used as a handle to refer to a particular Npatch Data.
-  static const int INVALID_NPATCH_DATA_ID = -1; ///< Used to represent a null TextureId or error
+  typedef int32_t           NPatchDataId;                ///< The NPatchDataId type. This is used as a handle to refer to a particular Npatch Data.
+  static const NPatchDataId INVALID_NPATCH_DATA_ID = -1; ///< Used to represent a null TextureId or error
 
   /**
    * @brief Loading State of the NPatch image.
@@ -269,22 +269,32 @@ private:
    */
   void LoadComplete(bool loadSuccess, TextureInformation textureInformation) override;
 
+  /**
+   * This is called by the TextureUploadObserver when an observer is destroyed.
+   * We use the callback to know when to remove an observer from our notify list.
+   * @param[in] observer The observer that generated the callback
+   */
+  void ObserverDestroyed(TextureUploadObserver* observer);
+
 private:
   using ObserverListType = Dali::Vector<TextureUploadObserver*>;
 
   NPatchDataId                 mId;
-  ObserverListType             mObserverList;      ///< Container used to store all observer clients of this Texture
-  VisualUrl                    mUrl;               ///< Url of the N-Patch
-  TextureSet                   mTextureSet;        ///< Texture containing the cropped image
-  NPatchUtility::StretchRanges mStretchPixelsX;    ///< X stretch pixels
-  NPatchUtility::StretchRanges mStretchPixelsY;    ///< Y stretch pixels
-  std::size_t                  mHash;              ///< Hash code for the Url
-  uint32_t                     mCroppedWidth;      ///< Width of the cropped middle part of N-patch
-  uint32_t                     mCroppedHeight;     ///< Height of the cropped middle part of N-patch
-  Rect<int>                    mBorder;            ///< The size of the border
-  LoadingState                 mLoadingState;      ///< True if the data loading is completed
-  bool                         mPreMultiplyOnLoad; ///< Whether to multiply alpha into color channels on load
-  void*                        mRenderingMap;      ///< NPatch rendering data
+  ObserverListType             mObserverList;    ///< Container used to store all observer clients of this Texture
+  ObserverListType             mQueuedObservers; ///< Container observers when user try to add during notify observers
+  VisualUrl                    mUrl;             ///< Url of the N-Patch
+  TextureSet                   mTextureSet;      ///< Texture containing the cropped image
+  NPatchUtility::StretchRanges mStretchPixelsX;  ///< X stretch pixels
+  NPatchUtility::StretchRanges mStretchPixelsY;  ///< Y stretch pixels
+  std::size_t                  mHash;            ///< Hash code for the Url
+  uint32_t                     mCroppedWidth;    ///< Width of the cropped middle part of N-patch
+  uint32_t                     mCroppedHeight;   ///< Height of the cropped middle part of N-patch
+  Rect<int>                    mBorder;          ///< The size of the border
+  LoadingState                 mLoadingState;    ///< True if the data loading is completed
+  void*                        mRenderingMap;    ///< NPatch rendering data
+
+  bool mPreMultiplyOnLoad : 1; ///< Whether to multiply alpha into color channels on load
+  bool mObserverNotifying : 1; ///< Whether this NPatchData notifying observers or not.
 };
 
 } // namespace Internal
index 5b2b713..24d919a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 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.
@@ -23,6 +23,7 @@
 
 // EXTERNAL HEADERS
 #include <dali/devel-api/common/hash.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
 #include <dali/integration-api/debug.h>
 
 namespace Dali
@@ -39,24 +40,35 @@ constexpr auto UNINITIALIZED_ID    = int32_t{0};  ///< uninitialised id, use to
 } // Anonymous namespace
 
 NPatchLoader::NPatchLoader()
-: mCurrentNPatchDataId(0)
+: mCurrentNPatchDataId(0),
+  mRemoveProcessorRegistered(false)
 {
 }
 
 NPatchLoader::~NPatchLoader()
 {
+  if(mRemoveProcessorRegistered && Adaptor::IsAvailable())
+  {
+    Adaptor::Get().UnregisterProcessor(*this, true);
+    mRemoveProcessorRegistered = false;
+  }
 }
 
 NPatchData::NPatchDataId NPatchLoader::GenerateUniqueNPatchDataId()
 {
+  // Skip invalid id generation.
+  if(DALI_UNLIKELY(mCurrentNPatchDataId == NPatchData::INVALID_NPATCH_DATA_ID))
+  {
+    mCurrentNPatchDataId = 0;
+  }
   return mCurrentNPatchDataId++;
 }
 
-std::size_t NPatchLoader::Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading)
+NPatchData::NPatchDataId NPatchLoader::Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading)
 {
-  NPatchData* data = GetNPatchData(url, border, preMultiplyOnLoad);
+  std::shared_ptr<NPatchData> data = GetNPatchData(url, border, preMultiplyOnLoad);
 
-  DALI_ASSERT_ALWAYS(data && "NPatchData creation failed!");
+  DALI_ASSERT_ALWAYS(data.get() && "NPatchData creation failed!");
 
   if(data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
   {
@@ -85,7 +97,7 @@ std::size_t NPatchLoader::Load(TextureManager& textureManager, TextureUploadObse
     auto preMultiplyOnLoading = preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
                                                   : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
 
-    Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(url, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, data, true, preMultiplyOnLoading);
+    Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(url, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, data.get(), true, preMultiplyOnLoading);
 
     if(pixelBuffer)
     {
@@ -115,19 +127,42 @@ int32_t NPatchLoader::GetCacheIndexFromId(const NPatchData::NPatchDataId id)
   return INVALID_CACHE_INDEX;
 }
 
-bool NPatchLoader::GetNPatchData(const NPatchData::NPatchDataId id, const NPatchData*& data)
+bool NPatchLoader::GetNPatchData(const NPatchData::NPatchDataId id, std::shared_ptr<const NPatchData>& data)
 {
   int32_t cacheIndex = GetCacheIndexFromId(id);
   if(cacheIndex != INVALID_CACHE_INDEX)
   {
-    data = mCache[cacheIndex].mData.get();
+    data = mCache[cacheIndex].mData;
     return true;
   }
   data = nullptr;
   return false;
 }
 
-void NPatchLoader::Remove(std::size_t id, TextureUploadObserver* textureObserver)
+void NPatchLoader::RequestRemove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver)
+{
+  // Remove observer first
+  if(textureObserver)
+  {
+    int32_t cacheIndex = GetCacheIndexFromId(id);
+    if(cacheIndex != INVALID_CACHE_INDEX)
+    {
+      NPatchInfo& info(mCache[cacheIndex]);
+
+      info.mData->RemoveObserver(textureObserver);
+    }
+  }
+
+  mRemoveQueue.push_back({id, nullptr});
+
+  if(!mRemoveProcessorRegistered && Adaptor::IsAvailable())
+  {
+    mRemoveProcessorRegistered = true;
+    Adaptor::Get().RegisterProcessor(*this, true);
+  }
+}
+
+void NPatchLoader::Remove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver)
 {
   int32_t cacheIndex = GetCacheIndexFromId(id);
   if(cacheIndex == INVALID_CACHE_INDEX)
@@ -145,7 +180,22 @@ void NPatchLoader::Remove(std::size_t id, TextureUploadObserver* textureObserver
   }
 }
 
-NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad)
+void NPatchLoader::Process(bool postProcessor)
+{
+  for(auto& iter : mRemoveQueue)
+  {
+    Remove(iter.first, iter.second);
+  }
+  mRemoveQueue.clear();
+
+  if(Adaptor::IsAvailable())
+  {
+    Adaptor::Get().UnregisterProcessor(*this, true);
+    mRemoveProcessorRegistered = false;
+  }
+}
+
+std::shared_ptr<NPatchData> NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad)
 {
   std::size_t                              hash  = CalculateHash(url.GetUrl());
   std::vector<NPatchInfo>::size_type       index = UNINITIALIZED_ID;
@@ -164,7 +214,7 @@ NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& b
         if(mCache[index].mData->GetBorder() == border)
         {
           mCache[index].mReferenceCount++;
-          return mCache[index].mData.get();
+          return mCache[index].mData;
         }
         else
         {
@@ -197,7 +247,7 @@ NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& b
   // If this is new image loading, make new cache data
   if(infoPtr == nullptr)
   {
-    NPatchInfo info(new NPatchData());
+    NPatchInfo info(std::make_shared<NPatchData>());
     info.mData->SetId(GenerateUniqueNPatchDataId());
     info.mData->SetHash(hash);
     info.mData->SetUrl(url);
@@ -210,7 +260,7 @@ NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& b
   // Else if LOAD_COMPLETE, Same url but border is different - use the existing texture
   else if(infoPtr->mData->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
   {
-    NPatchInfo info(new NPatchData());
+    NPatchInfo info(std::make_shared<NPatchData>());
 
     info.mData->SetId(GenerateUniqueNPatchDataId());
     info.mData->SetHash(hash);
@@ -245,7 +295,7 @@ NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& b
 
   DALI_ASSERT_ALWAYS(infoPtr && "NPatchInfo creation failed!");
 
-  return infoPtr->mData.get();
+  return infoPtr->mData;
 }
 
 } // namespace Internal
index 539009b..a55ab6f 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_NPATCH_LOADER_H
 
 /*
- * Copyright (c) 2022 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.
 
 // EXTERNAL INCLUDES
 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/integration-api/processor-interface.h>
 #include <dali/public-api/rendering/texture-set.h>
-#include <memory> // for std::unique_ptr
+#include <memory> // for std::shared_ptr
 #include <string>
+#include <utility> // for std::pair
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/utility/npatch-utilities.h>
@@ -44,7 +46,7 @@ namespace Internal
  * small space and there's not usually a lot of them. Usually N patches are specified in
  * toolkit default style and there is 1-2 per control that are shared across the whole application.
  */
-class NPatchLoader
+class NPatchLoader : public Integration::Processor
 {
 public:
   /**
@@ -69,16 +71,7 @@ public:
    * @param [in] synchronousLoading True if the image will be loaded in synchronous time.
    * @return id of the texture.
    */
-  std::size_t Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading);
-
-  /**
-   * @brief Set loaded PixelBuffer and its information
-   *
-   * @param [in] id cache data id
-   * @param [in] pixelBuffer of loaded image
-   * @param [in] preMultiplied True if the image had pre-multiplied alpha applied
-   */
-  void SetNPatchData(std::size_t id, Devel::PixelBuffer& pixelBuffer, bool preMultiplied);
+  NPatchData::NPatchDataId Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading);
 
   /**
    * @brief Retrieve N patch data matching to an id
@@ -86,24 +79,39 @@ public:
    * @param [out] data const pointer to the NPatchData
    * @return true if data matching to id was really found
    */
-  bool GetNPatchData(const NPatchData::NPatchDataId id, const NPatchData*& data);
+  bool GetNPatchData(const NPatchData::NPatchDataId id, std::shared_ptr<const NPatchData>& data);
 
   /**
-   * @brief Remove a texture matching id.
+   * @brief Request remove a texture matching id.
    * Erase the observer from the observer list of cache if we need.
-   * This API decrease cached NPatchInfo reference.
-   * If the NPatchInfo reference become 0, the textureSet will be reset.
    *
    * @param [in] id cache data id
    * @param [in] textureObserver The NPatchVisual that requested loading.
    */
-  void Remove(std::size_t id, TextureUploadObserver* textureObserver);
+  void RequestRemove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver);
+
+protected: // Implementation of Processor
+  /**
+   * @copydoc Dali::Integration::Processor::Process()
+   */
+  void Process(bool postProcessor) override;
 
 private:
   NPatchData::NPatchDataId GenerateUniqueNPatchDataId();
 
   int32_t GetCacheIndexFromId(const NPatchData::NPatchDataId id);
 
+  /**
+   * @brief Remove a texture matching id.
+   * Erase the observer from the observer list of cache if we need.
+   * This API decrease cached NPatchInfo reference.
+   * If the NPatchInfo reference become 0, the textureSet will be reset.
+   *
+   * @param [in] id cache data id
+   * @param [in] textureObserver The NPatchVisual that requested loading.
+   */
+  void Remove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver);
+
 private:
   /**
    * @brief Information of NPatchData
@@ -111,7 +119,7 @@ private:
    */
   struct NPatchInfo
   {
-    NPatchInfo(NPatchData* data)
+    NPatchInfo(std::shared_ptr<NPatchData> data)
     : mData(data),
       mReferenceCount(1u)
     {
@@ -137,7 +145,7 @@ private:
     NPatchInfo(const NPatchInfo& info) = delete;            // Do not use copy constructor
     NPatchInfo& operator=(const NPatchInfo& info) = delete; // Do not use copy assign
 
-    std::unique_ptr<NPatchData> mData;
+    std::shared_ptr<NPatchData> mData;
     std::int16_t                mReferenceCount; ///< The number of N-patch visuals that use this data.
   };
 
@@ -151,7 +159,7 @@ private:
    *                                   image has no alpha channel
    * @return NPatchData pointer that Load function will used.
    */
-  NPatchData* GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad);
+  std::shared_ptr<NPatchData> GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad);
 
 protected:
   /**
@@ -167,6 +175,10 @@ protected:
 private:
   NPatchData::NPatchDataId mCurrentNPatchDataId;
   std::vector<NPatchInfo>  mCache;
+
+  std::vector<std::pair<NPatchData::NPatchDataId, TextureUploadObserver*>> mRemoveQueue; ///< Queue of textures to remove at PostProcess. It will be cleared after PostProcess.
+
+  bool mRemoveProcessorRegistered : 1; ///< Flag if remove processor registered or not.
 };
 
 } // namespace Internal
index 8b44d99..a4929e1 100644 (file)
@@ -79,7 +79,7 @@ void NPatchVisual::LoadImages()
     bool preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader ? true : false;
     mId                    = mLoader.Load(textureManager, this, mImageUrl, mBorder, preMultiplyOnLoad, synchronousLoading);
 
-    const NPatchData* data;
+    std::shared_ptr<const NPatchData> data;
     if(mLoader.GetNPatchData(mId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
     {
       EnablePreMultipliedAlpha(data->IsPreMultiplied());
@@ -125,7 +125,7 @@ void NPatchVisual::GetNaturalSize(Vector2& naturalSize)
   naturalSize.y = 0u;
 
   // load now if not already loaded
-  const NPatchData* data;
+  std::shared_ptr<const NPatchData> data;
   if(mLoader.GetNPatchData(mId, data) && data->GetLoadingState() != NPatchData::LoadingState::LOADING)
   {
     naturalSize.x = data->GetCroppedWidth();
@@ -222,7 +222,7 @@ void NPatchVisual::DoSetOnScene(Actor& actor)
   // at this case, we try to SetResouce to mPlaceActor twice. so, we should avoid that case.
   mPlacementActor = actor;
 
-  const NPatchData* data;
+  std::shared_ptr<const NPatchData> data;
   if(mImpl->mRenderer && mLoader.GetNPatchData(mId, data) && data->GetLoadingState() != NPatchData::LoadingState::LOADING)
   {
     // If mAuxiliaryUrl need to be loaded, we should wait it until LoadComplete called.
@@ -239,7 +239,7 @@ void NPatchVisual::DoSetOffScene(Actor& actor)
   {
     if(mId != NPatchData::INVALID_NPATCH_DATA_ID)
     {
-      mLoader.Remove(mId, this);
+      mLoader.RequestRemove(mId, this);
       mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
       mId                    = NPatchData::INVALID_NPATCH_DATA_ID;
     }
@@ -319,7 +319,7 @@ NPatchVisual::~NPatchVisual()
     {
       if(mId != NPatchData::INVALID_NPATCH_DATA_ID)
       {
-        mLoader.Remove(mId, this);
+        mLoader.RequestRemove(mId, this);
         mId = NPatchData::INVALID_NPATCH_DATA_ID;
       }
       if(mAuxiliaryTextureId != TextureManager::INVALID_TEXTURE_ID)
@@ -350,8 +350,8 @@ void NPatchVisual::OnInitialize()
 
 Geometry NPatchVisual::CreateGeometry()
 {
-  Geometry          geometry;
-  const NPatchData* data;
+  Geometry                          geometry;
+  std::shared_ptr<const NPatchData> data;
   if(mLoader.GetNPatchData(mId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
   {
     if(data->GetStretchPixelsX().Size() == 1 && data->GetStretchPixelsY().Size() == 1)
@@ -405,8 +405,8 @@ Geometry NPatchVisual::CreateGeometry()
 
 Shader NPatchVisual::CreateShader()
 {
-  Shader            shader;
-  const NPatchData* data;
+  Shader                            shader;
+  std::shared_ptr<const NPatchData> data;
   // 0 is either no data (load failed?) or no stretch regions on image
   // for both cases we use the default shader
   NPatchUtility::StretchRanges::SizeType xStretchCount = 0;
@@ -485,8 +485,8 @@ Shader NPatchVisual::CreateShader()
 
 void NPatchVisual::ApplyTextureAndUniforms()
 {
-  const NPatchData* data;
-  TextureSet        textureSet;
+  std::shared_ptr<const NPatchData> data;
+  TextureSet                        textureSet;
 
   if(mLoader.GetNPatchData(mId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
   {
@@ -548,7 +548,7 @@ Geometry NPatchVisual::GetNinePatchGeometry(VisualFactoryCache::GeometryType sub
 
 void NPatchVisual::SetResource()
 {
-  const NPatchData* data;
+  std::shared_ptr<const NPatchData> data;
   if(mImpl->mRenderer && mLoader.GetNPatchData(mId, data))
   {
     Geometry geometry = CreateGeometry();
@@ -625,7 +625,7 @@ void NPatchVisual::LoadComplete(bool loadSuccess, TextureInformation textureInfo
   // If auxiliaryUrl didn't required OR auxiliaryUrl load done.
   if(!mAuxiliaryUrl.IsValid() || mAuxiliaryResourceStatus != Toolkit::Visual::ResourceStatus::PREPARING)
   {
-    const NPatchData* data;
+    std::shared_ptr<const NPatchData> data;
     // and.. If Url loading done.
     if(mImpl->mRenderer && mLoader.GetNPatchData(mId, data) && data->GetLoadingState() != NPatchData::LoadingState::LOADING)
     {
index 6b4bf10..3001091 100644 (file)
@@ -264,8 +264,8 @@ VisualUrl::Type VisualFactoryCache::GetBrokenImageVisualType(int index)
 
 Geometry VisualFactoryCache::GetNPatchGeometry(int index)
 {
-  Geometry          geometry;
-  const NPatchData* data;
+  Geometry                          geometry;
+  std::shared_ptr<const NPatchData> data;
   if(mNPatchLoader.GetNPatchData(mBrokenImageInfoContainer[index].npatchId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
   {
     if(data->GetStretchPixelsX().Size() == 1 && data->GetStretchPixelsY().Size() == 1)
@@ -298,8 +298,8 @@ Geometry VisualFactoryCache::GetNPatchGeometry(int index)
 
 Shader VisualFactoryCache::GetNPatchShader(int index)
 {
-  Shader            shader;
-  const NPatchData* data;
+  Shader                            shader;
+  std::shared_ptr<const NPatchData> data;
   // 0 is either no data (load failed?) or no stretch regions on image
   // for both cases we use the default shader
   NPatchUtility::StretchRanges::SizeType xStretchCount = 0;
@@ -336,8 +336,8 @@ Shader VisualFactoryCache::GetNPatchShader(int index)
 
 void VisualFactoryCache::ApplyTextureAndUniforms(Renderer& renderer, int index)
 {
-  const NPatchData* data;
-  TextureSet        textureSet;
+  std::shared_ptr<const NPatchData> data;
+  TextureSet                        textureSet;
   if(mNPatchLoader.GetNPatchData(mBrokenImageInfoContainer[index].npatchId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
   {
     textureSet                               = data->GetTextures();
@@ -366,8 +366,8 @@ void VisualFactoryCache::UpdateBrokenImageRenderer(Renderer& renderer, const Vec
         mBrokenImageInfoContainer[index].visualType = visualUrl.GetType();
         if(mBrokenImageInfoContainer[index].visualType == VisualUrl::Type::N_PATCH)
         {
-          const NPatchData* data;
-          Rect<int>         border;
+          std::shared_ptr<const NPatchData> data;
+          Rect<int>                         border;
           mBrokenImageInfoContainer[index].npatchId = mNPatchLoader.Load(mTextureManager, NULL, mBrokenImageInfoContainer[index].url, border, mPreMultiplyOnLoad, true);
           if(mNPatchLoader.GetNPatchData(mBrokenImageInfoContainer[index].npatchId, data) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
           {