ImageVisual postpones adding renderer to actor until image is loaded 62/88562/16
authorXiangyin Ma <x1.ma@samsung.com>
Mon, 19 Sep 2016 17:09:31 +0000 (18:09 +0100)
committerXiangyin Ma <x1.ma@samsung.com>
Thu, 13 Oct 2016 17:24:57 +0000 (18:24 +0100)
Change-Id: Id219a0f0ca7bc67f11f8dcda9126d97f3bb07d51

14 files changed:
automated-tests/src/dali-toolkit/utc-Dali-AsyncImageLoader.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp
dali-toolkit/devel-api/file.list
dali-toolkit/devel-api/image-loader/atlas-upload-observer.cpp [new file with mode: 0644]
dali-toolkit/devel-api/image-loader/atlas-upload-observer.h [new file with mode: 0644]
dali-toolkit/devel-api/image-loader/image-atlas.cpp
dali-toolkit/devel-api/image-loader/image-atlas.h
dali-toolkit/internal/image-loader/image-atlas-impl.cpp
dali-toolkit/internal/image-loader/image-atlas-impl.h
dali-toolkit/internal/visuals/image-atlas-manager.cpp
dali-toolkit/internal/visuals/image-atlas-manager.h
dali-toolkit/internal/visuals/image/image-visual.cpp
dali-toolkit/internal/visuals/image/image-visual.h

index 21603f3..3ee9a77 100644 (file)
@@ -206,7 +206,30 @@ int UtcDaliAsyncImageLoaderCancel(void)
   END_TEST;
 }
 
   END_TEST;
 }
 
-int UtcDaliAsyncImageLoaderCancelAll(void)
+int UtcDaliAsncImageLoaderCancelAll01(void)
+{
+  ToolkitTestApplication application;
+
+  AsyncImageLoader loader = AsyncImageLoader::New();
+
+  // Test that it is safe to call CancelAll even there is no loading task requested.
+  try
+  {
+    loader.CancelAll();
+  }
+  catch(Dali::DaliException& e)
+  {
+    DALI_TEST_ASSERT(e, "AsyncImageLoader::LoadAll", TEST_LOCATION);
+  }
+
+  // Test that cancelling a non-existing loading task will return false
+  uint32_t id = 1;
+  DALI_TEST_CHECK( !(loader.Cancel( id )) );
+
+  END_TEST;
+}
+
+int UtcDaliAsyncImageLoaderCancelAll02(void)
 {
   ToolkitTestApplication application;
 
 {
   ToolkitTestApplication application;
 
index a945850..5fe744f 100644 (file)
@@ -60,6 +60,22 @@ bool IsOverlap( Rect<int> rect1, Rect<int> rect2 )
      && rect2.y < rect1.y+rect1.height;
 }
 
      && rect2.y < rect1.y+rect1.height;
 }
 
+static unsigned int gCountOfTestFuncCall;
+class TestUploadObserver : public AtlasUploadObserver
+{
+public:
+  TestUploadObserver()
+  {}
+
+  virtual ~TestUploadObserver()
+  {}
+
+  void UploadCompleted()
+  {
+    gCountOfTestFuncCall++;
+  }
+};
+
 } // anonymous namespace
 
 void dali_image_atlas_startup(void)
 } // anonymous namespace
 
 void dali_image_atlas_startup(void)
@@ -241,6 +257,100 @@ int UtcDaliImageAtlasUploadP(void)
   END_TEST;
 }
 
   END_TEST;
 }
 
+int UtcDaliImageAtlasUploadWithObserver01(void)
+{
+  TestApplication application;
+  ImageAtlas atlas = ImageAtlas::New( 200, 200 );
+
+  EventThreadCallback* eventTrigger = EventThreadCallback::Get();
+  CallbackBase* callback = eventTrigger->GetCallback();
+
+  gCountOfTestFuncCall = 0;
+  TestUploadObserver uploadObserver;
+
+  Vector4 textureRect1;
+  atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34), FittingMode::DEFAULT, true, &uploadObserver );
+  Vector4 textureRect2;
+  atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50), FittingMode::DEFAULT, true, NULL );
+  Vector4 textureRect3;
+  atlas.Upload( textureRect3, gImage_128_RGB, ImageDimensions(128, 128), FittingMode::DEFAULT, true, &uploadObserver );
+
+  // waiting until all three images are loaded and uploaded to atlas
+  eventTrigger->WaitingForTrigger( 3 );
+  CallbackBase::Execute( *callback );
+  application.SendNotification();
+  application.Render(RENDER_FRAME_INTERVAL);
+
+  // Check that TestFunc is called twice
+  DALI_TEST_EQUALS( gCountOfTestFuncCall, 2, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliImageAtlasUploadWithObserver02(void)
+{
+  TestApplication application;
+  ImageAtlas atlas = ImageAtlas::New( 200, 200 );
+
+  EventThreadCallback* eventTrigger = EventThreadCallback::Get();
+  CallbackBase* callback = eventTrigger->GetCallback();
+
+  gCountOfTestFuncCall = 0;
+  TestUploadObserver* uploadObserver = new TestUploadObserver;
+
+  Vector4 textureRect1;
+  atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34), FittingMode::DEFAULT, true, uploadObserver );
+  Vector4 textureRect2;
+  atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50), FittingMode::DEFAULT, true, uploadObserver );
+  Vector4 textureRect3;
+  atlas.Upload( textureRect3, gImage_128_RGB, ImageDimensions(128, 128), FittingMode::DEFAULT, true, uploadObserver );
+
+  // destroy the object.
+  delete uploadObserver;
+
+ // waiting until all three images are loaded and uploaded to atlas
+  eventTrigger->WaitingForTrigger( 3 );
+  CallbackBase::Execute( *callback );
+  application.Render(RENDER_FRAME_INTERVAL);
+  application.SendNotification();
+
+  // Check that TestFunc is called twice
+  DALI_TEST_EQUALS( gCountOfTestFuncCall, 0, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliImageAtlasUploadWithObserver03(void)
+{
+  TestApplication application;
+
+  gCountOfTestFuncCall = 0;
+  TestUploadObserver* uploadObserver = new TestUploadObserver;
+
+  {
+    ImageAtlas atlas = ImageAtlas::New( 200, 200 );
+
+    Vector4 textureRect1;
+    atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34), FittingMode::DEFAULT, true, uploadObserver );
+    Vector4 textureRect2;
+    atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50), FittingMode::DEFAULT, true, uploadObserver );
+    Vector4 textureRect3;
+    atlas.Upload( textureRect3, gImage_128_RGB, ImageDimensions(128, 128), FittingMode::DEFAULT, true, uploadObserver );
+  }
+
+  //ImageAtlas is out of scope, so it will get destroyed
+
+  application.Render(RENDER_FRAME_INTERVAL);
+  application.SendNotification();
+  application.SendNotification();
+  application.Render(RENDER_FRAME_INTERVAL);
+
+  // Check that TestFunc is called twice
+  DALI_TEST_EQUALS( gCountOfTestFuncCall, 0, TEST_LOCATION );
+
+  END_TEST;
+}
+
 int UtcDaliImageAtlasRemove(void)
 {
   TestApplication application;
 int UtcDaliImageAtlasRemove(void)
 {
   TestApplication application;
index 779259d..12c3f17 100644 (file)
@@ -393,6 +393,7 @@ int UtcDaliImageViewAsyncLoadingWithAltasing(void)
   callStack.Reset();
   callStack.Enable(true);
 
   callStack.Reset();
   callStack.Enable(true);
 
+  BitmapLoader::ResetLatestCreated();
   ImageView imageView = ImageView::New( gImage_34_RGBA, ImageDimensions( 34, 34 ) );
 
   // By default, Aysnc loading is used
   ImageView imageView = ImageView::New( gImage_34_RGBA, ImageDimensions( 34, 34 ) );
 
   // By default, Aysnc loading is used
index 188ed1b..509f22e 100755 (executable)
@@ -22,6 +22,7 @@ devel_api_src_files = \
   $(devel_api_src_dir)/controls/tool-bar/tool-bar.cpp \
   $(devel_api_src_dir)/focus-manager/keyinput-focus-manager.cpp \
   $(devel_api_src_dir)/image-loader/async-image-loader.cpp \
   $(devel_api_src_dir)/controls/tool-bar/tool-bar.cpp \
   $(devel_api_src_dir)/focus-manager/keyinput-focus-manager.cpp \
   $(devel_api_src_dir)/image-loader/async-image-loader.cpp \
+  $(devel_api_src_dir)/image-loader/atlas-upload-observer.cpp \
   $(devel_api_src_dir)/image-loader/image-atlas.cpp \
   $(devel_api_src_dir)/scripting/script.cpp \
   $(devel_api_src_dir)/transition-effects/cube-transition-cross-effect.cpp \
   $(devel_api_src_dir)/image-loader/image-atlas.cpp \
   $(devel_api_src_dir)/scripting/script.cpp \
   $(devel_api_src_dir)/transition-effects/cube-transition-cross-effect.cpp \
@@ -82,6 +83,7 @@ devel_api_focus_manager_header_files = \
 
 devel_api_image_loader_header_files = \
   $(devel_api_src_dir)/image-loader/async-image-loader.h \
 
 devel_api_image_loader_header_files = \
   $(devel_api_src_dir)/image-loader/async-image-loader.h \
+  $(devel_api_src_dir)/image-loader/atlas-upload-observer.h \
   $(devel_api_src_dir)/image-loader/image-atlas.h
 
 devel_api_scripting_header_files = \
   $(devel_api_src_dir)/image-loader/image-atlas.h
 
 devel_api_scripting_header_files = \
diff --git a/dali-toolkit/devel-api/image-loader/atlas-upload-observer.cpp b/dali-toolkit/devel-api/image-loader/atlas-upload-observer.cpp
new file mode 100644 (file)
index 0000000..f362edc
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 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 "atlas-upload-observer.h"
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+AtlasUploadObserver::AtlasUploadObserver()
+{}
+
+AtlasUploadObserver::~AtlasUploadObserver()
+{
+  // Notify the registerd ImageAtlas object about the destruction of observer.
+  const std::size_t size( mAtlasList.Count() );
+  for( std::size_t i = 0; i < size; ++i )
+  {
+    if( mAtlasList[i] )
+    {
+      mAtlasList[i]->ObserverDestroyed( this );
+    }
+  }
+  mAtlasList.Clear();
+}
+
+void AtlasUploadObserver::Register( Internal::ImageAtlas& imageAtlas )
+{
+  // Add to the list so that the ImageAtlas could get notified in the destructor.
+  // If the same atlas is exist in the list already, we would still save the duplicated copy.
+  mAtlasList.PushBack( &imageAtlas );
+}
+
+void AtlasUploadObserver::Unregister( Internal::ImageAtlas& imageAtlas )
+{
+  const std::size_t size( mAtlasList.Count() );
+  for( std::size_t i = 0; i < size; i++ )
+  {
+    if( mAtlasList[i] == &imageAtlas )
+    {
+      // Remove from list
+      mAtlasList.Erase( mAtlasList.Begin() + i );
+      // If there are duplicated copies of same pointer, only the first one is removed
+      return;
+    }
+  }
+}
+
+}
+
+}
diff --git a/dali-toolkit/devel-api/image-loader/atlas-upload-observer.h b/dali-toolkit/devel-api/image-loader/atlas-upload-observer.h
new file mode 100644 (file)
index 0000000..2688c1a
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef DALI_TOOLKIT_ATLAS_UPLOAD_OBSERVER_H
+#define DALI_TOOLKIT_ATLAS_UPLOAD_OBSERVER_H
+
+/*
+ * Copyright (c) 2016 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.
+ *
+ */
+
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/signals/callback.h>
+
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+class ImageAtlas;
+}
+
+/**
+ * @brief Base class used to observe the upload status of the ImageAtlas when requesting an image atlasing.
+ *
+ * Derived class should implement the UploadCompleted method which would get executed once the texture is ready.
+ */
+class DALI_IMPORT_API AtlasUploadObserver
+{
+public:
+
+  /**
+   * @brief Constructor.
+   */
+  AtlasUploadObserver();
+
+  /**
+   * @brief Virtual destructor.
+   */
+  virtual ~AtlasUploadObserver();
+
+  /**
+   * The action to be taken once the upload is completed.
+   */
+  virtual void UploadCompleted() = 0;
+
+public: // not intended for developer, called by ImageAtlas internally to get notified when this observer dies
+
+  /**
+   * @brief Register an ImageAtlas which be notified when the observer is destructing.
+   * @param[in] imageAtlas The ImageAtlas object to get notification about the destruction of the observer.
+   */
+  void Register( Internal::ImageAtlas& imageAtlas );
+
+  /**
+   * @brief Unregister an ImageAtlas which be notified when the observer is destructing.
+   * @param[in] imageAtlas The ImageAtlas object to get notification about the destruction of the observer.
+   */
+  void Unregister( Internal::ImageAtlas& imageAtlas );
+
+private:
+
+  Vector<Internal::ImageAtlas*> mAtlasList; ///< The list of the registered ImageAtlas object
+
+};
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* DALI_TOOLKIT_ATLAS_UPLOAD_OBSERVER_H */
index a7174d5..fd6825e 100644 (file)
@@ -80,7 +80,17 @@ bool ImageAtlas::Upload( Vector4& textureRect,
                          FittingMode::Type fittingMode,
                          bool orientationCorrection )
 {
                          FittingMode::Type fittingMode,
                          bool orientationCorrection )
 {
-  return GetImplementation(*this).Upload( textureRect, url, size, fittingMode, orientationCorrection );
+  return Upload( textureRect, url, size, fittingMode, orientationCorrection, NULL );
+}
+
+bool ImageAtlas::Upload( Vector4& textureRect,
+                         const std::string& url,
+                         ImageDimensions size,
+                         FittingMode::Type fittingMode,
+                         bool orientationCorrection,
+                         AtlasUploadObserver* atlasUploadObserver )
+{
+  return GetImplementation(*this).Upload( textureRect, url, size, fittingMode, orientationCorrection, atlasUploadObserver );
 }
 
 bool ImageAtlas::Upload( Vector4& textureRect, PixelData pixelData )
 }
 
 bool ImageAtlas::Upload( Vector4& textureRect, PixelData pixelData )
index c85194f..98796bd 100644 (file)
@@ -26,6 +26,9 @@
 #include <dali/public-api/images/pixel-data.h>
 #include <dali/public-api/rendering/texture.h>
 
 #include <dali/public-api/images/pixel-data.h>
 #include <dali/public-api/rendering/texture.h>
 
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/image-loader/atlas-upload-observer.h>
+
 namespace Dali
 {
 
 namespace Dali
 {
 
@@ -114,7 +117,7 @@ public:
   /**
    * @brief Upload a resource image to the atlas.
    *
   /**
    * @brief Upload a resource image to the atlas.
    *
-   * @note To make the atlasing efficient, an valid size should be provided.
+   * @note To make the atlasing efficient, a valid size should be provided.
    *       If size is not provided, then the image file will be opened to read the actual size for loading.
    *       Do not set a size that is bigger than the actual image size, as the up-scaling is not available,
    *       the content of the area not covered by actual image is undefined, it will not be cleared.
    *       If size is not provided, then the image file will be opened to read the actual size for loading.
    *       Do not set a size that is bigger than the actual image size, as the up-scaling is not available,
    *       the content of the area not covered by actual image is undefined, it will not be cleared.
@@ -135,6 +138,32 @@ public:
                bool orientationCorrection = true );
 
   /**
                bool orientationCorrection = true );
 
   /**
+   * @brief Upload a resource image to the atlas.
+   *
+   * @note To make the atlasing efficient, a valid size should be provided.
+   *       If size is not provided, then the image file will be opened to read the actual size for loading.
+   *       Do not set a size that is bigger than the actual image size, as the up-scaling is not available,
+   *       the content of the area not covered by actual image is undefined, it will not be cleared.
+   *
+   * SamplingMode::BOX_THEN_LINEAR is used to sampling pixels from the input image while fitting it to desired size.
+   *
+   * @param [out] textureRect The texture area of the resource image in the atlas.
+   * @param [in] url The URL of the resource image file to use.
+   * @param [in] size The width and height to fit the loaded image to.
+   * @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
+   * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
+   * @param[in] atlasUploadObserver The observer to observe the upload state inside the ImageAtlas.
+   * @return True if there is enough space to fit this image in,false otherwise.
+   * @note The valid callback function here is required to have the signature of void( void ).
+   */
+  bool Upload( Vector4& textureRect,
+               const std::string& url,
+               ImageDimensions size,
+               FittingMode::Type fittingMode,
+               bool orientationCorrection,
+               AtlasUploadObserver* atlasUploadObserver );
+
+  /**
    * @brief Upload a pixel buffer to atlas
    *
    * @param [out] textureRect The texture area of the resource image in the atlas.
    * @brief Upload a pixel buffer to atlas
    *
    * @param [out] textureRect The texture area of the resource image in the atlas.
index 67f257c..bdd2780 100644 (file)
@@ -49,7 +49,19 @@ ImageAtlas::ImageAtlas( SizeType width, SizeType height, Pixel::Format pixelForm
 
 ImageAtlas::~ImageAtlas()
 {
 
 ImageAtlas::~ImageAtlas()
 {
-  mIdRectContainer.Clear();
+  const std::size_t count = mLoadingTaskInfoContainer.Count();
+  for( std::size_t i=0; i < count; ++i )
+  {
+    // Call unregister to every observer in the list.
+    // Note that, the Atlas can be registered to same observer multiple times, and the Unregister method only remove one item each time.
+    // In this way, the atlas is actually detached from a observer either every upload call invoked by this observer is completed or atlas is destroyed.
+    if( mLoadingTaskInfoContainer[i]->observer )
+    {
+      mLoadingTaskInfoContainer[i]->observer->Unregister( *this );
+    }
+  }
+
+  mLoadingTaskInfoContainer.Clear();
 }
 
 IntrusivePtr<ImageAtlas> ImageAtlas::New( SizeType width, SizeType height, Pixel::Format pixelFormat )
 }
 
 IntrusivePtr<ImageAtlas> ImageAtlas::New( SizeType width, SizeType height, Pixel::Format pixelFormat )
@@ -82,7 +94,8 @@ bool ImageAtlas::Upload( Vector4& textureRect,
                          const std::string& url,
                          ImageDimensions size,
                          FittingMode::Type fittingMode,
                          const std::string& url,
                          ImageDimensions size,
                          FittingMode::Type fittingMode,
-                         bool orientationCorrection )
+                         bool orientationCorrection,
+                         AtlasUploadObserver* atlasUploadObserver )
 {
   ImageDimensions dimensions = size;
   ImageDimensions zero;
 {
   ImageDimensions dimensions = size;
   ImageDimensions zero;
@@ -93,7 +106,7 @@ bool ImageAtlas::Upload( Vector4& textureRect,
     {
       if( !mBrokenImageUrl.empty() )
       {
     {
       if( !mBrokenImageUrl.empty() )
       {
-        return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true );
+        return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true, atlasUploadObserver );
       }
       else
       {
       }
       else
       {
@@ -108,14 +121,20 @@ bool ImageAtlas::Upload( Vector4& textureRect,
   if( mPacker.Pack( dimensions.GetWidth(), dimensions.GetHeight(), packPositionX, packPositionY ) )
   {
     unsigned short loadId = mAsyncLoader.Load( url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection );
   if( mPacker.Pack( dimensions.GetWidth(), dimensions.GetHeight(), packPositionX, packPositionY ) )
   {
     unsigned short loadId = mAsyncLoader.Load( url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection );
-    mIdRectContainer.PushBack( new IdRectPair( loadId, packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight() ) );
-
+    mLoadingTaskInfoContainer.PushBack( new LoadingTaskInfo( loadId, packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight(), atlasUploadObserver ) );
     // apply the half pixel correction
     textureRect.x = ( static_cast<float>( packPositionX ) +0.5f ) / mWidth; // left
     textureRect.y = ( static_cast<float>( packPositionY ) +0.5f ) / mHeight; // right
     textureRect.z = ( static_cast<float>( packPositionX + dimensions.GetX() )-0.5f ) / mWidth; // right
     textureRect.w = ( static_cast<float>( packPositionY + dimensions.GetY() )-0.5f ) / mHeight;// bottom
 
     // apply the half pixel correction
     textureRect.x = ( static_cast<float>( packPositionX ) +0.5f ) / mWidth; // left
     textureRect.y = ( static_cast<float>( packPositionY ) +0.5f ) / mHeight; // right
     textureRect.z = ( static_cast<float>( packPositionX + dimensions.GetX() )-0.5f ) / mWidth; // right
     textureRect.w = ( static_cast<float>( packPositionY + dimensions.GetY() )-0.5f ) / mHeight;// bottom
 
+    if( atlasUploadObserver )
+    {
+      // register to the observer,
+      // Not that a matching unregister call should be invoked in UploadToAtlas if the observer is still alive by then.
+      atlasUploadObserver->Register( *this );
+    }
+
     return true;
   }
 
     return true;
   }
 
@@ -150,33 +169,51 @@ void ImageAtlas::Remove( const Vector4& textureRect )
                        static_cast<SizeType>((textureRect.w-textureRect.y)*mHeight+1.f) );
 }
 
                        static_cast<SizeType>((textureRect.w-textureRect.y)*mHeight+1.f) );
 }
 
-void ImageAtlas::UploadToAtlas( unsigned int id, PixelData pixelData )
+void ImageAtlas::ObserverDestroyed( AtlasUploadObserver* observer )
 {
 {
-  if(  mIdRectContainer[0]->loadTaskId == id)
+  const std::size_t count = mLoadingTaskInfoContainer.Count();
+  for( std::size_t i=0; i < count; ++i )
   {
   {
+    if( mLoadingTaskInfoContainer[i]->observer == observer )
+    {
+      // the observer is destructing, so its member function should not be called anymore
+      mLoadingTaskInfoContainer[i]->observer = NULL;
+    }
+  }
+}
+
+void ImageAtlas::UploadToAtlas( uint32_t id, PixelData pixelData )
+{
+  if(  mLoadingTaskInfoContainer[0]->loadTaskId == id)
+  {
+    Rect<unsigned int> packRect( mLoadingTaskInfoContainer[0]->packRect  );
     if( !pixelData || ( pixelData.GetWidth() ==0 && pixelData.GetHeight() == 0 ))
     {
       if(!mBrokenImageUrl.empty()) // replace with the broken image
       {
     if( !pixelData || ( pixelData.GetWidth() ==0 && pixelData.GetHeight() == 0 ))
     {
       if(!mBrokenImageUrl.empty()) // replace with the broken image
       {
-        UploadBrokenImage( mIdRectContainer[0]->packRect );
+        UploadBrokenImage( packRect );
       }
     }
     else
     {
       }
     }
     else
     {
-      if( pixelData.GetWidth() < mIdRectContainer[0]->packRect.width || pixelData.GetHeight() < mIdRectContainer[0]->packRect.height  )
+      if( pixelData.GetWidth() < packRect.width || pixelData.GetHeight() < packRect.height  )
       {
         DALI_LOG_ERROR( "Can not upscale the image from actual loaded size [ %d, %d ] to specified size [ %d, %d ]\n",
       {
         DALI_LOG_ERROR( "Can not upscale the image from actual loaded size [ %d, %d ] to specified size [ %d, %d ]\n",
-                        pixelData.GetWidth(), pixelData.GetHeight(),
-                        mIdRectContainer[0]->packRect.width,  mIdRectContainer[0]->packRect.height );
+            pixelData.GetWidth(), pixelData.GetHeight(),
+            packRect.width, packRect.height );
       }
 
       }
 
-      mAtlas.Upload( pixelData, 0u, 0u,
-                    mIdRectContainer[0]->packRect.x, mIdRectContainer[0]->packRect.y,
-                    mIdRectContainer[0]->packRect.width, mIdRectContainer[0]->packRect.height );
+      mAtlas.Upload( pixelData, 0u, 0u, packRect.x, packRect.y, packRect.width, packRect.height );
     }
     }
-  }
 
 
-  mIdRectContainer.Erase( mIdRectContainer.Begin() );
+    if( mLoadingTaskInfoContainer[0]->observer )
+    {
+      mLoadingTaskInfoContainer[0]->observer->UploadCompleted();
+      mLoadingTaskInfoContainer[0]->observer->Unregister( *this );
+    }
+
+    mLoadingTaskInfoContainer.Erase( mLoadingTaskInfoContainer.Begin() );
+  }
 }
 
 void ImageAtlas::UploadBrokenImage( const Rect<unsigned int>& area )
 }
 
 void ImageAtlas::UploadBrokenImage( const Rect<unsigned int>& area )
index 34924d3..a773ea7 100644 (file)
@@ -23,6 +23,7 @@
 #include <dali/public-api/signals/connection-tracker.h>
 #include <dali/devel-api/common/owner-container.h>
 #include <dali/devel-api/images/atlas.h>
 #include <dali/public-api/signals/connection-tracker.h>
 #include <dali/devel-api/common/owner-container.h>
 #include <dali/devel-api/images/atlas.h>
+#include <dali/devel-api/common/owner-container.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/image-loader/image-atlas.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/image-loader/image-atlas.h>
@@ -80,7 +81,8 @@ public:
                const std::string& url,
                ImageDimensions size,
                FittingMode::Type fittingMode,
                const std::string& url,
                ImageDimensions size,
                FittingMode::Type fittingMode,
-               bool orientationCorrection);
+               bool orientationCorrection,
+               AtlasUploadObserver* atlasUploadObserver );
 
   /**
    * @copydoc Toolkit::ImageAtlas::Upload( Vector4&, PixelData )
 
   /**
    * @copydoc Toolkit::ImageAtlas::Upload( Vector4&, PixelData )
@@ -92,6 +94,11 @@ public:
    */
   void Remove( const Vector4& textureRect );
 
    */
   void Remove( const Vector4& textureRect );
 
+  /**
+   * Resets the destroying observer pointer so that we know not to call methods of this object any more.
+   */
+  void ObserverDestroyed( AtlasUploadObserver* observer );
+
 protected:
 
   /**
 protected:
 
   /**
@@ -104,7 +111,7 @@ private:
   /**
    * @copydoc PixelDataRequester::ProcessPixels
    */
   /**
    * @copydoc PixelDataRequester::ProcessPixels
    */
-  void UploadToAtlas( unsigned int id, PixelData pixelData );
+  void UploadToAtlas( uint32_t id, PixelData pixelData );
 
   /**
    * Upload broken image
 
   /**
    * Upload broken image
@@ -121,22 +128,29 @@ private:
 
 private:
 
 
 private:
 
-  struct IdRectPair
+  /**
+   * Each loading task( identified with an ID ) is associated with a rect region for packing the loaded pixel data into the atlas,
+   * and an AtlasUploadObserver whose UploadCompleted method should get executed once the sub texture is ready.
+   */
+  struct LoadingTaskInfo
   {
   {
-    IdRectPair( unsigned short loadTaskId,
-                unsigned int packPositionX,
-                unsigned int packPositionY,
-                unsigned int width,
-                unsigned int height )
+    LoadingTaskInfo( unsigned short loadTaskId,
+                     unsigned int packPositionX,
+                     unsigned int packPositionY,
+                     unsigned int width,
+                     unsigned int height,
+                     AtlasUploadObserver* observer )
     : loadTaskId( loadTaskId ),
     : loadTaskId( loadTaskId ),
-      packRect( packPositionX, packPositionY, width, height )
+      packRect( packPositionX, packPositionY, width, height ),
+      observer( observer )
     {}
 
     unsigned short loadTaskId;
     Rect<unsigned int> packRect;
     {}
 
     unsigned short loadTaskId;
     Rect<unsigned int> packRect;
+    AtlasUploadObserver* observer;
   };
 
   };
 
-  OwnerContainer<IdRectPair*> mIdRectContainer;
+  OwnerContainer<LoadingTaskInfo*> mLoadingTaskInfoContainer;
 
   Texture                   mAtlas;
   AtlasPacker               mPacker;
 
   Texture                   mAtlas;
   AtlasPacker               mPacker;
@@ -147,6 +161,7 @@ private:
   float                     mHeight;
   Pixel::Format             mPixelFormat;
 
   float                     mHeight;
   Pixel::Format             mPixelFormat;
 
+
 };
 
 } // namespace Internal
 };
 
 } // namespace Internal
index 66897e4..fe01571 100644 (file)
@@ -51,7 +51,8 @@ TextureSet ImageAtlasManager::Add( Vector4& textureRect,
                                  const std::string& url,
                                  ImageDimensions size,
                                  FittingMode::Type fittingMode,
                                  const std::string& url,
                                  ImageDimensions size,
                                  FittingMode::Type fittingMode,
-                                 bool orientationCorrection )
+                                 bool orientationCorrection,
+                                 AtlasUploadObserver* atlasUploadObserver )
 {
   ImageDimensions dimensions = size;
   ImageDimensions zero;
 {
   ImageDimensions dimensions = size;
   ImageDimensions zero;
@@ -71,7 +72,7 @@ TextureSet ImageAtlasManager::Add( Vector4& textureRect,
   unsigned int i = 0;
   for( AtlasContainer::iterator iter = mAtlasList.begin(); iter != mAtlasList.end(); ++iter)
   {
   unsigned int i = 0;
   for( AtlasContainer::iterator iter = mAtlasList.begin(); iter != mAtlasList.end(); ++iter)
   {
-    if( (*iter).Upload( textureRect, url, size, fittingMode, orientationCorrection ) )
+    if( (*iter).Upload( textureRect, url, size, fittingMode, orientationCorrection, atlasUploadObserver ) )
     {
       return mTextureSetList[i];
     }
     {
       return mTextureSetList[i];
     }
@@ -79,12 +80,12 @@ TextureSet ImageAtlasManager::Add( Vector4& textureRect,
   }
 
   CreateNewAtlas();
   }
 
   CreateNewAtlas();
-  mAtlasList.back().Upload( textureRect, url, size, fittingMode, orientationCorrection );
+  mAtlasList.back().Upload( textureRect, url, size, fittingMode, orientationCorrection, atlasUploadObserver );
   return mTextureSetList.back();
 }
 
 TextureSet ImageAtlasManager::Add( Vector4& textureRect,
   return mTextureSetList.back();
 }
 
 TextureSet ImageAtlasManager::Add( Vector4& textureRect,
-                                 PixelData pixelData )
+                                   PixelData pixelData )
 {
 
   // big buffer, atlasing is not applied
 {
 
   // big buffer, atlasing is not applied
index 4586236..5907ca2 100644 (file)
@@ -32,6 +32,8 @@ namespace Dali
 namespace Toolkit
 {
 
 namespace Toolkit
 {
 
+class AtlasUploadObserver;
+
 namespace Internal
 {
 
 namespace Internal
 {
 
@@ -65,14 +67,15 @@ public:
    * @param [in] size The width and height to fit the loaded image to.
    * @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
    * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
    * @param [in] size The width and height to fit the loaded image to.
    * @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
    * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
+   * @param [in] atlasUploadObserver The object to observe the uploading state inside ImageAtlas.
    * @return The texture set containing the image.
    */
   TextureSet Add( Vector4& textureRect,
    * @return The texture set containing the image.
    */
   TextureSet Add( Vector4& textureRect,
-                const std::string& url,
-                ImageDimensions size = ImageDimensions(),
-                FittingMode::Type fittingMode = FittingMode::DEFAULT,
-                bool orientationCorrection = true );
-
+                  const std::string& url,
+                  ImageDimensions size = ImageDimensions(),
+                  FittingMode::Type fittingMode = FittingMode::DEFAULT,
+                  bool orientationCorrection = true,
+                  AtlasUploadObserver* atlasUploadObserver = NULL );
   /**
    * @brief Add a pixel buffer to the atlas
    *
   /**
    * @brief Add a pixel buffer to the atlas
    *
index 4e545ab..f4652dd 100644 (file)
@@ -478,7 +478,7 @@ TextureSet ImageVisual::CreateTextureSet( Vector4& textureRect, const std::strin
   }
   else
   {
   }
   else
   {
-    textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, url, mDesiredSize, mFittingMode, mSamplingMode );
+    textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, url, mDesiredSize, mFittingMode, true, this );
     mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
     if( !textureSet ) // big image, no atlasing
     {
     mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
     if( !textureSet ) // big image, no atlasing
     {
@@ -509,6 +509,7 @@ void ImageVisual::InitializeRenderer( const std::string& imageUrl )
 
   mImageUrl = imageUrl;
   mImpl->mRenderer.Reset();
 
   mImageUrl = imageUrl;
   mImpl->mRenderer.Reset();
+  mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
 
   if( !mImpl->mCustomShader &&
       ( strncasecmp( imageUrl.c_str(), HTTP_URL,  sizeof(HTTP_URL)  -1 ) != 0 ) && // ignore remote images
 
   if( !mImpl->mCustomShader &&
       ( strncasecmp( imageUrl.c_str(), HTTP_URL,  sizeof(HTTP_URL)  -1 ) != 0 ) && // ignore remote images
@@ -573,8 +574,22 @@ void ImageVisual::InitializeRenderer( const Image& image )
   }
 }
 
   }
 }
 
+void ImageVisual::UploadCompleted()
+{
+  // Resource image is loaded. If weak handle is holding a placement actor, it is the time to add the renderer to actor.
+  Actor actor = mPlacementActor.GetHandle();
+  if( actor )
+  {
+    actor.AddRenderer( mImpl->mRenderer );
+    // reset the weak handle so that the renderer only get added to actor once
+    mPlacementActor.Reset();
+  }
+}
+
 void ImageVisual::DoSetOnStage( Actor& actor )
 {
 void ImageVisual::DoSetOnStage( Actor& actor )
 {
+  mPlacementActor = actor;
+
   if( !mImageUrl.empty() )
   {
     InitializeRenderer( mImageUrl );
   if( !mImageUrl.empty() )
   {
     InitializeRenderer( mImageUrl );
@@ -589,7 +604,11 @@ void ImageVisual::DoSetOnStage( Actor& actor )
     mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
   }
 
     mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
   }
 
-  actor.AddRenderer( mImpl->mRenderer );
+  if( IsSynchronousResourceLoading() || !(mImpl->mFlags & Impl::IS_ATLASING_APPLIED) )
+  {
+    actor.AddRenderer( mImpl->mRenderer );
+    mPlacementActor.Reset();
+  }
 }
 
 void ImageVisual::DoSetOffStage( Actor& actor )
 }
 
 void ImageVisual::DoSetOffStage( Actor& actor )
@@ -606,6 +625,7 @@ void ImageVisual::DoSetOffStage( Actor& actor )
     actor.RemoveRenderer( mImpl->mRenderer );
     mImpl->mRenderer.Reset();
   }
     actor.RemoveRenderer( mImpl->mRenderer );
     mImpl->mRenderer.Reset();
   }
+  mPlacementActor.Reset();
 }
 
 void ImageVisual::DoCreatePropertyMap( Property::Map& map ) const
 }
 
 void ImageVisual::DoCreatePropertyMap( Property::Map& map ) const
index c3f2217..3079c93 100644 (file)
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
+#include <dali-toolkit/devel-api/image-loader/atlas-upload-observer.h>
 
 // EXTERNAL INCLUDES
 #include <dali/public-api/images/image.h>
 #include <dali/public-api/images/image-operations.h>
 #include <dali/public-api/images/resource-image.h>
 
 // EXTERNAL INCLUDES
 #include <dali/public-api/images/image.h>
 #include <dali/public-api/images/image-operations.h>
 #include <dali/public-api/images/resource-image.h>
+#include <dali/devel-api/object/weak-handle.h>
 
 namespace Dali
 {
 
 namespace Dali
 {
@@ -72,7 +74,7 @@ typedef IntrusivePtr< ImageVisual > ImageVisualPtr;
  *   "DEFAULT"
  *
  */
  *   "DEFAULT"
  *
  */
-class ImageVisual: public Visual::Base, public ConnectionTracker
+class ImageVisual: public Visual::Base, public ConnectionTracker, public AtlasUploadObserver
 {
 public:
 
 {
 public:
 
@@ -160,6 +162,14 @@ public:
    */
   void SetImage( Actor& actor, const Image& image );
 
    */
   void SetImage( Actor& actor, const Image& image );
 
+  /**
+   * @copydoc AtlasUploadObserver::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.
+   */
+  virtual void UploadCompleted();
+
 private:
 
   /**
 private:
 
   /**
@@ -249,6 +259,7 @@ private:
   Image mImage;
   PixelData mPixels;
   Vector4 mPixelArea;
   Image mImage;
   PixelData mPixels;
   Vector4 mPixelArea;
+  WeakHandle<Actor> mPlacementActor;
 
   std::string mImageUrl;
   Dali::ImageDimensions mDesiredSize;
 
   std::string mImageUrl;
   Dali::ImageDimensions mDesiredSize;