Automatic image atlasing 31/49931/33
authorXiangyin Ma <x1.ma@samsung.com>
Wed, 21 Oct 2015 15:52:35 +0000 (16:52 +0100)
committerXiangyin Ma <x1.ma@samsung.com>
Mon, 9 Nov 2015 13:37:17 +0000 (13:37 +0000)
Change-Id: Iccff3a5aad466a696211b95f29d1411009cbf102

34 files changed:
automated-tests/resources/gallery-small-1.jpg [new file with mode: 0644]
automated-tests/resources/icon-delete.png [new file with mode: 0644]
automated-tests/resources/icon-edit.png [new file with mode: 0644]
automated-tests/src/dali-toolkit/CMakeLists.txt
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-event-thread-callback.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-event-thread-callback.h [new file with mode: 0644]
automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp [new file with mode: 0644]
build/tizen/dali-toolkit/Makefile.am
dali-toolkit/devel-api/controls/renderer-factory/renderer-factory.cpp
dali-toolkit/devel-api/controls/renderer-factory/renderer-factory.h
dali-toolkit/devel-api/file.list
dali-toolkit/devel-api/image-atlas/image-atlas.cpp [new file with mode: 0644]
dali-toolkit/devel-api/image-atlas/image-atlas.h [new file with mode: 0644]
dali-toolkit/internal/controls/image-view/image-view-impl.cpp
dali-toolkit/internal/controls/image-view/image-view-impl.h
dali-toolkit/internal/controls/renderers/control-renderer-impl.cpp
dali-toolkit/internal/controls/renderers/image-atlas-manager.cpp [new file with mode: 0644]
dali-toolkit/internal/controls/renderers/image-atlas-manager.h [new file with mode: 0644]
dali-toolkit/internal/controls/renderers/image/image-renderer.cpp
dali-toolkit/internal/controls/renderers/image/image-renderer.h
dali-toolkit/internal/controls/renderers/npatch/npatch-renderer.cpp
dali-toolkit/internal/controls/renderers/renderer-factory-cache.cpp
dali-toolkit/internal/controls/renderers/renderer-factory-cache.h
dali-toolkit/internal/controls/renderers/renderer-factory-impl.cpp
dali-toolkit/internal/controls/renderers/renderer-factory-impl.h
dali-toolkit/internal/file.list
dali-toolkit/internal/image-atlas/atlas-packer.cpp [new file with mode: 0644]
dali-toolkit/internal/image-atlas/atlas-packer.h [new file with mode: 0644]
dali-toolkit/internal/image-atlas/image-atlas-impl.cpp [new file with mode: 0644]
dali-toolkit/internal/image-atlas/image-atlas-impl.h [new file with mode: 0644]
dali-toolkit/internal/image-atlas/image-load-thread.cpp [new file with mode: 0644]
dali-toolkit/internal/image-atlas/image-load-thread.h [new file with mode: 0644]
dali-toolkit/public-api/controls/image-view/image-view.cpp
dali-toolkit/public-api/controls/image-view/image-view.h

diff --git a/automated-tests/resources/gallery-small-1.jpg b/automated-tests/resources/gallery-small-1.jpg
new file mode 100644 (file)
index 0000000..9292310
Binary files /dev/null and b/automated-tests/resources/gallery-small-1.jpg differ
diff --git a/automated-tests/resources/icon-delete.png b/automated-tests/resources/icon-delete.png
new file mode 100644 (file)
index 0000000..f509cc0
Binary files /dev/null and b/automated-tests/resources/icon-delete.png differ
diff --git a/automated-tests/resources/icon-edit.png b/automated-tests/resources/icon-edit.png
new file mode 100644 (file)
index 0000000..ce3e327
Binary files /dev/null and b/automated-tests/resources/icon-edit.png differ
index 9b656a7..d6e2eaa 100644 (file)
@@ -51,6 +51,7 @@ SET(TC_SOURCES
    utc-Dali-Model3dView.cpp
    utc-Dali-ControlRenderer.cpp
    utc-Dali-RendererFactory.cpp
+   utc-Dali-ImageAtlas.cpp
 )
 
 # Append list of test harness files (Won't get parsed for test cases)
@@ -59,6 +60,7 @@ LIST(APPEND TC_SOURCES
    dali-toolkit-test-utils/toolkit-accessibility-adaptor.cpp
    dali-toolkit-test-utils/toolkit-application.cpp
    dali-toolkit-test-utils/toolkit-clipboard.cpp
+   dali-toolkit-test-utils/toolkit-event-thread-callback.cpp
    dali-toolkit-test-utils/toolkit-imf-manager.cpp
    dali-toolkit-test-utils/toolkit-physical-keyboard.cpp
    dali-toolkit-test-utils/toolkit-style-monitor.cpp
@@ -86,6 +88,8 @@ PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED
 
 SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -O0 -ggdb --coverage -Wall -Werror=return-type")
 
+ADD_DEFINITIONS(-DTEST_RESOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../resources\" )
+
 FOREACH(directory ${${CAPI_LIB}_LIBRARY_DIRS})
     SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -L${directory}")
 ENDFOREACH(directory ${CAPI_LIB_LIBRARY_DIRS})
@@ -99,6 +103,7 @@ INCLUDE_DIRECTORIES(
 ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.cpp ${TC_SOURCES})
 TARGET_LINK_LIBRARIES(${EXEC_NAME}
     ${${CAPI_LIB}_LIBRARIES}
+    -lpthread
 )
 
 INSTALL(PROGRAMS ${EXEC_NAME}
diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-event-thread-callback.cpp b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-event-thread-callback.cpp
new file mode 100644 (file)
index 0000000..e8ee062
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2015 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 "toolkit-event-thread-callback.h"
+
+// EXTERNAL INCLUDES
+#include <cstddef>
+#include <semaphore.h>
+#include <math.h>
+
+namespace Dali
+{
+
+namespace
+{
+EventThreadCallback* gEventThreadCallback = NULL;
+}
+
+struct EventThreadCallback::Impl
+{
+  CallbackBase* callback;
+  unsigned int triggeredCount;
+  unsigned int expectedCount;
+  sem_t mySemaphore;
+};
+
+EventThreadCallback::EventThreadCallback( CallbackBase* callback )
+: mImpl( new Impl() )
+{
+  mImpl->callback = callback;
+  mImpl->triggeredCount = 0u;
+  mImpl->expectedCount = INFINITY;
+  sem_init( &(mImpl->mySemaphore), 0, 0 );
+  gEventThreadCallback = this;
+}
+
+EventThreadCallback::~EventThreadCallback()
+{
+  delete mImpl;
+}
+
+void EventThreadCallback::Trigger()
+{
+  mImpl->triggeredCount++;
+  if(  mImpl->triggeredCount >= mImpl->expectedCount )
+  {
+    sem_post( &(mImpl->mySemaphore) );
+  }
+}
+
+void EventThreadCallback::WaitingForTrigger(unsigned int count)
+{
+  if(  mImpl->triggeredCount >= count )
+  {
+    return;
+  }
+  mImpl->expectedCount = count;
+  sem_wait( &(mImpl->mySemaphore) );
+}
+
+CallbackBase* EventThreadCallback::GetCallback()
+{
+  return mImpl->callback;
+}
+
+EventThreadCallback* EventThreadCallback::Get()
+{
+  return gEventThreadCallback;
+}
+
+}
diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-event-thread-callback.h b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-event-thread-callback.h
new file mode 100644 (file)
index 0000000..d67ca24
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef __DALI_TOOLKIT_EVENT_THREAD_CALLBACK_H__
+#define __DALI_TOOLKIT_EVENT_THREAD_CALLBACK_H__
+
+/*
+ * Copyright (c) 2015 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.
+ *
+ */
+#define __DALI_EVENT_THREAD_CALLBACK_H_
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/signals/callback.h>
+
+namespace Dali
+{
+
+class DALI_IMPORT_API EventThreadCallback
+{
+public:
+
+  EventThreadCallback( CallbackBase* callback );
+
+  ~EventThreadCallback();
+
+  void Trigger();
+
+  void WaitingForTrigger(unsigned int count);
+
+  CallbackBase* GetCallback();
+
+  static EventThreadCallback* Get();
+
+private:
+
+  // undefined copy constructor.
+  EventThreadCallback( const EventThreadCallback& );
+
+  // undefined assignment operator
+  EventThreadCallback& operator=( const EventThreadCallback& );
+
+private:
+
+  struct Impl;
+  Impl* mImpl;
+};
+
+}
+
+#endif /* __DALI_TOOLKIT_EVENT_THREAD_CALLBACK_H__ */
diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp
new file mode 100644 (file)
index 0000000..3883553
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2015 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 <stdlib.h>
+#include <unistd.h>
+#include <dali/dali.h>
+#include <dali-toolkit-test-suite-utils.h>
+#include <toolkit-event-thread-callback.h>
+#include <dali/devel-api/images/atlas.h>
+#include <dali-toolkit/devel-api/image-atlas/image-atlas.h>
+#include <dali-toolkit/public-api/controls/image-view/image-view.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+namespace
+{
+// resolution: 34*34, pixel format: RGBA8888
+static const char* gImage_34_RGBA = TEST_RESOURCE_DIR "/icon-edit.png";
+// resolution: 50*50, pixel format: RGBA8888
+static const char* gImage_50_RGBA = TEST_RESOURCE_DIR "/icon-delete.png";
+// resolution: 128*128, pixel format: RGB888
+static const char* gImage_128_RGB = TEST_RESOURCE_DIR "/gallery-small-1.jpg";
+
+// this is image is not exist, for negative test
+static const char* gImageNonExist = "non-exist.jpg";
+
+const int RENDER_FRAME_INTERVAL = 16; ///< Duration of each frame in ms. (at approx 60FPS)
+
+Rect<int> TextureCoordinateToPixelArea( const Vector4& textureCoordinate, float size )
+{
+  Vector4 temp = textureCoordinate * size;
+  Rect<int> pixelArea;
+  pixelArea.x = static_cast<int>( temp.x );
+  pixelArea.y = static_cast<int>( temp.y );
+  pixelArea.width = static_cast<int>( temp.z-temp.x+1.f );
+  pixelArea.height = static_cast<int>( temp.w-temp.y+1.f );
+
+  return pixelArea;
+}
+
+bool IsOverlap( Rect<int> rect1, Rect<int> rect2 )
+{
+ return rect1.x < rect2.x+rect2.width
+     && rect2.x < rect1.x+rect1.width
+     && rect1.y < rect2.y+rect2.height
+     && rect2.y < rect1.y+rect1.height;
+}
+
+}
+
+void dali_image_atlas_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void dali_image_atlas_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+int UtcDaliImageAtlasNew(void)
+{
+  ToolkitTestApplication application;
+
+  // invoke default handle constructor
+  ImageAtlas atlas;
+
+  DALI_TEST_CHECK( !atlas );
+
+  // initialise handle
+  atlas = ImageAtlas::New( 32, 32 );
+
+  DALI_TEST_CHECK( atlas );
+  END_TEST;
+}
+
+int UtcDaliImageAtlasCopyConstructor(void)
+{
+  ToolkitTestApplication application;
+
+  ImageAtlas atlas = ImageAtlas::New( 32, 32);
+  ImageAtlas atlasCopy(atlas);
+
+  DALI_TEST_EQUALS( (bool)atlasCopy, true, TEST_LOCATION );
+  END_TEST;
+}
+
+int UtcDaliImageAtlasAssignmentOperator(void)
+{
+  ToolkitTestApplication application;
+
+  ImageAtlas atlas = ImageAtlas::New( 32, 32 );
+
+  ImageAtlas atlas2;
+  DALI_TEST_EQUALS( (bool)atlas2, false, TEST_LOCATION );
+
+  atlas2 = atlas;
+  DALI_TEST_EQUALS( (bool)atlas2, true, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliImageAtlasGetAtlas(void)
+{
+  ToolkitTestApplication application;
+
+  ImageAtlas atlas = ImageAtlas::New( 32, 32 );
+  Image image = atlas.GetAtlas();
+
+  // test the atlas created
+  DALI_TEST_EQUALS( (bool)image, true, TEST_LOCATION );
+  DALI_TEST_CHECK( image.GetHeight() == 32u );
+  DALI_TEST_CHECK( image.GetWidth() == 32u );
+
+  Atlas coreAtlas = Atlas::DownCast( image );
+  DALI_TEST_EQUALS( (bool)coreAtlas, true, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliImageAtlasSetBrokenImage(void)
+{
+  ToolkitTestApplication application;
+  unsigned int size = 200;
+  ImageAtlas atlas = ImageAtlas::New( size, size );
+
+  Vector4 textureRect;
+  atlas.Upload( textureRect, gImageNonExist );
+  DALI_TEST_EQUALS( textureRect, Vector4::ZERO, TEST_LOCATION );
+
+  // Set broken image
+  TestPlatformAbstraction& platform = application.GetPlatform();
+  platform.SetClosestImageSize(Vector2( 34, 34));
+  atlas.SetBrokenImage( gImage_34_RGBA );
+
+  // the non-exit image will be replaced with the broken image
+  platform.SetClosestImageSize(Vector2( 0, 0));
+  atlas.Upload( textureRect, gImageNonExist );
+
+  Rect<int> pixelArea = TextureCoordinateToPixelArea(textureRect, size);
+  DALI_TEST_EQUALS( pixelArea.width, 34.f, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.height, 34.f, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliImageAtlasUploadP(void)
+{
+  ToolkitTestApplication application;
+  unsigned int size = 200;
+  ImageAtlas atlas = ImageAtlas::New( size, size );
+
+  EventThreadCallback* eventTrigger = EventThreadCallback::Get();
+  CallbackBase* callback = eventTrigger->GetCallback();
+
+  TraceCallStack& callStack = application.GetGlAbstraction().GetTextureTrace();
+  callStack.Reset();
+  callStack.Enable(true);
+
+  Vector4 textureRect1;
+  atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34) );
+  Vector4 textureRect2;
+  atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50) );
+  Vector4 textureRect3;
+  atlas.Upload( textureRect3, gImage_128_RGB, ImageDimensions(128, 128) );
+
+  eventTrigger->WaitingForTrigger( 3 );// waiting until all three images are loaded
+
+  CallbackBase::Execute( *callback );
+
+  application.SendNotification();
+  application.Render(RENDER_FRAME_INTERVAL);
+
+  callStack.Enable(false);
+
+  Rect<int> pixelArea1 = TextureCoordinateToPixelArea(textureRect1, size);
+  DALI_TEST_EQUALS( pixelArea1.width, 34, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea1.height, 34, TEST_LOCATION );
+  std::stringstream out;
+  out<<pixelArea1.x<<", "<<pixelArea1.y<<", "<<pixelArea1.width<<", "<<pixelArea1.height;
+  DALI_TEST_CHECK(  callStack.FindMethodAndParams("TexSubImage2D", out.str()) );
+
+  Rect<int> pixelArea2 = TextureCoordinateToPixelArea(textureRect2, size);
+  DALI_TEST_EQUALS( pixelArea2.width, 50, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea2.height, 50, TEST_LOCATION );
+  out.str("");
+  out<<pixelArea2.x<<", "<<pixelArea2.y<<", "<<pixelArea2.width<<", "<<pixelArea2.height;
+  DALI_TEST_CHECK(  callStack.FindMethodAndParams("TexSubImage2D", out.str()) );
+
+  Rect<int> pixelArea3 = TextureCoordinateToPixelArea(textureRect3, size);
+  DALI_TEST_EQUALS( pixelArea3.width, 128, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea3.height, 128, TEST_LOCATION );
+  out.str("");
+  out<<pixelArea3.x<<", "<<pixelArea3.y<<", "<<pixelArea3.width<<", "<<pixelArea3.height;
+  DALI_TEST_CHECK( callStack.FindMethodAndParams("TexSubImage2D", out.str()) );
+
+  DALI_TEST_CHECK( ! IsOverlap(pixelArea1, pixelArea2) );
+  DALI_TEST_CHECK( ! IsOverlap(pixelArea1, pixelArea3) );
+  DALI_TEST_CHECK( ! IsOverlap(pixelArea2, pixelArea3) );
+
+  END_TEST;
+}
+
+int UtcDaliImageAtlasRemove(void)
+{
+  TestApplication application;
+  unsigned int size = 100;
+  ImageAtlas atlas = ImageAtlas::New( size, size );
+  Vector4 textureRect1;
+  atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34) );
+
+  atlas.Remove( textureRect1 );
+
+  Vector4 textureRect2;
+  atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50) );
+
+  // one pixel gap
+  Rect<int> pixelArea = TextureCoordinateToPixelArea(textureRect2, size);
+  DALI_TEST_EQUALS( pixelArea.x, 0.f, TEST_LOCATION );
+  DALI_TEST_EQUALS( pixelArea.y, 0.f, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliImageAtlasImageView(void)
+{
+  ToolkitTestApplication application;
+
+  TraceCallStack& callStack = application.GetGlAbstraction().GetTextureTrace();
+  callStack.Reset();
+  callStack.Enable(true);
+
+  ImageView imageView1 = ImageView::New( gImage_34_RGBA, ImageDimensions(34, 34) );
+  ImageView imageView2 = ImageView::New( gImage_50_RGBA, ImageDimensions(50, 50) );
+  Stage::GetCurrent().Add( imageView1 );
+  Stage::GetCurrent().Add( imageView2 );
+
+  EventThreadCallback* eventTrigger = EventThreadCallback::Get();
+  while( eventTrigger == NULL) // waiting uintil the ImageAtlas is created by ImageAtlasManager
+  {
+    usleep(10);
+    eventTrigger = EventThreadCallback::Get();
+  }
+  CallbackBase* callback = eventTrigger->GetCallback();
+
+  eventTrigger->WaitingForTrigger( 2 );// waiting until both images are loaded
+
+  CallbackBase::Execute( *callback );
+
+  application.SendNotification();
+  application.Render(RENDER_FRAME_INTERVAL);
+
+  callStack.Enable(false);
+
+  DALI_TEST_CHECK(  callStack.FindMethodAndParams("TexSubImage2D", "0, 0, 34, 34" ) );
+  DALI_TEST_CHECK(  callStack.FindMethodAndParams("TexSubImage2D", "0, 34, 50, 50" ) );
+
+  callStack.Reset();
+  callStack.Enable(true);
+
+  // remove the imageView2 from stage, the second image will also be removed from atlas
+  // then the space on the atlas will be used by the third image added.
+  Stage::GetCurrent().Remove( imageView2 );
+  application.SendNotification();
+  application.Render(RENDER_FRAME_INTERVAL);
+  ImageView imageView3 = ImageView::New( gImage_128_RGB, ImageDimensions(100, 100) );
+  Stage::GetCurrent().Add( imageView3 );
+
+  eventTrigger->WaitingForTrigger( 3 ); // waiting for the third image loaded
+  CallbackBase::Execute( *callback );
+
+  application.SendNotification();
+  application.Render(RENDER_FRAME_INTERVAL);
+
+  callStack.Enable(false);
+  DALI_TEST_CHECK(  callStack.FindMethodAndParams("TexSubImage2D", "0, 34, 100, 100" ) );
+
+  END_TEST;
+}
index 50bd34e..833348a 100644 (file)
@@ -99,6 +99,7 @@ develapisliderdir =             $(develapicontrolsdir)/slider
 develapishadowviewdir =         $(develapicontrolsdir)/shadow-view
 develapisuperblurviewdir =      $(develapicontrolsdir)/super-blur-view
 develapifocusmanagerdir =       $(develapidir)/focus-manager
+develapiimageatlasdir =         $(develapidir)/image-atlas
 develapiscriptingdir =          $(develapidir)/scripting
 develapishadereffectsdir =      $(develapidir)/shader-effects
 develapitransitioneffectsdir =  $(develapidir)/transition-effects
@@ -112,6 +113,7 @@ develapibubbleemitter_HEADERS =     $(devel_api_bubble_emitter_header_files)
 develapibuilder_HEADERS =           $(devel_api_builder_header_files)
 develapieffectsview_HEADERS =       $(devel_api_effects_view_header_files)
 develapifocusmanager_HEADERS =      $(devel_api_focus_manager_header_files)
+develapiimageatlas_HEADERS =        $(devel_api_image_atlas_header_files)
 develapimagnifier_HEADERS =         $(devel_api_magnifier_header_files)
 develapipopup_HEADERS =             $(devel_api_popup_header_files)
 develapirendererfactory_HEADERS =   $(devel_api_renderer_factory_header_files)
index 3ed8e73..6eaaea2 100644 (file)
@@ -112,14 +112,14 @@ void RendererFactory::ResetRenderer( ControlRenderer& renderer, Actor& actor, co
   GetImplementation( *this ).ResetRenderer( renderer, actor, image );
 }
 
-ControlRenderer RendererFactory::GetControlRenderer( const std::string& url )
+ControlRenderer RendererFactory::GetControlRenderer( const std::string& url, ImageDimensions size )
 {
-  return GetImplementation( *this ).GetControlRenderer( url );
+  return GetImplementation( *this ).GetControlRenderer( url, size );
 }
 
-void RendererFactory::ResetRenderer( ControlRenderer& renderer, Actor& actor, const std::string& url )
+void RendererFactory::ResetRenderer( ControlRenderer& renderer, Actor& actor, const std::string& url, ImageDimensions size )
 {
-  GetImplementation( *this ).ResetRenderer( renderer, actor, url );
+  GetImplementation( *this ).ResetRenderer( renderer, actor, url, size );
 }
 
 void RendererFactory::ResetRenderer( ControlRenderer& renderer, Actor& actor, const Property::Map& propertyMap )
index 6d7a4b8..5d4052b 100644 (file)
@@ -19,6 +19,7 @@
 
 // EXTERNAL INCLUDES
 #include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/images/image-operations.h>
 
 // INTERNAK INCLUDES
 #include <dali-toolkit/devel-api/controls/renderer-factory/control-renderer.h>
@@ -147,9 +148,11 @@ public:
    * @brief Request the control renderer to render the given resource at the url.
    *
    * @param[in] url The URL to the resource to be rendered.
+   * @param[in] size The width and height to fit the loaded image to.
    * @return The pointer pointing to the control renderer
    */
-  ControlRenderer GetControlRenderer( const std::string& url );
+  ControlRenderer GetControlRenderer( const std::string& url,
+                                      ImageDimensions size = ImageDimensions() );
 
   /**
    * @brief Request the current control renderer to render the given resource at the url
@@ -160,8 +163,10 @@ public:
    * @param[in] renderer The ControlRenderer to reset
    * @param[in] actor The Actor the renderer is applied to if, empty if the renderer has not been applied to any Actor
    * @param[in] url The URL to the resource to be rendered.
+   * @param[in] size The width and height to fit the loaded image to.
    */
-  void ResetRenderer( ControlRenderer& renderer, Actor& actor, const std::string& url );
+  void ResetRenderer( ControlRenderer& renderer, Actor& actor, const std::string& url,
+                      ImageDimensions size = ImageDimensions() );
 
 
   /**
index bf62a04..f234c23 100755 (executable)
@@ -19,6 +19,7 @@ devel_api_src_files = \
   $(devel_api_src_dir)/controls/text-controls/text-selection-toolbar.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-atlas/image-atlas.cpp \
   $(devel_api_src_dir)/styling/style-manager.cpp \
   $(devel_api_src_dir)/scripting/script.cpp \
   $(devel_api_src_dir)/transition-effects/cube-transition-cross-effect.cpp \
@@ -62,6 +63,9 @@ devel_api_slider_header_files = \
 devel_api_focus_manager_header_files = \
   $(devel_api_src_dir)/focus-manager/keyinput-focus-manager.h
 
+devel_api_image_atlas_header_files = \
+  $(devel_api_src_dir)/image-atlas/image-atlas.h
+
 devel_api_styling_header_files = \
   $(devel_api_src_dir)/styling/style-manager.h
 
diff --git a/dali-toolkit/devel-api/image-atlas/image-atlas.cpp b/dali-toolkit/devel-api/image-atlas/image-atlas.cpp
new file mode 100644 (file)
index 0000000..3aa9fd2
--- /dev/null
@@ -0,0 +1,87 @@
+ /*
+ * Copyright (c) 2015 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 "image-atlas.h"
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/image-atlas/image-atlas-impl.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+ImageAtlas::ImageAtlas()
+{
+}
+
+ImageAtlas::~ImageAtlas()
+{
+}
+
+ImageAtlas::ImageAtlas(Internal::ImageAtlas* internal)
+: BaseHandle( internal )
+{
+}
+
+ImageAtlas::ImageAtlas( const ImageAtlas& handle )
+: BaseHandle( handle )
+{
+}
+
+ImageAtlas& ImageAtlas::operator=( const ImageAtlas& handle )
+{
+  BaseHandle::operator=(handle);
+  return *this;
+}
+
+ImageAtlas ImageAtlas::New(SizeType width, SizeType height,
+                           Pixel::Format pixelFormat)
+{
+  IntrusivePtr<Internal::ImageAtlas> internal = Internal::ImageAtlas::New( width, height, pixelFormat);
+  return ImageAtlas( internal.Get() );
+}
+
+Image ImageAtlas::GetAtlas()
+{
+  return GetImplementation( *this ).GetAtlas();
+}
+
+void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl )
+{
+  GetImplementation( *this ).SetBrokenImage( brokenImageUrl );
+}
+
+bool ImageAtlas::Upload( Vector4& textureRect,
+                         const std::string& url,
+                         ImageDimensions size,
+                         FittingMode::Type fittingMode,
+                         bool orientationCorrection )
+{
+  return GetImplementation(*this).Upload( textureRect, url, size, fittingMode, orientationCorrection );
+}
+
+void ImageAtlas::Remove(const Vector4& textureRect)
+{
+  GetImplementation(*this).Remove( textureRect );
+}
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/devel-api/image-atlas/image-atlas.h b/dali-toolkit/devel-api/image-atlas/image-atlas.h
new file mode 100644 (file)
index 0000000..cef3eb7
--- /dev/null
@@ -0,0 +1,148 @@
+#ifndef __DALI_TOOLKIT_IMAGE_ATLAS_H__
+#define __DALI_TOOLKIT_IMAGE_ATLAS_H__
+/*
+ * Copyright (c) 2015 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 <stdint.h>
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/images/image.h>
+#include <dali/public-api/images/image-operations.h>
+#include <dali/public-api/images/pixel.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal DALI_INTERNAL
+{
+class ImageAtlas;
+}
+
+/**
+ * @brief An ImageAtlas is a large image containing multiple smaller images.
+ *
+ * Only images with url provided are supported for uploading.
+ * The image are loaded by a worker thread to avoid blocking the main event thread.
+ */
+class DALI_IMPORT_API ImageAtlas : public BaseHandle
+{
+public:
+
+  typedef uint32_t SizeType;
+
+public:
+
+  /**
+   * @brief Create a new ImageAtlas.
+   *
+   * @param [in] width          The atlas width in pixels.
+   * @param [in] height         The atlas height in pixels.
+   * @param [in] pixelFormat    The pixel format (rgba 32 bit by default).
+   * @return A handle to a new ImageAtlas.
+   */
+  static ImageAtlas New( SizeType width, SizeType height,
+                         Pixel::Format pixelFormat = Pixel::RGBA8888 );
+
+  /**
+   * @brief Create an empty handle.
+   *
+   * Calling member functions of an empty handle is not allowed.
+   */
+  ImageAtlas();
+
+  /**
+   * @brief Destructor.
+   */
+  ~ImageAtlas();
+
+  /**
+   * @brief This copy constructor is required for (smart) pointer semantics.
+   *
+   * @param [in] handle A reference to the copied handle
+   */
+  ImageAtlas( const ImageAtlas& handle );
+
+  /**
+   * @brief This assignment operator is required for (smart) pointer semantics.
+   *
+   * @param [in] rhs  A reference to the copied handle
+   * @return A reference to this
+   */
+  ImageAtlas& operator=( const ImageAtlas& handle );
+
+  /**
+   * @brief Get the atlas image.
+   *
+   * This atlas image is still valid after destroying the ImageAtlas object.
+   *
+   * @return the atlas image with type of Dali::Atlas
+   */
+  Image GetAtlas();
+
+  /**
+   * @brief Set the broken image which is used to replace the image if loading fails.
+   *
+   * @param[in] brokenImageUrl The url of the broken image.
+   */
+  void SetBrokenImage( const std::string& brokenImageUrl );
+
+  /**
+   * @brief Upload a resource image to the atlas.
+   *
+   * @note To make the atlasing efficient, an 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.
+   * @return True if there is enough space to fit this image in,false otherwise.
+   */
+  bool Upload( Vector4& textureRect,
+               const std::string& url,
+               ImageDimensions size = ImageDimensions(),
+               FittingMode::Type fittingMode = FittingMode::DEFAULT,
+               bool orientationCorrection = true );
+
+  /**
+   * @brief Remove the image at the given rectangle.
+   *
+   * The rectangular area is marked unoccupied, so new image can be added to this area.
+   *
+   * @param [in] textureRect The texture area to be removed.
+   */
+  void Remove( const Vector4& textureRect );
+
+public: // Not intended for developer use
+
+  explicit DALI_INTERNAL ImageAtlas( Internal::ImageAtlas* impl );
+};
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* __DALI_TOOLKIT_IMAGE_ATLAS_H__ */
index c7f1d8c..59a3b33 100644 (file)
@@ -103,7 +103,7 @@ void ImageView::SetImage( Property::Map map )
   mImageSize = ImageDimensions( width, height );
 }
 
-void ImageView::SetImage( const std::string& url )
+void ImageView::SetImage( const std::string& url, ImageDimensions size )
 {
   if( mUrl != url )
   {
@@ -112,10 +112,17 @@ void ImageView::SetImage( const std::string& url )
 
     mUrl = url;
 
-    Actor self = Self();
-    Toolkit::RendererFactory::Get().ResetRenderer( mRenderer, self, mUrl );
+    if( size.GetWidth() == 0u && size.GetHeight() == 0u )
+    {
+      mImageSize = ResourceImage::GetImageSize( mUrl );
+    }
+    else
+    {
+      mImageSize = size;
+    }
 
-    mImageSize = ResourceImage::GetImageSize( mUrl );
+    Actor self = Self();
+    Toolkit::RendererFactory::Get().ResetRenderer( mRenderer, self, mUrl, mImageSize );
   }
 }
 
@@ -209,7 +216,7 @@ void ImageView::SetProperty( BaseObject* object, Property::Index index, const Pr
         if( value.Get( imageUrl ) )
         {
           ImageView& impl = GetImpl( imageView );
-          impl.SetImage( imageUrl );
+          impl.SetImage( imageUrl, ImageDimensions() );
         }
 
         // if its not a string then get a Property::Map from the property if possible.
index 92c7f70..124ae5f 100644 (file)
@@ -75,7 +75,7 @@ public:
   /**
    * @copydoc Dali::Toolkit::SetImage
    */
-  void SetImage( const std::string& imageUrl );
+  void SetImage( const std::string& imageUrl, ImageDimensions size );
 
   // Properties
   /**
index 04792c5..3b4a949 100644 (file)
@@ -130,8 +130,6 @@ void ControlRenderer::SetOffStage( Actor& actor )
   if( GetIsOnStage() )
   {
     DoSetOffStage( actor );
-    actor.RemoveRenderer( mImpl->mRenderer );
-    mImpl->mRenderer.Reset();
 
     mImpl->mFlags &= ~Impl::IS_ON_STAGE;
   }
@@ -143,6 +141,8 @@ void ControlRenderer::DoSetOnStage( Actor& actor )
 
 void ControlRenderer::DoSetOffStage( Actor& actor )
 {
+  actor.RemoveRenderer( mImpl->mRenderer );
+  mImpl->mRenderer.Reset();
 }
 
 void ControlRenderer::CreatePropertyMap( Property::Map& map ) const
diff --git a/dali-toolkit/internal/controls/renderers/image-atlas-manager.cpp b/dali-toolkit/internal/controls/renderers/image-atlas-manager.cpp
new file mode 100644 (file)
index 0000000..4541ae3
--- /dev/null
@@ -0,0 +1,123 @@
+ /*
+ * Copyright (c) 2015 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 "image-atlas-manager.h"
+
+// EXTERNAL HEADER
+#include <dali/public-api/images/resource-image.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+namespace
+{
+const uint32_t DEFAULT_ATLAS_SIZE( 1024u ); // this size can fit 8 by 8 images of average size 128*128
+const uint32_t MAX_ITEM_SIZE( 512u  );
+const uint32_t MAX_ITEM_AREA( MAX_ITEM_SIZE*MAX_ITEM_SIZE  );
+}
+
+ImageAtlasManager::ImageAtlasManager( Shader shader, const std::string& textureUniformName )
+: mShader( shader ),
+  mTextureUniformName( textureUniformName ),
+  mBrokenImageUrl( "" )
+{
+}
+
+ImageAtlasManager::~ImageAtlasManager()
+{
+}
+
+Material ImageAtlasManager::Add( Vector4& textureRect,
+                                 const std::string& url,
+                                 ImageDimensions size,
+                                 FittingMode::Type fittingMode,
+                                 bool orientationCorrection )
+{
+  ImageDimensions dimensions = size;
+  ImageDimensions zero;
+  if( size == zero )
+  {
+    dimensions = ResourceImage::GetImageSize( url );
+  }
+
+  // big image, atlasing is not applied
+  if( static_cast<uint32_t>(dimensions.GetWidth()) * static_cast<uint32_t>(dimensions.GetHeight()) > MAX_ITEM_AREA
+      || dimensions.GetWidth()>DEFAULT_ATLAS_SIZE
+      || dimensions.GetHeight()>DEFAULT_ATLAS_SIZE)
+  {
+    return Material();
+  }
+
+  unsigned int i = 0;
+  for( AtlasContainer::iterator iter = mAtlasList.begin(); iter != mAtlasList.end(); ++iter)
+  {
+    if( (*iter).Upload( textureRect, url, size, fittingMode, orientationCorrection ) )
+    {
+      return mMaterialList[i];
+    }
+    i++;
+  }
+
+  Toolkit::ImageAtlas newAtlas = Toolkit::ImageAtlas::New( DEFAULT_ATLAS_SIZE, DEFAULT_ATLAS_SIZE  );
+  if( !mBrokenImageUrl.empty() )
+  {
+    newAtlas.SetBrokenImage( mBrokenImageUrl );
+  }
+  mAtlasList.push_back( newAtlas );
+  Material newMaterial = Material::New( mShader );
+  newMaterial.AddTexture( newAtlas.GetAtlas(), mTextureUniformName );
+  mMaterialList.push_back( newMaterial );
+
+  newAtlas.Upload( textureRect, url, size, fittingMode, orientationCorrection );
+
+  return newMaterial;
+}
+
+void ImageAtlasManager::Remove( Material material, const Vector4& textureRect )
+{
+  unsigned int i = 0;
+  for( MaterialContainer::iterator iter = mMaterialList.begin(); iter != mMaterialList.end(); ++iter)
+  {
+    if( (*iter) == material )
+    {
+      mAtlasList[i].Remove(textureRect);
+      return;
+    }
+    i++;
+  }
+}
+
+void ImageAtlasManager::SetBrokenImage( const std::string& brokenImageUrl )
+{
+  if( !brokenImageUrl.empty() )
+  {
+    mBrokenImageUrl = brokenImageUrl;
+  }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/controls/renderers/image-atlas-manager.h b/dali-toolkit/internal/controls/renderers/image-atlas-manager.h
new file mode 100644 (file)
index 0000000..b0729dd
--- /dev/null
@@ -0,0 +1,127 @@
+#ifndef __DALI_TOOLKIT_IMAGE_ATLAS_MANAGER_H__
+#define __DALI_TOOLKIT_IMAGE_ATLAS_MANAGER_H__
+
+/*
+ * Copyright (c) 2015 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/common/vector-wrapper.h>
+#include <dali/public-api/object/ref-object.h>
+#include <dali/devel-api/rendering/material.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/image-atlas/image-atlas.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+/**
+ * The manager for automatic image atlasing. Owned by RendererFactory
+ */
+class ImageAtlasManager : public RefObject
+{
+public:
+  typedef std::vector< Toolkit::ImageAtlas > AtlasContainer;
+  typedef std::vector< Material > MaterialContainer;
+
+public:
+
+  /**
+   * Construtor
+   *
+   * @param[in] shader The shader for material.
+   * @param[in] textureUniformName The texture uniform name for the atlas image.
+   */
+  ImageAtlasManager( Shader shader, const std::string& textureUniformName );
+
+  /**
+   * @brief Add an image to the atlas.
+   *
+   * @note To make the atlasing efficient, an valid size should be provided.
+   *       If size is not provided, then the image file will be opened to read the actual size for loading.
+   *
+   * 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.
+   * @return True if there is enough space to fit this image in,false otherwise.
+   */
+  Material Add( Vector4& textureRect,
+                const std::string& url,
+                ImageDimensions size = ImageDimensions(),
+                FittingMode::Type fittingMode = FittingMode::DEFAULT,
+                bool orientationCorrection = true );
+
+  /**
+   * Remove the image at the given rectangle from the material.
+   *
+   * @param [in] material The material containing the atlas image.
+   * @param [in] textureRect The texture area to be removed.
+   */
+  void Remove( Material material, const Vector4& textureRect );
+
+  /**
+   * @brief Set the broken image which is used to replace the image if loading fails.
+   *
+   * @param[in] brokenImageUrl The url of the broken image.
+   */
+  void SetBrokenImage( const std::string& brokenImageUrl );
+
+protected:
+
+  /**
+   * Destructor
+   */
+  virtual ~ImageAtlasManager();
+
+  /**
+   * Undefined copy constructor.
+   */
+  ImageAtlasManager(const ImageAtlasManager&);
+
+  /**
+   * Undefined assignment operator.
+   */
+  ImageAtlasManager& operator=(const ImageAtlasManager& rhs);
+
+
+private:
+
+  AtlasContainer    mAtlasList;
+  MaterialContainer mMaterialList;
+  Shader            mShader;
+  std::string       mTextureUniformName;
+  std::string       mBrokenImageUrl;
+
+};
+
+} // name Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // __DALI_TOOLKIT_ATLAS_MANAGER_H__
index b78cdab..b07a17f 100644 (file)
@@ -27,6 +27,7 @@
 #include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
 #include <dali-toolkit/internal/controls/renderers/control-renderer-impl.h>
 #include <dali-toolkit/internal/controls/renderers/control-renderer-data-impl.h>
+#include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
 
 namespace Dali
 {
@@ -65,13 +66,16 @@ const char * const BOX_THEN_LINEAR("boxThenLinear");
 const char * const NO_FILTER("noFilter");
 const char * const DONT_CARE("dontCare");
 
-std::string TEXTURE_UNIFORM_NAME = "sTexture";
+const std::string TEXTURE_UNIFORM_NAME = "sTexture";
+const std::string TEXTURE_RECT_UNIFORM_NAME = "uTextureRect";
+const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
 
 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
   attribute mediump vec2 aPosition;\n
   varying mediump vec2 vTexCoord;\n
   uniform mediump mat4 uMvpMatrix;\n
   uniform mediump vec3 uSize;\n
+  uniform mediump vec4 uTextureRect;\n
   \n
   void main()\n
   {\n
@@ -79,7 +83,7 @@ const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
     vertexPosition.xyz *= uSize;\n
     vertexPosition = uMvpMatrix * vertexPosition;\n
     \n
-    vTexCoord = aPosition + vec2(0.5);\n
+    vTexCoord = mix( uTextureRect.xy, uTextureRect.zw, aPosition + vec2(0.5));\n
     gl_Position = vertexPosition;\n
   }\n
 );
@@ -186,8 +190,10 @@ Geometry CreateGeometry( RendererFactoryCache& factoryCache, ImageDimensions gri
 
 } //unnamed namespace
 
-ImageRenderer::ImageRenderer( RendererFactoryCache& factoryCache )
+ImageRenderer::ImageRenderer( RendererFactoryCache& factoryCache, ImageAtlasManager& atlasManager )
 : ControlRenderer( factoryCache ),
+  mAtlasManager( atlasManager ),
+  mTextureRect( FULL_TEXTURE_RECT ),
   mDesiredSize(),
   mFittingMode( FittingMode::DEFAULT ),
   mSamplingMode( SamplingMode::DEFAULT )
@@ -317,7 +323,7 @@ void ImageRenderer::DoInitialize( Actor& actor, const Property::Map& propertyMap
     //clean the cache
     if( !oldImageUrl.empty() )
     {
-      mFactoryCache.CleanRendererCache( oldImageUrl );
+      CleanCache( oldImageUrl );
     }
 
     //Initialize the renderer
@@ -385,24 +391,14 @@ Renderer ImageRenderer::CreateRenderer() const
   if( !mImpl->mCustomShader )
   {
     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
-    shader = mFactoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
-    if( !shader )
-    {
-      shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
-      mFactoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
-    }
+    shader = GetImageShader(mFactoryCache);
   }
   else
   {
     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
     {
-      shader = mFactoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
-      if( !shader )
-      {
-        shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
-        mFactoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
-      }
+      shader = GetImageShader(mFactoryCache);
     }
     else
     {
@@ -430,14 +426,29 @@ void ImageRenderer::InitializeRenderer( const std::string& imageUrl )
     mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
     if( !mImpl->mRenderer )
     {
-      mImpl->mRenderer = CreateRenderer();
+        Material material = mAtlasManager.Add(mTextureRect, imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
+        if( material )
+        {
+          Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
+          mImpl->mRenderer = Renderer::New( geometry, material );
+          SetTextureRectUniform(mTextureRect);
+        }
+        else // big image, atlasing is not applied
+        {
+          mImpl->mRenderer = CreateRenderer();
 
-      ResourceImage image = Dali::ResourceImage::New( imageUrl );
-      image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
-      Material material = mImpl->mRenderer.GetMaterial();
-      material.AddTexture( image, TEXTURE_UNIFORM_NAME );
+          ResourceImage image = Dali::ResourceImage::New( imageUrl );
+          image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
+          Material material = mImpl->mRenderer.GetMaterial();
+          material.AddTexture( image, TEXTURE_UNIFORM_NAME );
+        }
 
-      mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
+        mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
+    }
+    else
+    {
+      Property::Value textureRect = mImpl->mRenderer.GetProperty( mImpl->mRenderer.GetPropertyIndex(TEXTURE_RECT_UNIFORM_NAME) );
+      textureRect.Get( mTextureRect );
     }
     mImpl->mFlags |= Impl::IS_FROM_CACHE;
   }
@@ -462,6 +473,7 @@ void ImageRenderer::InitializeRenderer( const Image& image )
 
   mImpl->mRenderer = CreateRenderer();
   ApplyImageToSampler( image );
+  SetTextureRectUniform( FULL_TEXTURE_RECT );
 }
 
 
@@ -484,6 +496,9 @@ void ImageRenderer::DoSetOnStage( Actor& actor )
       ResourceImage resourceImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
       resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
       image = resourceImage;
+
+      // Set value to the uTextureRect uniform
+      SetTextureRectUniform( FULL_TEXTURE_RECT );
     }
 
     ApplyImageToSampler( image );
@@ -495,11 +510,16 @@ void ImageRenderer::DoSetOffStage( Actor& actor )
   //If we own the image then make sure we release it when we go off stage
   if( !mImageUrl.empty() )
   {
-    //clean the renderer from the cache since it may no longer be in use
-    mFactoryCache.CleanRendererCache( mImageUrl );
+    actor.RemoveRenderer( mImpl->mRenderer );
+    CleanCache(mImageUrl);
 
     mImage.Reset();
   }
+  else
+  {
+    actor.RemoveRenderer( mImpl->mRenderer );
+    mImpl->mRenderer.Reset();
+  }
 }
 
 void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
@@ -598,13 +618,23 @@ void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
   }
 }
 
-void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl )
+Shader ImageRenderer::GetImageShader( RendererFactoryCache& factoryCache )
 {
-  SetImage( actor, imageUrl, 0, 0, Dali::FittingMode::DEFAULT, Dali::SamplingMode::DEFAULT );
+  Shader shader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
+  if( !shader )
+  {
+    shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
+    factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
+  }
+  return shader;
 }
 
-void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, int desiredWidth, int desiredHeight, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
+void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, ImageDimensions size, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
 {
+  mDesiredSize = size;
+  mFittingMode = fittingMode;
+  mSamplingMode = samplingMode;
+
   if( mImageUrl != imageUrl )
   {
     if( mImpl->mRenderer )
@@ -620,7 +650,7 @@ void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, int des
         //clean the cache
         if( !mImageUrl.empty() )
         {
-          mFactoryCache.CleanRendererCache( mImageUrl );
+          CleanCache(mImageUrl);
         }
 
         //Initialize the renderer
@@ -641,9 +671,7 @@ void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, int des
     }
 
     mImageUrl = imageUrl;
-    mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
-    mFittingMode = fittingMode;
-    mSamplingMode = samplingMode;
+
     mImage.Reset();
   }
 }
@@ -665,7 +693,7 @@ void ImageRenderer::SetImage( Actor& actor, const Image& image )
         //clean the cache
         if( !mImageUrl.empty() )
         {
-          mFactoryCache.CleanRendererCache( mImageUrl );
+          CleanCache(mImageUrl);
         }
 
         //Initialize the renderer
@@ -682,6 +710,7 @@ void ImageRenderer::SetImage( Actor& actor, const Image& image )
         ApplyImageToSampler( image );
       }
     }
+    SetTextureRectUniform( FULL_TEXTURE_RECT );
 
     mImage = image;
     mImageUrl.clear();
@@ -722,6 +751,32 @@ void ImageRenderer::OnImageLoaded( ResourceImage image )
   }
 }
 
+void ImageRenderer::SetTextureRectUniform( const Vector4& textureRect )
+{
+  if( mImpl->mRenderer )
+  {
+    Property::Index index = mImpl->mRenderer.GetPropertyIndex( TEXTURE_RECT_UNIFORM_NAME );
+    if( index == Property::INVALID_INDEX )
+    {
+      index = mImpl->mRenderer.RegisterProperty( TEXTURE_RECT_UNIFORM_NAME, textureRect );
+    }
+    else
+    {
+      mImpl->mRenderer.SetProperty( index, textureRect );
+    }
+  }
+}
+
+void ImageRenderer::CleanCache(const std::string& url)
+{
+  Material material = mImpl->mRenderer.GetMaterial();
+  mImpl->mRenderer.Reset();
+  if( mFactoryCache.CleanRendererCache( url ) )
+  {
+    mAtlasManager.Remove( material, mTextureRect );
+  }
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index 8498e54..c11c6b0 100644 (file)
@@ -20,6 +20,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/controls/renderers/control-renderer-impl.h>
+#include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
 
 // EXTERNAL INCLUDES
 #include <dali/public-api/images/image.h>
@@ -76,9 +77,10 @@ public:
   /**
    * @brief Constructor.
    *
-   * @param[in] factoryCache A pointer pointing to the RendererFactoryCache object
+   * @param[in] factoryCache The RendererFactoryCache object
+   * @param[in] atlasManager The atlasManager object
    */
-  ImageRenderer( RendererFactoryCache& factoryCache );
+  ImageRenderer( RendererFactoryCache& factoryCache, ImageAtlasManager& atlasManager );
 
   /**
    * @brief A reference counted object may only be deleted by calling Unreference().
@@ -131,13 +133,10 @@ protected:
 public:
 
   /**
-   * @brief Sets the image of this renderer to the resource at imageUrl
-   * The renderer will load the Image asynchronously when the associated actor is put on stage, and destroy the image when it is off stage
-   *
-   * @param[in] actor The Actor the renderer is applied to if, empty if the renderer has not been applied to any Actor
-   * @param[in] imageUrl The URL to to image resource to use
+   * Get the standard image rendering shader.
+   * @param[in] factoryCache A pointer pointing to the RendererFactoryCache object
    */
-  void SetImage( Actor& actor, const std::string& imageUrl );
+  static Shader GetImageShader( RendererFactoryCache& factoryCache );
 
   /**
    * @brief Sets the image of this renderer to the resource at imageUrl
@@ -145,12 +144,15 @@ public:
    *
    * @param[in] actor The Actor the renderer is applied to if, empty if the renderer has not been applied to any Actor
    * @param[in] imageUrl The URL to to image resource to use
-   * @param[in] desiredWidth The desired width of the resource to load
-   * @param[in] desiredHeight The desired height of the resource to load
+   * @param[in] size The width and height to fit the loaded image to.
    * @param[in] fittingMode The FittingMode of the resource to load
    * @param[in] samplingMode The SamplingMode of the resource to load
    */
-  void SetImage( Actor& actor, const std::string& imageUrl, int desiredWidth, int desiredHeight, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode );
+  void SetImage( Actor& actor,
+                 const std::string& imageUrl,
+                 ImageDimensions size=ImageDimensions(),
+                 FittingMode::Type fittingMode = FittingMode::DEFAULT,
+                 Dali::SamplingMode::Type samplingMode = SamplingMode::BOX_THEN_LINEAR );
 
   /**
    * @brief Sets the image of this renderer to use
@@ -196,8 +198,21 @@ private:
    */
   void OnImageLoaded( ResourceImage image );
 
+  /**
+   * Set the value to the uTextureRect uniform
+   * @param[in] textureRect The texture rectangular area.
+   */
+  void SetTextureRectUniform( const Vector4& textureRect  );
+
+  /**
+   * Clean the renderer from cache, and remove the image from atlas if it is not used anymore
+   */
+  void CleanCache(const std::string& url);
+
 private:
   Image mImage;
+  ImageAtlasManager& mAtlasManager;
+  Vector4 mTextureRect;
 
   std::string mImageUrl;
   Dali::ImageDimensions mDesiredSize;
index 13bf830..e13e85d 100644 (file)
@@ -372,6 +372,8 @@ void NPatchRenderer::DoSetOnStage( Actor& actor )
 void NPatchRenderer::DoSetOffStage( Actor& actor )
 {
   mCroppedImage.Reset();
+  actor.RemoveRenderer( mImpl->mRenderer );
+  mImpl->mRenderer.Reset();
 }
 
 void NPatchRenderer::DoCreatePropertyMap( Property::Map& map ) const
index c7b53b6..524137a 100644 (file)
@@ -122,7 +122,7 @@ void RendererFactoryCache::SaveRenderer( const std::string& key, Renderer& rende
   }
 }
 
-void RendererFactoryCache::CleanRendererCache( const std::string& key )
+bool RendererFactoryCache::CleanRendererCache( const std::string& key )
 {
   int index = FindRenderer( key );
   if( index != -1 )
@@ -134,8 +134,10 @@ void RendererFactoryCache::CleanRendererCache( const std::string& key )
 
       delete cachedRenderer;
       cachedRenderer = NULL;
+      return true;
     }
   }
+  return false;
 }
 
 Geometry RendererFactoryCache::CreateQuadGeometry()
index 947fd47..d1c26ee 100644 (file)
@@ -136,8 +136,10 @@ public:
    * @brief Cleans the renderer cache by removing the renderer from the cache based on the given key if there are no longer any references to it
    *
    * @param[in] key The key used for caching
+   *
+   * @return True if the renderer is no longer used anywhere, false otherwise
    */
-  void CleanRendererCache( const std::string& key );
+  bool CleanRendererCache( const std::string& key );
 
 protected:
 
index ef698f4..0a25ffa 100644 (file)
@@ -31,6 +31,7 @@
 #include <dali-toolkit/internal/controls/renderers/npatch/npatch-renderer.h>
 #include <dali-toolkit/internal/controls/renderers/image/image-renderer.h>
 #include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
+#include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
 
 namespace
 {
@@ -42,6 +43,8 @@ const char * const GRADIENT_RENDERER("gradientRenderer");
 const char * const IMAGE_RENDERER("imageRenderer");
 const char * const N_PATCH_RENDERER("nPatchRenderer");
 
+const std::string TEXTURE_UNIFORM_NAME = "sTexture";
+
 const char * const BROKEN_RENDERER_IMAGE_URL( DALI_IMAGE_DIR "broken.png");
 }
 
@@ -100,7 +103,8 @@ Toolkit::ControlRenderer RendererFactory::GetControlRenderer( const Property::Ma
     }
     else if( typeValue ==  IMAGE_RENDERER )
     {
-      rendererPtr = new ImageRenderer( *( mFactoryCache.Get() ) );
+      CreateAtlasManager();
+      rendererPtr = new ImageRenderer( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
     }
     else if( typeValue ==  N_PATCH_RENDERER )
     {
@@ -195,7 +199,8 @@ Toolkit::ControlRenderer RendererFactory::GetControlRenderer( const Image& image
   }
   else
   {
-    ImageRenderer* rendererPtr = new ImageRenderer( *( mFactoryCache.Get() ) );
+    CreateAtlasManager();
+    ImageRenderer* rendererPtr = new ImageRenderer( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
     Actor actor;
     rendererPtr->SetImage( actor, image );
 
@@ -237,7 +242,7 @@ void RendererFactory::ResetRenderer( Toolkit::ControlRenderer& renderer, Actor&
   }
 }
 
-Toolkit::ControlRenderer RendererFactory::GetControlRenderer( const std::string& url )
+Toolkit::ControlRenderer RendererFactory::GetControlRenderer( const std::string& url, ImageDimensions size )
 {
   if( !mFactoryCache )
   {
@@ -253,15 +258,16 @@ Toolkit::ControlRenderer RendererFactory::GetControlRenderer( const std::string&
   }
   else
   {
-    ImageRenderer* rendererPtr = new ImageRenderer( *( mFactoryCache.Get() ) );
+    CreateAtlasManager();
+    ImageRenderer* rendererPtr = new ImageRenderer( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
     Actor actor;
-    rendererPtr->SetImage( actor, url );
+    rendererPtr->SetImage( actor, url, size );
 
     return Toolkit::ControlRenderer( rendererPtr );
   }
 }
 
-void RendererFactory::ResetRenderer( Toolkit::ControlRenderer& renderer, Actor& actor, const std::string& url )
+void RendererFactory::ResetRenderer( Toolkit::ControlRenderer& renderer, Actor& actor, const std::string& url, ImageDimensions size )
 {
   if( renderer )
   {
@@ -279,7 +285,7 @@ void RendererFactory::ResetRenderer( Toolkit::ControlRenderer& renderer, Actor&
       ImageRenderer* rendererPtr = dynamic_cast< ImageRenderer* >( &GetImplementation( renderer ) );
       if( rendererPtr )
       {
-        rendererPtr->SetImage( actor, url );
+        rendererPtr->SetImage( actor, url, size );
         return;
       }
     }
@@ -287,7 +293,7 @@ void RendererFactory::ResetRenderer( Toolkit::ControlRenderer& renderer, Actor&
     renderer.RemoveAndReset( actor );
   }
 
-  renderer = GetControlRenderer( url );
+  renderer = GetControlRenderer( url, size );
   if( actor && actor.OnStage() )
   {
     renderer.SetOnStage( actor );
@@ -330,6 +336,16 @@ Image RendererFactory::GetBrokenRendererImage()
   return ResourceImage::New( BROKEN_RENDERER_IMAGE_URL );
 }
 
+void RendererFactory::CreateAtlasManager()
+{
+  if( !mAtlasManager )
+  {
+    Shader shader = ImageRenderer::GetImageShader( *( mFactoryCache.Get() ) );
+    mAtlasManager = new ImageAtlasManager(shader, TEXTURE_UNIFORM_NAME);
+    mAtlasManager->SetBrokenImage( BROKEN_RENDERER_IMAGE_URL );
+  }
+}
+
 } // namespace Internal
 
 } // namespace Toolkit
index fed299a..9b55182 100644 (file)
@@ -36,6 +36,9 @@ namespace Internal
 class RendererFactoryCache;
 typedef IntrusivePtr<RendererFactoryCache> RendererFactoryCachePtr;
 
+class ImageAtlasManager;
+typedef IntrusivePtr<ImageAtlasManager> ImageAtlasManagerPtr;
+
 /**
  * @copydoc Toolkit::RendererFactory
  */
@@ -85,14 +88,14 @@ public:
   void ResetRenderer( Toolkit::ControlRenderer& renderer, Actor& actor, const Image& image );
 
   /**
-   * @copydoc Toolkit::RenderFactory::GetControlRenderer( const std::string& )
+   * @copydoc Toolkit::RenderFactory::GetControlRenderer( const std::string&, ImageDimensions )
    */
-  Toolkit::ControlRenderer GetControlRenderer( const std::string& image );
+  Toolkit::ControlRenderer GetControlRenderer( const std::string& image, ImageDimensions size );
 
   /**
-   * @copydoc Toolkit::RendererFactory::ResetRenderer( Toolkit::ControlRenderer&, Actor& actor, const std::string& )
+   * @copydoc Toolkit::RendererFactory::ResetRenderer( Toolkit::ControlRenderer&, Actor& actor, const std::string&, ImageDimensions )
    */
-  void ResetRenderer( Toolkit::ControlRenderer& renderer, Actor& actor, const std::string& image );
+  void ResetRenderer( Toolkit::ControlRenderer& renderer, Actor& actor, const std::string& image, ImageDimensions size );
 
 public:
   /**
@@ -110,6 +113,11 @@ protected:
 private:
 
   /**
+   * Prepare the atlas manager
+   */
+  void CreateAtlasManager();
+
+  /**
    * Undefined copy constructor.
    */
   RendererFactory(const RendererFactory&);
@@ -122,6 +130,7 @@ private:
 private:
 
   RendererFactoryCachePtr mFactoryCache;
+  ImageAtlasManagerPtr mAtlasManager;
 };
 
 } // namespace Internal
index 34d10cc..a30cf52 100644 (file)
@@ -12,6 +12,7 @@ toolkit_src_files = \
    $(toolkit_src_dir)/builder/replacement.cpp \
    $(toolkit_src_dir)/controls/renderers/control-renderer-impl.cpp \
    $(toolkit_src_dir)/controls/renderers/control-renderer-data-impl.cpp \
+   $(toolkit_src_dir)/controls/renderers/image-atlas-manager.cpp \
    $(toolkit_src_dir)/controls/renderers/renderer-factory-cache.cpp \
    $(toolkit_src_dir)/controls/renderers/renderer-factory-impl.cpp \
    $(toolkit_src_dir)/controls/renderers/border/border-renderer.cpp \
@@ -74,6 +75,9 @@ toolkit_src_files = \
    $(toolkit_src_dir)/filters/emboss-filter.cpp \
    $(toolkit_src_dir)/filters/image-filter.cpp \
    $(toolkit_src_dir)/filters/spread-filter.cpp \
+   $(toolkit_src_dir)/image-atlas/atlas-packer.cpp \
+   $(toolkit_src_dir)/image-atlas/image-atlas-impl.cpp \
+   $(toolkit_src_dir)/image-atlas/image-load-thread.cpp \
    $(toolkit_src_dir)/styling/style-manager-impl.cpp \
    $(toolkit_src_dir)/text/bidirectional-support.cpp \
    $(toolkit_src_dir)/text/character-set-conversion.cpp \
diff --git a/dali-toolkit/internal/image-atlas/atlas-packer.cpp b/dali-toolkit/internal/image-atlas/atlas-packer.cpp
new file mode 100644 (file)
index 0000000..7178741
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2015 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-packer.h"
+
+// EXTERNAL HEADER
+#include <stdlib.h> // For abs()
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+namespace
+{
+
+bool ApproximatelyEqual( uint32_t a, uint32_t b  )
+{
+  return abs( a-b ) <= 1;
+}
+
+}
+
+AtlasPacker::Node::Node( Node* parent, SizeType x, SizeType y, SizeType width, SizeType height  )
+: rectArea( x, y, width, height ),
+  parent(parent),
+  occupied( false )
+{
+  child[0] = NULL;
+  child[1] = NULL;
+}
+
+AtlasPacker:: AtlasPacker( SizeType atlasWidth, SizeType atlasHeight )
+: mAvailableArea( atlasWidth * atlasHeight )
+{
+  mRoot = new Node( NULL, 0u, 0u, atlasWidth, atlasHeight );
+}
+
+AtlasPacker::~AtlasPacker()
+{
+  DeleteNode( mRoot );
+}
+
+bool AtlasPacker::Pack( SizeType blockWidth, SizeType blockHeight,
+                        SizeType& packPositionX, SizeType& packPositionY)
+{
+  Node* firstFit = InsertNode( mRoot, blockWidth, blockHeight );
+  if( firstFit != NULL )
+  {
+    firstFit->occupied = true;
+    packPositionX = firstFit->rectArea.x;
+    packPositionY = firstFit->rectArea.y;
+    mAvailableArea -= blockWidth*blockHeight;
+    return true;
+  }
+  return false;
+}
+
+void AtlasPacker::DeleteBlock( SizeType packPositionX, SizeType packPositionY, SizeType blockWidth, SizeType blockHeight )
+{
+  Node* node =  SearchNode( mRoot, packPositionX, packPositionY, blockWidth, blockHeight  );
+  if( node != NULL )
+  {
+    mAvailableArea += blockWidth*blockHeight;
+    MergeToNonOccupied( node );
+  }
+}
+
+unsigned int AtlasPacker::GetAvailableArea() const
+{
+  return mAvailableArea;
+}
+
+AtlasPacker::Node* AtlasPacker::InsertNode( Node* root, SizeType blockWidth, SizeType blockHeight )
+{
+  if( root == NULL )
+  {
+    return NULL;
+  }
+
+  if( root->occupied )
+  {
+    // if not the leaf, then try insert into the first child.
+    Node* newNode = InsertNode(root->child[0], blockWidth, blockHeight);
+    if( newNode == NULL )// no room, try insert into the second child.
+    {
+      newNode = InsertNode(root->child[1], blockWidth, blockHeight);
+    }
+    return newNode;
+  }
+
+  // too small, return
+  if( root->rectArea.width < blockWidth || root->rectArea.height < blockHeight )
+  {
+    return NULL;
+  }
+
+  // right size, accept
+  if( root->rectArea.width == blockWidth && root->rectArea.height == blockHeight )
+  {
+    return root;
+  }
+
+  //too much room, need to split
+  SplitNode( root, blockWidth, blockHeight );
+  // insert into the first child created.
+  return InsertNode( root->child[0], blockWidth, blockHeight);
+}
+
+void AtlasPacker::SplitNode( Node* node, SizeType blockWidth, SizeType blockHeight )
+{
+  node->occupied = true;
+
+  // decide which way to split
+  SizeType remainingWidth = node->rectArea.width - blockWidth;
+  SizeType remainingHeight = node->rectArea.height - blockHeight;
+
+  if( remainingWidth > remainingHeight ) // split vertically
+  {
+    node->child[0] = new Node( node, node->rectArea.x, node->rectArea.y, blockWidth, node->rectArea.height  );
+    node->child[1] = new Node( node, node->rectArea.x+blockWidth, node->rectArea.y, node->rectArea.width-blockWidth, node->rectArea.height );
+  }
+  else // split horizontally
+  {
+    node->child[0] = new Node( node, node->rectArea.x, node->rectArea.y, node->rectArea.width, blockHeight  );
+    node->child[1] = new Node( node, node->rectArea.x, node->rectArea.y+blockHeight, node->rectArea.width, node->rectArea.height-blockHeight );
+  }
+}
+
+AtlasPacker::Node* AtlasPacker::SearchNode( Node* node, SizeType packPositionX, SizeType packPositionY, SizeType blockWidth, SizeType blockHeight  )
+{
+  if( node == NULL )
+  {
+    return NULL;
+  }
+
+  if( node->child[0] != NULL) //not a leaf
+  {
+    Node* newNode = SearchNode(node->child[0], packPositionX, packPositionY, blockWidth, blockHeight);
+    if( newNode == NULL )// try search from the second child.
+    {
+      newNode = SearchNode(node->child[1], packPositionX, packPositionY, blockWidth, blockHeight);
+    }
+    return newNode;
+  }
+  else if( ApproximatelyEqual(node->rectArea.x, packPositionX) && ApproximatelyEqual(node->rectArea.y, packPositionY )
+           && ApproximatelyEqual(node->rectArea.width, blockWidth) && ApproximatelyEqual( node->rectArea.height, blockHeight) )
+  {
+    return node;
+  }
+
+  return NULL;
+}
+
+void AtlasPacker::MergeToNonOccupied( Node* node )
+{
+  node->occupied = false;
+  Node* parent = node->parent;
+  // both child are not occupied, merge the space to parent
+  if( parent !=NULL && parent->child[0]->occupied == false && parent->child[1]->occupied == false)
+  {
+    delete parent->child[0];
+    parent->child[0] = NULL;
+    delete parent->child[1];
+    parent->child[0] = NULL;
+
+    MergeToNonOccupied( parent );
+  }
+}
+
+void AtlasPacker::DeleteNode( Node *node )
+{
+  if( node != NULL )
+  {
+    DeleteNode( node->child[0] );
+    DeleteNode( node->child[1] );
+    delete node;
+  }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/image-atlas/atlas-packer.h b/dali-toolkit/internal/image-atlas/atlas-packer.h
new file mode 100644 (file)
index 0000000..bab993f
--- /dev/null
@@ -0,0 +1,169 @@
+#ifndef __DALI_TOOLKIT_ATLAS_PACKER_H__
+#define __DALI_TOOLKIT_ATLAS_PACKER_H__
+
+/*
+ * Copyright (c) 2015 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 <stdint.h>
+#include <dali/public-api/math/rect.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+/**
+ * Binary space tree based bin packing algorithm.
+ * It is initialised with a fixed width and height and will fit each block into the first node where it fits
+ * and then split that node into 2 parts (down and right) to track the remaining empty space.
+ */
+class AtlasPacker
+{
+public:
+
+  /**
+   * rectangular area (x,y,width,height)
+   */
+  typedef uint32_t SizeType;
+  typedef Rect<SizeType> RectArea;
+
+  /**
+   * Tree node.
+   */
+  struct Node
+  {
+    Node( Node* parent, SizeType x, SizeType y, SizeType width, SizeType height );
+
+    RectArea rectArea;
+    Node* parent;
+    Node* child[2];
+    bool occupied;
+  };
+
+  /**
+   * Constructor.
+   *
+   * @param[in] atlasWidth The width of the atlas.
+   * @param[in] atlasHeight The height of the atlas.
+   */
+  AtlasPacker( SizeType atlasWidth, SizeType atlasHeight );
+
+  /**
+   * Destructor
+   */
+  ~AtlasPacker();
+
+  /**
+   * Pack a block into the atlas.
+   *
+   * @param[in] blockWidth The width of the block to pack.
+   * @param[in] blockHeight The height of the block to pack.
+   * @param[out] packPositionX The x coordinate of the position to pack the block.
+   * @param[out] packPositionY The y coordinate of the position to pack the block.
+   * @return True if there are room for this block, false otherwise.
+   */
+  bool Pack( SizeType blockWidth, SizeType blockHeight,
+             SizeType& packPositionX, SizeType& packPositionY);
+
+  /**
+   * Delete the block.
+   *
+   * @param[in] packPositionX The x coordinate of the pack position.
+   * @param[in] packPositionY The y coordinate of the pack position.
+   * @param[in] blockWidth The width of the block to delete.
+   * @param[in] blockHeight The height of the block to delete.
+   */
+  void DeleteBlock( SizeType packPositionX, SizeType packPositionY, SizeType blockWidth, SizeType blockHeight );
+
+  /**
+   * Query how much empty space left.
+   *
+   * @return The area available for packing.
+   */
+  unsigned int GetAvailableArea() const;
+
+private:
+
+  /*
+   * Search the node which can pack the block with given size.
+   *
+   * @param[in] root The root node of the subtree to be searched.
+   * @param[in] blockWidth The width of the block to pack.
+   * @param[in] blockHeight The height of the block to pack.
+   * @return The poniter pointing to node that can pack the block.
+   *          If it is NULL, there are no room in the subtree to pack the block.
+   */
+  Node* InsertNode( Node* root, SizeType blockWidth, SizeType blockHeight );
+
+  /**
+   * Split the node into two to fit the block width/size.
+   *
+   * @parm[in] node The node to split.
+   * @param[in] blockWidth The width of the block to pack.
+   * @param[in] blockHeight The height of the block to pack.
+   */
+  void SplitNode( Node* node, SizeType blockWidth, SizeType blockHeight );
+
+  /**
+   * Search the node at the given position and with the given size.
+
+   * @param[in] node The root node of the subtree to be searched.
+   * @param[in] packPositionX The x coordinate of the pack position.
+   * @param[in] packPositionY The y coordinate of the pack position.
+   * @param[in] blockWidth The width of the block.
+   * @param[in] blockHeight The height of the block.
+   */
+  Node* SearchNode( Node* node, SizeType packPositionX, SizeType packPositionY, SizeType blockWidth, SizeType blockHeight  );
+
+  /**
+   * Merge the rect of the node to non-occupied area.
+   *
+   * @param[in] node The node to me merged to the non-occupied area
+   */
+  void MergeToNonOccupied( Node* node );
+
+  /**
+   * Delete a node and its subtree.
+   *
+   * @parm[in] node The node to delete.
+   */
+  void DeleteNode( Node* node );
+
+  // Undefined
+  AtlasPacker( const AtlasPacker& imageAtlas);
+
+  // Undefined
+  AtlasPacker& operator=( const AtlasPacker& imageAtlas );
+
+private:
+
+  Node* mRoot; ///< The root of the binary space tree
+  unsigned int mAvailableArea;
+
+};
+
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* __DALI_TOOLKIT_ATLAS_PACKER_H__ */
diff --git a/dali-toolkit/internal/image-atlas/image-atlas-impl.cpp b/dali-toolkit/internal/image-atlas/image-atlas-impl.cpp
new file mode 100644 (file)
index 0000000..a46c6f2
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2015 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 "image-atlas-impl.h"
+
+// EXTERNAL INCLUDES
+#include <string.h>
+#include <dali/public-api/signals/callback.h>
+#include <dali/public-api/images/resource-image.h>
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+ImageAtlas::ImageAtlas( SizeType width, SizeType height, Pixel::Format pixelFormat )
+: mPacker( width, height ),
+  mLoadQueue(),
+  mCompleteQueue( new EventThreadCallback( MakeCallback( this, &ImageAtlas::UploadToAtlas ) ) ),
+  mLoadingThread( mLoadQueue, mCompleteQueue ),
+  mBrokenImageUrl(""),
+  mBrokenImageSize(),
+  mPixelFormat( pixelFormat ),
+  mLoadingThreadStarted( false )
+{
+  mAtlas = Atlas::New( width, height, pixelFormat );
+  mWidth = static_cast<float>(width);
+  mHeight = static_cast<float>( height );
+}
+
+ImageAtlas::~ImageAtlas()
+{
+  if( mLoadingThreadStarted )
+  {
+    // add an empty task would stop the loading thread from contional wait.
+    mLoadQueue.AddTask( NULL );
+    // stop the loading thread
+    mLoadingThread.Join();
+    // The atlas can still be used as texture after ImageAtlas has been thrown away,
+    // so make sure all the loaded bitmap been uploaded to atlas
+    UploadToAtlas();
+  }
+}
+
+IntrusivePtr<ImageAtlas> ImageAtlas::New( SizeType width, SizeType height, Pixel::Format pixelFormat )
+{
+  IntrusivePtr<ImageAtlas> internal = new ImageAtlas( width, height, pixelFormat );
+  return internal;
+}
+
+Image ImageAtlas::GetAtlas()
+{
+  return mAtlas;
+}
+
+void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl )
+{
+  mBrokenImageSize = ResourceImage::GetImageSize( brokenImageUrl );
+  if(mBrokenImageSize.GetWidth() > 0 && mBrokenImageSize.GetHeight() > 0 ) // check the url is valid
+  {
+    mBrokenImageUrl = brokenImageUrl;
+  }
+}
+
+bool ImageAtlas::Upload( Vector4& textureRect,
+                         const std::string& url,
+                         ImageDimensions size,
+                         FittingMode::Type fittingMode,
+                         bool orientationCorrection )
+{
+  ImageDimensions dimensions = size;
+  ImageDimensions zero;
+  if( size == zero ) // image size not provided
+  {
+    dimensions = ResourceImage::GetImageSize( url );
+    if( dimensions == zero ) // Fail to read the image & broken image file exists
+    {
+      if( !mBrokenImageUrl.empty() )
+      {
+        return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true );
+      }
+      else
+      {
+        textureRect = Vector4::ZERO;
+        return true;
+      }
+    }
+  }
+
+  if( static_cast<unsigned int>(dimensions.GetWidth() * dimensions.GetHeight()) > mPacker.GetAvailableArea() )
+  {
+    return false;
+  }
+
+  unsigned int packPositionX = 0;
+  unsigned int packPositionY = 0;
+  if( mPacker.Pack( dimensions.GetWidth(), dimensions.GetHeight(), packPositionX, packPositionY ) )
+  {
+    if( !mLoadingThreadStarted )
+    {
+      mLoadingThread.Start();
+      mLoadingThreadStarted = true;
+    }
+
+    LoadingTask* newTask = new LoadingTask(BitmapLoader::New(url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection ),
+                                           packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight());
+    mLoadQueue.AddTask( newTask );
+
+    // 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
+
+    return true;
+  }
+
+  return false;
+}
+
+void ImageAtlas::Remove( const Vector4& textureRect )
+{
+  mPacker.DeleteBlock( static_cast<SizeType>(textureRect.x*mWidth),
+                       static_cast<SizeType>(textureRect.y*mHeight),
+                       static_cast<SizeType>((textureRect.z-textureRect.x)*mWidth+1.f),
+                       static_cast<SizeType>((textureRect.w-textureRect.y)*mHeight+1.f) );
+}
+
+void ImageAtlas::UploadToAtlas()
+{
+  while( LoadingTask* next = mCompleteQueue.NextTask() )
+  {
+    if( ! next->loader.IsLoaded() )
+    {
+      if(!mBrokenImageUrl.empty()) // replace with the broken image
+      {
+        UploadBrokenImage( next->packRect );
+      }
+
+      DALI_LOG_ERROR( "Failed to load the image: %s\n", (next->loader.GetUrl()).c_str());
+    }
+    else
+    {
+      if( next->loader.GetPixelData()->GetWidth() < next->packRect.width || next->loader.GetPixelData()->GetHeight() < next->packRect.height  )
+      {
+        DALI_LOG_ERROR( "Can not upscale the image from actual loaded size [ %d, %d ] to specified size [ %d, %d ]\n",
+                        next->loader.GetPixelData()->GetWidth(),
+                        next->loader.GetPixelData()->GetHeight(),
+                        next->packRect.width,
+                        next->packRect.height );
+      }
+
+      mAtlas.Upload( next->loader.GetPixelData(), next->packRect.x, next->packRect.y );
+    }
+
+    delete next;
+  }
+}
+
+void ImageAtlas::UploadBrokenImage( const Rect<SizeType>& area )
+{
+  BitmapLoader loader = BitmapLoader::New(mBrokenImageUrl, ImageDimensions( area.width, area.height ) );
+  loader.Load();
+  SizeType loadedWidth = loader.GetPixelData()->GetWidth();
+  SizeType loadedHeight = loader.GetPixelData()->GetHeight();
+
+  bool needBackgroundClear = false;
+  SizeType packX = area.x;
+  SizeType packY = area.y;
+  // locate the broken image in the middle.
+  if( area.width > loadedWidth)
+  {
+    packX += (area.width - loadedWidth)/2;
+    needBackgroundClear = true;
+  }
+  if( area.height > loadedHeight)
+  {
+    packY += (area.height - loadedHeight)/2;
+    needBackgroundClear = true;
+  }
+
+  if( needBackgroundClear )
+  {
+    SizeType size = area.width * area.height * Pixel::GetBytesPerPixel( mPixelFormat );
+    PixelBuffer* buffer = new PixelBuffer [size];
+    PixelDataPtr background = PixelData::New( buffer, area.width, area.height, mPixelFormat, PixelData::DELETE_ARRAY );
+    for( SizeType idx = 0; idx < size; idx++ )
+    {
+      buffer[idx] = 0x00;
+    }
+    mAtlas.Upload( background, area.x, area.y );
+  }
+
+  mAtlas.Upload( loader.GetPixelData(), packX, packY );
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/image-atlas/image-atlas-impl.h b/dali-toolkit/internal/image-atlas/image-atlas-impl.h
new file mode 100644 (file)
index 0000000..c3e621c
--- /dev/null
@@ -0,0 +1,152 @@
+#ifndef __DALI_TOOLKIT_IMAGE_ATLAS_IMPL_H__
+#define __DALI_TOOLKIT_IMAGE_ATLAS_IMPL_H__
+
+/*
+ * Copyright (c) 2015 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 <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/object/base-object.h>
+#include <dali/devel-api/images/atlas.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/image-atlas/image-atlas.h>
+#include <dali-toolkit/internal/image-atlas/atlas-packer.h>
+#include <dali-toolkit/internal/image-atlas/image-load-thread.h>
+
+namespace Dali
+{
+class EventThreadCallback;
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+class ImageAtlas : public BaseObject
+{
+public:
+
+  typedef Toolkit::ImageAtlas::SizeType SizeType;
+
+  /**
+   * Constructor
+   * @param [in] width          The atlas width in pixels.
+   * @param [in] height         The atlas height in pixels.
+   * @param [in] pixelFormat    The pixel format.
+   */
+  ImageAtlas( SizeType width, SizeType height, Pixel::Format pixelFormat );
+
+  /**
+   * @copydoc Toolkit::ImageAtlas::New
+   */
+  static IntrusivePtr<ImageAtlas> New( SizeType width, SizeType height, Pixel::Format pixelFormat );
+
+  /**
+   * @copydoc Toolkit::ImageAtlas::GetAtlas
+   */
+  Image GetAtlas();
+
+  /**
+   * @copydoc Toolkit::ImageAtlas::SetBrokenImage
+   */
+  void SetBrokenImage( const std::string& brokenImageUrl );
+
+  /**
+   * @copydoc Toolkit::ImageAtlas::Upload
+   */
+  bool Upload( Vector4& textureRect,
+               const std::string& url,
+               ImageDimensions size,
+               FittingMode::Type fittingMode,
+               bool orientationCorrection);
+
+  /**
+   * @copydoc Toolkit::ImageAtlas::Remove
+   */
+  void Remove( const Vector4& textureRect );
+
+protected:
+
+  /**
+   * Destructor
+   */
+  ~ImageAtlas();
+
+private:
+
+  /**
+   * Upload the bitmap to atlas when the image is loaded in the worker thread.
+   */
+  void UploadToAtlas();
+
+  /**
+   * Upload broken image
+   *
+   * @param[in] area The pixel area for uploading.
+   */
+  void UploadBrokenImage( const Rect<SizeType>& area );
+
+  // Undefined
+  ImageAtlas( const ImageAtlas& imageAtlas);
+
+  // Undefined
+  ImageAtlas& operator=( const ImageAtlas& imageAtlas );
+
+private:
+
+  Atlas                mAtlas;
+  AtlasPacker          mPacker;
+
+  LoadQueue            mLoadQueue;
+  CompleteQueue        mCompleteQueue;
+  ImageLoadThread      mLoadingThread;
+
+  std::string          mBrokenImageUrl;
+  ImageDimensions      mBrokenImageSize;
+  float                mWidth;
+  float                mHeight;
+  Pixel::Format        mPixelFormat;
+  bool                 mLoadingThreadStarted;
+
+};
+
+} // namespace Internal
+
+inline const Internal::ImageAtlas& GetImplementation( const Toolkit::ImageAtlas& imageAtlas )
+{
+  DALI_ASSERT_ALWAYS( imageAtlas && "ImageAtlas handle is empty" );
+
+  const BaseObject& handle = imageAtlas.GetBaseObject();
+
+  return static_cast<const Internal::ImageAtlas&>(handle);
+}
+
+inline Internal::ImageAtlas& GetImplementation( Toolkit::ImageAtlas& imageAtlas )
+{
+  DALI_ASSERT_ALWAYS( imageAtlas && "ImageAtlas handle is empty" );
+
+  BaseObject& handle = imageAtlas.GetBaseObject();
+
+  return static_cast<Internal::ImageAtlas&>(handle);
+}
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* __DALI_TOOLKIT_IMAGE_ATLAS_IMPL_H__ */
diff --git a/dali-toolkit/internal/image-atlas/image-load-thread.cpp b/dali-toolkit/internal/image-atlas/image-load-thread.cpp
new file mode 100644 (file)
index 0000000..d106690
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2015 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 "image-load-thread.h"
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+LoadingTask::LoadingTask(BitmapLoader loader, uint32_t packPositionX, uint32_t packPositionY, uint32_t width, uint32_t height )
+: loader( loader ),
+  packRect( packPositionX, packPositionY, width, height )
+{
+}
+
+LoadQueue::LoadQueue()
+{
+}
+
+LoadQueue::~LoadQueue()
+{
+}
+
+LoadingTask* LoadQueue::NextTask()
+{
+  // Lock while popping task out from the queue
+  ConditionalWait::ScopedLock lock( mConditionalWait );
+
+  while( mTasks.Empty() )
+  {
+    mConditionalWait.Wait( lock );
+  }
+
+  Vector< LoadingTask* >::Iterator next = mTasks.Begin();
+  LoadingTask* nextTask = *next;
+  mTasks.Erase( next );
+
+  return nextTask;
+}
+
+void LoadQueue::AddTask( LoadingTask* task )
+{
+  bool wasEmpty = false;
+
+  {
+    // Lock while adding task to the queue
+    ConditionalWait::ScopedLock lock( mConditionalWait );
+    wasEmpty = mTasks.Empty();
+    mTasks.PushBack( task );
+  }
+
+  if( wasEmpty)
+  {
+    // wake up the image loading thread
+    mConditionalWait.Notify();
+  }
+}
+
+CompleteQueue::CompleteQueue(EventThreadCallback* trigger)
+: mTrigger( trigger )
+{}
+
+CompleteQueue::~CompleteQueue()
+{
+  delete mTrigger;
+}
+
+LoadingTask* CompleteQueue::NextTask()
+{
+  while( mTasks.Empty() )
+  {
+    return NULL;
+  }
+
+  // Lock while popping task out from the queue
+  Mutex::ScopedLock lock( mMutex );
+
+  Vector< LoadingTask* >::Iterator next = mTasks.Begin();
+  LoadingTask* nextTask = *next;
+  mTasks.Erase( next );
+
+  return nextTask;
+}
+
+void CompleteQueue::AddTask( LoadingTask* task )
+{
+  // Lock while adding task to the queue
+  Mutex::ScopedLock lock( mMutex );
+  mTasks.PushBack( task );
+
+  // wake up the main thread
+  mTrigger->Trigger();
+}
+
+
+ImageLoadThread::ImageLoadThread( LoadQueue& loadQueue, CompleteQueue& completeQueue )
+: mLoadQueue( loadQueue ),
+  mCompleteQueue( completeQueue )
+{
+}
+
+ImageLoadThread::~ImageLoadThread()
+{
+}
+
+void ImageLoadThread::Run()
+{
+  while( LoadingTask* task = mLoadQueue.NextTask() )
+  {
+    task->loader.Load();
+    mCompleteQueue.AddTask( task );
+  }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/image-atlas/image-load-thread.h b/dali-toolkit/internal/image-atlas/image-load-thread.h
new file mode 100644 (file)
index 0000000..700815c
--- /dev/null
@@ -0,0 +1,204 @@
+#ifndef __DALI_TOOLKIT_IMAGE_LOAD_THREAD_H__
+#define __DALI_TOOLKIT_IMAGE_LOAD_THREAD_H__
+
+/*
+ * Copyright (c) 2015 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 <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/object/ref-object.h>
+#include <dali/devel-api/threading/conditional-wait.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/devel-api/threading/thread.h>
+#include <dali/devel-api/adaptor-framework/bitmap-loader.h>
+#include <dali/devel-api/adaptor-framework/event-thread-callback.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+/**
+ * The task of loading and packing an image into the atlas.
+ */
+struct LoadingTask
+{
+  /**
+   * Constructor.
+   */
+  LoadingTask( BitmapLoader loader, uint32_t packPositionX, uint32_t packPositionY, uint32_t width, uint32_t height  );
+
+private:
+
+  // Undefined
+  LoadingTask( const LoadingTask& queue );
+
+  // Undefined
+  LoadingTask& operator=( const LoadingTask& queue );
+
+public:
+
+  BitmapLoader   loader;    ///< The loader used to load the bitmap from URL
+  Rect<uint32_t> packRect;  ///< The x coordinate of the position to pack the image.
+
+};
+
+/**
+ * The queue of the tasks waiting to load the bitmap from the URL in the worker thread/
+ */
+class LoadQueue //: public TaskQueue
+{
+public:
+
+  /**
+   * Constructor
+   */
+  LoadQueue();
+
+  /**
+   * Destructor.
+   */
+  ~LoadQueue();
+
+  /**
+   * Pop the next task out from the queue.
+   *
+   * @return The next task to be processed.
+   */
+  LoadingTask* NextTask();
+
+  /**
+   * Add a task in to the queue
+   *
+   * @param[in] task The task added to the queue.
+   */
+  void AddTask( LoadingTask* task );
+
+private:
+
+  // Undefined
+  LoadQueue( const LoadQueue& queue );
+
+  // Undefined
+  LoadQueue& operator=( const LoadQueue& queue );
+
+private:
+
+  Vector< LoadingTask* > mTasks;
+  ConditionalWait mConditionalWait;
+};
+
+/**
+ * The queue of the tasks, with the image loaded, waiting for the main thread to upload the bitmap.
+ */
+class CompleteQueue //: public TaskQueue
+{
+public:
+
+  /**
+   * Constructor
+   *
+   * @param[in] mTrigger The trigger to wake up the main thread.
+   */
+  CompleteQueue( EventThreadCallback* mTrigger );
+
+  /**
+   * Destructor.
+   */
+  ~CompleteQueue();
+
+  /**
+   * Pop the next task out from the queue.
+   *
+   * @return The next task to be processed.
+   */
+  LoadingTask* NextTask();
+
+  /**
+   * Add a task in to the queue
+   *
+   * @param[in] task The task added to the queue.
+   */
+  void AddTask( LoadingTask* task );
+
+private:
+
+  // Undefined
+  CompleteQueue( const CompleteQueue& queue );
+
+  // Undefined
+  CompleteQueue& operator=( const CompleteQueue& queue );
+
+private:
+
+  Vector< LoadingTask* > mTasks;
+  Dali::Mutex            mMutex;
+  EventThreadCallback*   mTrigger;
+};
+
+/**
+ * The worker thread for image loading.
+ */
+class ImageLoadThread : public Thread
+{
+public:
+
+  /**
+   * Constructor.
+   *
+   * @param[in] loadQueue The task queue with images for loading.
+   * @param[in] completeQurue The task queue with images loaded.
+   */
+  ImageLoadThread( LoadQueue& loadQueue, CompleteQueue& completeQueue );
+
+  /**
+   * Destructor.
+   */
+  virtual ~ImageLoadThread();
+
+protected:
+
+  /**
+   * The entry function of the worker thread.
+   * It fetches loading task from the loadQueue, loads the image and adds to the completeQueue.
+   */
+  virtual void Run();
+
+private:
+
+  // Undefined
+  ImageLoadThread( const ImageLoadThread& thread );
+
+  // Undefined
+  ImageLoadThread& operator=( const ImageLoadThread& thread );
+
+private:
+
+  LoadQueue& mLoadQueue;          ///<The task queue with images for loading.
+  CompleteQueue& mCompleteQueue;  ///<The task queue with images loaded.
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* __DALI_TOOLKIT_IMAGE_LOAD_THREAD_H__ */
index e3d2b67..8c408e9 100644 (file)
@@ -65,10 +65,11 @@ ImageView ImageView::New( Image image )
   return ImageView( imageView );
 }
 
-ImageView ImageView::New( const std::string& url )
+ImageView ImageView::New( const std::string& url,
+                          ImageDimensions size)
 {
   ImageView imageView = Internal::ImageView::New();
-  imageView.SetImage( url );
+  imageView.SetImage( url, size );
   return imageView;
 }
 
@@ -82,9 +83,10 @@ void ImageView::SetImage( Image image )
   Dali::Toolkit::GetImpl( *this ).SetImage( image );
 }
 
-void ImageView::SetImage( const std::string& url )
+void ImageView::SetImage( const std::string& url,
+                          ImageDimensions size )
 {
-  Dali::Toolkit::GetImpl( *this ).SetImage( url );
+  Dali::Toolkit::GetImpl( *this ).SetImage( url, size );
 }
 
 Image ImageView::GetImage() const
index 4e98ccb..4a7545e 100644 (file)
@@ -21,6 +21,9 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/public-api/controls/control.h>
 
+// EXTERNAL INCLUDES
+#include <dali/public-api/images/image-operations.h>
+
 namespace Dali
 {
 
@@ -89,11 +92,17 @@ public:
   /**
    * @brief Create an initialized ImageView from an Image resource url
    *
+   * @note A valid size is preferable for efficiency.
+   *       However, do not set 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 the string is empty, ImageView will display nothing
    * @param[in] url The url of the image resource to display.
+   * @param [in] size The width and height to fit the loaded image to.
    * @return A handle to a newly allocated ImageView.
    */
-  static ImageView New( const std::string& url );
+  static ImageView New( const std::string& url,
+                        ImageDimensions size = ImageDimensions() );
 
   /**
    * @brief Destructor
@@ -144,8 +153,10 @@ public:
    * @since DALi 1.1.4
    *
    * @param[in] url The Image resource to display.
+   * @param [in] size The width and height to fit the loaded image to.
    */
-  void SetImage( const std::string& url );
+  void SetImage( const std::string& url,
+                 ImageDimensions size = ImageDimensions() );
 
   /**
    * @deprecated Gets the Image