Adding Async remote loading to ImageVisual 74/120674/12
authorDavid Steele <david.steele@samsung.com>
Thu, 16 Mar 2017 20:14:38 +0000 (20:14 +0000)
committerAgnelo Vaz <agnelo.vaz@samsung.com>
Tue, 4 Apr 2017 15:29:55 +0000 (08:29 -0700)
Implemented a cut down version of TextureManager to handle Async loading for
local and remote images, without atlasing or handling for broken image.

Modified test harness to manage multiple event triggers using a round-robin
semaphore test. (Required, as we now have 2 AsyncImageLoader threads in
TextureManager, which each generate an EventThreadTrigger).

Change-Id: I06fec6109985c7458a9135f7c3ef85a3f4503c7a
Signed-off-by: David Steele <david.steele@samsung.com>
25 files changed:
automated-tests/resources/broken.png [new file with mode: 0644]
automated-tests/resources/button-up.9.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
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-event-thread-callback.h
automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/utc-Dali-Visual.cpp
automated-tests/src/dali-toolkit/utc-Dali-VisualFactory.cpp
dali-toolkit/internal/file.list
dali-toolkit/internal/image-loader/async-image-loader-impl.cpp
dali-toolkit/internal/image-loader/async-image-loader-impl.h
dali-toolkit/internal/image-loader/image-atlas-impl.cpp
dali-toolkit/internal/image-loader/image-load-thread.cpp
dali-toolkit/internal/image-loader/image-load-thread.h
dali-toolkit/internal/visuals/image-atlas-manager.cpp
dali-toolkit/internal/visuals/image/image-visual.cpp
dali-toolkit/internal/visuals/image/image-visual.h
dali-toolkit/internal/visuals/texture-manager.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/texture-manager.h [new file with mode: 0644]
dali-toolkit/internal/visuals/texture-upload-observer.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/texture-upload-observer.h [new file with mode: 0644]
dali-toolkit/internal/visuals/visual-factory-cache.cpp
dali-toolkit/internal/visuals/visual-factory-cache.h
dali-toolkit/public-api/image-loader/async-image-loader.cpp

diff --git a/automated-tests/resources/broken.png b/automated-tests/resources/broken.png
new file mode 100644 (file)
index 0000000..2d1c272
Binary files /dev/null and b/automated-tests/resources/broken.png differ
diff --git a/automated-tests/resources/button-up.9.png b/automated-tests/resources/button-up.9.png
new file mode 100644 (file)
index 0000000..a2e2e01
Binary files /dev/null and b/automated-tests/resources/button-up.9.png differ
index c7a1b8b..9b8bd63 100755 (executable)
@@ -18,6 +18,7 @@ SET(TC_SOURCES
    utc-Dali-FlexContainer.cpp
    utc-Dali-GaussianBlurView.cpp
    utc-Dali-ImageView.cpp
+   utc-Dali-ImageVisual.cpp
    utc-Dali-JsonParser.cpp
    utc-Dali-KeyInputFocusManager.cpp
    utc-Dali-PageTurnView.cpp
index b809b87..0f037d3 100644 (file)
 #include <climits>
 #include <cstdio>
 #include <unistd.h>
-
-namespace Dali
-{
+#include <vector>
+#include <algorithm>
 
 namespace
 {
-EventThreadCallback* gEventThreadCallback = NULL;
+// Note, this is not thread safe - however, should not be using
+// triggers from multiple threads - they should all be created on
+// event thread.
+std::vector<Dali::EventThreadCallback*> gEventThreadCallbacks;
 }
 
+
+namespace Dali
+{
+
 struct EventThreadCallback::Impl
 {
   CallbackBase* callback;
-  unsigned int triggeredCount;
-  unsigned int expectedCount;
   sem_t mySemaphore;
 };
 
@@ -46,38 +50,42 @@ EventThreadCallback::EventThreadCallback( CallbackBase* callback )
 : mImpl( new Impl() )
 {
   mImpl->callback = callback;
-  mImpl->triggeredCount = 0u;
-  mImpl->expectedCount = UINT_MAX;
   sem_init( &(mImpl->mySemaphore), 0, 0 );
-  gEventThreadCallback = this;
+
+  gEventThreadCallbacks.push_back(this);
 }
 
 EventThreadCallback::~EventThreadCallback()
 {
+  std::vector<EventThreadCallback*>::iterator iter =
+    std::find(gEventThreadCallbacks.begin(), gEventThreadCallbacks.end(), this);
+  if( iter != gEventThreadCallbacks.end() )
+  {
+    gEventThreadCallbacks.erase(iter);
+  }
   delete mImpl;
 }
 
 void EventThreadCallback::Trigger()
 {
-  mImpl->triggeredCount++;
-  if(  mImpl->triggeredCount >= mImpl->expectedCount )
-  {
-    sem_post( &(mImpl->mySemaphore) );
-  }
+  sem_post( &(mImpl->mySemaphore) );
 }
 
-bool EventThreadCallback::WaitingForTrigger(unsigned int count, unsigned int seconds)
+// returns true if timed out rather than triggered
+bool EventThreadCallback::WaitingForTrigger()
 {
-  if(  mImpl->triggeredCount >= count )
-  {
-    return true;
-  }
   struct timespec now;
   clock_gettime( CLOCK_REALTIME, &now );
-  now.tv_sec += seconds;
-  mImpl->expectedCount = count;
+  if( now.tv_nsec < 999900000 ) // 999, 900, 000
+    now.tv_nsec += 100000;
+  else
+  {
+    now.tv_sec += 1;
+    now.tv_nsec = 0;
+  }
+
   int error = sem_timedwait( &(mImpl->mySemaphore), &now );
-  return error != 0;
+  return error != 0; // true if timeout
 }
 
 CallbackBase* EventThreadCallback::GetCallback()
@@ -85,19 +93,14 @@ CallbackBase* EventThreadCallback::GetCallback()
   return mImpl->callback;
 }
 
-EventThreadCallback* EventThreadCallback::Get()
-{
-  return gEventThreadCallback;
 }
 
-}
 
 namespace Test
 {
 
 bool WaitForEventThreadTrigger( int triggerCount )
 {
-  bool success = true;
   const int TEST_TIMEOUT(30);
 
   struct timespec startTime;
@@ -106,23 +109,31 @@ bool WaitForEventThreadTrigger( int triggerCount )
   now.tv_sec = startTime.tv_sec;
   now.tv_nsec = startTime.tv_nsec;
 
-  Dali::EventThreadCallback* eventTrigger = NULL;
-  while( eventTrigger == NULL )
+  // Round robin poll of each semaphore:
+  while ( triggerCount > 0 )
   {
-    eventTrigger = Dali::EventThreadCallback::Get();
+    if( gEventThreadCallbacks.size() > 0 )
+    {
+      for( std::vector<Dali::EventThreadCallback*>::iterator iter = gEventThreadCallbacks.begin();
+           iter != gEventThreadCallbacks.end(); ++iter )
+      {
+        Dali::EventThreadCallback* eventTrigger = (*iter);
+        Dali::CallbackBase* callback = eventTrigger->GetCallback();
+        bool timedout = eventTrigger->WaitingForTrigger();
+        if( ! timedout )
+        {
+          // Semaphore was unlocked - execute the trigger
+          Dali::CallbackBase::Execute( *callback );
+          triggerCount--;
+        }
+      }
+    }
     clock_gettime( CLOCK_REALTIME, &now );
     if( now.tv_sec - startTime.tv_sec > TEST_TIMEOUT )
     {
-      success = false;
+      // Ensure we break out of the loop if elapsed time has passed
       break;
     }
-    usleep(10);
-  }
-  if( eventTrigger != NULL )
-  {
-    Dali::CallbackBase* callback = eventTrigger->GetCallback();
-    eventTrigger->WaitingForTrigger( triggerCount, TEST_TIMEOUT - (now.tv_sec - startTime.tv_sec) );
-    Dali::CallbackBase::Execute( *callback );
   }
 
   clock_gettime( CLOCK_REALTIME, &now );
@@ -130,7 +141,7 @@ bool WaitForEventThreadTrigger( int triggerCount )
   {
     fprintf(stderr, "WaitForEventThreadTrigger took %ld seconds\n", now.tv_sec - startTime.tv_sec );
   }
-  return success;
+  return triggerCount == 0;
 }
 
 }
index a0303be..1ae27d2 100644 (file)
@@ -36,12 +36,10 @@ public:
 
   void Trigger();
 
-  bool WaitingForTrigger(unsigned int count, unsigned int seconds );
+  bool WaitingForTrigger();
 
   CallbackBase* GetCallback();
 
-  static EventThreadCallback* Get();
-
 private:
 
   // undefined copy constructor.
index f6164fb..cca026f 100644 (file)
@@ -467,7 +467,7 @@ int UtcDaliImageAtlasImageView(void)
   application.GetPlatform().SetClosestImageSize(  Vector2(100, 100) );
   Stage::GetCurrent().Add( imageView3 );
 
-  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 3 ), true, TEST_LOCATION );
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
 
   application.SendNotification();
   application.Render(RENDER_FRAME_INTERVAL);
diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp
new file mode 100644 (file)
index 0000000..fd94447
--- /dev/null
@@ -0,0 +1,845 @@
+/*
+ * Copyright (c) 2017 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 <iostream>
+#include <stdlib.h>
+#include <dali-toolkit-test-suite-utils.h>
+#include <toolkit-timer.h>
+#include <toolkit-bitmap-loader.h>
+#include <toolkit-event-thread-callback.h>
+#include <dali/public-api/rendering/renderer.h>
+#include <dali/public-api/rendering/texture-set.h>
+#include <dali/public-api/rendering/shader.h>
+#include <dali/devel-api/images/nine-patch-image.h>
+#include <dali/devel-api/object/handle-devel.h>
+#include <dali-toolkit/devel-api/align-enums.h>
+#include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
+#include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
+#include <dali-toolkit/devel-api/visual-factory/transition-data.h>
+#include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include "dummy-control.h"
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+void dali_image_visual_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void dali_image_visual_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+namespace
+{
+const char* TEST_IMAGE_FILE_NAME =  TEST_RESOURCE_DIR  "/gallery_small-1.jpg";
+const char* TEST_LARGE_IMAGE_FILE_NAME =  TEST_RESOURCE_DIR "/tbcol.png";
+const char* TEST_SMALL_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/icon-edit.png";
+const char* TEST_REMOTE_IMAGE_FILE_NAME = "https://www.tizen.org/sites/all/themes/tizen_theme/logo.png";
+}
+
+
+Actor CreateActorWithImageVisual(const Property::Map& map)
+{
+  VisualFactory factory = VisualFactory::Get();
+  DummyControl actor = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  Visual::Base visual = factory.CreateVisual( map );
+  DALI_TEST_CHECK( visual );
+  dummyImpl.RegisterVisual( Control::CONTROL_PROPERTY_END_INDEX + 1, visual );
+  actor.SetSize( 200.f, 200.f );
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+  return actor;
+}
+
+void TestVisualRender( ToolkitTestApplication& application,
+                       DummyControl& actor,
+                       Visual::Base& visual,
+                       std::size_t expectedSamplers = 0,
+                       ImageDimensions imageDimensions = ImageDimensions(),
+                       Integration::ResourcePointer resourcePtr = Integration::ResourcePointer())
+{
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual( Control::CONTROL_PROPERTY_END_INDEX + 1, visual );
+
+  if( resourcePtr )
+  {
+    // set the image size, for test case, this needs to be set before loading started
+    application.GetPlatform().SetClosestImageSize(  Vector2(imageDimensions.GetWidth(), imageDimensions.GetHeight()) );
+  }
+
+  actor.SetSize( 200.f, 200.f );
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+
+  Stage::GetCurrent().Add( actor );
+
+  application.SendNotification(); // Send messages to update
+  application.Render();           // process update and render
+  application.SendNotification(); // process any signals to event
+
+  if( resourcePtr )
+  {
+    DALI_TEST_EQUALS( application.GetPlatform().WasCalled(TestPlatformAbstraction::LoadResourceSynchronouslyFunc ), true, TEST_LOCATION);
+  }
+
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION );
+}
+
+static void TestMixColor( Visual::Base visual, Property::Index mixColorIndex, const Vector4& testColor )
+{
+  Property::Map map;
+  visual.CreatePropertyMap(map);
+  Property::Value* value = map.Find( mixColorIndex );
+  DALI_TEST_CHECK( value );
+  Vector3 mixColor1;
+  DALI_TEST_CHECK( value->Get( mixColor1 ) );
+  DALI_TEST_EQUALS( mixColor1, Vector3(testColor), 0.001, TEST_LOCATION );
+
+  value = map.Find( DevelVisual::Property::MIX_COLOR );
+  DALI_TEST_CHECK( value );
+  Vector4 mixColor2;
+  DALI_TEST_CHECK( value->Get( mixColor2 ) );
+  DALI_TEST_EQUALS( mixColor2, testColor, 0.001, TEST_LOCATION );
+
+  value = map.Find( DevelVisual::Property::OPACITY );
+  DALI_TEST_CHECK( value );
+  float opacity;
+  DALI_TEST_CHECK( value->Get( opacity ) );
+  DALI_TEST_EQUALS( opacity, testColor.a, 0.001, TEST_LOCATION );
+}
+
+
+
+int UtcDaliImageVisualPropertyMap(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "Request image visual with a Property::Map" );
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK( factory );
+
+  Property::Map propertyMap;
+  propertyMap.Insert( Visual::Property::TYPE,  Visual::IMAGE );
+  propertyMap.Insert( ImageVisual::Property::URL,  TEST_LARGE_IMAGE_FILE_NAME );
+
+  Visual::Base visual = factory.CreateVisual( propertyMap );
+  DALI_TEST_CHECK( visual );
+
+  // For tesing the LoadResourceFunc is called, a big image size should be set, so the atlasing is not applied.
+  // Image with a size smaller than 512*512 will be uploaded as a part of the atlas.
+
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+  TraceCallStack& textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  DummyControl actor = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual( Control::CONTROL_PROPERTY_END_INDEX + 1, visual );
+
+  actor.SetSize( 200.f, 200.f );
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+
+  Stage::GetCurrent().Add( actor );
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION );
+
+  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+
+  Stage::GetCurrent().Remove( actor );
+  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+
+  END_TEST;
+}
+
+
+int UtcDaliImageVisualRemoteImageLoad(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "Request remote image visual with a Property::Map" );
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK( factory );
+
+  Property::Map propertyMap;
+  propertyMap.Insert( Visual::Property::TYPE,  Visual::IMAGE );
+  propertyMap.Insert( ImageVisual::Property::URL,  TEST_REMOTE_IMAGE_FILE_NAME );
+
+  Visual::Base visual = factory.CreateVisual( propertyMap );
+  DALI_TEST_CHECK( visual );
+
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+  TraceCallStack& textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  DummyControl actor = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual( Control::CONTROL_PROPERTY_END_INDEX + 1, visual );
+
+  actor.SetSize( 200.f, 200.f );
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+
+  Stage::GetCurrent().Add( actor );
+  application.SendNotification();
+
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+
+  Stage::GetCurrent().Remove( actor );
+  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualTextureReuse1(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "Request remote image visual with a Property::Map; request a second visual with the same property map - should reuse texture" );
+
+  Property::Map propertyMap;
+  propertyMap.Insert( Visual::Property::TYPE,  Visual::IMAGE );
+  propertyMap.Insert( ImageVisual::Property::URL, TEST_LARGE_IMAGE_FILE_NAME );
+
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+  TraceCallStack& textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+  TraceCallStack& drawTrace = gl.GetDrawTrace();
+  drawTrace.Enable(true);
+
+  Actor actor = CreateActorWithImageVisual( propertyMap );
+  Stage::GetCurrent().Add( actor );
+  application.SendNotification();
+
+  // Wait for image to load
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+  DALI_TEST_EQUALS( drawTrace.FindMethod("DrawArrays"), true, TEST_LOCATION );
+  textureTrace.Reset();
+  drawTrace.Reset();
+
+  Actor actor2 = CreateActorWithImageVisual( propertyMap );
+  Stage::GetCurrent().Add(actor2);
+
+  application.SendNotification(); // Send messages to update
+  application.Render();           // process update and render
+  application.SendNotification(); // process any signals to event
+
+  DALI_TEST_EQUALS( actor2.GetRendererCount(), 1u, TEST_LOCATION );
+
+  tet_infoline("Test that 2 draw calls occur with no new texture gens/binds, i.e. both\n"
+               "draw calls use the same texture as the previous draw call\n" );
+
+  DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), false, TEST_LOCATION );
+  DALI_TEST_EQUALS( drawTrace.CountMethod("DrawArrays"), 2, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureTrace.CountMethod("BindTexture"), 0, TEST_LOCATION );
+
+  tet_infoline("Test that removing 1 actor doesn't delete the texture\n");
+
+  Stage::GetCurrent().Remove( actor );
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+  DALI_TEST_EQUALS( textureTrace.CountMethod("DeleteTextures"), 0, TEST_LOCATION );
+
+  tet_infoline("Test that removing last actor does delete the texture\n");
+
+  Stage::GetCurrent().Remove( actor2 );
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK( actor2.GetRendererCount() == 0u );
+  DALI_TEST_EQUALS( textureTrace.CountMethod("DeleteTextures"), 1, TEST_LOCATION );
+
+  END_TEST;
+}
+
+
+int UtcDaliImageVisualTextureReuse2(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "Request remote image visual with a Property::Map; request a second visual with the same url but different property map - should create new texture" );
+
+  Property::Map propertyMap;
+  propertyMap.Insert( Visual::Property::TYPE,  Visual::IMAGE );
+  propertyMap.Insert( ImageVisual::Property::URL, TEST_REMOTE_IMAGE_FILE_NAME );
+
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+  TraceCallStack& textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+  TraceCallStack& drawTrace = gl.GetDrawTrace();
+  drawTrace.Enable(true);
+
+  Actor actor = CreateActorWithImageVisual( propertyMap );
+  Stage::GetCurrent().Add( actor );
+  application.SendNotification();
+
+  // Wait for image to load
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+  DALI_TEST_EQUALS( drawTrace.FindMethod("DrawArrays"), true, TEST_LOCATION );
+  textureTrace.Reset();
+  drawTrace.Reset();
+
+  propertyMap.Insert( ImageVisual::Property::SAMPLING_MODE, Dali::SamplingMode::NEAREST );
+  propertyMap.Insert( ImageVisual::Property::DESIRED_WIDTH, 100 );
+  propertyMap.Insert( ImageVisual::Property::DESIRED_HEIGHT, 100 );
+  Actor actor2 = CreateActorWithImageVisual( propertyMap );
+  Stage::GetCurrent().Add(actor2);
+
+  application.SendNotification();
+
+  // Wait for image to load
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS( actor2.GetRendererCount(), 1u, TEST_LOCATION );
+
+  tet_infoline("Test that 2 draw calls occur with 1 new texture gen/bind, i.e. both "
+               "renderers are using different textures\n" );
+
+  DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION );
+  DALI_TEST_EQUALS( drawTrace.CountMethod("DrawArrays"), 2, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureTrace.CountMethod("BindTexture"), 2, TEST_LOCATION );
+
+  tet_infoline("Test that removing 1 actor deletes it's texture\n");
+
+  Stage::GetCurrent().Remove( actor );
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+  DALI_TEST_EQUALS( textureTrace.CountMethod("DeleteTextures"), 1, TEST_LOCATION );
+
+  tet_infoline("Test that removing last actor deletes it's texture\n");
+
+  Stage::GetCurrent().Remove( actor2 );
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_CHECK( actor2.GetRendererCount() == 0u );
+  DALI_TEST_EQUALS( textureTrace.CountMethod("DeleteTextures"), 2, TEST_LOCATION );
+
+  END_TEST;
+}
+
+
+int UtcDaliImageVisualImageHandle(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "Request image visual with an image handle" );
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK( factory );
+
+  Image image = ResourceImage::New(TEST_IMAGE_FILE_NAME);
+  Visual::Base visual = factory.CreateVisual( image );
+
+  // For tesing the LoadResourceFunc is called, a big image size should be set, so the atlasing is not applied.
+  // Image with a size smaller than 512*512 will be uploaded as a part of the atlas.
+
+  const int width=512;
+  const int height=513;
+
+  Integration::Bitmap* bitmap = Integration::Bitmap::New( Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD );
+  bitmap->GetPackedPixelsProfile()->ReserveBuffer( Pixel::RGBA8888, width, height,width, height );
+
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+  TraceCallStack& textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  DummyControl actor = DummyControl::New();
+  TestVisualRender( application, actor, visual, 1u,
+                    ImageDimensions(width, height),
+                    Integration::ResourcePointer(bitmap) );
+
+  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+  END_TEST;
+}
+
+int UtcDaliImageVisualCustomWrapModePixelArea(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "Request image visual with a Property::Map, test custom wrap mode and pixel area with atlasing" );
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK( factory );
+
+  // Test wrap mode with atlasing. Image with a size smaller than 512*512 will be uploaded as a part of the atlas.
+  const int width=34;
+  const int height=34;
+  const Vector4 pixelArea(-0.5f, -0.5f, 2.f, 2.f);
+
+  Property::Map propertyMap;
+  propertyMap.Insert( Visual::Property::TYPE,  Visual::IMAGE );
+  propertyMap.Insert( ImageVisual::Property::URL, TEST_SMALL_IMAGE_FILE_NAME );
+  propertyMap.Insert( ImageVisual::Property::DESIRED_WIDTH, width );
+  propertyMap.Insert( ImageVisual::Property::DESIRED_HEIGHT, height );
+  propertyMap.Insert( ImageVisual::Property::SYNCHRONOUS_LOADING, true );
+  propertyMap.Insert( ImageVisual::Property::PIXEL_AREA, pixelArea );
+  propertyMap.Insert( ImageVisual::Property::WRAP_MODE_U, WrapMode::MIRRORED_REPEAT );
+  propertyMap.Insert( ImageVisual::Property::WRAP_MODE_V, WrapMode::REPEAT );
+  propertyMap.Insert( DevelImageVisual::Property::ATLASING, true );
+
+  Visual::Base visual = factory.CreateVisual( propertyMap );
+  DALI_TEST_CHECK( visual );
+
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+  TraceCallStack& textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+  TraceCallStack& texParameterTrace = gl.GetTexParameterTrace();
+  texParameterTrace.Enable( true );
+
+  DummyControl actor = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual( Control::CONTROL_PROPERTY_END_INDEX + 1, visual );
+  actor.SetSize(2000, 2000);
+  actor.SetParentOrigin(ParentOrigin::CENTER);
+  Stage::GetCurrent().Add( actor );
+
+  // loading started
+  application.SendNotification();
+  application.Render();
+
+  BitmapLoader loader = BitmapLoader::GetLatestCreated();
+  DALI_TEST_CHECK( loader );
+  loader.WaitForLoading();// waiting until the image to be loaded
+  DALI_TEST_CHECK( loader.IsLoaded() );
+
+  DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+
+  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+
+  // WITH atlasing, the wrapping is handled manually in shader, so the following gl function should not be called
+  std::stringstream out;
+  out << GL_TEXTURE_2D << ", " << GL_TEXTURE_WRAP_S << ", " << GL_MIRRORED_REPEAT;
+  DALI_TEST_CHECK( !texParameterTrace.FindMethodAndParams("TexParameteri", out.str()) );
+  out.str("");
+  out << GL_TEXTURE_2D << ", " << GL_TEXTURE_WRAP_T << ", " << GL_REPEAT;
+  DALI_TEST_CHECK( !texParameterTrace.FindMethodAndParams("TexParameteri", out.str()) );
+
+  // test the uniforms which used to handle the wrap mode
+  Renderer renderer = actor.GetRendererAt( 0u );
+  DALI_TEST_CHECK( renderer );
+
+  Property::Value pixelAreaValue = renderer.GetProperty( renderer.GetPropertyIndex( "pixelArea" ) );
+  DALI_TEST_EQUALS( pixelAreaValue.Get<Vector4>(), pixelArea, TEST_LOCATION );
+  Vector4 pixelAreaUniform;
+  DALI_TEST_CHECK( gl.GetUniformValue<Vector4>( "pixelArea", pixelAreaUniform ) );
+  DALI_TEST_EQUALS( pixelArea, pixelAreaUniform, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+  Property::Value wrapModeValue = renderer.GetProperty( renderer.GetPropertyIndex( "wrapMode" ) );
+  Vector2 wrapMode( WrapMode::MIRRORED_REPEAT-1, WrapMode::REPEAT-1 );
+  DALI_TEST_EQUALS( wrapModeValue.Get<Vector2>(), wrapMode, TEST_LOCATION );
+  Vector2 wrapModeUniform;
+  DALI_TEST_CHECK( gl.GetUniformValue<Vector2>( "wrapMode", wrapModeUniform ) );
+  DALI_TEST_EQUALS( wrapMode, wrapModeUniform, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+  actor.Unparent( );
+  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualCustomWrapModeNoAtlas(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "Request image visual with a Property::Map, test custom wrap mode and pixel area without atlasing" );
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK( factory );
+
+  // Test wrap mode without atlasing. Image with a size bigger than 512*512 will NOT be uploaded as a part of the atlas.
+  const int width=600;
+  const int height=600;
+  const Vector4 pixelArea(-0.5f, -0.5f, 2.f, 2.f);
+
+  Property::Map propertyMap;
+  propertyMap.Insert( Visual::Property::TYPE,  Visual::IMAGE );
+  propertyMap.Insert( ImageVisual::Property::URL, TEST_LARGE_IMAGE_FILE_NAME );
+  propertyMap.Insert( ImageVisual::Property::DESIRED_WIDTH, width );
+  propertyMap.Insert( ImageVisual::Property::DESIRED_HEIGHT, height );
+  propertyMap.Insert( ImageVisual::Property::SYNCHRONOUS_LOADING, true );
+  propertyMap.Insert( ImageVisual::Property::PIXEL_AREA, pixelArea );
+  propertyMap.Insert( ImageVisual::Property::WRAP_MODE_U, WrapMode::MIRRORED_REPEAT );
+  propertyMap.Insert( ImageVisual::Property::WRAP_MODE_V, WrapMode::REPEAT );
+
+  Visual::Base visual = factory.CreateVisual( propertyMap );
+  DALI_TEST_CHECK( visual );
+
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+  TraceCallStack& textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+  TraceCallStack& texParameterTrace = gl.GetTexParameterTrace();
+  texParameterTrace.Enable( true );
+
+  DummyControl actor = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual( Control::CONTROL_PROPERTY_END_INDEX + 1, visual );
+  actor.SetSize(2000, 2000);
+  actor.SetParentOrigin(ParentOrigin::CENTER);
+  Stage::GetCurrent().Add( actor );
+
+  // loading started
+  application.SendNotification();
+  application.Render();
+
+  BitmapLoader loader = BitmapLoader::GetLatestCreated();
+  DALI_TEST_CHECK( loader );
+  loader.WaitForLoading();// waiting until the image to be loaded
+  DALI_TEST_CHECK( loader.IsLoaded() );
+
+  DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+
+  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+
+  // WITHOUT atlasing, the wrapping is handled by setting gl texture parameters
+  std::stringstream out;
+  out << GL_TEXTURE_2D << ", " << GL_TEXTURE_WRAP_S << ", " << GL_MIRRORED_REPEAT;
+  DALI_TEST_CHECK( texParameterTrace.FindMethodAndParams("TexParameteri", out.str()) );
+  out.str("");
+  out << GL_TEXTURE_2D << ", " << GL_TEXTURE_WRAP_T << ", " << GL_REPEAT;
+  DALI_TEST_CHECK( texParameterTrace.FindMethodAndParams("TexParameteri", out.str()) );
+
+  // test the uniforms which used to handle the wrap mode
+  Renderer renderer = actor.GetRendererAt( 0u );
+  DALI_TEST_CHECK( renderer );
+
+  Property::Value pixelAreaValue = renderer.GetProperty( renderer.GetPropertyIndex( "pixelArea" ) );
+  DALI_TEST_EQUALS( pixelAreaValue.Get<Vector4>(), pixelArea, TEST_LOCATION );
+  Vector4 pixelAreaUniform;
+  DALI_TEST_CHECK( gl.GetUniformValue<Vector4>( "pixelArea", pixelAreaUniform ) );
+  DALI_TEST_EQUALS( pixelArea, pixelAreaUniform, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+  Property::Index wrapModeIndex = renderer.GetPropertyIndex( "wrapMode" );
+  DALI_TEST_CHECK(wrapModeIndex == Property::INVALID_INDEX);
+
+  actor.Unparent();
+  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualAnimateMixColor(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "Animate mix color" );
+
+  application.GetPlatform().SetClosestImageSize( Vector2(100, 100) );
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  propertyMap.Insert(Visual::Property::TYPE,  Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, TEST_IMAGE_FILE_NAME );
+  propertyMap.Insert("mixColor", Color::BLUE);
+  propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
+  Visual::Base visual = factory.CreateVisual( propertyMap );
+
+  DummyControl actor = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
+
+  actor.SetSize(2000, 2000);
+  actor.SetParentOrigin(ParentOrigin::CENTER);
+  actor.SetColor(Color::BLACK);
+  Stage::GetCurrent().Add(actor);
+
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION);
+
+  Renderer renderer = actor.GetRendererAt(0);
+  Property::Index index = DevelHandle::GetPropertyIndex( renderer, DevelVisual::Property::MIX_COLOR );
+  Property::Value blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
+  DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::AUTO, TEST_LOCATION );
+
+  tet_infoline("Test that the renderer has the mixColor property");
+  DALI_TEST_CHECK( index != Property::INVALID_INDEX );
+
+  const Vector4 TARGET_MIX_COLOR( 1.0f, 0.0f, 0.0f, 0.5f );
+
+  Property::Map map;
+  map["target"] = "testVisual";
+  map["property"] = "mixColor";
+  map["initialValue"] = Color::MAGENTA;
+  map["targetValue"] = TARGET_MIX_COLOR;
+  map["animator"] = Property::Map()
+    .Add("alphaFunction", "LINEAR")
+    .Add("timePeriod", Property::Map()
+         .Add("delay", 0.0f)
+         .Add("duration", 4.0f));
+
+  Dali::Toolkit::TransitionData transition = TransitionData::New( map );
+
+  Animation animation = dummyImpl.CreateTransition( transition );
+
+  blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
+  DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::ON, TEST_LOCATION );
+
+  animation.AnimateTo( Property(actor, Actor::Property::COLOR), Color::WHITE );
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(0); // Ensure animation starts
+  application.Render(2000u); // Halfway point
+  Vector4 testColor(1.0f, 0.0f, 0.5f, 0.75f );
+
+  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Vector4(0.5f, 0.5f, 0.5f, 1.0f )), true, TEST_LOCATION );
+  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", Vector3(testColor)), true, TEST_LOCATION );
+  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<float>("opacity", testColor.a), true, TEST_LOCATION );
+
+  application.Render(2000u); // Halfway point between blue and white
+
+  DALI_TEST_EQUALS( actor.GetCurrentColor(), Color::WHITE, TEST_LOCATION );
+  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Color::WHITE ), true, TEST_LOCATION );
+  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", Vector3(TARGET_MIX_COLOR)), true, TEST_LOCATION );
+  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<float>("opacity", TARGET_MIX_COLOR.a), true, TEST_LOCATION );
+
+  TestMixColor( visual, DevelVisual::Property::MIX_COLOR, TARGET_MIX_COLOR );
+
+  blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
+  DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::ON, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualAnimateOpacity(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "Animate image visual opacity" );
+
+  application.GetPlatform().SetClosestImageSize( Vector2(100, 100) );
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  propertyMap.Insert(Visual::Property::TYPE,  Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, TEST_IMAGE_FILE_NAME );
+  propertyMap.Insert("opacity", 0.5f);
+  propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
+  Visual::Base visual = factory.CreateVisual( propertyMap );
+
+  DummyControl actor = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
+
+  actor.SetSize(2000, 2000);
+  actor.SetParentOrigin(ParentOrigin::CENTER);
+  actor.SetColor(Color::BLACK);
+  Stage::GetCurrent().Add(actor);
+
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION);
+
+  Renderer renderer = actor.GetRendererAt(0);
+  tet_infoline("Test that the renderer has the opacity property");
+  Property::Index index = DevelHandle::GetPropertyIndex( renderer, DevelVisual::Property::OPACITY );
+  DALI_TEST_CHECK( index != Property::INVALID_INDEX );
+
+
+  Property::Value blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
+  DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::ON, TEST_LOCATION );
+
+  {
+    tet_infoline( "Test that the opacity can be increased to full via animation, and that the blend mode is set appropriately at the start and end of the animation." );
+
+    Property::Map map;
+    map["target"] = "testVisual";
+    map["property"] = "opacity";
+    map["targetValue"] = 1.0f;
+    map["animator"] = Property::Map()
+      .Add("alphaFunction", "LINEAR")
+      .Add("timePeriod", Property::Map()
+           .Add("delay", 0.0f)
+           .Add("duration", 4.0f));
+
+    Dali::Toolkit::TransitionData transition = TransitionData::New( map );
+    Animation animation = dummyImpl.CreateTransition( transition );
+    animation.Play();
+
+    application.SendNotification();
+    application.Render(0);     // Ensure animation starts
+    application.Render(2000u); // Halfway point through animation
+    application.SendNotification(); // Handle any signals
+
+    DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<float>("opacity", 0.75f), true, TEST_LOCATION );
+
+    application.Render(2001u); // end
+    application.SendNotification(); // ensure animation finished signal is sent
+
+    DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<float>("opacity", 1.0f), true, TEST_LOCATION );
+
+    blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
+    DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::AUTO, TEST_LOCATION );
+  }
+
+
+  {
+    tet_infoline( "Test that the opacity can be reduced via animation, and that the blend mode is set appropriately at the start and end of the animation." );
+
+    Property::Map map;
+    map["target"] = "testVisual";
+    map["property"] = DevelVisual::Property::OPACITY;
+    map["targetValue"] = 0.1f;
+    map["animator"] = Property::Map()
+      .Add("alphaFunction", "LINEAR")
+      .Add("timePeriod", Property::Map()
+           .Add("delay", 0.0f)
+           .Add("duration", 4.0f));
+
+    Dali::Toolkit::TransitionData transition = TransitionData::New( map );
+    Animation animation = dummyImpl.CreateTransition( transition );
+    animation.Play();
+
+    blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
+    DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::ON, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render(0);     // Ensure animation starts
+    application.Render(2000u); // Halfway point
+    application.SendNotification();
+
+    DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<float>("opacity", 0.55f), true, TEST_LOCATION );
+
+    application.Render(2016u); // end
+    application.SendNotification();
+
+    DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<float>("opacity", 0.1f), true, TEST_LOCATION );
+
+    blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
+    DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::ON, TEST_LOCATION );
+  }
+
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualAnimatePixelArea(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "ImageVisual animate pixel area" );
+
+  application.GetPlatform().SetClosestImageSize( Vector2(100, 100) );
+
+  VisualFactory factory = VisualFactory::Get();
+  Property::Map propertyMap;
+  propertyMap.Insert(Visual::Property::TYPE,  Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, TEST_IMAGE_FILE_NAME );
+  propertyMap.Insert("mixColor", Color::BLUE);
+  propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
+  Visual::Base visual = factory.CreateVisual( propertyMap );
+
+  DummyControl actor = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
+
+  actor.SetSize(2000, 2000);
+  actor.SetParentOrigin(ParentOrigin::CENTER);
+  actor.SetColor(Color::BLACK);
+  Stage::GetCurrent().Add(actor);
+
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION);
+
+  Renderer renderer = actor.GetRendererAt(0);
+  Property::Index index = DevelHandle::GetPropertyIndex( renderer, DevelVisual::Property::MIX_COLOR );
+
+  tet_infoline("Test that the renderer has the mixColor property");
+  DALI_TEST_CHECK( index != Property::INVALID_INDEX );
+
+  // TransitionData only takes string keys
+  Property::Map map;
+  map["target"] = "testVisual";
+  map["property"] = "pixelArea";
+  map["initialValue"] = Vector4( 0,0,0,1 );
+  map["targetValue"] = Vector4( 0,0,1,1 ); // Animate width from zero to full
+  map["animator"] = Property::Map()
+    .Add("alphaFunction", "LINEAR")
+    .Add("timePeriod", Property::Map()
+         .Add("delay", 0.0f)
+         .Add("duration", 4.0f));
+
+  Dali::Toolkit::TransitionData transition = TransitionData::New( map );
+
+  Animation animation = dummyImpl.CreateTransition( transition );
+  animation.AnimateTo( Property(actor, Actor::Property::COLOR), Color::WHITE );
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(0);     // Ensure animation starts
+  application.Render(2000u); // Halfway point
+
+  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<Vector4>("pixelArea", Vector4(0.0f, 0.0f, 0.5f, 1.0f )), true, TEST_LOCATION );
+
+  application.Render(2000u); // End of animation
+
+  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<Vector4>("pixelArea", Vector4( 0.0f, 0.0f, 1.0f, 1.0f )), true, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualTextureCancelRemoteLoad(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "Request remote image visual, then destroy visual to cancel load" );
+
+  Property::Map propertyMap;
+  propertyMap.Insert( Visual::Property::TYPE,  Visual::IMAGE );
+  propertyMap.Insert( ImageVisual::Property::URL, TEST_REMOTE_IMAGE_FILE_NAME );
+
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+  TraceCallStack& textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+  TraceCallStack& drawTrace = gl.GetDrawTrace();
+  drawTrace.Enable(true);
+
+  Actor actor = CreateActorWithImageVisual( propertyMap );
+  Stage::GetCurrent().Add( actor );
+  application.SendNotification();
+
+  Stage::GetCurrent().Remove( actor );
+  application.SendNotification();
+
+  DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), false, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), false, TEST_LOCATION );
+  DALI_TEST_EQUALS( drawTrace.FindMethod("DrawArrays"), false, TEST_LOCATION );
+
+  END_TEST;
+}
index 9e39e36..80429f4 100644 (file)
@@ -1371,260 +1371,6 @@ int UtcDaliVisualAnimatePrimitiveVisual(void)
   END_TEST;
 }
 
-int UtcDaliVisualAnimateImageVisualMixColor(void)
-{
-  ToolkitTestApplication application;
-  tet_infoline( "UtcDaliAnimateImageVisual mix color" );
-
-  application.GetPlatform().SetClosestImageSize( Vector2(100, 100) );
-
-  VisualFactory factory = VisualFactory::Get();
-  Property::Map propertyMap;
-  propertyMap.Insert(Visual::Property::TYPE,  Visual::IMAGE);
-  propertyMap.Insert(ImageVisual::Property::URL, TEST_IMAGE_FILE_NAME );
-  propertyMap.Insert("mixColor", Color::BLUE);
-  propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
-  Visual::Base visual = factory.CreateVisual( propertyMap );
-
-  DummyControl actor = DummyControl::New(true);
-  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(actor.GetImplementation());
-  dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
-
-  actor.SetSize(2000, 2000);
-  actor.SetParentOrigin(ParentOrigin::CENTER);
-  actor.SetColor(Color::BLACK);
-  Stage::GetCurrent().Add(actor);
-
-  DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION);
-
-  Renderer renderer = actor.GetRendererAt(0);
-  Property::Index index = DevelHandle::GetPropertyIndex( renderer, DevelVisual::Property::MIX_COLOR );
-  Property::Value blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
-  DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::AUTO, TEST_LOCATION );
-
-  tet_infoline("Test that the renderer has the mixColor property");
-  DALI_TEST_CHECK( index != Property::INVALID_INDEX );
-
-  const Vector4 TARGET_MIX_COLOR( 1.0f, 0.0f, 0.0f, 0.5f );
-
-  Property::Map map;
-  map["target"] = "testVisual";
-  map["property"] = "mixColor";
-  map["initialValue"] = Color::MAGENTA;
-  map["targetValue"] = TARGET_MIX_COLOR;
-  map["animator"] = Property::Map()
-    .Add("alphaFunction", "LINEAR")
-    .Add("timePeriod", Property::Map()
-         .Add("delay", 0.0f)
-         .Add("duration", 4.0f));
-
-  Dali::Toolkit::TransitionData transition = TransitionData::New( map );
-
-  Animation animation = dummyImpl.CreateTransition( transition );
-
-  blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
-  DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::ON, TEST_LOCATION );
-
-  animation.AnimateTo( Property(actor, Actor::Property::COLOR), Color::WHITE );
-  animation.Play();
-
-  application.SendNotification();
-  application.Render(0);
-  application.Render(2000u); // halfway point
-  Vector4 testColor(1.0f, 0.0f, 0.5f, 0.75f );
-
-  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Vector4(0.5f, 0.5f, 0.5f, 1.0f )), true, TEST_LOCATION );
-  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", Vector3(testColor)), true, TEST_LOCATION );
-  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<float>("opacity", testColor.a), true, TEST_LOCATION );
-
-  application.Render(2000u); // halfway point between blue and white
-
-  DALI_TEST_EQUALS( actor.GetCurrentColor(), Color::WHITE, TEST_LOCATION );
-  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<Vector4>("uColor", Color::WHITE ), true, TEST_LOCATION );
-  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", Vector3(TARGET_MIX_COLOR)), true, TEST_LOCATION );
-  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<float>("opacity", TARGET_MIX_COLOR.a), true, TEST_LOCATION );
-
-  TestMixColor( visual, DevelVisual::Property::MIX_COLOR, TARGET_MIX_COLOR );
-
-  blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
-  DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::ON, TEST_LOCATION );
-
-  END_TEST;
-}
-
-
-int UtcDaliVisualAnimateImageVisualOpacity(void)
-{
-  ToolkitTestApplication application;
-  tet_infoline( "UtcDaliAnimateImageVisual mix color" );
-
-  application.GetPlatform().SetClosestImageSize( Vector2(100, 100) );
-
-  VisualFactory factory = VisualFactory::Get();
-  Property::Map propertyMap;
-  propertyMap.Insert(Visual::Property::TYPE,  Visual::IMAGE);
-  propertyMap.Insert(ImageVisual::Property::URL, TEST_IMAGE_FILE_NAME );
-  propertyMap.Insert("opacity", 0.5f);
-  propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
-  Visual::Base visual = factory.CreateVisual( propertyMap );
-
-  DummyControl actor = DummyControl::New(true);
-  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(actor.GetImplementation());
-  dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
-
-  actor.SetSize(2000, 2000);
-  actor.SetParentOrigin(ParentOrigin::CENTER);
-  actor.SetColor(Color::BLACK);
-  Stage::GetCurrent().Add(actor);
-
-  DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION);
-
-  Renderer renderer = actor.GetRendererAt(0);
-  tet_infoline("Test that the renderer has the opacity property");
-  Property::Index index = DevelHandle::GetPropertyIndex( renderer, DevelVisual::Property::OPACITY );
-  DALI_TEST_CHECK( index != Property::INVALID_INDEX );
-
-
-  Property::Value blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
-  DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::ON, TEST_LOCATION );
-
-  {
-    tet_infoline( "Test that the opacity can be increased to full via animation, and that the blend mode is set appropriately at the start and end of the animation." );
-
-    Property::Map map;
-    map["target"] = "testVisual";
-    map["property"] = "opacity";
-    map["targetValue"] = 1.0f;
-    map["animator"] = Property::Map()
-      .Add("alphaFunction", "LINEAR")
-      .Add("timePeriod", Property::Map()
-           .Add("delay", 0.0f)
-           .Add("duration", 4.0f));
-
-    Dali::Toolkit::TransitionData transition = TransitionData::New( map );
-    Animation animation = dummyImpl.CreateTransition( transition );
-    animation.Play();
-
-    application.SendNotification();
-    application.Render(0);
-    application.Render(2000u); // halfway point
-    application.SendNotification();
-
-    DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<float>("opacity", 0.75f), true, TEST_LOCATION );
-
-    application.Render(2001u); // end
-    application.SendNotification();
-
-    DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<float>("opacity", 1.0f), true, TEST_LOCATION );
-
-    blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
-    DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::AUTO, TEST_LOCATION );
-  }
-
-
-  {
-    tet_infoline( "Test that the opacity can be reduced via animation, and that the blend mode is set appropriately at the start and end of the animation." );
-
-    Property::Map map;
-    map["target"] = "testVisual";
-    map["property"] = DevelVisual::Property::OPACITY;
-    map["targetValue"] = 0.1f;
-    map["animator"] = Property::Map()
-      .Add("alphaFunction", "LINEAR")
-      .Add("timePeriod", Property::Map()
-           .Add("delay", 0.0f)
-           .Add("duration", 4.0f));
-
-    Dali::Toolkit::TransitionData transition = TransitionData::New( map );
-    Animation animation = dummyImpl.CreateTransition( transition );
-    animation.Play();
-
-    blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
-    DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::ON, TEST_LOCATION );
-
-    application.SendNotification();
-    application.Render(0);
-    application.Render(2000u); // halfway point
-    application.SendNotification();
-
-    DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<float>("opacity", 0.55f), true, TEST_LOCATION );
-
-    application.Render(2016u); // end
-    application.SendNotification();
-
-    DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<float>("opacity", 0.1f), true, TEST_LOCATION );
-
-    blendModeValue = renderer.GetProperty( Renderer::Property::BLEND_MODE );
-    DALI_TEST_EQUALS( blendModeValue.Get<int>(), (int)BlendMode::ON, TEST_LOCATION );
-  }
-
-
-  END_TEST;
-}
-
-int UtcDaliVisualAnimateImageVisualPixelArea(void)
-{
-  ToolkitTestApplication application;
-  tet_infoline( "UtcDaliAnimateImageVisual pixel area" );
-
-  application.GetPlatform().SetClosestImageSize( Vector2(100, 100) );
-
-  VisualFactory factory = VisualFactory::Get();
-  Property::Map propertyMap;
-  propertyMap.Insert(Visual::Property::TYPE,  Visual::IMAGE);
-  propertyMap.Insert(ImageVisual::Property::URL, TEST_IMAGE_FILE_NAME );
-  propertyMap.Insert("mixColor", Color::BLUE);
-  propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
-  Visual::Base visual = factory.CreateVisual( propertyMap );
-
-  DummyControl actor = DummyControl::New(true);
-  Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(actor.GetImplementation());
-  dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
-
-  actor.SetSize(2000, 2000);
-  actor.SetParentOrigin(ParentOrigin::CENTER);
-  actor.SetColor(Color::BLACK);
-  Stage::GetCurrent().Add(actor);
-
-  DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION);
-
-  Renderer renderer = actor.GetRendererAt(0);
-  Property::Index index = DevelHandle::GetPropertyIndex( renderer, DevelVisual::Property::MIX_COLOR );
-
-  tet_infoline("Test that the renderer has the mixColor property");
-  DALI_TEST_CHECK( index != Property::INVALID_INDEX );
-
-  // TransitionData only takes string keys
-  Property::Map map;
-  map["target"] = "testVisual";
-  map["property"] = "pixelArea";
-  map["initialValue"] = Vector4( 0,0,0,1 );
-  map["targetValue"] = Vector4( 0,0,1,1 ); // Animate width from zero to full
-  map["animator"] = Property::Map()
-    .Add("alphaFunction", "LINEAR")
-    .Add("timePeriod", Property::Map()
-         .Add("delay", 0.0f)
-         .Add("duration", 4.0f));
-
-  Dali::Toolkit::TransitionData transition = TransitionData::New( map );
-
-  Animation animation = dummyImpl.CreateTransition( transition );
-  animation.AnimateTo( Property(actor, Actor::Property::COLOR), Color::WHITE );
-  animation.Play();
-
-  application.SendNotification();
-  application.Render(0);
-  application.Render(2000u); // halfway point
-
-  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<Vector4>("pixelArea", Vector4(0.0f, 0.0f, 0.5f, 1.0f )), true, TEST_LOCATION );
-
-  application.Render(2000u);
-
-  DALI_TEST_EQUALS( application.GetGlAbstraction().CheckUniformValue<Vector4>("pixelArea", Vector4( 0.0f, 0.0f, 1.0f, 1.0f )), true, TEST_LOCATION );
-
-  END_TEST;
-}
-
 
 int UtcDaliVisualWireframeVisual(void)
 {
index 4966804..acf3192 100644 (file)
@@ -38,8 +38,7 @@ namespace
 {
 typedef NinePatchImage::StretchRanges StretchRanges;
 
-const char* TEST_IMAGE_FILE_NAME =  "gallery_image_01.jpg";
-const char* TEST_NPATCH_FILE_NAME =  "gallery_image_01.9.png";
+const char* TEST_NPATCH_FILE_NAME =  TEST_RESOURCE_DIR  "/button-up-1.9.png";
 const char* TEST_SVG_FILE_NAME = TEST_RESOURCE_DIR "/svg1.svg";
 const char* TEST_OBJ_FILE_NAME = TEST_RESOURCE_DIR "/Cube.obj";
 const char* TEST_MTL_FILE_NAME = TEST_RESOURCE_DIR "/ToyRobot-Metal.mtl";
@@ -51,8 +50,7 @@ const char* TEST_GIF_FILE_NAME = TEST_RESOURCE_DIR "/anim.gif";
 
 // resolution: 34*34, pixel format: RGBA8888
 static const char* gImage_34_RGBA = TEST_RESOURCE_DIR "/icon-edit.png";
-// resolution: 600*600, pixel format: RGB888
-static const char* gImage_600_RGB = TEST_RESOURCE_DIR "/test-image-600.jpg";
+
 
 Property::Map DefaultTransform()
 {
@@ -592,243 +590,9 @@ int UtcDaliVisualFactoryDefaultOffsetsGradientVisual(void)
   END_TEST;
 }
 
-int UtcDaliVisualFactoryGetImageVisual1(void)
-{
-  ToolkitTestApplication application;
-  tet_infoline( "UtcDaliVisualFactoryGetImageVisual1: Request image visual with a Property::Map" );
-
-  VisualFactory factory = VisualFactory::Get();
-  DALI_TEST_CHECK( factory );
-
-  Property::Map propertyMap;
-  propertyMap.Insert( Visual::Property::TYPE,  Visual::IMAGE );
-  propertyMap.Insert( ImageVisual::Property::URL,  TEST_IMAGE_FILE_NAME );
-
-  Visual::Base visual = factory.CreateVisual( propertyMap );
-  DALI_TEST_CHECK( visual );
-
-  // For tesing the LoadResourceFunc is called, a big image size should be set, so the atlasing is not applied.
-  // Image with a size smaller than 512*512 will be uploaded as a part of the atlas.
-
-  const int width=512;
-  const int height=513;
-  TestGlAbstraction& gl = application.GetGlAbstraction();
-  TraceCallStack& textureTrace = gl.GetTextureTrace();
-  textureTrace.Enable(true);
-
-  Integration::Bitmap* bitmap = Integration::Bitmap::New( Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD );
-  bitmap->GetPackedPixelsProfile()->ReserveBuffer( Pixel::RGBA8888, width, height,width, height );
-
-  DummyControl actor = DummyControl::New();
-  TestVisualRender( application, actor, visual, 1u,
-                    ImageDimensions(width, height),
-                    Integration::ResourcePointer( bitmap ) );
-
-  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
-
-  Stage::GetCurrent().Remove( actor );
-  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
-
-  END_TEST;
-}
-
-int UtcDaliVisualFactoryGetImageVisual2(void)
-{
-  ToolkitTestApplication application;
-  tet_infoline( "UtcDaliVisualFactoryGetImageVisual2: Request image visual with an image handle" );
 
-  VisualFactory factory = VisualFactory::Get();
-  DALI_TEST_CHECK( factory );
-
-  Image image = ResourceImage::New(TEST_IMAGE_FILE_NAME);
-  Visual::Base visual = factory.CreateVisual( image );
-
-  // For tesing the LoadResourceFunc is called, a big image size should be set, so the atlasing is not applied.
-  // Image with a size smaller than 512*512 will be uploaded as a part of the atlas.
-
-  const int width=512;
-  const int height=513;
-
-  Integration::Bitmap* bitmap = Integration::Bitmap::New( Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_DISCARD );
-  bitmap->GetPackedPixelsProfile()->ReserveBuffer( Pixel::RGBA8888, width, height,width, height );
 
-  TestGlAbstraction& gl = application.GetGlAbstraction();
-  TraceCallStack& textureTrace = gl.GetTextureTrace();
-  textureTrace.Enable(true);
 
-  DummyControl actor = DummyControl::New();
-  TestVisualRender( application, actor, visual, 1u,
-                    ImageDimensions(width, height),
-                    Integration::ResourcePointer(bitmap) );
-
-  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
-  END_TEST;
-}
-
-int UtcDaliVisualFactoryGetImageVisual3(void)
-{
-  ToolkitTestApplication application;
-  tet_infoline( "UtcDaliVisualFactoryGetImageVisual3: Request image visual with a Property::Map, test custom wrap mode and pixel area with atlasing" );
-
-  VisualFactory factory = VisualFactory::Get();
-  DALI_TEST_CHECK( factory );
-
-  // Test wrap mode with atlasing. Image with a size smaller than 512*512 will be uploaded as a part of the atlas.
-  const int width=34;
-  const int height=34;
-  const Vector4 pixelArea(-0.5f, -0.5f, 2.f, 2.f);
-
-  Property::Map propertyMap;
-  propertyMap.Insert( Visual::Property::TYPE,  Visual::IMAGE );
-  propertyMap.Insert( ImageVisual::Property::URL,  gImage_34_RGBA );
-  propertyMap.Insert( ImageVisual::Property::DESIRED_WIDTH, width );
-  propertyMap.Insert( ImageVisual::Property::DESIRED_HEIGHT, height );
-  propertyMap.Insert( ImageVisual::Property::SYNCHRONOUS_LOADING, true );
-  propertyMap.Insert( ImageVisual::Property::PIXEL_AREA, pixelArea );
-  propertyMap.Insert( ImageVisual::Property::WRAP_MODE_U, WrapMode::MIRRORED_REPEAT );
-  propertyMap.Insert( ImageVisual::Property::WRAP_MODE_V, WrapMode::REPEAT );
-  propertyMap.Insert( DevelImageVisual::Property::ATLASING, true );
-
-  Visual::Base visual = factory.CreateVisual( propertyMap );
-  DALI_TEST_CHECK( visual );
-
-  TestGlAbstraction& gl = application.GetGlAbstraction();
-  TraceCallStack& textureTrace = gl.GetTextureTrace();
-  textureTrace.Enable(true);
-  TraceCallStack& texParameterTrace = gl.GetTexParameterTrace();
-  texParameterTrace.Enable( true );
-
-  DummyControl actor = DummyControl::New();
-  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
-  dummyImpl.RegisterVisual( Control::CONTROL_PROPERTY_END_INDEX + 1, visual );
-  actor.SetSize(2000, 2000);
-  actor.SetParentOrigin(ParentOrigin::CENTER);
-  Stage::GetCurrent().Add( actor );
-
-  // loading started
-  application.SendNotification();
-  application.Render();
-  application.Render();
-  application.SendNotification();
-  BitmapLoader loader = BitmapLoader::GetLatestCreated();
-  DALI_TEST_CHECK( loader );
-  loader.WaitForLoading();// waiting until the image to be loaded
-  DALI_TEST_CHECK( loader.IsLoaded() );
-
-  DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
-
-  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
-
-  // WITH atlasing, the wrapping is handled manually in shader, so the following gl function should not be called
-  std::stringstream out;
-  out << GL_TEXTURE_2D << ", " << GL_TEXTURE_WRAP_S << ", " << GL_MIRRORED_REPEAT;
-  DALI_TEST_CHECK( !texParameterTrace.FindMethodAndParams("TexParameteri", out.str()) );
-  out.str("");
-  out << GL_TEXTURE_2D << ", " << GL_TEXTURE_WRAP_T << ", " << GL_REPEAT;
-  DALI_TEST_CHECK( !texParameterTrace.FindMethodAndParams("TexParameteri", out.str()) );
-
-  // test the uniforms which used to handle the wrap mode
-  Renderer renderer = actor.GetRendererAt( 0u );
-  DALI_TEST_CHECK( renderer );
-
-  Property::Value pixelAreaValue = renderer.GetProperty( renderer.GetPropertyIndex( "pixelArea" ) );
-  DALI_TEST_EQUALS( pixelAreaValue.Get<Vector4>(), pixelArea, TEST_LOCATION );
-  Vector4 pixelAreaUniform;
-  DALI_TEST_CHECK( gl.GetUniformValue<Vector4>( "pixelArea", pixelAreaUniform ) );
-  DALI_TEST_EQUALS( pixelArea, pixelAreaUniform, Math::MACHINE_EPSILON_100, TEST_LOCATION );
-
-  Property::Value wrapModeValue = renderer.GetProperty( renderer.GetPropertyIndex( "wrapMode" ) );
-  Vector2 wrapMode( WrapMode::MIRRORED_REPEAT-1, WrapMode::REPEAT-1 );
-  DALI_TEST_EQUALS( wrapModeValue.Get<Vector2>(), wrapMode, TEST_LOCATION );
-  Vector2 wrapModeUniform;
-  DALI_TEST_CHECK( gl.GetUniformValue<Vector2>( "wrapMode", wrapModeUniform ) );
-  DALI_TEST_EQUALS( wrapMode, wrapModeUniform, Math::MACHINE_EPSILON_100, TEST_LOCATION );
-
-  actor.Unparent( );
-  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
-
-  END_TEST;
-}
-
-int UtcDaliVisualFactoryGetImageVisual4(void)
-{
-  ToolkitTestApplication application;
-  tet_infoline( "UtcDaliVisualFactoryGetImageVisual4: Request image visual with a Property::Map, test custom wrap mode and pixel area without atlasing" );
-
-  VisualFactory factory = VisualFactory::Get();
-  DALI_TEST_CHECK( factory );
-
-  // Test wrap mode without atlasing. Image with a size bigger than 512*512 will NOT be uploaded as a part of the atlas.
-  const int width=600;
-  const int height=600;
-  const Vector4 pixelArea(-0.5f, -0.5f, 2.f, 2.f);
-
-  Property::Map propertyMap;
-  propertyMap.Insert( Visual::Property::TYPE,  Visual::IMAGE );
-  propertyMap.Insert( ImageVisual::Property::URL,  gImage_600_RGB );
-  propertyMap.Insert( ImageVisual::Property::DESIRED_WIDTH, width );
-  propertyMap.Insert( ImageVisual::Property::DESIRED_HEIGHT, height );
-  propertyMap.Insert( ImageVisual::Property::SYNCHRONOUS_LOADING, true );
-  propertyMap.Insert( ImageVisual::Property::PIXEL_AREA, pixelArea );
-  propertyMap.Insert( ImageVisual::Property::WRAP_MODE_U, WrapMode::MIRRORED_REPEAT );
-  propertyMap.Insert( ImageVisual::Property::WRAP_MODE_V, WrapMode::REPEAT );
-
-  Visual::Base visual = factory.CreateVisual( propertyMap );
-  DALI_TEST_CHECK( visual );
-
-  TestGlAbstraction& gl = application.GetGlAbstraction();
-  TraceCallStack& textureTrace = gl.GetTextureTrace();
-  textureTrace.Enable(true);
-  TraceCallStack& texParameterTrace = gl.GetTexParameterTrace();
-  texParameterTrace.Enable( true );
-
-  DummyControl actor = DummyControl::New();
-  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
-  dummyImpl.RegisterVisual( Control::CONTROL_PROPERTY_END_INDEX + 1, visual );
-  actor.SetSize(2000, 2000);
-  actor.SetParentOrigin(ParentOrigin::CENTER);
-  Stage::GetCurrent().Add( actor );
-
-  // loading started
-  application.SendNotification();
-  application.Render();
-  application.Render();
-  application.SendNotification();
-  BitmapLoader loader = BitmapLoader::GetLatestCreated();
-  DALI_TEST_CHECK( loader );
-  loader.WaitForLoading();// waiting until the image to be loaded
-  DALI_TEST_CHECK( loader.IsLoaded() );
-
-  DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
-
-  DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
-
-  // WITHOUT atlasing, the wrapping is handled by setting gl texture parameters
-  std::stringstream out;
-  out << GL_TEXTURE_2D << ", " << GL_TEXTURE_WRAP_S << ", " << GL_MIRRORED_REPEAT;
-  DALI_TEST_CHECK( texParameterTrace.FindMethodAndParams("TexParameteri", out.str()) );
-  out.str("");
-  out << GL_TEXTURE_2D << ", " << GL_TEXTURE_WRAP_T << ", " << GL_REPEAT;
-  DALI_TEST_CHECK( texParameterTrace.FindMethodAndParams("TexParameteri", out.str()) );
-
-  // test the uniforms which used to handle the wrap mode
-  Renderer renderer = actor.GetRendererAt( 0u );
-  DALI_TEST_CHECK( renderer );
-
-  Property::Value pixelAreaValue = renderer.GetProperty( renderer.GetPropertyIndex( "pixelArea" ) );
-  DALI_TEST_EQUALS( pixelAreaValue.Get<Vector4>(), pixelArea, TEST_LOCATION );
-  Vector4 pixelAreaUniform;
-  DALI_TEST_CHECK( gl.GetUniformValue<Vector4>( "pixelArea", pixelAreaUniform ) );
-  DALI_TEST_EQUALS( pixelArea, pixelAreaUniform, Math::MACHINE_EPSILON_100, TEST_LOCATION );
-
-  Property::Index wrapModeIndex = renderer.GetPropertyIndex( "wrapMode" );
-  DALI_TEST_CHECK(wrapModeIndex == Property::INVALID_INDEX);
-
-  actor.Unparent();
-  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
-
-  END_TEST;
-}
 
 int UtcDaliVisualFactoryGetNPatchVisual1(void)
 {
index 0799c9e..c0dd44b 100644 (file)
@@ -28,6 +28,8 @@ toolkit_src_files = \
    $(toolkit_src_dir)/visuals/svg/svg-visual.cpp \
    $(toolkit_src_dir)/visuals/text/text-visual.cpp \
    $(toolkit_src_dir)/visuals/transition-data-impl.cpp \
+   $(toolkit_src_dir)/visuals/texture-manager.cpp \
+   $(toolkit_src_dir)/visuals/texture-upload-observer.cpp \
    $(toolkit_src_dir)/visuals/visual-base-data-impl.cpp \
    $(toolkit_src_dir)/visuals/visual-base-impl.cpp \
    $(toolkit_src_dir)/visuals/visual-factory-cache.cpp \
index 1fbef38..8454992 100644 (file)
@@ -49,7 +49,7 @@ IntrusivePtr<AsyncImageLoader> AsyncImageLoader::New()
   return internal;
 }
 
-uint32_t AsyncImageLoader::Load( const std::string& url,
+uint32_t AsyncImageLoader::Load( const VisualUrl& url,
                                  ImageDimensions dimensions,
                                  FittingMode::Type fittingMode,
                                  SamplingMode::Type samplingMode,
@@ -83,7 +83,7 @@ void AsyncImageLoader::CancelAll()
 
 void AsyncImageLoader::ProcessLoadedImage()
 {
-  while( LoadingTask *next =  mLoadThread.NextCompletedTask() )
+  while( LoadingTask *next = mLoadThread.NextCompletedTask() )
   {
     mLoadedSignal.Emit( next->id, next->pixelData );
     delete next;
index 92a1652..fe4c73b 100644 (file)
@@ -51,7 +51,7 @@ public:
   /**
    * @copydoc Toolkit::AsyncImageLoader::Load( const std::string&, ImageDimensions, FittingMode::Type, SamplingMode::Type, bool )
    */
-  uint32_t Load( const std::string& url,
+  uint32_t Load( const VisualUrl& url,
                  ImageDimensions dimensions,
                  FittingMode::Type fittingMode,
                  SamplingMode::Type samplingMode,
@@ -85,14 +85,11 @@ protected:
   ~AsyncImageLoader();
 
 private:
-
   Toolkit::AsyncImageLoader::ImageLoadedSignalType mLoadedSignal;
 
   ImageLoadThread mLoadThread;
   uint32_t        mLoadTaskId;
   bool            mIsLoadThreadStarted;
-
-
 };
 
 } // namespace Internal
index d784d3e..30f1ba7 100644 (file)
@@ -21,7 +21,7 @@
 // EXTERNAL INCLUDES
 #include <string.h>
 #include <dali/public-api/signals/callback.h>
-#include <dali/public-api/images/resource-image.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
 #include <dali/devel-api/adaptor-framework/bitmap-loader.h>
 #include <dali/integration-api/debug.h>
 
@@ -127,7 +127,7 @@ float ImageAtlas::GetOccupancyRate() const
 
 void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl )
 {
-  mBrokenImageSize = ResourceImage::GetImageSize( brokenImageUrl );
+  mBrokenImageSize = Dali::GetClosestImageSize( brokenImageUrl );
   if(mBrokenImageSize.GetWidth() > 0 && mBrokenImageSize.GetHeight() > 0 ) // check the url is valid
   {
     mBrokenImageUrl = brokenImageUrl;
@@ -145,7 +145,7 @@ bool ImageAtlas::Upload( Vector4& textureRect,
   ImageDimensions zero;
   if( size == zero ) // image size not provided
   {
-    dimensions = ResourceImage::GetImageSize( url );
+    dimensions = Dali::GetClosestImageSize( url );
     if( dimensions == zero ) // Fail to read the image & broken image file exists
     {
       if( !mBrokenImageUrl.empty() )
index d0b504d..a06953e 100644 (file)
@@ -30,7 +30,7 @@ namespace Toolkit
 namespace Internal
 {
 
-LoadingTask::LoadingTask( uint32_t id, const std::string& url, ImageDimensions dimensions,
+LoadingTask::LoadingTask( uint32_t id, const VisualUrl& url, ImageDimensions dimensions,
                           FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection )
 : pixelData(),
   url( url ),
@@ -44,7 +44,14 @@ LoadingTask::LoadingTask( uint32_t id, const std::string& url, ImageDimensions d
 
 void LoadingTask::Load()
 {
-  pixelData = Dali::LoadImageFromFile( url, dimensions, fittingMode, samplingMode, orientationCorrection );
+  if( url.IsLocal() )
+  {
+    pixelData = Dali::LoadImageFromFile( url.GetUrl(), dimensions, fittingMode, samplingMode, orientationCorrection );
+  }
+  else
+  {
+    pixelData = Dali::DownloadImageSynchronously ( url.GetUrl(), dimensions, fittingMode, samplingMode, orientationCorrection );
+  }
 }
 
 
index f46d435..f3d9632 100644 (file)
@@ -26,7 +26,7 @@
 #include <dali/devel-api/threading/mutex.h>
 #include <dali/devel-api/threading/thread.h>
 #include <dali/devel-api/adaptor-framework/event-thread-callback.h>
-
+#include <dali-toolkit/internal/visuals/visual-url.h>
 
 namespace Dali
 {
@@ -51,8 +51,9 @@ struct LoadingTask
    * @param [in] samplingMode The filtering method used when sampling pixels from the input image while fitting it to desired size.
    * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
    */
-  LoadingTask( uint32_t id, const std::string& url, ImageDimensions dimensions,
-               FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection );
+  LoadingTask( uint32_t id, const VisualUrl& url, ImageDimensions dimensions,
+               FittingMode::Type fittingMode, SamplingMode::Type samplingMode,
+               bool orientationCorrection );
 
   /**
    * Load the image
@@ -69,13 +70,13 @@ private:
 
 public:
 
-  PixelData pixelData;             ///< pixelData handle after successfull load
-  std::string url;                 ///< url of the image to load
-  uint32_t     id;                 ///< The unique id associated with this task.
-  ImageDimensions dimensions;      ///< dimensions to load
-  FittingMode::Type fittingMode;   ///< fitting options
-  SamplingMode::Type samplingMode; ///< sampling options
-  bool orientationCorrection:1;    ///< if orientation correction is needed
+  PixelData          pixelData;     ///< pixelData handle after successfull load
+  VisualUrl          url;           ///< url of the image to load
+  uint32_t           id;            ///< The unique id associated with this task.
+  ImageDimensions    dimensions;    ///< dimensions to load
+  FittingMode::Type  fittingMode;   ///< fitting options
+  SamplingMode::Type samplingMode;  ///< sampling options
+  bool               orientationCorrection:1; ///< if orientation correction is needed
 
 };
 
index fe01571..d959c1a 100644 (file)
@@ -20,7 +20,7 @@
 
 // EXTERNAL HEADER
 #include <dali/devel-api/images/texture-set-image.h>
-#include <dali/public-api/images/resource-image.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
 
 namespace Dali
 {
@@ -58,7 +58,7 @@ TextureSet ImageAtlasManager::Add( Vector4& textureRect,
   ImageDimensions zero;
   if( size == zero )
   {
-    dimensions = ResourceImage::GetImageSize( url );
+    dimensions = Dali::GetClosestImageSize( url );
   }
 
   // big image, atlasing is not applied
index 3c82d75..06a9601 100644 (file)
@@ -34,6 +34,7 @@
 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
+#include <dali-toolkit/internal/visuals/texture-manager.h>
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
@@ -261,11 +262,13 @@ ImageVisual::ImageVisual( VisualFactoryCache& factoryCache,
   mPlacementActor(),
   mImageUrl( imageUrl ),
   mDesiredSize( size ),
+  mTextureId( TextureManager::INVALID_TEXTURE_ID ),
   mFittingMode( fittingMode ),
   mSamplingMode( samplingMode ),
   mWrapModeU( WrapMode::DEFAULT ),
   mWrapModeV( WrapMode::DEFAULT ),
-  mAttemptAtlasing( false )
+  mAttemptAtlasing( false ),
+  mTextureLoading( false )
 {
 }
 
@@ -277,11 +280,13 @@ ImageVisual::ImageVisual( VisualFactoryCache& factoryCache, const Image& image )
   mPlacementActor(),
   mImageUrl(),
   mDesiredSize(),
+  mTextureId( TextureManager::INVALID_TEXTURE_ID ),
   mFittingMode( FittingMode::DEFAULT ),
   mSamplingMode( SamplingMode::DEFAULT ),
   mWrapModeU( WrapMode::DEFAULT ),
   mWrapModeV( WrapMode::DEFAULT ),
-  mAttemptAtlasing( false )
+  mAttemptAtlasing( false ),
+  mTextureLoading( false )
 {
 }
 
@@ -465,16 +470,16 @@ void ImageVisual::GetNaturalSize( Vector2& naturalSize )
   }
   else if( mImageUrl.IsValid() && mImageUrl.GetLocation() == VisualUrl::LOCAL )
   {
-    ImageDimensions dimentions = Dali::GetClosestImageSize( mImageUrl.GetUrl() );
-    naturalSize.x = dimentions.GetWidth();
-    naturalSize.y = dimentions.GetHeight();
+    ImageDimensions dimensions = Dali::GetClosestImageSize( mImageUrl.GetUrl() );
+    naturalSize.x = dimensions.GetWidth();
+    naturalSize.y = dimensions.GetHeight();
     return;
   }
 
   naturalSize = Vector2::ZERO;
 }
 
-void ImageVisual::CreateRenderer( TextureSet& textures )
+void ImageVisual::CreateRenderer( TextureSet& textureSet )
 {
   Geometry geometry;
   Shader shader;
@@ -509,8 +514,11 @@ void ImageVisual::CreateRenderer( TextureSet& textures )
   shader.RegisterProperty( PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_ON ); // Set default to align
 
   mImpl->mRenderer = Renderer::New( geometry, shader );
-  DALI_ASSERT_DEBUG( textures );
-  mImpl->mRenderer.SetTextures( textures );
+  if( textureSet )
+  {
+    mImpl->mRenderer.SetTextures( textureSet );
+  }
+  // else still waiting for texture load to finish.
 
   //Register transform properties
   mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
@@ -583,12 +591,16 @@ void ImageVisual::LoadResourceSynchronously()
     BitmapLoader loader = BitmapLoader::New( mImageUrl.GetUrl(), mDesiredSize, mFittingMode, mSamplingMode );
     loader.Load();
     mPixels = loader.GetPixelData();
+    mTextureLoading = false;
   }
 }
 
 TextureSet ImageVisual::CreateTextureSet( Vector4& textureRect, bool synchronousLoading, bool attemptAtlasing )
 {
   TextureSet textureSet;
+
+  mTextureLoading = false;
+
   textureRect = FULL_TEXTURE_RECT;
   if( synchronousLoading )
   {
@@ -622,23 +634,34 @@ TextureSet ImageVisual::CreateTextureSet( Vector4& textureRect, bool synchronous
     {
       textureSet = mFactoryCache.GetAtlasManager()->Add( textureRect, mImageUrl.GetUrl(), mDesiredSize, mFittingMode, true, this );
       mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
+      mTextureLoading = true;
     }
     if( !textureSet ) // big image, no atlasing or atlasing failed
     {
       mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
-      ResourceImage resourceImage = Dali::ResourceImage::New( mImageUrl.GetUrl(), mDesiredSize, mFittingMode, mSamplingMode );
-      resourceImage.LoadingFinishedSignal().Connect( this, &ImageVisual::OnImageLoaded );
-      textureSet = TextureSet::New();
-      TextureSetImage( textureSet, 0u, resourceImage );
+      TextureManager& textureManager = mFactoryCache.GetTextureManager();
+      mTextureId = textureManager.RequestLoad( mImageUrl, mDesiredSize, mFittingMode,
+                                               mSamplingMode, TextureManager::NO_ATLAS, this );
+
+      TextureManager::LoadState loadState = textureManager.GetTextureState( mTextureId );
+
+      mTextureLoading = ( loadState == TextureManager::LOADING );
+
+      if( loadState == TextureManager::UPLOADED )
+      {
+        // UploadComplete has already been called - keep the same texture set
+        textureSet = textureManager.GetTextureSet(mTextureId);
+      }
     }
   }
 
-  if( !(mImpl->mFlags & Impl::IS_ATLASING_APPLIED) )
+  if( ! (mImpl->mFlags & Impl::IS_ATLASING_APPLIED) && textureSet )
   {
     Sampler sampler = Sampler::New();
     sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
     textureSet.SetSampler( 0u, sampler );
   }
+
   return textureSet;
 }
 
@@ -646,7 +669,7 @@ void ImageVisual::InitializeRenderer()
 {
   mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
 
-  if( !mImpl->mCustomShader && mImageUrl.GetLocation() == VisualUrl::LOCAL )
+  if( ! mImpl->mCustomShader && mImageUrl.GetLocation() == VisualUrl::LOCAL )
   {
     bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
 
@@ -697,18 +720,6 @@ void ImageVisual::InitializeRenderer( const Image& image )
   ApplyImageToSampler( image );
 }
 
-void ImageVisual::UploadCompleted()
-{
-  // Resource image is loaded. If weak handle is holding a placement actor, it is the time to add the renderer to actor.
-  Actor actor = mPlacementActor.GetHandle();
-  if( actor )
-  {
-    actor.AddRenderer( mImpl->mRenderer );
-    // reset the weak handle so that the renderer only get added to actor once
-    mPlacementActor.Reset();
-  }
-}
-
 void ImageVisual::DoSetOnStage( Actor& actor )
 {
   if( mImageUrl.IsValid() )
@@ -726,7 +737,6 @@ void ImageVisual::DoSetOnStage( Actor& actor )
   }
 
   mPlacementActor = actor;
-
   // Search the Actor tree to find if Layer UI behaviour set.
   Layer layer = actor.GetLayer();
   if ( layer && layer.GetBehavior() == Layer::LAYER_3D )
@@ -740,7 +750,7 @@ void ImageVisual::DoSetOnStage( Actor& actor )
     mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
   }
 
-  if( IsSynchronousResourceLoading() || !(mImpl->mFlags & Impl::IS_ATLASING_APPLIED) )
+  if( mTextureLoading == false )
   {
     actor.AddRenderer( mImpl->mRenderer );
     mPlacementActor.Reset();
@@ -755,10 +765,10 @@ void ImageVisual::DoSetOffStage( Actor& actor )
   actor.RemoveRenderer( mImpl->mRenderer);
   if( mImageUrl.IsValid() )
   {
-    RemoveFromAtlas( mImageUrl.GetUrl() );
+    RemoveTexture( mImageUrl.GetUrl() );
     mImage.Reset();
   }
-
+  mTextureLoading = false;
   mImpl->mRenderer.Reset();
   mPlacementActor.Reset();
 }
@@ -876,34 +886,74 @@ void ImageVisual::ApplyImageToSampler( const Image& image )
   }
 }
 
-void ImageVisual::OnImageLoaded( ResourceImage image )
+// From existing atlas manager
+void ImageVisual::UploadCompleted()
 {
-  if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
+  // Texture has been uploaded. If weak handle is holding a placement actor, it is the time to add the renderer to actor.
+  Actor actor = mPlacementActor.GetHandle();
+  if( actor )
+  {
+    actor.AddRenderer( mImpl->mRenderer );
+
+    // reset the weak handle so that the renderer only get added to actor once
+    mPlacementActor.Reset();
+  }
+  mTextureLoading = false;
+}
+
+// From Texture Manager
+void ImageVisual::UploadComplete( bool loadingSuccess, TextureSet textureSet, bool usingAtlas, const Vector4& atlasRectangle )
+{
+  Actor actor = mPlacementActor.GetHandle();
+  if( actor )
   {
-    Image brokenImage = VisualFactoryCache::GetBrokenVisualImage();
     if( mImpl->mRenderer )
     {
-      ApplyImageToSampler( brokenImage );
+      actor.AddRenderer( mImpl->mRenderer );
+      // reset the weak handle so that the renderer only get added to actor once
+      mPlacementActor.Reset();
+
+      if( loadingSuccess )
+      {
+        Sampler sampler = Sampler::New();
+        sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
+        textureSet.SetSampler( 0u, sampler );
+        mImpl->mRenderer.SetTextures(textureSet);
+      }
+      else
+      {
+        Image brokenImage = VisualFactoryCache::GetBrokenVisualImage();
+        ApplyImageToSampler( brokenImage );
+      }
     }
   }
+  mTextureLoading = false;
 }
 
-void ImageVisual::RemoveFromAtlas(const std::string& url)
+void ImageVisual::RemoveTexture(const std::string& url)
 {
-  Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f );
-  Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME );
-  if( index != Property::INVALID_INDEX )
+  if( mTextureId != TextureManager::INVALID_TEXTURE_ID )
   {
-    Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index );
-    atlasRectValue.Get( atlasRect );
+    mFactoryCache.GetTextureManager().Remove( mTextureId );
+    mTextureId = TextureManager::INVALID_TEXTURE_ID;
   }
+  else
+  {
+    Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f );
+    Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME );
+    if( index != Property::INVALID_INDEX )
+    {
+      Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index );
+      atlasRectValue.Get( atlasRect );
+    }
 
-  TextureSet textureSet = mImpl->mRenderer.GetTextures();
-  mImpl->mRenderer.Reset();
+    TextureSet textureSet = mImpl->mRenderer.GetTextures();
+    mImpl->mRenderer.Reset();
 
-  if( index != Property::INVALID_INDEX )
-  {
-    mFactoryCache.GetAtlasManager()->Remove( textureSet, atlasRect );
+    if( index != Property::INVALID_INDEX )
+    {
+      mFactoryCache.GetAtlasManager()->Remove( textureSet, atlasRect );
+    }
   }
 }
 
index f771f6d..9113437 100644 (file)
@@ -27,6 +27,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/image-loader/atlas-upload-observer.h>
+#include <dali-toolkit/internal/visuals/texture-upload-observer.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-url.h>
 
@@ -94,7 +95,7 @@ typedef IntrusivePtr< ImageVisual > ImageVisualPtr;
  * If the Visual is in a LayerUI it will pixel align the image, using a Layer3D will disable pixel alignment.
  * Changing layer behaviour between LayerUI to Layer3D whilst the visual is already staged will not have an effect.
  */
-class ImageVisual: public Visual::Base, public ConnectionTracker, public AtlasUploadObserver
+class ImageVisual: public Visual::Base, public ConnectionTracker, public AtlasUploadObserver, public TextureUploadObserver
 {
 public:
 
@@ -231,6 +232,14 @@ public:
    */
   virtual void UploadCompleted();
 
+  /**
+   * @copydoc TextureUploadObserver::UploadCompleted
+   *
+   * To avoid rendering garbage pixels, renderer should be added to actor after the resources are ready.
+   * This callback is the place to add the renderer as it would be called once the loading is finished.
+   */
+  virtual void UploadComplete( bool success, TextureSet textureSet, bool usingAtlas, const Vector4& atlasRectangle );
+
 private:
 
   /**
@@ -286,21 +295,15 @@ private:
   TextureSet CreateTextureSet( Vector4& textureRect, bool synchronousLoading, bool attemptAtlasing );
 
   /**
-   * Callback function of image resource loading succeed
-   * @param[in] image The Image content that we attempted to load from mImageUrl
-   */
-  void OnImageLoaded( ResourceImage image );
-
-  /**
    * Set the value to the uTextureRect uniform
    * @param[in] textureRect The texture rectangular area.
    */
   void SetTextureRectUniform( const Vector4& textureRect  );
 
   /**
-   * Remove the image from atlas if it is not used anymore.
+   * Remove the texture if it is not used anymore.
    */
-  void RemoveFromAtlas(const std::string& url);
+  void RemoveTexture(const std::string& url);
 
   /**
    * Helper method to set individual values by index key.
@@ -318,13 +321,14 @@ private:
   VisualUrl mImageUrl;
 
   Dali::ImageDimensions mDesiredSize;
+  TextureManager::TextureId mTextureId;
+
   Dali::FittingMode::Type mFittingMode:3;
   Dali::SamplingMode::Type mSamplingMode:4;
   Dali::WrapMode::Type mWrapModeU:3;
   Dali::WrapMode::Type mWrapModeV:3;
-
-  bool mAttemptAtlasing:1; // If true will attempt atlasing, otherwise create unique texture
-
+  bool mAttemptAtlasing:1; ///< If true will attempt atlasing, otherwise create unique texture
+  bool mTextureLoading:1;  ///< True if the texture is being loaded asynchronously, or false when it has loaded.
 };
 
 } // namespace Internal
diff --git a/dali-toolkit/internal/visuals/texture-manager.cpp b/dali-toolkit/internal/visuals/texture-manager.cpp
new file mode 100644 (file)
index 0000000..7c8098d
--- /dev/null
@@ -0,0 +1,505 @@
+ /*
+ * Copyright (c) 2017 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 "texture-manager.h"
+
+// EXTERNAL HEADERS
+#include <dali/devel-api/common/hash.h>
+#include <dali/devel-api/images/texture-set-image.h>
+#include <dali/integration-api/debug.h>
+
+// INTERNAL HEADERS
+#include <dali/integration-api/debug.h>
+#include <dali-toolkit/internal/image-loader/async-image-loader-impl.h>
+#include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
+#include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+namespace
+{
+
+#ifdef DEBUG_ENABLED
+Debug::Filter* gTextureManagerLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_TEXTURE_MANAGER" );
+#endif
+
+const uint32_t      DEFAULT_ATLAS_SIZE( 1024u );                     ///< This size can fit 8 by 8 images of average size 128 * 128
+const Vector4       FULL_ATLAS_RECT( 0.0f, 0.0f, 1.0f, 1.0f );       ///< UV Rectangle that covers the full Texture
+const char * const  BROKEN_IMAGE_URL( DALI_IMAGE_DIR "broken.png" ); ///< URL For the broken image placeholder
+const int           INVALID_INDEX( -1 );                             ///< Invalid index used to represent a non-existant TextureInfo struct
+const int           INVALID_CACHE_INDEX( -1 ); ///< Invalid Cache index
+
+} // Anonymous namespace
+
+
+TextureManager::TextureManager()
+: mAsyncLocalLoader( Toolkit::AsyncImageLoader::New() ),
+  mAsyncRemoteLoader( Toolkit::AsyncImageLoader::New() ),
+  mCurrentTextureId( 0 )
+{
+  mAsyncLocalLoader.ImageLoadedSignal().Connect( this, &TextureManager::AsyncLocalLoadComplete );
+  mAsyncRemoteLoader.ImageLoadedSignal().Connect( this, &TextureManager::AsyncRemoteLoadComplete );
+}
+
+TextureManager::TextureId TextureManager::RequestLoad(
+  const VisualUrl&         url,
+  const ImageDimensions    desiredSize,
+  FittingMode::Type        fittingMode,
+  Dali::SamplingMode::Type samplingMode,
+  const UseAtlas           useAtlas,
+  TextureUploadObserver*   observer )
+{
+  // First check if the requested Texture is cached.
+  const TextureHash textureHash = GenerateHash( url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas );
+
+  // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
+  int cacheIndex = FindCachedTexture( textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas );
+  TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
+
+  // Check if the requested Texture exists in the cache.
+  if( cacheIndex != INVALID_CACHE_INDEX )
+  {
+    // Mark this texture being used by another client resource.
+    ++( mTextureInfoContainer[ cacheIndex ].referenceCount );
+    textureId = mTextureInfoContainer[ cacheIndex ].textureId;
+
+    DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture @%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId );
+  }
+  else
+  {
+    // We need a new Texture.
+    textureId = GenerateUniqueTextureId();
+    mTextureInfoContainer.push_back( TextureInfo( textureId, url.GetUrl(), desiredSize, fittingMode, samplingMode, false, useAtlas, textureHash ) );
+    cacheIndex = mTextureInfoContainer.size() - 1u;
+
+    DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId );
+  }
+
+  // The below code path is common whether we are using the cache or not.
+  // The textureInfoIndex now refers to either a pre-existing cached TextureInfo, or a new TextureInfo just created.
+  TextureInfo& textureInfo( mTextureInfoContainer[ cacheIndex ] );
+
+  DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureInfo loadState:%s\n",
+                 textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
+                 textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
+                 textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
+                 textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
+
+  // Check if we should add the observer. Only do this if we have not loaded yet and it will not have loaded by the end of this method.
+  switch( textureInfo.loadState )
+  {
+    case TextureManager::NOT_STARTED:
+    {
+      LoadTexture( textureInfo );
+      ObserveTexture( textureInfo, observer );
+      break;
+    }
+    case TextureManager::LOADING:
+    {
+      ObserveTexture( textureInfo, observer );
+      break;
+    }
+    case TextureManager::UPLOADED:
+    {
+      if( observer )
+      {
+        // The Texture has already loaded. The other observers have already been notified.
+        // We need to send a "late" loaded notification for this observer.
+        observer->UploadComplete( textureInfo.loadingSucceeded,
+                                  textureInfo.textureSet, textureInfo.useAtlas,
+                                  textureInfo.atlasRect );
+      }
+      break;
+    }
+    case TextureManager::CANCELLED:
+    {
+      // A cancelled texture hasn't finished loading yet. Treat as a loading texture
+      // (it's ref count has already been incremented, above)
+      textureInfo.loadState = TextureManager::LOADING;
+      ObserveTexture( textureInfo, observer );
+      break;
+    }
+  }
+
+  // Return the TextureId for which this Texture can now be referenced by externally.
+  return textureId;
+}
+
+void TextureManager::Remove( const TextureManager::TextureId textureId )
+{
+  int textureInfoIndex = GetCacheIndexFromId( textureId );
+  if( textureInfoIndex != INVALID_INDEX )
+  {
+    TextureInfo& textureInfo( mTextureInfoContainer[ textureInfoIndex ] );
+
+
+    DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::Remove(%d) cacheIdx:%d loadState:%s\n",
+                   textureId, textureInfoIndex,
+                   textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
+                   textureInfo.loadState == TextureManager::LOADING ? "LOADING" :
+                   textureInfo.loadState == TextureManager::UPLOADED ? "UPLOADED" :
+                   textureInfo.loadState == TextureManager::CANCELLED ? "CANCELLED" : "Unknown" );
+
+    // Decrement the reference count and check if this is the last user of this Texture.
+    if( --textureInfo.referenceCount <= 0 )
+    {
+      // This is the last remove for this Texture.
+      textureInfo.referenceCount = 0;
+      bool removeTextureInfo = false;
+
+      // If loaded, we can remove the TextureInfo and the Atlas (if atlased).
+      if( textureInfo.loadState == UPLOADED )
+      {
+        if( textureInfo.atlas )
+        {
+          textureInfo.atlas.Remove( textureInfo.atlasRect );
+        }
+        removeTextureInfo = true;
+      }
+      else if( textureInfo.loadState == LOADING )
+      {
+        // We mark the textureInfo for removal.
+        // Once the load has completed, this method will be called again.
+        textureInfo.loadState = CANCELLED;
+      }
+      else
+      {
+        // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
+        removeTextureInfo = true;
+      }
+
+      // If the state allows us to remove the TextureInfo data, we do so.
+      if( removeTextureInfo )
+      {
+        // Permanently remove the textureInfo struct.
+        mTextureInfoContainer.erase( mTextureInfoContainer.begin() + textureInfoIndex );
+      }
+    }
+  }
+}
+
+TextureManager::LoadState TextureManager::GetTextureState( TextureId textureId )
+{
+  LoadState loadState = TextureManager::NOT_STARTED;
+
+  int cacheIndex = GetCacheIndexFromId( textureId );
+  if( cacheIndex != INVALID_CACHE_INDEX )
+  {
+    TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
+    loadState = cachedTextureInfo.loadState;
+  }
+  return loadState;
+}
+
+TextureSet TextureManager::GetTextureSet( TextureId textureId )
+{
+  TextureSet textureSet;// empty handle
+
+  int cacheIndex = GetCacheIndexFromId( textureId );
+  if( cacheIndex != INVALID_CACHE_INDEX )
+  {
+    TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
+    textureSet = cachedTextureInfo.textureSet;
+  }
+  return textureSet;
+}
+
+
+
+bool TextureManager::LoadTexture( TextureInfo& textureInfo )
+{
+  bool success = true;
+
+  if( textureInfo.loadState == NOT_STARTED )
+  {
+    textureInfo.loadState = LOADING;
+
+    if( !textureInfo.loadSynchronously )
+    {
+      if( textureInfo.url.IsLocal() )
+      {
+        mAsyncLocalLoadingInfoContainer.push_back( AsyncLoadingInfo( textureInfo.textureId ) );
+        mAsyncLocalLoadingInfoContainer.back().loadId = GetImplementation(mAsyncLocalLoader).Load(
+          textureInfo.url, textureInfo.desiredSize,
+          textureInfo.fittingMode, textureInfo.samplingMode, true );
+      }
+      else
+      {
+        mAsyncRemoteLoadingInfoContainer.push_back( AsyncLoadingInfo( textureInfo.textureId ) );
+        mAsyncRemoteLoadingInfoContainer.back().loadId = GetImplementation(mAsyncRemoteLoader).Load(
+          textureInfo.url, textureInfo.desiredSize,
+          textureInfo.fittingMode, textureInfo.samplingMode, true );
+      }
+    }
+  }
+
+  return success;
+}
+
+void TextureManager::ObserveTexture( TextureInfo& textureInfo,
+                                     TextureUploadObserver* observer )
+{
+  if( observer )
+  {
+    textureInfo.observerList.PushBack( observer );
+    observer->DestructionSignal().Connect( this, &TextureManager::ObserverDestroyed );
+  }
+}
+
+void TextureManager::AsyncLocalLoadComplete( uint32_t id, PixelData pixelData )
+{
+  AsyncLoadComplete( mAsyncLocalLoadingInfoContainer, id, pixelData );
+}
+
+void TextureManager::AsyncRemoteLoadComplete( uint32_t id, PixelData pixelData )
+{
+  AsyncLoadComplete( mAsyncRemoteLoadingInfoContainer, id, pixelData );
+}
+
+void TextureManager::AsyncLoadComplete( AsyncLoadingInfoContainerType& loadingContainer, uint32_t id, PixelData pixelData )
+{
+  DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( id:%d )\n", id );
+
+  if( loadingContainer.size() >= 1u )
+  {
+    AsyncLoadingInfo loadingInfo = loadingContainer.front();
+
+    if( loadingInfo.loadId == id )
+    {
+      int cacheIndex = GetCacheIndexFromId( loadingInfo.textureId );
+      if( cacheIndex != INVALID_CACHE_INDEX )
+      {
+        // Once we have found the TextureInfo data, we call a common function used to process loaded data for both sync and async loads.
+        TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
+
+        DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "  CacheIndex:%d LoadState: %d\n", cacheIndex, textureInfo.loadState );
+
+        // Only perform atlasing if the load has not been cancelled since the request.
+        if( textureInfo.loadState != CANCELLED )
+        {
+          // Perform atlasing and finalize the load.
+          PostLoad( textureInfo, pixelData );
+        }
+        else
+        {
+          Remove( textureInfo.textureId );
+        }
+      }
+    }
+
+    loadingContainer.pop_front();
+  }
+}
+
+
+bool TextureManager::PostLoad( TextureInfo& textureInfo, PixelData pixelData )
+{
+  bool success = false;
+
+  // Was the load successful?
+  if( pixelData && ( pixelData.GetWidth() != 0 ) && ( pixelData.GetHeight() != 0 ) )
+  {
+    // Regardless of whether the atlasing succeeds or not, we have a valid image, so we mark it as successful.
+    success = true;
+
+    bool usingAtlas = false;
+
+    // No atlas support for now
+    textureInfo.useAtlas = NO_ATLAS;
+
+    if( ! usingAtlas )
+    {
+      DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "  TextureManager::PostLoad() textureId:%d\n", textureInfo.textureId );
+
+      Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight() );
+      texture.Upload( pixelData );
+      textureInfo.textureSet = TextureSet::New();
+      textureInfo.textureSet.SetTexture( 0u, texture );
+    }
+  }
+
+  if( ! success )
+  {
+    DALI_LOG_ERROR( "TextureManager::AsyncImageLoad(%s) failed\n", textureInfo.url.GetUrl().c_str() );
+    // @todo If the load was unsuccessful, upload the broken image.
+  }
+
+  // Update the load state.
+  // Note: This is regardless of success as we care about whether a
+  // load attempt is in progress or not.  If unsuccessful, a broken
+  // image is still loaded.
+  textureInfo.loadState = UPLOADED;
+
+  // We need to store the load succeeded state as if a future request to load this texture comes in,
+  // we need to re-broadcast the UploadComplete notification to that observer.
+  textureInfo.loadingSucceeded = success;
+
+  // If there is an observer: Notify the load is complete, whether successful or not:
+  const unsigned int observerCount = textureInfo.observerList.Count();
+  for( unsigned int i = 0; i < observerCount; ++i )
+  {
+    TextureUploadObserver* observer = textureInfo.observerList[i];
+    if( observer )
+    {
+      observer->UploadComplete( success, textureInfo.textureSet, textureInfo.useAtlas, textureInfo.atlasRect );
+      observer->DestructionSignal().Disconnect( this, &TextureManager::ObserverDestroyed );
+    }
+  }
+
+  textureInfo.observerList.Clear();
+
+  return success;
+}
+
+TextureManager::TextureId TextureManager::GenerateUniqueTextureId()
+{
+  return mCurrentTextureId++;
+}
+
+int TextureManager::GetCacheIndexFromId( const TextureId textureId )
+{
+  const unsigned int size = mTextureInfoContainer.size();
+
+  for( unsigned int i = 0; i < size; ++i )
+  {
+    if( mTextureInfoContainer[i].textureId == textureId )
+    {
+      return i;
+    }
+  }
+
+  DALI_LOG_WARNING( "Cannot locate TextureId: %d\n", textureId );
+  return INVALID_CACHE_INDEX;
+}
+
+TextureManager::TextureHash TextureManager::GenerateHash(
+  const std::string&             url,
+  const ImageDimensions          size,
+  const FittingMode::Type        fittingMode,
+  const Dali::SamplingMode::Type samplingMode,
+  const UseAtlas                 useAtlas )
+{
+  std::string hashTarget( url );
+  const size_t urlLength = hashTarget.length();
+  const uint16_t width = size.GetWidth();
+  const uint16_t height = size.GetWidth();
+
+  // If either the width or height has been specified, include the resizing options in the hash
+  if( width != 0 || height != 0 )
+  {
+    // We are appending 5 bytes to the URL to form the hash input.
+    hashTarget.resize( urlLength + 5u );
+    char* hashTargetPtr = &( hashTarget[ urlLength ] );
+
+    // Pack the width and height (4 bytes total).
+    *hashTargetPtr++ = size.GetWidth() & 0xff;
+    *hashTargetPtr++ = ( size.GetWidth() >> 8u ) & 0xff;
+    *hashTargetPtr++ = size.GetHeight() & 0xff;
+    *hashTargetPtr++ = ( size.GetHeight() >> 8u ) & 0xff;
+
+    // Bit-pack the FittingMode, SamplingMode and atlasing.
+    // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit
+    *hashTargetPtr   = ( fittingMode << 4u ) | ( samplingMode << 1 ) | useAtlas;
+  }
+  else
+  {
+    // We are not including sizing information, but we still need an extra byte for atlasing.
+    hashTarget.resize( urlLength + 1u );
+    // Add the atlasing to the hash input.
+    hashTarget[ urlLength ] = useAtlas;
+  }
+
+  return Dali::CalculateHash( hashTarget );
+}
+
+int TextureManager::FindCachedTexture(
+  const TextureManager::TextureHash hash,
+  const std::string&                url,
+  const ImageDimensions             size,
+  const FittingMode::Type           fittingMode,
+  const Dali::SamplingMode::Type    samplingMode,
+  const bool                        useAtlas )
+{
+  // Default to an invalid ID, in case we do not find a match.
+  int cacheIndex = INVALID_CACHE_INDEX;
+
+  // Iterate through our hashes to find a match.
+  const unsigned int count = mTextureInfoContainer.size();
+  for( unsigned int i = 0u; i < count; ++i )
+  {
+    if( mTextureInfoContainer[i].hash == hash )
+    {
+      // We have a match, now we check all the original parameters in case of a hash collision.
+      TextureInfo& textureInfo( mTextureInfoContainer[i] );
+
+      if( ( url == textureInfo.url.GetUrl() ) &&
+          ( useAtlas == textureInfo.useAtlas ) &&
+          ( size == textureInfo.desiredSize ) &&
+          ( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
+            ( fittingMode == textureInfo.fittingMode &&
+              samplingMode == textureInfo.samplingMode ) ) )
+      {
+        // The found Texture is a match.
+        cacheIndex = i;
+        break;
+      }
+    }
+  }
+
+  return cacheIndex;
+}
+
+void TextureManager::ObserverDestroyed( TextureUploadObserver* observer )
+{
+  const unsigned int count = mTextureInfoContainer.size();
+  for( unsigned int i = 0; i < count; ++i )
+  {
+    TextureInfo& textureInfo( mTextureInfoContainer[i] );
+    for( TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin(); j != textureInfo.observerList.End(); ++j )
+    {
+      if( *j == observer )
+      {
+        textureInfo.observerList.Erase( j );
+        break;
+      }
+    }
+  }
+}
+
+TextureManager::~TextureManager()
+{
+  mTextureInfoContainer.clear();
+  mAsyncLocalLoadingInfoContainer.clear();
+  mAsyncRemoteLoadingInfoContainer.clear();
+}
+
+
+
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/visuals/texture-manager.h b/dali-toolkit/internal/visuals/texture-manager.h
new file mode 100644 (file)
index 0000000..312b12e
--- /dev/null
@@ -0,0 +1,368 @@
+#ifndef DALI_TOOLKIT_TEXTURE_MANAGER_H
+#define DALI_TOOLKIT_TEXTURE_MANAGER_H
+
+/*
+ * Copyright (c) 2017 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/dali-vector.h>
+#include <dali/public-api/object/ref-object.h>
+#include <dali/public-api/rendering/texture-set.h>
+#include <dali/devel-api/common/owner-container.h>
+#include <deque>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/image-loader/image-atlas.h>
+#include <dali-toolkit/public-api/image-loader/async-image-loader.h>
+#include <dali-toolkit/internal/visuals/texture-upload-observer.h>
+#include <dali-toolkit/internal/visuals/visual-url.h>
+
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+/**
+ * The TextureManager provides a common Image loading API for Visuals.
+ *
+ * The TextureManager is responsible for providing sync, async, atlased and non-atlased loads.
+ * Texture caching is provided and performed when possible.
+ * Broken Images are automatically provided on load failure.
+ */
+class TextureManager : public ConnectionTracker
+{
+public:
+
+  typedef int32_t TextureId;       ///< The TextureId type. This is used as a handle to refer to a particular Texture.
+  static const int INVALID_TEXTURE_ID = -1; ///< Used to represent a null TextureId or error
+
+  enum UseAtlas
+  {
+    NO_ATLAS,
+    USE_ATLAS
+  };
+
+  enum LoadType
+  {
+    LOAD_ASYNCHRONOUSLY,
+    LOAD_SYNCHRONOUSLY
+  };
+
+  /**
+   * @brief The LoadState Enumeration represents the current state of a particular Textures life-cycle.
+   */
+  enum LoadState
+  {
+    NOT_STARTED,     ///< Default
+    LOADING,         ///< Loading has been started, but not finished.
+    UPLOADED,        ///< Loaded (and ready).
+    CANCELLED,       ///< Removed before loading completed
+  };
+
+public:
+
+  /**
+   * Constructor.
+   */
+  TextureManager();
+
+  /**
+   * Destructor.
+   */
+  ~TextureManager();
+
+
+// TextureManager Main API:
+
+  /**
+   * @brief Requests an image load of the given URL.
+   *
+   * The parameters are used to specify how the image is loaded.
+   * The observer has the UploadComplete method called when the load is ready.
+   *
+   * When the client has finished with the Texture, Remove() should be called.
+   *
+   * @param[in] url               The URL of the image to load
+   * @param[in] desiredSize       The size the image is likely to appear at. This can be set to 0,0 for automatic
+   * @param[in] fittingMode       The FittingMode to use
+   * @param[in] samplingMode      The SamplingMode to use
+   * @param[in] useAtlasing       Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
+   *                              but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
+   * @param[in] observer          The client object should inherit from this and provide the "UploadCompleted" virtual.
+   *                              This is called when an image load completes (or fails).
+   * @return                      A TextureId to use as a handle to reference this Texture
+   */
+  TextureId RequestLoad( const VisualUrl&         url,
+                         const ImageDimensions    desiredSize,
+                         FittingMode::Type        fittingMode,
+                         Dali::SamplingMode::Type samplingMode,
+                         const UseAtlas           useAtlasing,
+                         TextureUploadObserver*   observer );
+
+  /**
+   * @brief Remove a Texture from the TextureManager.
+   *
+   * Textures are cached and therefore only the removal of the last
+   * occurrence of a Texture will cause its removal internally.
+   *
+   * @param[in] textureId The ID of the Texture to remove.
+   */
+  void Remove( const TextureManager::TextureId textureId );
+
+  /**
+   * @brief Get the current state of a texture
+   * @param[in] textureId The texture id to query
+   * @return The loading state if the texture is valid, or NOT_STARTED if the textureId
+   * is not valid.
+   */
+  LoadState GetTextureState( TextureId textureId );
+
+  /**
+   * @brief Get the associated texture set if the texture id is valid
+   * @param[in] textureId The texture Id to look up
+   * @return the associated texture set, or an empty handle if textureId is not valid
+   */
+  TextureSet GetTextureSet( TextureId textureId );
+
+private:
+
+
+  typedef size_t TextureHash; ///< The type used to store the hash used for Texture caching.
+
+  /**
+   * @brief This struct is used to manage the life-cycle of Texture loading and caching.
+   * TODO-TX: pimpl this
+   */
+  struct TextureInfo
+  {
+    TextureInfo( TextureId textureId,
+                 const VisualUrl& url,
+                 ImageDimensions desiredSize,
+                 FittingMode::Type fittingMode,
+                 Dali::SamplingMode::Type samplingMode,
+                 bool loadSynchronously,
+                 UseAtlas useAtlas,
+                 TextureManager::TextureHash hash )
+    : url( url ),
+      desiredSize( desiredSize ),
+      useSize( desiredSize ),
+      atlasRect( 0.0f, 0.0f, 1.0f, 1.0f ), // Full atlas rectangle
+      textureId( textureId ),
+      hash( hash ),
+      referenceCount( 1u ),
+      loadState( NOT_STARTED ),
+      fittingMode( fittingMode ),
+      samplingMode( samplingMode ),
+      loadSynchronously( loadSynchronously ),
+      useAtlas( useAtlas ),
+      loadingSucceeded( false )
+    {
+    }
+
+    /**
+     * Container type used to store all observer clients of this Texture
+     */
+    typedef Dali::Vector< TextureUploadObserver* > ObserverListType;
+
+    ObserverListType observerList; ///< Container used to store all observer clients of this Texture
+    Toolkit::ImageAtlas atlas;     ///< The atlas this Texture lays within (if any)
+    PixelData pixelData;           ///< The PixelData holding the image data (this is used if atlasing is deferred)
+    TextureSet textureSet;         ///< The TextureSet holding the Texture
+    VisualUrl url;                 ///< The URL of the image
+    ImageDimensions desiredSize;   ///< The size requested
+    ImageDimensions useSize;       ///< The size used
+    Vector4 atlasRect;             ///< The atlas rect used if atlased
+    TextureId textureId;           ///< The TextureId associated with this Texture
+    TextureManager::TextureHash hash; ///< The hash used to cache this Texture
+    int16_t referenceCount;        ///< The reference count of clients using this Texture
+    LoadState loadState:3;         ///< The load state showing the load progress of the Texture
+    FittingMode::Type fittingMode:2; ///< The requested FittingMode
+    Dali::SamplingMode::Type samplingMode:3; ///< The requested SamplingMode
+    bool loadSynchronously;        ///< True if synchronous loading was requested
+    UseAtlas useAtlas; ///< USE_ATLAS if an atlas was requested. This is updated to false if atlas is not used
+    bool loadingSucceeded;         ///< True if the image was loaded successfully
+  };
+
+  // Structs:
+
+  /**
+   * Struct to hold information about a requested Async load.
+   * This is used to look up a TextureManager::TextureId from the returned AsyncLoad Id.
+   */
+  struct AsyncLoadingInfo
+  {
+    AsyncLoadingInfo( TextureId textureId )
+    : textureId( textureId ),
+      loadId( 0 )
+    {
+    }
+
+    TextureId           textureId;   ///< The external Texture Id assigned to this load
+    unsigned short      loadId;      ///< The load Id used by the async loader to reference this load
+  };
+
+  /**
+   * @brief This struct is used within a container to manage atlas creation and destruction.
+   */
+  struct AtlasInfo
+  {
+    AtlasInfo( Toolkit::ImageAtlas atlas, TextureSet textureSet )
+    : atlas( atlas ),
+      textureSet( textureSet )
+    {
+    }
+
+    Toolkit::ImageAtlas                 atlas;                          ///< The ImageAtlas object
+    TextureSet                          textureSet;                     ///< The TextureSet is kept in the struct to allow fast lookup of TextureSet to Atlas
+  };
+
+  // Private typedefs:
+
+  typedef std::deque<AsyncLoadingInfo>  AsyncLoadingInfoContainerType;  ///< The container type used to manage Asynchronous loads in progress
+  typedef std::vector<AtlasInfo>        AtlasInfoContainerType;         ///< The container type used to manage Atlas creation and destruction
+  typedef std::vector<TextureInfo>      TextureInfoContainerType;       ///< The container type used to manage the life-cycle and caching of Textures
+
+  /**
+   * @brief Used internally to initiate a load.
+   * @param[in] textureInfo The TextureInfo struct associated with the Texture
+   * @return                True if the load was initiated
+   */
+  bool LoadTexture( TextureInfo& textureInfo );
+
+  /**
+   * Add the observer to the observer list
+   * @param[in] textureInfo The TextureInfo struct associated with the texture
+   * observer The observer wishing to observe the texture upload
+   */
+  void ObserveTexture( TextureInfo & textureInfo, TextureUploadObserver* observer );
+
+  /**
+   * @brief This signal handler is called when the async local loader finishes loading.
+   * @param[in] id        This is the async image loaders Id
+   * @param[in] pixelData The loaded image data
+   */
+  void AsyncLocalLoadComplete( uint32_t id, PixelData pixelData );
+
+  /**
+   * @brief This signal handler is called when the async local loader finishes loading.
+   * @param[in] id        This is the async image loaders Id
+   * @param[in] pixelData The loaded image data
+   */
+  void AsyncRemoteLoadComplete( uint32_t id, PixelData pixelData );
+
+  /**
+   * Common method to handle loading completion
+   * @param[in] container The Async loading container
+   * @param[in] id        This is the async image loaders Id
+   * @param[in] pixelData The loaded image data
+   */
+  void AsyncLoadComplete( AsyncLoadingInfoContainerType& container, uint32_t id, PixelData pixelData );
+
+  /**
+   * @brief Performs Post-Load steps including atlasing.
+   * @param[in]           textureInfo The struct associated with this Texture
+   * @param[in]           pixelData   The image pixelData
+   * @return                          True if successful
+   */
+  bool PostLoad( TextureManager::TextureInfo& textureInfo, PixelData pixelData );
+
+  /**
+   * @brief Generates a new, unique TextureId
+   * @return A unique TextureId
+   */
+  TextureManager::TextureId GenerateUniqueTextureId();
+
+  /**
+   * @brief Used to lookup an index into the TextureInfoContainer from a TextureId
+   * @param[in] textureId The TextureId to look up
+   * @return              The cache index
+   */
+  int GetCacheIndexFromId( TextureId textureId );
+
+
+  /**
+   * @brief Generates a hash for caching based on the input parameters.
+   * @param[in] url          The URL of the image to load
+   * @param[in] size         The image size
+   * @param[in] fittingMode  The FittingMode to use
+   * @param[in] samplingMode The SamplingMode to use
+   * @param[in] useAtlas     True if atlased
+   * @return                 A hash of the provided data for caching.
+   */
+  TextureHash GenerateHash( const std::string& url, const ImageDimensions size,
+                            const FittingMode::Type fittingMode,
+                            const Dali::SamplingMode::Type samplingMode, const UseAtlas useAtlas );
+
+  /**
+   * @brief Looks up a cached texture by its hash.
+   * If found, the given parameters are used to check there is no hash-collision.
+   * @param[in] hash The hash to look up
+   * @param[in] url          The URL of the image to load
+   * @param[in] size         The image size
+   * @param[in] fittingMode  The FittingMode to use
+   * @param[in] samplingMode The SamplingMode to use
+   * @param[in] useAtlas     True if atlased
+   * @return                 A TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found.
+   */
+  TextureManager::TextureId FindCachedTexture( const TextureManager::TextureHash hash, const std::string& url, const ImageDimensions size,
+      const FittingMode::Type fittingMode, const Dali::SamplingMode::Type samplingMode, const bool useAtlas );
+
+
+private:
+
+  /**
+   * Undefined copy constructor.
+   */
+  TextureManager( const TextureManager& );
+
+  /**
+   * Undefined assignment operator.
+   */
+  TextureManager& operator=( const TextureManager& rhs );
+
+  /**
+   * This is called by the TextureManagerUploadObserver when an observer is destroyed.
+   * We use the callback to know when to remove an observer from our notify list.
+   * @param[in] observer The observer that generated the callback
+   */
+  void ObserverDestroyed( TextureUploadObserver* observer );
+
+private:  // Member Variables:
+
+  AsyncLoadingInfoContainerType         mAsyncLocalLoadingInfoContainer;     ///< Used to manage Asynchronous loads in progress
+  AsyncLoadingInfoContainerType         mAsyncRemoteLoadingInfoContainer;     ///< Used to manage Asynchronous loads in progress
+  AtlasInfoContainerType                mAtlasContainer;                ///< Used to manage Atlas creation and destruction
+  TextureInfoContainerType              mTextureInfoContainer;          ///< Used to manage the life-cycle and caching of Textures
+  Toolkit::AsyncImageLoader             mAsyncLocalLoader;              ///< The Asynchronous image loader used to provide all local async loads
+  Toolkit::AsyncImageLoader             mAsyncRemoteLoader;             ///< The Asynchronous image loader used to provide all remote async loads
+  TextureId                             mCurrentTextureId;              ///< The current value used for the unique Texture Id generation
+
+};
+
+
+} // name Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXTURE_MANAGER_H
diff --git a/dali-toolkit/internal/visuals/texture-upload-observer.cpp b/dali-toolkit/internal/visuals/texture-upload-observer.cpp
new file mode 100644 (file)
index 0000000..8ffd35f
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017 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 "texture-upload-observer.h"
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+TextureUploadObserver::TextureUploadObserver()
+{
+}
+
+TextureUploadObserver::~TextureUploadObserver()
+{
+  if( !mDestructionSignal.Empty() )
+  {
+    mDestructionSignal.Emit( this );
+  }
+}
+
+TextureUploadObserver::DestructionSignalType& TextureUploadObserver::DestructionSignal()
+{
+  return mDestructionSignal;
+}
+
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/visuals/texture-upload-observer.h b/dali-toolkit/internal/visuals/texture-upload-observer.h
new file mode 100644 (file)
index 0000000..c300274
--- /dev/null
@@ -0,0 +1,84 @@
+#ifndef DALI_TOOLKIT_INTERNAL_TEXTURE_UPLOAD_OBSERVER_H
+#define DALI_TOOLKIT_INTERNAL_TEXTURE_UPLOAD_OBSERVER_H
+
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/signals/dali-signal.h>
+
+namespace Dali
+{
+
+class TextureSet;
+
+namespace Toolkit
+{
+
+
+/**
+ * @brief Base class used to observe the upload status of a texture.
+ *
+ * Derived class must implement the UploadComplete method which is
+ * executed once the texture is ready to draw.
+ */
+class DALI_IMPORT_API TextureUploadObserver
+{
+public:
+
+  typedef Signal< void ( TextureUploadObserver* ) > DestructionSignalType; ///< Signal prototype for the Destruction Signal.
+
+  /**
+   * @brief Constructor.
+   */
+  TextureUploadObserver();
+
+  /**
+   * @brief Virtual destructor.
+   */
+  virtual ~TextureUploadObserver();
+
+  /**
+   * The action to be taken once the async load has finished and the upload to GPU is completed.
+   * This should be overridden by the deriving class.
+   *
+   * @param[in] loadSuccess True if the texture load was successful (i.e. the resource is available). If false, then the resource failed to load. In future, this will automatically upload a "broken" image.
+   * @param[in] textureSet  The TextureSet containing the Texture
+   * @param[in] useAtlasing True if atlasing was used (note: this may be different to what was requested)
+   * @param[in] atlasRect   If using atlasing, this is the rectangle within the atlas to use.
+   */
+  virtual void UploadComplete( bool loadSuccess, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect ) = 0;
+
+  /**
+   * @brief Returns the destruction signal.
+   * This is emitted when the observer is destroyed.
+   * This is used by the observer notifier to mark this observer as destroyed (IE. It no longer needs notifying).
+   */
+  DestructionSignalType& DestructionSignal();
+
+private:
+
+  DestructionSignalType mDestructionSignal; ///< The destruction signal emitted when the observer is destroyed.
+
+};
+
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_INTERNAL_TEXTURE_UPLOAD_OBSERVER_H
index dd8d306..d9a566d 100644 (file)
@@ -112,6 +112,11 @@ ImageAtlasManagerPtr VisualFactoryCache::GetAtlasManager()
   return mAtlasManager;
 }
 
+TextureManager& VisualFactoryCache::GetTextureManager()
+{
+  return mTextureManager;
+}
+
 NPatchLoader& VisualFactoryCache::GetNPatchLoader()
 {
   return mNPatchLoader;
index 470cf07..cc68e0f 100644 (file)
@@ -28,6 +28,7 @@
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/visuals/npatch-loader.h>
 #include <dali-toolkit/internal/visuals/svg/svg-rasterize-thread.h>
+#include <dali-toolkit/internal/visuals/texture-manager.h>
 
 namespace Dali
 {
@@ -37,11 +38,12 @@ namespace Toolkit
 
 namespace Internal
 {
-
 class ImageAtlasManager;
+class NPatchLoader;
+class TextureManager;
+
 typedef IntrusivePtr<ImageAtlasManager> ImageAtlasManagerPtr;
 
-class NPatchLoader;
 
 /**
  * Caches shaders and geometries. Owned by VisualFactory.
@@ -145,6 +147,12 @@ public:
   ImageAtlasManagerPtr GetAtlasManager();
 
   /**
+   * Get the texture manager
+   * @return A reference to the texture manager
+   */
+  TextureManager& GetTextureManager();
+
+  /**
    * Get the N-Patch texture cache.
    * @return A reference to the N patch loader
    */
@@ -185,8 +193,8 @@ private:
   Shader mShader[SHADER_TYPE_MAX+1];
 
   ImageAtlasManagerPtr mAtlasManager;
-  NPatchLoader mNPatchLoader;
-
+  TextureManager       mTextureManager;
+  NPatchLoader         mNPatchLoader;
   SvgRasterizeThread*  mSvgRasterizeThread;
 };
 
index c1e23ee..b01134d 100644 (file)
@@ -19,6 +19,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/image-loader/async-image-loader-impl.h>
+#include <dali-toolkit/internal/visuals/visual-url.h>
 
 namespace Dali
 {
@@ -63,12 +64,12 @@ AsyncImageLoader AsyncImageLoader::New()
 
 uint32_t AsyncImageLoader::Load( const std::string& url )
 {
-  return GetImplementation( *this ).Load( url, ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true );
+  return GetImplementation( *this ).Load( Toolkit::Internal::VisualUrl(url), ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true );
 }
 
 uint32_t AsyncImageLoader::Load( const std::string& url, ImageDimensions dimensions )
 {
-  return GetImplementation( *this ).Load( url, dimensions, FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true );
+  return GetImplementation( *this ).Load( Toolkit::Internal::VisualUrl(url), dimensions, FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true );
 }
 
 uint32_t AsyncImageLoader::Load( const std::string& url,
@@ -77,7 +78,7 @@ uint32_t AsyncImageLoader::Load( const std::string& url,
                                  SamplingMode::Type samplingMode,
                                  bool orientationCorrection )
 {
-  return GetImplementation(*this).Load( url, dimensions, fittingMode, samplingMode, orientationCorrection );
+  return GetImplementation(*this).Load( Toolkit::Internal::VisualUrl(url), dimensions, fittingMode, samplingMode, orientationCorrection );
 }
 
 bool AsyncImageLoader::Cancel( uint32_t loadingTaskId )