Remove Atlas usage from ImageVisual
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / image / image-visual.cpp
index 4e545ab..e5ce0a3 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 // CLASS HEADER
-#include "image-visual.h"
+#include <dali-toolkit/internal/visuals/image/image-visual.h>
 
 // EXTERNAL HEADER
 #include <cstring> // for strncasecmp
@@ -190,17 +190,71 @@ Geometry CreateGeometry( VisualFactoryCache& factoryCache, ImageDimensions gridS
 
 ImageVisual::ImageVisual( VisualFactoryCache& factoryCache )
 : Visual::Base( factoryCache ),
+  mImage(),
+  mPixels(),
+  mTextureSet(),
   mPixelArea( FULL_TEXTURE_RECT ),
+  mPlacementActor(),
+  mImageUrl(),
+  mNativeFragmentShaderCode(),
   mDesiredSize(),
   mFittingMode( FittingMode::DEFAULT ),
   mSamplingMode( SamplingMode::DEFAULT ),
   mWrapModeU( WrapMode::DEFAULT ),
   mWrapModeV( WrapMode::DEFAULT ),
-  mNativeFragmentShaderCode( ),
   mNativeImageFlag( false )
 {
 }
 
+ImageVisual::ImageVisual( VisualFactoryCache& factoryCache,
+                          const std::string& imageUrl,
+                          ImageDimensions size,
+                          FittingMode::Type fittingMode,
+                          Dali::SamplingMode::Type samplingMode )
+: Visual::Base( factoryCache ),
+  mImage(),
+  mPixels(),
+  mTextureSet(),
+  mPixelArea( FULL_TEXTURE_RECT ),
+  mPlacementActor(),
+  mImageUrl( imageUrl ),
+  mNativeFragmentShaderCode(),
+  mDesiredSize( size ),
+  mFittingMode( fittingMode ),
+  mSamplingMode( samplingMode ),
+  mWrapModeU( WrapMode::DEFAULT ),
+  mWrapModeV( WrapMode::DEFAULT ),
+  mNativeImageFlag( false )
+{
+}
+
+ImageVisual::ImageVisual( VisualFactoryCache& factoryCache, const Image& image )
+: Visual::Base( factoryCache ),
+  mImage( image ),
+  mPixels(),
+  mPixelArea( FULL_TEXTURE_RECT ),
+  mPlacementActor(),
+  mImageUrl(),
+  mNativeFragmentShaderCode(),
+  mDesiredSize(),
+  mFittingMode( FittingMode::DEFAULT ),
+  mSamplingMode( SamplingMode::DEFAULT ),
+  mWrapModeU( WrapMode::DEFAULT ),
+  mWrapModeV( WrapMode::DEFAULT ),
+  mNativeImageFlag( false )
+{
+  NativeImage newNativeImage = NativeImage::DownCast( image );
+  if( newNativeImage )
+  {
+    SetNativeFragmentShaderCode( newNativeImage );
+    mNativeImageFlag = true;
+  }
+  else
+  {
+    mNativeImageFlag = false;
+  }
+}
+
 ImageVisual::~ImageVisual()
 {
 }
@@ -221,13 +275,17 @@ void ImageVisual::DoInitialize( Actor& actor, const Property::Map& propertyMap )
     Property::Value* fittingValue = propertyMap.Find( Toolkit::ImageVisual::Property::FITTING_MODE, IMAGE_FITTING_MODE );
     if( fittingValue )
     {
-      Scripting::GetEnumerationProperty( *fittingValue, FITTING_MODE_TABLE, FITTING_MODE_TABLE_COUNT, mFittingMode );
+      int value;
+      Scripting::GetEnumerationProperty( *fittingValue, FITTING_MODE_TABLE, FITTING_MODE_TABLE_COUNT, value );
+      mFittingMode = Dali::FittingMode::Type( value );
     }
 
     Property::Value* samplingValue = propertyMap.Find( Toolkit::ImageVisual::Property::SAMPLING_MODE, IMAGE_SAMPLING_MODE );
     if( samplingValue )
     {
-      Scripting::GetEnumerationProperty( *samplingValue, SAMPLING_MODE_TABLE, SAMPLING_MODE_TABLE_COUNT, mSamplingMode );
+      int value;
+      Scripting::GetEnumerationProperty( *samplingValue, SAMPLING_MODE_TABLE, SAMPLING_MODE_TABLE_COUNT, value );
+      mSamplingMode = Dali::SamplingMode::Type( value );
     }
 
     int desiredWidth = 0;
@@ -253,13 +311,17 @@ void ImageVisual::DoInitialize( Actor& actor, const Property::Map& propertyMap )
     Property::Value* wrapModeValueU = propertyMap.Find( Toolkit::ImageVisual::Property::WRAP_MODE_U, IMAGE_WRAP_MODE_U );
     if( wrapModeValueU )
     {
-      Scripting::GetEnumerationProperty( *wrapModeValueU, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, mWrapModeU );
+      int value;
+      Scripting::GetEnumerationProperty( *wrapModeValueU, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, value );
+      mWrapModeU = Dali::WrapMode::Type( value );
     }
 
     Property::Value* wrapModeValueV = propertyMap.Find( Toolkit::ImageVisual::Property::WRAP_MODE_V, IMAGE_WRAP_MODE_V );
     if( wrapModeValueV )
     {
-      Scripting::GetEnumerationProperty( *wrapModeValueV, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, mWrapModeV );
+      int value;
+      Scripting::GetEnumerationProperty( *wrapModeValueV, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, value );
+      mWrapModeV = Dali::WrapMode::Type( value );
     }
 
     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
@@ -282,22 +344,9 @@ void ImageVisual::DoInitialize( Actor& actor, const Property::Map& propertyMap )
 
   // if sync loading is required, the loading should start immediately when new image url is set or the actor is off stage
   // ( for on-stage actor with image url unchanged, resource loading is already finished)
-  if( ( !mImpl->mRenderer || imageURLValue) && IsSynchronousResourceLoading() )
-  {
-    DoSynchronousResourceLoading();
-  }
-
-  // remove old renderer if exit
-  if( mImpl->mRenderer )
+  if( imageURLValue && IsSynchronousResourceLoading() )
   {
-    if( actor ) //remove old renderer from actor
-    {
-      actor.RemoveRenderer( mImpl->mRenderer );
-    }
-    if( !oldImageUrl.empty() ) //clean old renderer from cache
-    {
-      CleanCache( oldImageUrl );
-    }
+    LoadResourceSynchronously();
   }
 
   NativeImage nativeImage = NativeImage::DownCast( mImage );
@@ -339,7 +388,7 @@ void ImageVisual::GetNaturalSize( Vector2& naturalSize ) const
   naturalSize = Vector2::ZERO;
 }
 
-Renderer ImageVisual::CreateRenderer() const
+void ImageVisual::CreateRenderer()
 {
   Geometry geometry;
   Shader shader;
@@ -348,7 +397,8 @@ Renderer ImageVisual::CreateRenderer() const
   // Renderer can't be shared between NativeImage and other image types.
   if( !mNativeFragmentShaderCode.empty() )
   {
-    return CreateNativeImageRenderer();
+    CreateNativeImageRenderer();
+    return;
   }
 
   if( !mImpl->mCustomShader )
@@ -378,12 +428,13 @@ Renderer ImageVisual::CreateRenderer() const
     }
   }
 
-  Renderer renderer = Renderer::New( geometry, shader );
-
-  return renderer;
+  mImpl->mRenderer = Renderer::New( geometry, shader );
+  // apply the texture set as well so it's only done in one place
+  DALI_ASSERT_DEBUG( mTextureSet );
+  mImpl->mRenderer.SetTextures( mTextureSet );
 }
 
-Renderer ImageVisual::CreateNativeImageRenderer() const
+void ImageVisual::CreateNativeImageRenderer()
 {
   Geometry geometry;
   Shader shader;
@@ -407,10 +458,10 @@ Renderer ImageVisual::CreateNativeImageRenderer() const
     }
   }
 
-  TextureSet textureSet = TextureSet::New();
-  Renderer renderer = Renderer::New( geometry, shader );
-  renderer.SetTextures( textureSet );
-  return renderer;
+  mImpl->mRenderer = Renderer::New( geometry, shader );
+  // apply the texture set as well so it's only done in one place
+  DALI_ASSERT_DEBUG( mTextureSet );
+  mImpl->mRenderer.SetTextures( mTextureSet );
 }
 
 
@@ -419,7 +470,7 @@ bool ImageVisual::IsSynchronousResourceLoading() const
   return mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
 }
 
-void ImageVisual::DoSynchronousResourceLoading()
+void ImageVisual::LoadResourceSynchronously()
 {
   if( !mImageUrl.empty() )
   {
@@ -429,64 +480,49 @@ void ImageVisual::DoSynchronousResourceLoading()
   }
 }
 
-Image ImageVisual::LoadImage( const std::string& url, bool synchronousLoading )
+void ImageVisual::CreateTextureSet( Vector4& textureRect, const std::string& url, bool synchronousLoading, bool attemptAtlasing )
 {
-  if( synchronousLoading )
-  {
-    if( !mPixels )
-    {
-      // use broken image
-      return VisualFactoryCache::GetBrokenVisualImage();
-    }
-    Atlas image = Atlas::New( mPixels.GetWidth(), mPixels.GetHeight(), mPixels.GetPixelFormat() );
-    image.Upload( mPixels, 0, 0 );
-    return image;
-  }
-  else
-  {
-    ResourceImage resourceImage = Dali::ResourceImage::New( url, mDesiredSize, mFittingMode, mSamplingMode );
-    resourceImage.LoadingFinishedSignal().Connect( this, &ImageVisual::OnImageLoaded );
-    return resourceImage;
-  }
-}
-
-TextureSet ImageVisual::CreateTextureSet( Vector4& textureRect, const std::string& url, bool synchronousLoading )
-{
-  TextureSet textureSet;
   textureRect = FULL_TEXTURE_RECT;
   if( synchronousLoading )
   {
     if( !mPixels )
     {
       // use broken image
-      textureSet = TextureSet::New();
-      TextureSetImage( textureSet, 0u, VisualFactoryCache::GetBrokenVisualImage() );
+      mTextureSet = TextureSet::New();
+      TextureSetImage( mTextureSet, 0u, VisualFactoryCache::GetBrokenVisualImage() );
     }
     else
     {
-      textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, mPixels );
-      mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
-      if( !textureSet ) // big image, no atlasing
+      if( attemptAtlasing )
+      {
+        mTextureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, mPixels );
+        mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
+      }
+      if( !mTextureSet ) // big image, no atlasing or atlasing failed
       {
         mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
-        Atlas image = Atlas::New( mPixels.GetWidth(), mPixels.GetHeight(), mPixels.GetPixelFormat() );
-        image.Upload( mPixels, 0, 0 );
-        textureSet = TextureSet::New();
-        TextureSetImage( textureSet, 0u, image );
+        Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, mPixels.GetPixelFormat(),
+                                        mPixels.GetWidth(), mPixels.GetHeight() );
+        texture.Upload( mPixels );
+        mTextureSet = TextureSet::New();
+        mTextureSet.SetTexture( 0u, texture );
       }
     }
   }
   else
   {
-    textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, url, mDesiredSize, mFittingMode, mSamplingMode );
-    mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
-    if( !textureSet ) // big image, no atlasing
+    if( attemptAtlasing )
+    {
+      mTextureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, url, mDesiredSize, mFittingMode, true, this );
+      mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
+    }
+    if( !mTextureSet ) // big image, no atlasing or atlasing failed
     {
       mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
       ResourceImage resourceImage = Dali::ResourceImage::New( url, mDesiredSize, mFittingMode, mSamplingMode );
       resourceImage.LoadingFinishedSignal().Connect( this, &ImageVisual::OnImageLoaded );
-      textureSet = TextureSet::New();
-      TextureSetImage( textureSet, 0u, resourceImage );
+      mTextureSet = TextureSet::New();
+      TextureSetImage( mTextureSet, 0u, resourceImage );
     }
   }
 
@@ -494,10 +530,8 @@ TextureSet ImageVisual::CreateTextureSet( Vector4& textureRect, const std::strin
   {
     Sampler sampler = Sampler::New();
     sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
-    textureSet.SetSampler( 0u, sampler );
+    mTextureSet.SetSampler( 0u, sampler );
   }
-
-  return textureSet;
 }
 
 void ImageVisual::InitializeRenderer( const std::string& imageUrl )
@@ -509,6 +543,7 @@ void ImageVisual::InitializeRenderer( const std::string& imageUrl )
 
   mImageUrl = imageUrl;
   mImpl->mRenderer.Reset();
+  mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
 
   if( !mImpl->mCustomShader &&
       ( strncasecmp( imageUrl.c_str(), HTTP_URL,  sizeof(HTTP_URL)  -1 ) != 0 ) && // ignore remote images
@@ -527,11 +562,10 @@ void ImageVisual::InitializeRenderer( const std::string& imageUrl )
     if( !mImpl->mRenderer ) // new renderer is needed
     {
       Vector4 atlasRect;
-      TextureSet textureSet = CreateTextureSet(atlasRect, imageUrl, IsSynchronousResourceLoading() );
-      Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
-      Shader shader( GetImageShader(mFactoryCache, mImpl->mFlags & Impl::IS_ATLASING_APPLIED, defaultWrapMode) );
-      mImpl->mRenderer = Renderer::New( geometry, shader );
-      mImpl->mRenderer.SetTextures( textureSet );
+      // texture set has to be created first as we need to know if atlasing succeeded or not
+      // when selecting the shader
+      CreateTextureSet( atlasRect, imageUrl, IsSynchronousResourceLoading(), true );
+      CreateRenderer();
 
       if( mImpl->mFlags & Impl::IS_ATLASING_APPLIED ) // the texture is packed inside atlas
       {
@@ -555,9 +589,9 @@ void ImageVisual::InitializeRenderer( const std::string& imageUrl )
   {
     // for custom shader or remote image, renderer is not cached and atlas is not applied
     mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
-    mImpl->mRenderer = CreateRenderer();
-    Image image = LoadImage( imageUrl, IsSynchronousResourceLoading() );
-    ApplyImageToSampler( image );
+    Vector4 atlasRect; // ignored in this case
+    CreateTextureSet( atlasRect, imageUrl, IsSynchronousResourceLoading(), false );
+    CreateRenderer();
   }
 }
 
@@ -565,7 +599,9 @@ void ImageVisual::InitializeRenderer( const Image& image )
 {
   mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
 
-  mImpl->mRenderer = CreateRenderer();
+  // don't reuse CreateTextureSet
+  mTextureSet = TextureSet::New();
+  CreateRenderer();
 
   if( image )
   {
@@ -573,8 +609,22 @@ void ImageVisual::InitializeRenderer( const Image& image )
   }
 }
 
+void ImageVisual::UploadCompleted()
+{
+  // Resource image is loaded. If weak handle is holding a placement actor, it is the time to add the renderer to actor.
+  Actor actor = mPlacementActor.GetHandle();
+  if( actor )
+  {
+    actor.AddRenderer( mImpl->mRenderer );
+    // reset the weak handle so that the renderer only get added to actor once
+    mPlacementActor.Reset();
+  }
+}
+
 void ImageVisual::DoSetOnStage( Actor& actor )
 {
+  mPlacementActor = actor;
+
   if( !mImageUrl.empty() )
   {
     InitializeRenderer( mImageUrl );
@@ -589,7 +639,11 @@ void ImageVisual::DoSetOnStage( Actor& actor )
     mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
   }
 
-  actor.AddRenderer( mImpl->mRenderer );
+  if( IsSynchronousResourceLoading() || !(mImpl->mFlags & Impl::IS_ATLASING_APPLIED) )
+  {
+    actor.AddRenderer( mImpl->mRenderer );
+    mPlacementActor.Reset();
+  }
 }
 
 void ImageVisual::DoSetOffStage( Actor& actor )
@@ -606,6 +660,8 @@ void ImageVisual::DoSetOffStage( Actor& actor )
     actor.RemoveRenderer( mImpl->mRenderer );
     mImpl->mRenderer.Reset();
   }
+  mPlacementActor.Reset();
+  mTextureSet.Reset(); // release texture when going offstage
 }
 
 void ImageVisual::DoCreatePropertyMap( Property::Map& map ) const
@@ -689,145 +745,16 @@ Shader ImageVisual::GetImageShader( VisualFactoryCache& factoryCache, bool atlas
   return shader;
 }
 
-void ImageVisual::SetImage( Actor& actor, const std::string& imageUrl, ImageDimensions size, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
-{
-  if( mImageUrl != imageUrl )
-  {
-    std::string oldImageUrl = mImageUrl;
-    mImageUrl = imageUrl;
-    mDesiredSize = size;
-    mFittingMode = fittingMode;
-    mSamplingMode = samplingMode;
-    mImage.Reset();
-
-    if( IsSynchronousResourceLoading() )
-    {
-      DoSynchronousResourceLoading();
-    }
-
-    if( mImpl->mRenderer )
-    {
-      if( GetIsFromCache() ) // if renderer is from cache, remove the old one
-      {
-        //remove old renderer
-        if( actor )
-        {
-          actor.RemoveRenderer( mImpl->mRenderer );
-        }
-
-        //clean the cache
-        if( !oldImageUrl.empty() )
-        {
-          CleanCache(oldImageUrl);
-        }
-
-        if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
-        {
-          SetOnStage(actor);
-        }
-      }
-      else // if renderer is not from cache, reuse the same renderer and only change the texture
-      {
-        Image image = LoadImage( imageUrl, IsSynchronousResourceLoading() );
-        ApplyImageToSampler( image );
-      }
-    }
-  }
-}
-
-void ImageVisual::SetImage( Actor& actor, const Image& image )
-{
-  if( mImage != image )
-  {
-    NativeImage newNativeImage = NativeImage::DownCast( image );
-    bool newRendererFlag = true;
-
-    if( newNativeImage && !mNativeImageFlag )
-    {
-      SetNativeFragmentShaderCode( newNativeImage );
-    }
-
-    if( ( newNativeImage && mNativeImageFlag ) || ( !newNativeImage && !mNativeImageFlag ) )
-    {
-      newRendererFlag = false;
-    }
-
-    if( newNativeImage )
-    {
-      mNativeImageFlag = true;
-    }
-    else
-    {
-      mNativeFragmentShaderCode.clear();
-      mNativeImageFlag = false;
-    }
-
-    mImage = image;
-
-    if( mImpl->mRenderer )
-    {
-      // if renderer is from cache, remove the old one, and create new renderer
-      if( GetIsFromCache() )
-      {
-        //remove old renderer
-        if( actor )
-        {
-          actor.RemoveRenderer( mImpl->mRenderer );
-        }
-
-        //clean the cache
-        if( !mImageUrl.empty() )
-        {
-          CleanCache(mImageUrl);
-        }
-        mImageUrl.clear();
-
-        if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
-        {
-          SetOnStage(actor);
-        }
-      }
-      // if input image is nativeImage and mImage is regular image or the reverse, remove the old one, and create new renderer
-      else if( newRendererFlag )
-      {
-        //remove old renderer
-        if( actor )
-        {
-          actor.RemoveRenderer( mImpl->mRenderer );
-        }
-
-        if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
-        {
-          SetOnStage(actor);
-        }
-      }
-      else // if renderer is not from cache, reuse the same renderer and only change the texture
-      {
-        ApplyImageToSampler( image );
-      }
-    }
-
-    mImageUrl.clear();
-    mDesiredSize = ImageDimensions();
-    mFittingMode = FittingMode::DEFAULT;
-    mSamplingMode = SamplingMode::DEFAULT;
-  }
-}
-
 void ImageVisual::ApplyImageToSampler( const Image& image )
 {
   if( image )
   {
-    TextureSet textureSet = mImpl->mRenderer.GetTextures();
-    if( !textureSet )
-    {
-      textureSet = TextureSet::New();
-      mImpl->mRenderer.SetTextures( textureSet );
-    }
-    TextureSetImage( textureSet, 0u, image );
+    DALI_ASSERT_DEBUG( mTextureSet ); // texture set should always exist by this time
+
+    TextureSetImage( mTextureSet, 0u, image );
     Sampler sampler = Sampler::New();
     sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
-    textureSet.SetSampler( 0u, sampler );
+    mTextureSet.SetSampler( 0u, sampler );
   }
 }
 
@@ -845,20 +772,21 @@ void ImageVisual::OnImageLoaded( ResourceImage image )
 
 void ImageVisual::CleanCache(const std::string& url)
 {
-  TextureSet textureSet = mImpl->mRenderer.GetTextures();
-
-  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( IsFromCache() )
   {
-    Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index );
-    atlasRectValue.Get( atlasRect );
-  }
+    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 );
+    }
 
-  mImpl->mRenderer.Reset();
-  if( mFactoryCache.CleanRendererCache( url ) && index != Property::INVALID_INDEX )
-  {
-    mFactoryCache.GetAtlasManager()->Remove( textureSet, atlasRect );
+    mImpl->mRenderer.Reset();
+    if( mFactoryCache.CleanRendererCache( url ) && index != Property::INVALID_INDEX )
+    {
+      mFactoryCache.GetAtlasManager()->Remove( mTextureSet, atlasRect );
+    }
   }
 }