Make NPatchData receive LoadComplete call from TextureManager. 92/248592/18
authorSeungho Baek <sbsh.baek@samsung.com>
Mon, 30 Nov 2020 05:45:41 +0000 (14:45 +0900)
committerSeungho Baek <sbsh.baek@samsung.com>
Wed, 16 Dec 2020 08:12:05 +0000 (17:12 +0900)
 - If an NPatchVisual is destroyed before it receives LoadComplete,
   the loading process of the npatch image is never completed.
 - At that time, because the cache data is already created, every request of the image after that is ignored.
 - In this patch, NPatchData is separated from NpatchLoader and NPatchData request npatch loading to Texture Manager instead of NPatchVisual as a representative.
 - After the loading is completed, NPatchData receives LoadComplete call and makes textureSet.
 - And, The NpatchData sends UploadComplete call to the each NPatchVisuals those originally request it.

 + Additionally, this patch includes an method to remove unused cached data.

Change-Id: I0879a9eaa62aac8533fbc6b8d9416805ab7a0675
Signed-off-by: Seungho Baek <sbsh.baek@samsung.com>
12 files changed:
automated-tests/src/dali-toolkit-internal/addons/test-rendering-addon.cpp
automated-tests/src/dali-toolkit/utc-Dali-VisualFactory.cpp
dali-toolkit/internal/controls/control/control-data-impl.cpp
dali-toolkit/internal/file.list
dali-toolkit/internal/visuals/npatch-data.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/npatch-data.h [new file with mode: 0644]
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/npatch/npatch-visual.h
dali-toolkit/internal/visuals/texture-manager-impl.cpp
dali-toolkit/internal/visuals/texture-manager-impl.h

index 6756093..a94f92b 100644 (file)
@@ -76,7 +76,7 @@ static Geometry CreateGeometryMapInternal(const void* opacityMap,
   return Dali::Geometry::New();
 }
 
-static void* NPatchBuildInternal(const Devel::PixelBuffer& pixelBuffer, Toolkit::Internal::NPatchLoader::Data* data )
+static void* NPatchBuildInternal(const Devel::PixelBuffer& pixelBuffer, Toolkit::Internal::NPatchData* data )
 {
   gCallStack.emplace_back( "BuildNPatch" );
   fprintf(stderr, "AddOn::NPatchBuild()\n");
index db708c1..929825c 100644 (file)
@@ -771,9 +771,6 @@ int UtcDaliVisualFactoryGetNPatchVisual3(void)
 
     DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
 
-    application.GetScene().Remove( actor );
-    DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
-
     Vector2 naturalSize( 0.0f, 0.0f );
     visual.GetNaturalSize( naturalSize );
     DALI_TEST_EQUALS( naturalSize, Vector2( imageSize.GetWidth() - 2.0f, imageSize.GetHeight() - 2.0f ), TEST_LOCATION );
@@ -984,6 +981,79 @@ int UtcDaliVisualFactoryGetNPatchVisual7(void)
   END_TEST;
 }
 
+int UtcDaliVisualFactoryGetNPatchVisual8(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualFactoryGetNPatchVisual8: Add 9-patch visual on stage, instantly remove it and add new 9-patch visual with same propertyMap" );
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK( factory );
+
+  // Get actual size of test image
+  ImageDimensions imageSize = Dali::GetClosestImageSize( TEST_9_PATCH_FILE_NAME );
+
+  Property::Map propertyMap;
+  propertyMap.Insert( Toolkit::Visual::Property::TYPE, Visual::N_PATCH );
+  propertyMap.Insert( ImageVisual::Property::URL, TEST_9_PATCH_FILE_NAME );
+  propertyMap.Insert( ImageVisual::Property::SYNCHRONOUS_LOADING, false );
+  {
+    Visual::Base visual = factory.CreateVisual( propertyMap );
+    DALI_TEST_CHECK( visual );
+
+    Vector2 naturalSize( 0.0f, 0.0f );
+    visual.GetNaturalSize( naturalSize );
+    DALI_TEST_EQUALS( naturalSize, Vector2( imageSize.GetWidth(), imageSize.GetHeight() ), TEST_LOCATION );
+
+    TestGlAbstraction& gl = application.GetGlAbstraction();
+    TraceCallStack& textureTrace = gl.GetTextureTrace();
+    textureTrace.Enable(true);
+
+    DummyControl actor = DummyControl::New(true);
+
+    DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+    dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
+
+    actor.SetProperty( Actor::Property::SIZE, Vector2( 200.f, 200.f ) );
+    DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+
+    application.GetScene().Add( actor );
+    actor.Unparent();
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render();
+
+    visual = factory.CreateVisual( propertyMap );
+    DALI_TEST_CHECK( visual );
+
+    visual.GetNaturalSize( naturalSize );
+    DALI_TEST_EQUALS( naturalSize, Vector2( imageSize.GetWidth(), imageSize.GetHeight() ), TEST_LOCATION );
+
+    actor = DummyControl::New(true);
+
+    DummyControlImpl& dummyImpl2 = static_cast<DummyControlImpl&>(actor.GetImplementation());
+    dummyImpl2.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
+
+    actor.SetProperty( Actor::Property::SIZE, Vector2( 200.f, 200.f ) );
+    DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+
+    application.GetScene().Add( actor );
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger(1 ), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render();
+
+    Renderer renderer = actor.GetRendererAt( 0 );
+    auto textures = renderer.GetTextures();
+
+    DALI_TEST_EQUALS( textures.GetTextureCount(), 1, TEST_LOCATION );
+  }
+
+  END_TEST;
+}
+
 int UtcDaliNPatchVisualAuxiliaryImage01(void)
 {
   ToolkitTestApplication application;
@@ -1061,7 +1131,7 @@ int UtcDaliNPatchVisualAuxiliaryImage02(void)
 
   Renderer renderer2 = imageView2.GetRendererAt( 0 );
   auto textureSet2 = renderer2.GetTextures();
-  DALI_TEST_EQUALS( textureSet1 == textureSet2, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureSet1 != textureSet2, true, TEST_LOCATION );
 
   END_TEST;
 }
index 2c9dca5..4e12efb 100755 (executable)
@@ -104,25 +104,30 @@ void Remove( DictionaryKeys& keys, const std::string& name )
   }
 }
 
-Toolkit::Visual::Type GetVisualTypeFromMap( const Property::Map& map )
+/**
+ *  Finds visual in given array, returning true if found along with the iterator for that visual as a out parameter
+ */
+bool FindVisual( Property::Index targetIndex, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter )
 {
-  Property::Value* typeValue = map.Find( Toolkit::Visual::Property::TYPE, VISUAL_TYPE  );
-  Toolkit::Visual::Type type = Toolkit::Visual::IMAGE;
-  if( typeValue )
+  for ( iter = visuals.Begin(); iter != visuals.End(); iter++ )
   {
-    Scripting::GetEnumerationProperty( *typeValue, VISUAL_TYPE_TABLE, VISUAL_TYPE_TABLE_COUNT, type );
+    if ( (*iter)->index ==  targetIndex )
+    {
+      return true;
+    }
   }
-  return type;
+  return false;
 }
 
 /**
  *  Finds visual in given array, returning true if found along with the iterator for that visual as a out parameter
  */
-bool FindVisual( Property::Index targetIndex, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter )
+bool FindVisual( std::string visualName, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter )
 {
   for ( iter = visuals.Begin(); iter != visuals.End(); iter++ )
   {
-    if ( (*iter)->index ==  targetIndex )
+    Toolkit::Visual::Base visual = (*iter)->visual;
+    if( visual && visual.GetName() == visualName )
     {
       return true;
     }
@@ -1558,59 +1563,47 @@ void Control::Impl::RecreateChangedVisuals( Dictionary<Property::Map>& stateVisu
     const std::string& visualName = (*iter).key;
     const Property::Map& toMap = (*iter).entry;
 
-    // is it a candidate for re-creation?
-    bool recreate = false;
-
-    Toolkit::Visual::Base visual = GetVisualByName( mVisuals, visualName );
-    if( visual )
+    Actor self = mControlImpl.Self();
+    RegisteredVisualContainer::Iterator registeredVisualsiter;
+    // Check if visual (visualName) is already registered, this is the current visual.
+    if(FindVisual(visualName, mVisuals, registeredVisualsiter))
     {
-      Property::Map fromMap;
-      visual.CreatePropertyMap( fromMap );
-
-      Toolkit::Visual::Type fromType = GetVisualTypeFromMap( fromMap );
-      Toolkit::Visual::Type toType = GetVisualTypeFromMap( toMap );
-
-      if( fromType != toType )
-      {
-        recreate = true;
-      }
-      else
+      Toolkit::Visual::Base& visual = (*registeredVisualsiter)->visual;
+      if(visual)
       {
-        if( fromType == Toolkit::Visual::IMAGE || fromType == Toolkit::Visual::N_PATCH
-            || fromType == Toolkit::Visual::SVG || fromType == Toolkit::Visual::ANIMATED_IMAGE )
-        {
-          Property::Value* fromUrl = fromMap.Find( Toolkit::ImageVisual::Property::URL, IMAGE_URL_NAME );
-          Property::Value* toUrl = toMap.Find( Toolkit::ImageVisual::Property::URL, IMAGE_URL_NAME );
+        // No longer required to know if the replaced visual's resources are ready
+        StopObservingVisual(visual);
 
-          if( fromUrl && toUrl )
+        // If control staged then visuals will be swapped once ready
+        if(self.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+        {
+          // Check if visual is currently in the process of being replaced ( is in removal container )
+          RegisteredVisualContainer::Iterator visualQueuedForRemoval;
+          if(FindVisual(visualName, mRemoveVisuals, visualQueuedForRemoval))
           {
-            std::string fromUrlString;
-            std::string toUrlString;
-            fromUrl->Get(fromUrlString);
-            toUrl->Get(toUrlString);
-
-            if( fromUrlString != toUrlString )
-            {
-              recreate = true;
-            }
+            // Visual with same visual name is already in removal container so current visual pending
+            // Only the the last requested visual will be displayed so remove current visual which is staged but not ready.
+            Toolkit::GetImplementation(visual).SetOffScene(self);
+            (*registeredVisualsiter)->visual.Reset();
+            mVisuals.Erase(registeredVisualsiter);
+          }
+          else
+          {
+            // current visual not already in removal container so add now.
+            DALI_LOG_INFO(gLogFilter, Debug::Verbose, "RegisterVisual Move current registered visual to removal Queue: %s \n", visualName.c_str());
+            MoveVisual(registeredVisualsiter, mVisuals, mRemoveVisuals);
           }
         }
+        else
+        {
+          // Control not staged or visual disabled so can just erase from registered visuals and new visual will be added later.
+          (*registeredVisualsiter)->visual.Reset();
+          mVisuals.Erase(registeredVisualsiter);
+        }
       }
 
-      const Property::Map* instancedMap = instancedProperties.FindConst( visualName );
-      if( recreate || instancedMap )
-      {
-        RemoveVisual( mVisuals, visualName );
-        Style::ApplyVisual( handle, visualName, toMap, instancedMap );
-      }
-      else
-      {
-        // @todo check to see if we can apply toMap without recreating the visual
-        // e.g. by setting only animatable properties
-        // For now, recreate all visuals, but merge in instance data.
-        RemoveVisual( mVisuals, visualName );
-        Style::ApplyVisual( handle, visualName, toMap, instancedMap );
-      }
+      const Property::Map* instancedMap = instancedProperties.FindConst(visualName);
+      Style::ApplyVisual(handle, visualName, toMap, instancedMap);
     }
   }
 }
index 29ec224..8cc93bc 100644 (file)
@@ -34,6 +34,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/visuals/image-atlas-manager.cpp
    ${toolkit_src_dir}/visuals/image/image-visual.cpp
    ${toolkit_src_dir}/visuals/mesh/mesh-visual.cpp
+   ${toolkit_src_dir}/visuals/npatch-data.cpp
    ${toolkit_src_dir}/visuals/npatch-loader.cpp
    ${toolkit_src_dir}/visuals/npatch/npatch-visual.cpp
    ${toolkit_src_dir}/visuals/primitive/primitive-visual.cpp
diff --git a/dali-toolkit/internal/visuals/npatch-data.cpp b/dali-toolkit/internal/visuals/npatch-data.cpp
new file mode 100644 (file)
index 0000000..1097395
--- /dev/null
@@ -0,0 +1,251 @@
+ /*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/visuals/npatch-data.h>
+
+// INTERNAL HEADERS
+#include <dali-toolkit/internal/visuals/rendering-addon.h>
+
+// EXTERNAL HEADERS
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+NPatchData::NPatchData()
+: mId(INVALID_NPATCH_DATA_ID),
+  mUrl(),
+  mTextureSet(),
+  mHash(0),
+  mCroppedWidth(0),
+  mCroppedHeight(0),
+  mBorder(0, 0, 0, 0),
+  mLoadingState(LoadingState::LOADING),
+  mRenderingMap{nullptr}
+{
+}
+
+NPatchData::~NPatchData()
+{
+  // If there is an opacity map, it has to be destroyed using addon call
+  if( mRenderingMap )
+  {
+    RenderingAddOn::Get().DestroyNPatch( mRenderingMap );
+  }
+}
+
+void NPatchData::SetId(const NPatchDataId id)
+{
+  mId = id;
+}
+
+NPatchData::NPatchDataId NPatchData::GetId() const
+{
+  return mId;
+}
+
+void NPatchData::AddObserver(TextureUploadObserver* textureObserver)
+{
+  mObserverList.PushBack( textureObserver );
+}
+
+void NPatchData::RemoveObserver(TextureUploadObserver* textureObserver)
+{
+  for(uint32_t index = 0; index < mObserverList.Count(); ++index )
+  {
+    if(textureObserver == mObserverList[index])
+    {
+      mObserverList.Erase( mObserverList.begin() + index );
+      break;
+    }
+  }
+}
+
+uint32_t NPatchData::GetObserverCount() const
+{
+  return mObserverList.Count();
+}
+
+void NPatchData::SetUrl(const std::string url)
+{
+  mUrl = url;
+}
+
+std::string NPatchData::GetUrl() const
+{
+  return mUrl;
+}
+
+void NPatchData::SetTextures(const TextureSet textureSet)
+{
+  mTextureSet = textureSet;
+}
+
+TextureSet NPatchData::GetTextures() const
+{
+  return mTextureSet;
+}
+
+void NPatchData::SetStretchPixelsX(const NPatchUtility::StretchRanges stretchPixelsX)
+{
+  mStretchPixelsX = stretchPixelsX;
+}
+
+void NPatchData::SetStretchPixelsY(const NPatchUtility::StretchRanges stretchPixelsY)
+{
+  mStretchPixelsY = stretchPixelsY;
+}
+
+NPatchUtility::StretchRanges NPatchData::GetStretchPixelsX() const
+{
+  return mStretchPixelsX;
+}
+
+NPatchUtility::StretchRanges NPatchData::GetStretchPixelsY() const
+{
+  return mStretchPixelsY;
+}
+
+void NPatchData::SetHash(std::size_t hash)
+{
+  mHash = hash;
+}
+
+std::size_t NPatchData::GetHash() const
+{
+  return mHash;
+}
+
+void NPatchData::SetCroppedWidth(uint32_t croppedWidth)
+{
+  mCroppedWidth = croppedWidth;
+}
+
+void NPatchData::SetCroppedHeight(uint32_t croppedHeight)
+{
+  mCroppedHeight = croppedHeight;
+}
+
+uint32_t NPatchData::GetCroppedWidth() const
+{
+  return mCroppedWidth;
+}
+
+uint32_t NPatchData::GetCroppedHeight() const
+{
+  return mCroppedHeight;
+}
+
+void NPatchData::SetBorder(const Rect<int> border)
+{
+  mBorder = border;
+}
+
+Rect<int> NPatchData::GetBorder() const
+{
+  return mBorder;
+}
+
+void NPatchData::SetPreMultiplyOnLoad(bool preMultiplyOnLoad)
+{
+  mPreMultiplyOnLoad = preMultiplyOnLoad;
+}
+
+bool NPatchData::IsPreMultiplied() const
+{
+  return mPreMultiplyOnLoad;
+}
+
+void NPatchData::SetLoadingState(const LoadingState loadingState)
+{
+  mLoadingState = loadingState;
+}
+
+NPatchData::LoadingState NPatchData::GetLoadingState() const
+{
+  return mLoadingState;
+}
+
+void* NPatchData::GetRenderingMap() const
+{
+  return mRenderingMap;
+}
+
+void NPatchData::SetLoadedNPatchData( Devel::PixelBuffer& pixelBuffer, bool preMultiplied )
+{
+  if( mBorder == Rect< int >( 0, 0, 0, 0 ) )
+  {
+    NPatchUtility::ParseBorders( pixelBuffer, mStretchPixelsX, mStretchPixelsY );
+
+    // Crop the image
+    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 ) ) );
+  }
+
+  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
+
+  Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() );
+  texture.Upload( pixels );
+
+  mTextureSet = TextureSet::New();
+  mTextureSet.SetTexture( 0u, texture );
+
+  mPreMultiplyOnLoad = preMultiplied;
+
+  mLoadingState = LoadingState::LOAD_COMPLETE;
+}
+
+void NPatchData::LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied )
+{
+  if(loadSuccess)
+  {
+    SetLoadedNPatchData( pixelBuffer, preMultiplied );
+  }
+  else
+  {
+    mLoadingState = LoadingState::LOAD_FAILED;
+  }
+
+  for(uint32_t index = 0; index < mObserverList.Count(); ++index)
+  {
+    TextureUploadObserver* observer = mObserverList[index];
+    observer->UploadComplete(loadSuccess, TextureManager::INVALID_TEXTURE_ID, mTextureSet, false, Vector4(), preMultiplied);
+  }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/visuals/npatch-data.h b/dali-toolkit/internal/visuals/npatch-data.h
new file mode 100644 (file)
index 0000000..60e6e6b
--- /dev/null
@@ -0,0 +1,303 @@
+#ifndef DALI_TOOLKIT_NPATCH_DATA_H
+#define DALI_TOOLKIT_NPATCH_DATA_H
+
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <string>
+#include <dali/public-api/rendering/texture-set.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+#include <dali-toolkit/devel-api/utility/npatch-utilities.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+class NPatchData : 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
+
+  /**
+   * @brief Loading State of the NPatch image.
+   */
+  enum class LoadingState
+  {
+    LOADING = 0,   ///< NPatch is on loading.
+    LOAD_COMPLETE, ///< NPatch loading is completed successfully.
+    LOAD_FAILED    ///< NPatch loading is failed.
+  };
+
+public:
+
+  /**
+   * Constructor
+   */
+  NPatchData();
+
+  /**
+   * Destructor, non-virtual as not a base class
+   */
+  ~NPatchData();
+
+public:
+
+  /**
+   * @brief Set cache data id.
+   *
+   * @param [in] id cache data id
+   */
+  void SetId(const NPatchDataId id);
+
+  /**
+   * @brief Retrieve cache data id
+   *
+   * @return cache data id.
+   */
+  NPatchDataId GetId() const;
+
+  /**
+   * @brief Add TextureUploadObserver that uses the image of this cache data.
+   *
+   * @param [in] textureObserver the TextureUploadObserver that uses the image of this cache data.
+   */
+  void AddObserver(TextureUploadObserver* textureObserver);
+
+  /**
+   * @brief Remove TextureUploadObserver.
+   *
+   * @param [in] textureObserver the TextureUploadObserver that will be removed in this cache data.
+   */
+  void RemoveObserver(TextureUploadObserver* textureObserver);
+
+  /**
+   * @brief Retrieve the number of observer.
+   *
+   * @return Return the number of observer.
+   */
+  uint32_t GetObserverCount() const;
+
+  /**
+   * @brief Set NPatch image url.
+   *
+   * @param [in] url NPatch image url
+   */
+  void SetUrl(const std::string url);
+
+  /**
+   * @brief Retrieve the image url.
+   *
+   * @return Return the image url.
+   */
+  std::string GetUrl() const;
+
+  /**
+   * @brief Set texture set on the cache data
+   *
+   * @param [in] textureSet loaded texture set
+   */
+  void SetTextures(const TextureSet textureSet);
+
+  /**
+   * @brief Retrieve loaded texture set.
+   *
+   * @return Return loaded texture set.
+   */
+  TextureSet GetTextures() const;
+
+  /**
+   * @brief Set X directional stretchPixels
+   *
+   * @param [in] stretchPixelsX stretchPixels for X direction
+   */
+  void SetStretchPixelsX(const NPatchUtility::StretchRanges stretchPixelsX);
+
+  /**
+   * @brief Set Y directional stretchPixels
+   *
+   * @param [in] stretchPixelsY stretchPixels for Y direction
+   */
+  void SetStretchPixelsY(const NPatchUtility::StretchRanges stretchPixelsY);
+
+  /**
+   * @brief Retrieve stretchPixels for X direction.
+   *
+   * @return Return stretchPixels for X direction.
+   */
+  NPatchUtility::StretchRanges GetStretchPixelsX() const;
+
+  /**
+   * @brief Retrieve stretchPixels for Y direction.
+   *
+   * @return Return stretchPixels for Y direction.
+   */
+  NPatchUtility::StretchRanges GetStretchPixelsY() const;
+
+  /**
+   * @brief Set cache data hash.
+   *
+   * @param [in] hash cache hash
+   */
+  void SetHash(std::size_t hash);
+
+  /**
+   * @brief Retrieve hash value of the cache.
+   *
+   * @return Return hash value of the cache.
+   */
+  std::size_t GetHash() const;
+
+  /**
+   * @brief Set croppedWidth of NPatch
+   *
+   * @param [in] croppedWidth croppedWidth of NPatch
+   */
+  void SetCroppedWidth(uint32_t croppedWidth);
+
+  /**
+   * @brief Set croppedHeight of NPatch
+   *
+   * @param [in] croppedHeight croppedHeight of NPatch
+   */
+  void SetCroppedHeight(uint32_t croppedHeight);
+
+  /**
+   * @brief Retrieve croppedWidth of NPatch.
+   *
+   * @return Return croppedWidth of NPatch.
+   */
+  uint32_t GetCroppedWidth() const;
+
+  /**
+   * @brief Retrieve croppedHeight of NPatch.
+   *
+   * @return Return croppedHeight of NPatch.
+   */
+  uint32_t GetCroppedHeight() const;
+
+  /**
+   * @brief Set border of NPatch.
+   *
+   * @param [in] border border of NPatch
+   */
+  void SetBorder(const Rect<int> border);
+
+  /**
+   * @brief Retrieve border of NPatch.
+   *
+   * @return Return border of NPatch.
+   */
+  Rect<int> GetBorder() const;
+
+  /**
+   * @brief Set whether the loaded image is premultiplied or not
+   *
+   * @param [in] preMultiplyOnLoad whether the loaded image is premultiplied or not
+   */
+  void SetPreMultiplyOnLoad(bool preMultiplyOnLoad);
+
+  /**
+   * @brief Retrieve whether the loaded image is premultiplied or not.
+   *
+   * @return Return true if the image is premultiplied alpha.
+   */
+  bool IsPreMultiplied() const;
+
+  /**
+   * @brief Set current loading state.
+   *
+   * @param [in] loadingState current loading state
+   */
+  void SetLoadingState(const LoadingState loadingState);
+
+  /**
+   * @brief Retrieve current loading state.
+   *
+   * @return Return current loading state.
+   */
+  LoadingState GetLoadingState() const;
+
+
+  /**
+   * @brief Retrieve NPatch rendering data.
+   *
+   * @return Return NPatch rendering data.
+   */
+  void* GetRenderingMap() const;
+
+  /**
+   * @brief Set loaded pixel buffer for the cache data.
+   *
+   * @param [in] pixelBuffer loaded pixel buffer.
+   * @param [in] preMultiplied whether the loaded image is premultiplied or not
+   */
+  void SetLoadedNPatchData( Devel::PixelBuffer& pixelBuffer, bool preMultiplied );
+
+private:
+
+  /**
+   * @copydoc TextureUploadObserver::UploadCompleted
+   *
+   * To avoid rendering garbage pixels, renderer should be added to actor after the resources are ready.
+   * This callback is the place to add the renderer as it would be called once the loading is finished.
+   */
+  void UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied ) override {}
+
+  /**
+   * @copydoc TextureUploadObserver::LoadComplete
+   *
+   * To avoid rendering garbage pixels, renderer should be added to actor after the resources are ready.
+   * This callback is the place to add the renderer as it would be called once the loading is finished.
+   */
+  void LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied ) override;
+
+
+private:
+
+  using ObserverListType = Dali::Vector<TextureUploadObserver*>;
+
+  NPatchDataId                 mId;
+  ObserverListType             mObserverList;      ///< Container used to store all observer clients of this Texture
+  std::string                  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
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_NPATCH_DATA_H
index 2550001..f5d92ab 100644 (file)
@@ -34,179 +34,160 @@ namespace Toolkit
 namespace Internal
 {
 
-namespace NPatchBuffer
+namespace
 {
 
-void SetLoadedNPatchData( NPatchLoader::Data* data, Devel::PixelBuffer& pixelBuffer )
-{
-  if( data->border == Rect< int >( 0, 0, 0, 0 ) )
-  {
-    NPatchUtility::ParseBorders( pixelBuffer, data->stretchPixelsX, data->stretchPixelsY );
-
-    // Crop the image
-    pixelBuffer.Crop( 1, 1, pixelBuffer.GetWidth() - 2, pixelBuffer.GetHeight() - 2 );
-  }
-  else
-  {
-    data->stretchPixelsX.PushBack( Uint16Pair( data->border.left, ( (pixelBuffer.GetWidth() >= static_cast< unsigned int >( data->border.right )) ? pixelBuffer.GetWidth() - data->border.right : 0 ) ) );
-    data->stretchPixelsY.PushBack( Uint16Pair( data->border.top, ( (pixelBuffer.GetHeight() >= static_cast< unsigned int >( data->border.bottom )) ? pixelBuffer.GetHeight() - data->border.bottom : 0 ) ) );
-  }
-
-  data->croppedWidth = pixelBuffer.GetWidth();
-  data->croppedHeight = pixelBuffer.GetHeight();
-
-  // Create opacity map
-  data->renderingMap = RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().BuildNPatch(pixelBuffer, data ) : nullptr;
-
-  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 );
+constexpr auto INVALID_CACHE_INDEX = int32_t{-1}; ///< Invalid Cache index
+constexpr auto UNINITIALIZED_ID = int32_t{0}; ///< uninitialised id, use to initialize ids
 
-  data->textureSet = TextureSet::New();
-  data->textureSet.SetTexture( 0u, texture );
+} // Anonymous namespace
 
-  data->loadCompleted = true;
-}
-
-} // namespace NPatchBuffer
-
-NPatchLoader::Data::~Data()
+NPatchLoader::NPatchLoader()
+: mCurrentNPatchDataId(0)
 {
-  // If there is an opacity map, it has to be destroyed using addon call
-  if( renderingMap )
-  {
-    RenderingAddOn::Get().DestroyNPatch( renderingMap );
-  }
 }
 
-NPatchLoader::NPatchLoader()
+NPatchLoader::~NPatchLoader()
 {
 }
 
-NPatchLoader::~NPatchLoader()
+NPatchData::NPatchDataId NPatchLoader::GenerateUniqueNPatchDataId()
 {
+  return mCurrentNPatchDataId++;
 }
 
 std::size_t NPatchLoader::Load( TextureManager& textureManager, TextureUploadObserver* textureObserver, const std::string& url, const Rect< int >& border, bool& preMultiplyOnLoad, bool synchronousLoading )
 {
   std::size_t hash = CalculateHash( url );
-  OwnerContainer< Data* >::SizeType index = UNINITIALIZED_ID;
-  const OwnerContainer< Data* >::SizeType count = mCache.Count();
-  int cachedIndex = -1;
-  Data* data;
+  OwnerContainer< NPatchData* >::SizeType index = UNINITIALIZED_ID;
+  const OwnerContainer< NPatchData* >::SizeType count = mCache.Count();
 
   for( ; index < count; ++index )
   {
-    if( mCache[ index ]->hash == hash )
+    if( mCache[ index ]->GetHash() == hash )
     {
       // hash match, check url as well in case of hash collision
-      if( mCache[ index ]->url == url )
+      if(mCache[ index ]->GetUrl() == url)
       {
         // Use cached data
-        if( mCache[ index ]->border == border )
+        if( mCache[ index ]->GetBorder() == border )
         {
-          if( mCache[ index ]->loadCompleted )
+          if( mCache[ index ]->GetLoadingState() == NPatchData::LoadingState::LOADING )
           {
-            return index + 1u; // valid indices are from 1 onwards
+            mCache[ index ]->AddObserver( textureObserver );
           }
-          mCache[ index ]->observerList.PushBack( textureObserver );
-          data = mCache[ index ];
-          return index + 1u; // valid indices are from 1 onwards
+          return mCache[ index ]->GetId(); // valid indices are from 1 onwards
         }
         else
         {
-          if( mCache[ index ]->loadCompleted )
+          if( mCache[ index ]->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
           {
             // Same url but border is different - use the existing texture
-            Data* data = new Data();
-            data->hash = hash;
-            data->url = url;
-            data->croppedWidth = mCache[ index ]->croppedWidth;
-            data->croppedHeight = mCache[ index ]->croppedHeight;
+            NPatchData* newData = new NPatchData();
+            newData->SetId(GenerateUniqueNPatchDataId());
+            newData->SetHash(hash);
+            newData->SetUrl(url);
+            newData->SetCroppedWidth(mCache[ index ]->GetCroppedWidth());
+            newData->SetCroppedHeight(mCache[ index ]->GetCroppedHeight());
 
-            data->textureSet = mCache[ index ]->textureSet;
+            newData->SetTextures(mCache[ index ]->GetTextures());
 
             NPatchUtility::StretchRanges stretchRangesX;
-            stretchRangesX.PushBack( Uint16Pair( border.left, ( (data->croppedWidth >= static_cast< unsigned int >( border.right )) ? data->croppedWidth - border.right : 0 ) ) );
+            stretchRangesX.PushBack( Uint16Pair( border.left, ( (newData->GetCroppedWidth() >= static_cast< unsigned int >( border.right )) ? newData->GetCroppedHeight() - border.right : 0 ) ) );
 
             NPatchUtility::StretchRanges stretchRangesY;
-            stretchRangesY.PushBack( Uint16Pair( border.top, ( (data->croppedHeight >= static_cast< unsigned int >( border.bottom )) ? data->croppedHeight - border.bottom : 0 ) ) );
+            stretchRangesY.PushBack( Uint16Pair( border.top, ( (newData->GetCroppedWidth() >= static_cast< unsigned int >( border.bottom )) ? newData->GetCroppedHeight() - border.bottom : 0 ) ) );
+
+            newData->SetStretchPixelsX(stretchRangesX);
+            newData->SetStretchPixelsY(stretchRangesY);
+            newData->SetBorder(border);
 
-            data->stretchPixelsX = stretchRangesX;
-            data->stretchPixelsY = stretchRangesY;
-            data->border = border;
+            newData->SetPreMultiplyOnLoad(mCache[ index ]->IsPreMultiplied());
 
-            data->loadCompleted = mCache[ index ]->loadCompleted;
+            newData->SetLoadingState(NPatchData::LoadingState::LOAD_COMPLETE);
+            newData->AddObserver( textureObserver );
 
-            mCache.PushBack( data );
+            mCache.PushBack( newData );
 
-            return mCache.Count(); // valid ids start from 1u
+            return newData->GetId(); // valid ids start from 1u
           }
         }
       }
     }
   }
 
-  if( cachedIndex == -1 )
-  {
-    data = new Data();
-    data->loadCompleted = false;
-    data->hash = hash;
-    data->url = url;
-    data->border = border;
-
-    mCache.PushBack( data );
-
-    cachedIndex = mCache.Count();
-  }
+  // If this is new image loading, make new cache data
+  NPatchData* data;
+  data = new NPatchData();
+  data->SetId(GenerateUniqueNPatchDataId());
+  data->SetHash(hash);
+  data->SetUrl(url);
+  data->SetBorder(border);
+  data->SetPreMultiplyOnLoad(preMultiplyOnLoad);
+  data->AddObserver(textureObserver);
+  mCache.PushBack(data);
 
   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,
-                                                                   textureObserver, true, preMultiplyOnLoading );
+                                                                   data, true, preMultiplyOnLoading );
 
   if( pixelBuffer )
   {
-    NPatchBuffer::SetLoadedNPatchData( data, pixelBuffer );
     preMultiplyOnLoad = ( preMultiplyOnLoading == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD ) ? true : false;
+    data->SetLoadedNPatchData( pixelBuffer, preMultiplyOnLoad );
   }
 
-  return cachedIndex;
+  return data->GetId();
 }
 
-void NPatchLoader::SetNPatchData( bool loadSuccess, std::size_t id, Devel::PixelBuffer& pixelBuffer, const Internal::VisualUrl& url, bool preMultiplied )
+int32_t NPatchLoader::GetCacheIndexFromId( const NPatchData::NPatchDataId id )
 {
-  Data* data;
-  data = mCache[ id - 1u ];
+  const unsigned int size = mCache.Count();
 
-  // To prevent recursion.
-  // data->loadCompleted will be set true in the NPatchBuffer::SetLoadedNPatchData when the first observer called this method.
-  if( data->loadCompleted )
+  for( unsigned int i = 0; i < size; ++i )
   {
-    return;
+    if( mCache[i]->GetId() == id )
+    {
+      return i;
+    }
   }
 
-  NPatchBuffer::SetLoadedNPatchData( data, pixelBuffer );
+  DALI_LOG_ERROR("Wrong NPatchDataId is used\n");
+  return INVALID_CACHE_INDEX;
+}
 
-  while( data->observerList.Count() )
+bool NPatchLoader::GetNPatchData( const NPatchData::NPatchDataId id, const NPatchData*& data )
+{
+  int32_t cacheIndex = GetCacheIndexFromId(id);
+  if( cacheIndex != INVALID_CACHE_INDEX )
   {
-    TextureUploadObserver* observer = data->observerList[0];
-    observer->LoadComplete( loadSuccess, Devel::PixelBuffer(), url, preMultiplied );
-    data->observerList.Erase( data->observerList.begin() );
+    data = mCache[cacheIndex];
+    return true;
   }
+  data = nullptr;
+  return false;
 }
 
-bool NPatchLoader::GetNPatchData( std::size_t id, const Data*& data )
+void NPatchLoader::Remove( std::size_t id, TextureUploadObserver* textureObserver )
 {
-  if( ( id > UNINITIALIZED_ID )&&( id <= mCache.Count() ) )
+  int32_t cacheIndex = GetCacheIndexFromId(id);
+  if( cacheIndex == INVALID_CACHE_INDEX )
   {
-    data = mCache[ id - 1u ]; // id's start from 1u
-    return true;
+    return;
+  }
+
+  NPatchData* data;
+  data = mCache[cacheIndex];
+
+  data->RemoveObserver(textureObserver);
+
+  if(data->GetObserverCount() == 0)
+  {
+      mCache.Erase( mCache.Begin() + cacheIndex );
   }
-  data = NULL;
-  return false;
 }
 
 } // namespace Internal
index a3aa464..b2b4a7c 100644 (file)
@@ -24,6 +24,7 @@
 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/npatch-data.h>
 #include <dali-toolkit/internal/visuals/texture-manager-impl.h>
 #include <dali-toolkit/devel-api/utility/npatch-utilities.h>
 
@@ -49,43 +50,6 @@ class NPatchLoader
 {
 public:
 
-  enum
-  {
-    UNINITIALIZED_ID = 0 ///< uninitialised id, use to initialize ids
-  };
-
-  struct Data
-  {
-    Data()
-    : url(),
-      textureSet(),
-      hash( 0 ),
-      croppedWidth( 0 ),
-      croppedHeight( 0 ),
-      border( 0, 0, 0, 0 ),
-      loadCompleted( false ),
-      renderingMap{ nullptr }
-    {}
-
-    ~Data();
-
-    using ObserverListType = Dali::Vector< TextureUploadObserver* >;
-
-    ObserverListType observerList;                 ///< Container used to store all observer clients of this Texture
-    std::string url;                               ///< Url of the N-Patch
-    TextureSet textureSet;                         ///< Texture containing the cropped image
-    NPatchUtility::StretchRanges stretchPixelsX;   ///< X stretch pixels
-    NPatchUtility::StretchRanges stretchPixelsY;   ///< Y stretch pixels
-    std::size_t hash;                              ///< Hash code for the Url
-    uint32_t croppedWidth;                         ///< Width of the cropped middle part of N-patch
-    uint32_t croppedHeight;                        ///< Height of the cropped middle part of N-patch
-    Rect< int > border;                            ///< The size of the border
-    bool loadCompleted;                            ///< True if the data loading is completed
-    void* renderingMap;                            ///< NPatch rendering data
-  };
-
-public:
-
   /**
    * Constructor
    */
@@ -113,21 +77,35 @@ public:
   /**
    * @brief Set loaded PixelBuffer and its information
    *
-   * @param [in] loadSuccess True if the texture load was successful (i.e. the resource is available). If false, then the resource failed to load.
    * @param [in] id cache data id
    * @param [in] pixelBuffer of loaded image
-   * @param [in] url           The url address of the loaded image.
    * @param [in] preMultiplied True if the image had pre-multiplied alpha applied
    */
-  void SetNPatchData( bool loadSuccess, std::size_t id, Devel::PixelBuffer& pixelBuffer, const Internal::VisualUrl& url, bool preMultiplied );
+  void SetNPatchData( std::size_t id, Devel::PixelBuffer& pixelBuffer, bool preMultiplied );
 
   /**
    * @brief Retrieve N patch data matching to an id
    * @param [in] id of data
-   * @param [out] data const pointer to the data
+   * @param [out] data const pointer to the NPatchData
    * @return true if data matching to id was really found
    */
-  bool GetNPatchData( std::size_t id, const Data*& data );
+  bool GetNPatchData( const NPatchData::NPatchDataId id, const NPatchData*& data );
+
+  /**
+   * @brief Remove a texture matching id.
+   * Erase the observer from the observer list of cache.
+   * If the observer list is empty, 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 );
+
+private:
+
+  NPatchData::NPatchDataId GenerateUniqueNPatchDataId();
+
+  int32_t GetCacheIndexFromId( const NPatchData::NPatchDataId id );
 
 protected:
 
@@ -143,8 +121,8 @@ protected:
 
 private:
 
-  OwnerContainer< Data* > mCache;
-
+  NPatchData::NPatchDataId mCurrentNPatchDataId;
+  OwnerContainer< NPatchData* > mCache;
 };
 
 } // name Internal
index 5c03ac2..6dcf020 100644 (file)
@@ -24,7 +24,6 @@
 #include <dali/integration-api/debug.h>
 
 // INTERNAL INCLUDES
-#include <dali-toolkit/public-api/visuals/image-visual-properties.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 #include <dali-toolkit/public-api/visuals/visual-properties.h>
 #include <dali-toolkit/internal/visuals/npatch-loader.h>
@@ -278,15 +277,15 @@ void NPatchVisual::LoadImages()
   TextureManager& textureManager = mFactoryCache.GetTextureManager();
   bool synchronousLoading = mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
 
-  if( NPatchLoader::UNINITIALIZED_ID == mId && mImageUrl.IsLocalResource() )
+  if( mId == NPatchData::INVALID_NPATCH_DATA_ID && mImageUrl.IsLocalResource() )
   {
     bool preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader ? true : false;
     mId = mLoader.Load( textureManager, this, mImageUrl.GetUrl(), mBorder, preMultiplyOnLoad, synchronousLoading );
 
-    const NPatchLoader::Data* data;
-    if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
+    const NPatchData* data;
+    if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
     {
-      EnablePreMultipliedAlpha( preMultiplyOnLoad );
+      EnablePreMultipliedAlpha( data->IsPreMultiplied() );
     }
   }
 
@@ -306,11 +305,11 @@ void NPatchVisual::GetNaturalSize( Vector2& naturalSize )
   naturalSize.y = 0u;
 
   // load now if not already loaded
-  const NPatchLoader::Data* data;
-  if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
+  const NPatchData* data;
+  if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() != NPatchData::LoadingState::LOADING )
   {
-    naturalSize.x = data->croppedWidth;
-    naturalSize.y = data->croppedHeight;
+    naturalSize.x = data->GetCroppedWidth();
+    naturalSize.y = data->GetCroppedHeight();
   }
   else
   {
@@ -386,6 +385,12 @@ void NPatchVisual::DoSetProperties( const Property::Map& propertyMap )
       mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
     }
   }
+
+  Property::Value* releasePolicy = propertyMap.Find( Toolkit::ImageVisual::Property::RELEASE_POLICY, RELEASE_POLICY_NAME );
+  if( releasePolicy )
+  {
+    releasePolicy->Get( mReleasePolicy );
+  }
 }
 
 void NPatchVisual::DoSetOnScene( Actor& actor )
@@ -393,7 +398,7 @@ void NPatchVisual::DoSetOnScene( Actor& actor )
   // load when first go on stage
   LoadImages();
 
-  const NPatchLoader::Data* data;
+  const NPatchData* data;
   if( mLoader.GetNPatchData( mId, data ) )
   {
     Geometry geometry = CreateGeometry();
@@ -402,11 +407,11 @@ void NPatchVisual::DoSetOnScene( Actor& actor )
     mImpl->mRenderer = Renderer::New( geometry, shader );
 
     mPlacementActor = actor;
-    if( data->loadCompleted )
+    if( data->GetLoadingState() != NPatchData::LoadingState::LOADING )
     {
       if( RenderingAddOn::Get().IsValid() )
       {
-        RenderingAddOn::Get().SubmitRenderTask( mImpl->mRenderer, data->renderingMap );
+        RenderingAddOn::Get().SubmitRenderTask( mImpl->mRenderer, data->GetRenderingMap() );
       }
 
       ApplyTextureAndUniforms();
@@ -421,6 +426,13 @@ void NPatchVisual::DoSetOnScene( Actor& actor )
 
 void NPatchVisual::DoSetOffScene( Actor& actor )
 {
+  if((mId != NPatchData::INVALID_NPATCH_DATA_ID) && mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
+  {
+    mLoader.Remove(mId, this);
+    mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
+    mId = NPatchData::INVALID_NPATCH_DATA_ID;
+  }
+
   actor.RemoveRenderer( mImpl->mRenderer );
   mImpl->mRenderer.Reset();
   mPlacementActor.Reset();
@@ -443,6 +455,7 @@ void NPatchVisual::DoCreatePropertyMap( Property::Map& map ) const
   map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() );
   map.Insert( Toolkit::ImageVisual::Property::BORDER_ONLY, mBorderOnly );
   map.Insert( Toolkit::ImageVisual::Property::BORDER, mBorder );
+  map.Insert( Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy );
 
   if( mAuxiliaryUrl.IsValid() )
   {
@@ -466,25 +479,31 @@ NPatchVisual::NPatchVisual( VisualFactoryCache& factoryCache )
   mLoader( factoryCache.GetNPatchLoader() ),
   mImageUrl(),
   mAuxiliaryUrl(),
-  mId( NPatchLoader::UNINITIALIZED_ID ),
+  mId(NPatchData::INVALID_NPATCH_DATA_ID),
   mBorderOnly( false ),
   mBorder(),
-  mAuxiliaryImageAlpha( 0.0f )
+  mAuxiliaryImageAlpha( 0.0f ),
+  mReleasePolicy( Toolkit::ImageVisual::ReleasePolicy::DETACHED )
 {
   EnablePreMultipliedAlpha( mFactoryCache.GetPreMultiplyOnLoad() );
 }
 
 NPatchVisual::~NPatchVisual()
 {
+  if((mId != NPatchData::INVALID_NPATCH_DATA_ID) && ( mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER ))
+  {
+    mLoader.Remove(mId, this);
+    mId = NPatchData::INVALID_NPATCH_DATA_ID;
+  }
 }
 
 Geometry NPatchVisual::CreateGeometry()
 {
   Geometry geometry;
-  const NPatchLoader::Data* data;
-  if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
+  const NPatchData* data;
+  if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
   {
-    if( data->stretchPixelsX.Size() == 1 && data->stretchPixelsY.Size() == 1 )
+    if( data->GetStretchPixelsX().Size() == 1 && data->GetStretchPixelsY().Size() == 1 )
     {
       if( DALI_UNLIKELY( mBorderOnly ) )
       {
@@ -492,13 +511,13 @@ Geometry NPatchVisual::CreateGeometry()
       }
       else
       {
-        if( data->renderingMap )
+        if( data->GetRenderingMap() )
         {
           uint32_t elementCount[2];
-          geometry = RenderingAddOn::Get().CreateGeometryGrid(data->renderingMap, Uint16Pair(3, 3), elementCount );
+          geometry = RenderingAddOn::Get().CreateGeometryGrid(data->GetRenderingMap(), Uint16Pair(3, 3), elementCount );
           if( mImpl->mRenderer )
           {
-            RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->renderingMap);
+            RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
           }
         }
         else
@@ -507,10 +526,10 @@ Geometry NPatchVisual::CreateGeometry()
         }
       }
     }
-    else if( data->stretchPixelsX.Size() > 0 || data->stretchPixelsY.Size() > 0)
+    else if( data->GetStretchPixelsX().Size() > 0 || data->GetStretchPixelsY().Size() > 0)
     {
-      Uint16Pair gridSize( 2 * data->stretchPixelsX.Size() + 1,  2 * data->stretchPixelsY.Size() + 1 );
-      if( !data->renderingMap )
+      Uint16Pair gridSize( 2 * data->GetStretchPixelsX().Size() + 1,  2 * data->GetStretchPixelsY().Size() + 1 );
+      if( !data->GetRenderingMap() )
       {
         geometry = !mBorderOnly ? CreateGridGeometry( gridSize ) : CreateBorderGeometry( gridSize );
       }
@@ -518,10 +537,10 @@ Geometry NPatchVisual::CreateGeometry()
       {
         uint32_t elementCount[2];
         geometry = !mBorderOnly ?
-                   RenderingAddOn::Get().CreateGeometryGrid(data->renderingMap, gridSize, elementCount ) : CreateBorderGeometry(gridSize );
+                   RenderingAddOn::Get().CreateGeometryGrid(data->GetRenderingMap(), gridSize, elementCount ) : CreateBorderGeometry(gridSize );
         if( mImpl->mRenderer )
         {
-          RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->renderingMap);
+          RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
         }
       }
     }
@@ -537,7 +556,7 @@ Geometry NPatchVisual::CreateGeometry()
 Shader NPatchVisual::CreateShader()
 {
   Shader shader;
-  const NPatchLoader::Data* data;
+  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;
@@ -551,8 +570,8 @@ Shader NPatchVisual::CreateShader()
   // ask loader for the regions
   if( mLoader.GetNPatchData( mId, data ) )
   {
-    xStretchCount = data->stretchPixelsX.Count();
-    yStretchCount = data->stretchPixelsY.Count();
+    xStretchCount = data->GetStretchPixelsX().Count();
+    yStretchCount = data->GetStretchPixelsY().Count();
   }
 
   if( DALI_LIKELY( !mImpl->mCustomShader ) )
@@ -616,25 +635,25 @@ Shader NPatchVisual::CreateShader()
 
 void NPatchVisual::ApplyTextureAndUniforms()
 {
-  const NPatchLoader::Data* data;
+  const NPatchData* data;
   TextureSet textureSet;
 
-  if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
+  if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
   {
-    textureSet = data->textureSet;
+    textureSet = data->GetTextures();
 
-    if( data->stretchPixelsX.Size() == 1 && data->stretchPixelsY.Size() == 1 )
+    if( data->GetStretchPixelsX().Size() == 1 && data->GetStretchPixelsY().Size() == 1 )
     {
       //special case for 9 patch
-      Uint16Pair stretchX = data->stretchPixelsX[ 0 ];
-      Uint16Pair stretchY = data->stretchPixelsY[ 0 ];
+      Uint16Pair stretchX = data->GetStretchPixelsX()[ 0 ];
+      Uint16Pair stretchY = data->GetStretchPixelsY()[ 0 ];
 
       uint16_t stretchWidth = ( stretchX.GetY() >= stretchX.GetX() ) ? stretchX.GetY() - stretchX.GetX() : 0;
       uint16_t stretchHeight = ( stretchY.GetY() >= stretchY.GetX() ) ? stretchY.GetY() - stretchY.GetX() : 0;
 
       mImpl->mRenderer.RegisterProperty( "uFixed[0]", Vector2::ZERO );
       mImpl->mRenderer.RegisterProperty( "uFixed[1]", Vector2( stretchX.GetX(), stretchY.GetX()) );
-      mImpl->mRenderer.RegisterProperty( "uFixed[2]", Vector2( data->croppedWidth - stretchWidth, data->croppedHeight - stretchHeight ) );
+      mImpl->mRenderer.RegisterProperty( "uFixed[2]", Vector2( data->GetCroppedWidth() - stretchWidth, data->GetCroppedHeight() - stretchHeight ) );
       mImpl->mRenderer.RegisterProperty( "uStretchTotal", Vector2( stretchWidth, stretchHeight ) );
     }
     else
@@ -642,8 +661,8 @@ void NPatchVisual::ApplyTextureAndUniforms()
       mImpl->mRenderer.RegisterProperty( "uNinePatchFactorsX[0]", Vector2::ZERO );
       mImpl->mRenderer.RegisterProperty( "uNinePatchFactorsY[0]", Vector2::ZERO );
 
-      RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsX", data->stretchPixelsX, data->croppedWidth );
-      RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsY", data->stretchPixelsY, data->croppedHeight );
+      RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsX", data->GetStretchPixelsX(), data->GetCroppedWidth() );
+      RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsY", data->GetStretchPixelsY(), data->GetCroppedHeight() );
     }
   }
   else
@@ -664,10 +683,10 @@ void NPatchVisual::ApplyTextureAndUniforms()
     // If the auxiliary image is smaller than the un-stretched NPatch, use CPU resizing to enlarge it to the
     // same size as the unstretched NPatch. This will give slightly higher quality results than just relying
     // on GL interpolation alone.
-    if( mAuxiliaryPixelBuffer.GetWidth() < data->croppedWidth &&
-        mAuxiliaryPixelBuffer.GetHeight() < data->croppedHeight )
+    if( mAuxiliaryPixelBuffer.GetWidth() < data->GetCroppedWidth() &&
+        mAuxiliaryPixelBuffer.GetHeight() < data->GetCroppedHeight() )
     {
-      mAuxiliaryPixelBuffer.Resize( data->croppedWidth, data->croppedHeight );
+      mAuxiliaryPixelBuffer.Resize( data->GetCroppedWidth(), data->GetCroppedHeight() );
     }
 
     // Note, this resets mAuxiliaryPixelBuffer handle
@@ -838,7 +857,7 @@ Geometry NPatchVisual::CreateBorderGeometry( Uint16Pair gridSize )
 
 void NPatchVisual::SetResource()
 {
-  const NPatchLoader::Data* data;
+  const NPatchData* data;
   if( mImpl->mRenderer && mLoader.GetNPatchData( mId, data ) )
   {
     Geometry geometry = CreateGeometry();
@@ -860,35 +879,32 @@ void NPatchVisual::SetResource()
   }
 }
 
+void NPatchVisual::UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied )
+{
+  EnablePreMultipliedAlpha( preMultiplied );
+  if(!loadSuccess)
+  {
+    // Image loaded and ready to display
+    ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
+  }
+
+  if( mAuxiliaryPixelBuffer || !mAuxiliaryUrl.IsValid() )
+  {
+    SetResource();
+  }
+}
+
 void NPatchVisual::LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied )
 {
-  if( url.GetUrl() == mAuxiliaryUrl.GetUrl() )
+  if( loadSuccess && url.GetUrl() == mAuxiliaryUrl.GetUrl() )
   {
     mAuxiliaryPixelBuffer = pixelBuffer;
-    const NPatchLoader::Data* data;
-    if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
-    {
-      SetResource();
-    }
+    SetResource();
   }
   else
   {
-    Devel::PixelBuffer loadedPixelBuffer;
-    if( loadSuccess )
-    {
-      loadedPixelBuffer = pixelBuffer;
-      EnablePreMultipliedAlpha( preMultiplied );
-    }
-    else
-    {
-      loadedPixelBuffer = LoadImageFromFile( mFactoryCache.GetTextureManager().GetBrokenImageUrl() );
-    }
-    mLoader.SetNPatchData( loadSuccess, mId, loadedPixelBuffer, url, preMultiplied );
-
-    if( mAuxiliaryPixelBuffer || !mAuxiliaryUrl.IsValid() )
-    {
-      SetResource();
-    }
+    // Image loaded and ready to display
+    ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
   }
 }
 
index 41252f6..19bdaff 100644 (file)
@@ -27,6 +27,7 @@
 #include <dali/public-api/object/weak-handle.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/public-api/visuals/image-visual-properties.h>
 #include <dali-toolkit/internal/visuals/texture-upload-observer.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-url.h>
@@ -211,7 +212,7 @@ private:
    * To avoid rendering garbage pixels, renderer should be added to actor after the resources are ready.
    * This callback is the place to add the renderer as it would be called once the loading is finished.
    */
-  void UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied ) override {}
+  void UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied ) override;
 
   /**
    * @copydoc TextureUploadObserver::LoadComplete
@@ -222,16 +223,16 @@ private:
   void LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied ) override;
 
 private:
-
-  WeakHandle<Actor>  mPlacementActor;       ///< Weakhandle to contain Actor during texture loading
-  NPatchLoader&      mLoader;               ///< reference to N patch loader for fast access
-  VisualUrl          mImageUrl;             ///< The url to the N patch to load
-  VisualUrl          mAuxiliaryUrl;         ///< An auxiliary image that can be displayed on top of the N-Patch
-  std::size_t        mId;                   ///< id of the N patch (from loader/cache)
-  Devel::PixelBuffer mAuxiliaryPixelBuffer; ///< pixel buffer of the auxiliary mask image
-  bool               mBorderOnly;           ///< if only border is desired
-  Rect<int>          mBorder;               ///< The size of the border
-  float              mAuxiliaryImageAlpha;  ///< The alpha value for the auxiliary image only
+  WeakHandle<Actor>        mPlacementActor;                 ///< Weakhandle to contain Actor during texture loading
+  NPatchLoader&            mLoader;                         ///< reference to N patch loader for fast access
+  VisualUrl                mImageUrl;                       ///< The url to the N patch to load
+  VisualUrl                mAuxiliaryUrl;                   ///< An auxiliary image that can be displayed on top of the N-Patch
+  NPatchData::NPatchDataId mId;                             ///< id of the N patch (from loader/cache)
+  Devel::PixelBuffer       mAuxiliaryPixelBuffer;           ///< pixel buffer of the auxiliary mask image
+  bool                     mBorderOnly;                     ///< if only border is desired
+  Rect<int>                mBorder;                         ///< The size of the border
+  float                    mAuxiliaryImageAlpha;            ///< The alpha value for the auxiliary image only
+  Toolkit::ImageVisual::ReleasePolicy::Type mReleasePolicy; ///< The release policy to determine when an image should no longer be cached.
 };
 
 } // namespace Internal
index ea0abc1..bcde348 100644 (file)
@@ -999,7 +999,6 @@ void TextureManager::PostLoad( TextureInfo& textureInfo, Devel::PixelBuffer& pix
   }
   else
   {
-    // @todo If the load was unsuccessful, upload the broken image.
     textureInfo.loadState = LoadState::LOAD_FAILED;
     CheckForWaitingTexture( textureInfo );
     NotifyObservers( textureInfo, false );
@@ -1382,11 +1381,6 @@ void TextureManager::SetBrokenImageUrl(const std::string& brokenImageUrl)
   mBrokenImageUrl = brokenImageUrl;
 }
 
-const std::string TextureManager::GetBrokenImageUrl()
-{
-  return mBrokenImageUrl;
-}
-
 Geometry TextureManager::GetRenderGeometry(TextureId textureId, uint32_t& frontElements, uint32_t& backElements )
 {
   return RenderingAddOn::Get().IsValid() ?
index 28e0869..89a8ed0 100644 (file)
@@ -428,12 +428,6 @@ public:
   void SetBrokenImageUrl(const std::string& brokenImageUrl);
 
   /**
-   * @brief Get an image to be used when a visual has failed to correctly render
-   * @return Returns The broken image url.
-   */
-  const std::string GetBrokenImageUrl();
-
-  /**
    * @brief Returns the geometry associated with texture.
    * @param[in] textureId Id of the texture
    * @param[out] frontElements number of front elements