Cache for the synchronous loading. 41/267041/24
authorseungho <sbsh.baek@samsung.com>
Wed, 24 Nov 2021 11:06:06 +0000 (20:06 +0900)
committerseungho <sbsh.baek@samsung.com>
Fri, 10 Dec 2021 04:31:41 +0000 (13:31 +0900)
Change-Id: Id793b3bcb72639d3c7a163196e29bdcf7d94718e
Signed-off-by: seungho <sbsh.baek@samsung.com>
automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp
dali-toolkit/internal/visuals/image-atlas-manager.cpp
dali-toolkit/internal/visuals/image-atlas-manager.h
dali-toolkit/internal/visuals/texture-manager-impl.cpp
dali-toolkit/internal/visuals/texture-manager-impl.h

index 04c51af..1553f97 100644 (file)
@@ -71,7 +71,8 @@ public:
   TestObserver()
   : mCompleteType( CompleteType::NOT_COMPLETED ),
     mLoaded(false),
-    mObserverCalled(false)
+    mObserverCalled(false),
+    mTextureSet()
   {
   }
 
@@ -81,6 +82,7 @@ public:
     mCompleteType = CompleteType::UPLOAD_COMPLETE;
     mLoaded = loadSuccess;
     mObserverCalled = true;
+    mTextureSet = textureSet;
   }
 
   virtual void LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied ) override
@@ -93,6 +95,7 @@ public:
   CompleteType mCompleteType;
   bool mLoaded;
   bool mObserverCalled;
+  TextureSet mTextureSet;
 };
 
 
@@ -590,3 +593,305 @@ int UtcTextureManagerUseInvalidMask(void)
 
   END_TEST;
 }
+
+int UtcTextureManagerSynchronousLoadingFail(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcTextureManagerSynchronousLoadingFail" );
+
+  TextureManager textureManager; // Create new texture manager
+
+  std::string maskname("");
+  TextureManager::MaskingDataPointer maskInfo = nullptr;
+  maskInfo.reset(new TextureManager::MaskingData());
+  maskInfo->mAlphaMaskUrl = maskname;
+  maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID;
+  maskInfo->mCropToMask = true;
+  maskInfo->mContentScaleFactor = 1.0f;
+
+  std::string filename("dummy");
+  auto textureId( TextureManager::INVALID_TEXTURE_ID );
+  Vector4 atlasRect( 0.f, 0.f, 0.f, 0.f );
+  Dali::ImageDimensions atlasRectSize( 0,0 );
+  bool atlasingStatus(false);
+  bool loadingStatus(false);
+  auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  ImageAtlasManagerPtr atlasManager = nullptr;
+  Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+  // load image synchronously.
+  TestObserver observer;
+  TextureSet textureSet = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    true, // synchronous loading.
+    textureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    loadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &observer,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION);
+  DALI_TEST_CHECK(!textureSet);  // texture loading fail.
+  DALI_TEST_CHECK(textureId == TextureManager::INVALID_TEXTURE_ID); // invalid texture id is returned.
+
+  END_TEST;
+}
+
+int UtcTextureManagerCachingSynchronousLoading(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcTextureManagerCachingSynchronousLoading" );
+
+  TextureManager textureManager; // Create new texture manager
+
+  std::string filename( TEST_IMAGE_FILE_NAME );
+
+  std::string maskname("");
+  TextureManager::MaskingDataPointer maskInfo = nullptr;
+  maskInfo.reset(new TextureManager::MaskingData());
+  maskInfo->mAlphaMaskUrl = maskname;
+  maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID;
+  maskInfo->mCropToMask = true;
+  maskInfo->mContentScaleFactor = 1.0f;
+
+  Vector4 atlasRect( 0.f, 0.f, 0.f, 0.f );
+  Dali::ImageDimensions atlasRectSize( 0,0 );
+  bool atlasingStatus(false);
+  bool loadingStatus(false);
+  auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  ImageAtlasManagerPtr atlasManager = nullptr;
+  Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+  // load image synchronously.
+  TestObserver observer;
+  auto textureId( TextureManager::INVALID_TEXTURE_ID );
+  TextureSet textureSet = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    true, // synchronous loading.
+    textureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    loadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &observer,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION);
+  DALI_TEST_CHECK(textureSet);  // texture is loaded.
+
+  // observer isn't called in synchronous loading.
+  DALI_TEST_EQUALS(observer.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer.mObserverCalled, false, TEST_LOCATION);
+
+
+  // load same image asynchronously.
+  TestObserver asyncObserver;
+  auto asyncTextureId( TextureManager::INVALID_TEXTURE_ID );
+  loadingStatus = false;
+  TextureSet asyncTextureSet = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    false, // asynchronous loading.
+    asyncTextureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    loadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &asyncObserver,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(asyncTextureId, textureId, TEST_LOCATION); // texture is loaded.
+  DALI_TEST_EQUALS(loadingStatus, false, TEST_LOCATION);
+  DALI_TEST_CHECK(asyncTextureSet);  // Cached texture.
+
+  // observer is directly called because textureSet is retrieved by cache.
+  DALI_TEST_EQUALS(asyncObserver.mLoaded, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver.mObserverCalled, true, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcTextureManagerAsyncSyncAsync(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcTextureManagerAsyncSyncAsync" );
+
+  TextureManager textureManager; // Create new texture manager
+
+  std::string filename( TEST_IMAGE_FILE_NAME );
+
+  std::string maskname("");
+  TextureManager::MaskingDataPointer maskInfo = nullptr;
+  maskInfo.reset(new TextureManager::MaskingData());
+  maskInfo->mAlphaMaskUrl = maskname;
+  maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID;
+  maskInfo->mCropToMask = true;
+  maskInfo->mContentScaleFactor = 1.0f;
+
+  Vector4 atlasRect( 0.f, 0.f, 0.f, 0.f );
+  Dali::ImageDimensions atlasRectSize( 0,0 );
+  bool atlasingStatus(false);
+  auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  ImageAtlasManagerPtr atlasManager = nullptr;
+  Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+  // load image asynchronously.
+  TestObserver asyncObserver1;
+  auto asyncTextureId1( TextureManager::INVALID_TEXTURE_ID );
+  bool asyncLoadingStatus1 = false;
+  TextureSet asyncTextureSet1 = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    false, // asynchronous loading.
+    asyncTextureId1,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    asyncLoadingStatus1,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &asyncObserver1,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(asyncLoadingStatus1, true, TEST_LOCATION); // texture is loading now.
+  DALI_TEST_CHECK(!asyncTextureSet1);  // texture is not loaded yet.
+
+  // observer is still not called.
+  DALI_TEST_EQUALS(asyncObserver1.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver1.mObserverCalled, false, TEST_LOCATION);
+
+
+  // load same image synchronously just after asynchronous loading.
+  TestObserver syncObserver;
+  auto textureId( TextureManager::INVALID_TEXTURE_ID );
+  bool syncLoadingStatus = false;
+  TextureSet syncTextureSet = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    true, // synchronous loading.
+    textureId,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    syncLoadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &syncObserver,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(asyncTextureId1, textureId, TEST_LOCATION); // texture is loaded.
+  DALI_TEST_EQUALS(syncLoadingStatus, false, TEST_LOCATION); // texture is loaded.
+  DALI_TEST_CHECK(syncTextureSet);  // texture is loaded.
+
+  // syncObserver isn't called in synchronous loading.
+  DALI_TEST_EQUALS(syncObserver.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(syncObserver.mObserverCalled, false, TEST_LOCATION);
+
+  // asyncObserver1 is still not called too.
+  DALI_TEST_EQUALS(asyncObserver1.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver1.mObserverCalled, false, TEST_LOCATION);
+
+
+
+  // load image asynchronously.
+  TestObserver asyncObserver2;
+  auto asyncTextureId2( TextureManager::INVALID_TEXTURE_ID );
+  bool asyncLoadingStatus2 = false;
+  TextureSet asyncTextureSet2 = textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    false, // asynchronous loading.
+    asyncTextureId2,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    asyncLoadingStatus2,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &asyncObserver2,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply
+  );
+
+  DALI_TEST_EQUALS(asyncLoadingStatus2, false, TEST_LOCATION); // texture is loaded by previous sync request
+  DALI_TEST_CHECK(asyncTextureSet2); // texture is loaded
+  DALI_TEST_CHECK(asyncTextureSet2 == syncTextureSet);  // check loaded two texture is same.
+
+  // observer is called synchronously because the texture is cached.
+  DALI_TEST_EQUALS(asyncObserver2.mLoaded, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver2.mObserverCalled, true, TEST_LOCATION);
+
+  asyncObserver2.mLoaded = false;
+  asyncObserver2.mObserverCalled = false;
+
+  application.SendNotification();
+  application.Render();
+
+  // Requested asynchronous loading at first is finished now and async observer is called now.
+  DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+  DALI_TEST_EQUALS(asyncObserver1.mLoaded, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver1.mObserverCalled, true, TEST_LOCATION);
+  DALI_TEST_CHECK(asyncObserver1.mTextureSet == asyncTextureSet2);  // check loaded two texture is same.
+
+  // asyncObserver2 was already called so it isn't called here.
+  DALI_TEST_EQUALS(asyncObserver2.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(asyncObserver2.mObserverCalled, false, TEST_LOCATION);
+
+  END_TEST;
+}
index a8de5a8..577a433 100644 (file)
@@ -46,6 +46,23 @@ ImageAtlasManager::~ImageAtlasManager()
 {
 }
 
+bool ImageAtlasManager::CheckAtlasAvailable(const VisualUrl& url, const ImageDimensions& size) const
+{
+  ImageDimensions dimensions = size;
+  ImageDimensions zero;
+  if(size == zero)
+  {
+    dimensions = Dali::GetClosestImageSize(url.GetUrl());
+  }
+
+  // big image, atlasing is not applied
+  if(static_cast<uint32_t>(dimensions.GetWidth()) * static_cast<uint32_t>(dimensions.GetHeight()) > MAX_ITEM_AREA || dimensions.GetWidth() > DEFAULT_ATLAS_SIZE || dimensions.GetHeight() > DEFAULT_ATLAS_SIZE)
+  {
+    return false;
+  }
+  return true;
+}
+
 TextureSet ImageAtlasManager::Add(Vector4&             textureRect,
                                   const VisualUrl&     url,
                                   ImageDimensions&     size,
index b8f5a11..d2630c1 100644 (file)
@@ -52,6 +52,15 @@ public:
   ImageAtlasManager();
 
   /**
+   * @brief Check whether the image of url could be Atlas or not.
+   *
+   * @param [in] url The URL of the resource image file to use.
+   * @param [in] size The width and height to fit the loaded image to.
+   * @return True if the image could be Atlas.
+   */
+  bool CheckAtlasAvailable(const VisualUrl& url, const ImageDimensions& size) const;
+
+  /**
    * @brief Add an image to the atlas.
    *
    * @note To make the atlasing efficient, an valid size should be provided.
index 093954e..49867c4 100644 (file)
@@ -122,8 +122,10 @@ TextureManager::MaskingData::MaskingData()
 }
 
 TextureManager::TextureManager()
-: mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); }),
-  mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); }),
+: mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]()
+                     { return AsyncLoadingHelper(*this); }),
+  mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]()
+                      { return AsyncLoadingHelper(*this); }),
   mExternalTextures(),
   mLifecycleObservers(),
   mLoadQueue(),
@@ -173,7 +175,7 @@ TextureSet TextureManager::LoadAnimatedImageTexture(
   else
   {
     auto preMultiply                    = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-    textureId                           = RequestLoadInternal(animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, TextureManager::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex);
+    textureId                           = RequestLoadInternal(animatedImageLoading.GetUrl(), INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, TextureManager::NO_ATLAS, false, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiply, animatedImageLoading, frameIndex, false);
     TextureManager::LoadState loadState = GetTextureStateInternal(textureId);
     if(loadState == TextureManager::LoadState::UPLOADED)
     {
@@ -220,7 +222,7 @@ Devel::PixelBuffer TextureManager::LoadPixelBuffer(
   }
   else
   {
-    RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u);
+    RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false);
   }
 
   return pixelBuffer;
@@ -251,113 +253,100 @@ TextureSet TextureManager::LoadTexture(
       }
     }
   }
-  else if(synchronousLoading)
+  else
   {
-    PixelData data;
-    if(url.IsValid())
+    // For Atlas
+    if(synchronousLoading && atlasingStatus && imageAtlasManager->CheckAtlasAvailable(url, desiredSize))
     {
-      Devel::PixelBuffer pixelBuffer;
-      if(url.IsBufferResource())
-      {
-        const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(url.GetUrl());
-        if(encodedImageBuffer)
-        {
-          pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection);
-        }
-      }
-      else
-      {
-        pixelBuffer = LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection);
-      }
+      Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection);
+
       if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
       {
-        Devel::PixelBuffer maskPixelBuffer = LoadImageFromFile(maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true);
+        Devel::PixelBuffer maskPixelBuffer = LoadImageSynchronously(maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true);
         if(maskPixelBuffer)
         {
           pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask);
         }
       }
+
+      PixelData data;
       if(pixelBuffer)
       {
         PreMultiply(pixelBuffer, preMultiplyOnLoad);
         data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
+
+        if(data)
+        {
+          textureSet = imageAtlasManager->Add(textureRect, data);
+          if(textureSet)
+          {
+            textureRectSize.SetWidth(data.GetWidth());
+            textureRectSize.SetHeight(data.GetHeight());
+          }
+        }
+        else
+        {
+          DALI_LOG_ERROR("TextureManager::LoadTexture: Synchronous Texture loading with atlasing is failed.\n");
+        }
       }
-    }
-    if(!data)
-    {
-      DALI_LOG_ERROR("TextureManager::LoadTexture: Synchronous loading is failed\n");
-    }
-    else
-    {
-      if(atlasingStatus) // attempt atlasing
-      {
-        textureSet = imageAtlasManager->Add(textureRect, data);
-      }
-      if(!textureSet) // big image, no atlasing or atlasing failed
-      {
-        atlasingStatus  = false;
-        Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(), data.GetWidth(), data.GetHeight());
-        texture.Upload(data);
-        textureSet = TextureSet::New();
-        textureSet.SetTexture(0u, texture);
-      }
-      else
+      if(!textureSet)
       {
-        textureRectSize.SetWidth(data.GetWidth());
-        textureRectSize.SetHeight(data.GetHeight());
+        atlasingStatus = false;
       }
     }
-  }
-  else
-  {
-    loadingStatus = true;
-    if(atlasingStatus)
-    {
-      textureSet = imageAtlasManager->Add(textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver);
-    }
-    if(!textureSet) // big image, no atlasing or atlasing failed
+
+    if(!textureSet)
     {
-      atlasingStatus = false;
-      if(!maskInfo || !maskInfo->mAlphaMaskUrl.IsValid())
+      loadingStatus = true;
+      if(atlasingStatus)
       {
-        textureId = RequestLoad(url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad);
+        textureSet = imageAtlasManager->Add(textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver);
       }
-      else
+      if(!textureSet) // big image, no atlasing or atlasing failed
       {
-        maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl);
-        textureId              = RequestLoad(url,
-                                maskInfo->mAlphaMaskId,
-                                maskInfo->mContentScaleFactor,
-                                desiredSize,
-                                fittingMode,
-                                samplingMode,
-                                TextureManager::NO_ATLAS,
-                                maskInfo->mCropToMask,
-                                textureObserver,
-                                orientationCorrection,
-                                reloadPolicy,
-                                preMultiplyOnLoad);
-      }
+        atlasingStatus = false;
+        if(!maskInfo || !maskInfo->mAlphaMaskUrl.IsValid())
+        {
+          textureId = RequestLoad(url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad, synchronousLoading);
+        }
+        else
+        {
+          maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, synchronousLoading);
+          textureId              = RequestLoad(url,
+                                  maskInfo->mAlphaMaskId,
+                                  maskInfo->mContentScaleFactor,
+                                  desiredSize,
+                                  fittingMode,
+                                  samplingMode,
+                                  TextureManager::NO_ATLAS,
+                                  maskInfo->mCropToMask,
+                                  textureObserver,
+                                  orientationCorrection,
+                                  reloadPolicy,
+                                  preMultiplyOnLoad,
+                                  synchronousLoading);
+        }
+
+        TextureManager::LoadState loadState = GetTextureStateInternal(textureId);
+        if(loadState == TextureManager::LoadState::UPLOADED)
+        {
+          // UploadComplete has already been called - keep the same texture set
+          textureSet = GetTextureSet(textureId);
+        }
 
-      TextureManager::LoadState loadState = GetTextureStateInternal(textureId);
-      if(loadState == TextureManager::LoadState::UPLOADED)
+        // If we are loading the texture, or waiting for the ready signal handler to complete, inform
+        // caller that they need to wait.
+        loadingStatus = (loadState == TextureManager::LoadState::LOADING ||
+                         loadState == TextureManager::LoadState::WAITING_FOR_MASK ||
+                         loadState == TextureManager::LoadState::MASK_APPLYING ||
+                         loadState == TextureManager::LoadState::MASK_APPLIED ||
+                         loadState == TextureManager::LoadState::NOT_STARTED ||
+                         mQueueLoadFlag);
+      }
+      else
       {
-        // UploadComplete has already been called - keep the same texture set
-        textureSet = GetTextureSet(textureId);
+        textureRectSize = desiredSize;
       }
-
-      // If we are loading the texture, or waiting for the ready signal handler to complete, inform
-      // caller that they need to wait.
-      loadingStatus = (loadState == TextureManager::LoadState::LOADING ||
-                       loadState == TextureManager::LoadState::WAITING_FOR_MASK ||
-                       loadState == TextureManager::LoadState::MASK_APPLYING ||
-                       loadState == TextureManager::LoadState::MASK_APPLIED ||
-                       loadState == TextureManager::LoadState::NOT_STARTED ||
-                       mQueueLoadFlag);
-    }
-    else
-    {
-      textureRectSize = desiredSize;
     }
   }
 
@@ -368,6 +357,11 @@ TextureSet TextureManager::LoadTexture(
     textureSet.SetSampler(0u, sampler);
   }
 
+  if(synchronousLoading)
+  {
+    loadingStatus = false;
+  }
+
   return textureSet;
 }
 
@@ -380,9 +374,10 @@ TextureManager::TextureId TextureManager::RequestLoad(
   TextureUploadObserver*          observer,
   bool                            orientationCorrection,
   TextureManager::ReloadPolicy    reloadPolicy,
-  TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
+  TextureManager::MultiplyOnLoad& preMultiplyOnLoad,
+  bool                            synchronousLoading)
 {
-  return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u);
+  return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
 }
 
 TextureManager::TextureId TextureManager::RequestLoad(
@@ -397,16 +392,17 @@ TextureManager::TextureId TextureManager::RequestLoad(
   TextureUploadObserver*          observer,
   bool                            orientationCorrection,
   TextureManager::ReloadPolicy    reloadPolicy,
-  TextureManager::MultiplyOnLoad& preMultiplyOnLoad)
+  TextureManager::MultiplyOnLoad& preMultiplyOnLoad,
+  bool                            synchronousLoading)
 {
-  return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u);
+  return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
 }
 
-TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl)
+TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl, bool synchronousLoading)
 {
   // Use the normal load procedure to get the alpha mask.
   auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-  return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u);
+  return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
 }
 
 TextureManager::TextureId TextureManager::RequestLoadInternal(
@@ -424,7 +420,8 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
   TextureManager::ReloadPolicy    reloadPolicy,
   TextureManager::MultiplyOnLoad& preMultiplyOnLoad,
   Dali::AnimatedImageLoading      animatedImageLoading,
-  uint32_t                        frameIndex)
+  uint32_t                        frameIndex,
+  bool                            synchronousLoading)
 {
   // First check if the requested Texture is cached.
   bool isAnimatedImage = (animatedImageLoading) ? true : false;
@@ -457,37 +454,30 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId);
   }
 
-  // Check if the requested Texture exist in Encoded Buffer
-  // This mean, that buffer is not cached, and need to be decoded.
-  if(textureId == INVALID_TEXTURE_ID && VisualUrl::BUFFER == url.GetProtocolType())
+  if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required
   {
-    std::string location = url.GetLocation();
-    if(location.size() > 0u)
+    if(VisualUrl::BUFFER == url.GetProtocolType())
     {
-      TextureId                 targetId           = std::stoi(location);
-      const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(targetId);
-      if(encodedImageBuffer)
+      std::string location = url.GetLocation();
+      if(location.size() > 0u)
       {
-        textureId = targetId;
-
-        // Increase EncodedImageBuffer reference during it contains mTextureInfoContainer.
-        UseExternalResource(url.GetUrl());
-
-        // Insert this buffer at mTextureInfoContainer.
-        // This buffer will decode at ImageLoaderThread.
-        bool preMultiply = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
-        mTextureInfoContainer.push_back(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex));
-        cacheIndex = mTextureInfoContainer.size() - 1u;
+        TextureId                 targetId           = std::stoi(location);
+        const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(targetId);
+        if(encodedImageBuffer)
+        {
+          textureId = targetId;
 
-        DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New buffered texture, cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId);
+          // Increase EncodedImageBuffer reference during it contains mTextureInfoContainer.
+          UseExternalResource(url.GetUrl());
+        }
       }
     }
-  }
 
-  if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required
-  {
-    // We need a new Texture.
-    textureId        = GenerateUniqueTextureId();
+    if(textureId == INVALID_TEXTURE_ID)
+    {
+      textureId = GenerateUniqueTextureId();
+    }
+
     bool preMultiply = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
     mTextureInfoContainer.push_back(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex));
     cacheIndex = mTextureInfoContainer.size() - 1u;
@@ -518,48 +508,101 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
     textureInfo.loadState = TextureManager::LoadState::NOT_STARTED;
   }
 
-  // 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)
+  if(!synchronousLoading)
   {
-    case TextureManager::LoadState::LOAD_FAILED: // Failed notifies observer which then stops observing.
-    case TextureManager::LoadState::NOT_STARTED:
-    {
-      LoadOrQueueTexture(textureInfo, observer); // If called inside NotifyObservers, queues until afterwards
-      break;
-    }
-    case TextureManager::LoadState::LOADING:
-    case TextureManager::LoadState::WAITING_FOR_MASK:
-    case TextureManager::LoadState::MASK_APPLYING:
-    case TextureManager::LoadState::MASK_APPLIED:
+    // 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)
     {
-      ObserveTexture(textureInfo, observer);
-      break;
-    }
-    case TextureManager::LoadState::UPLOADED:
-    {
-      if(observer)
+      case TextureManager::LoadState::LOAD_FAILED: // Failed notifies observer which then stops observing.
+      case TextureManager::LoadState::NOT_STARTED:
+      {
+        LoadOrQueueTexture(textureInfo, observer); // If called inside NotifyObservers, queues until afterwards
+        break;
+      }
+      case TextureManager::LoadState::LOADING:
+      case TextureManager::LoadState::WAITING_FOR_MASK:
+      case TextureManager::LoadState::MASK_APPLYING:
+      case TextureManager::LoadState::MASK_APPLIED:
       {
-        LoadOrQueueTexture(textureInfo, observer);
+        ObserveTexture(textureInfo, observer);
+        break;
+      }
+      case TextureManager::LoadState::UPLOADED:
+      {
+        if(observer)
+        {
+          LoadOrQueueTexture(textureInfo, observer);
+        }
+        break;
+      }
+      case TextureManager::LoadState::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::LoadState::LOADING;
+        ObserveTexture(textureInfo, observer);
+        break;
+      }
+      case TextureManager::LoadState::LOAD_FINISHED:
+      {
+        // Loading has already completed.
+        if(observer && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER)
+        {
+          LoadOrQueueTexture(textureInfo, observer);
+        }
+        break;
       }
-      break;
     }
-    case TextureManager::LoadState::CANCELLED:
+  }
+  else
+  {
+    // If the image is already finished to load, use cached texture.
+    // We don't need to consider Observer becaouse this is synchronous loading.
+    if(textureInfo.loadState == TextureManager::LoadState::UPLOADED ||
+       textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED)
     {
-      // 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::LoadState::LOADING;
-      ObserveTexture(textureInfo, observer);
-      break;
+      return textureId;
     }
-    case TextureManager::LoadState::LOAD_FINISHED:
+    else
     {
-      // Loading has already completed.
-      if(observer && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER)
+      Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection);;
+
+      if(!pixelBuffer)
       {
-        LoadOrQueueTexture(textureInfo, observer);
+        // If pixelBuffer loading is failed in synchronously, call Remove() method.
+        Remove(textureId, nullptr);
+        return INVALID_TEXTURE_ID;
+      }
+
+      if(storageType == StorageType::KEEP_PIXEL_BUFFER) // For the mask image loading.
+      {
+        textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data
+        textureInfo.loadState   = LoadState::LOAD_FINISHED;
+      }
+      else // For the image loading.
+      {
+        if(maskTextureId != INVALID_TEXTURE_ID)
+        {
+          int maskCacheIndex = GetCacheIndexFromId(maskTextureId);
+          if(maskCacheIndex != INVALID_CACHE_INDEX)
+          {
+            Devel::PixelBuffer maskPixelBuffer = mTextureInfoContainer[maskCacheIndex].pixelBuffer;
+            if(maskPixelBuffer)
+            {
+              pixelBuffer.ApplyMask(maskPixelBuffer, contentScale, cropToMask);
+            }
+          }
+          else
+          {
+            DALI_LOG_ERROR("Mask image is not stored in cache.\n");
+          }
+        }
+        PreMultiply(pixelBuffer, preMultiplyOnLoad);
+
+        // Upload texture
+        UploadTexture(pixelBuffer, textureInfo);
       }
-      break;
     }
   }
 
@@ -687,6 +730,28 @@ TextureManager::LoadState TextureManager::GetTextureStateInternal(TextureId text
   return loadState;
 }
 
+Devel::PixelBuffer TextureManager::LoadImageSynchronously(const VisualUrl&         url,
+                                                          const ImageDimensions    desiredSize,
+                                                          FittingMode::Type        fittingMode,
+                                                          Dali::SamplingMode::Type samplingMode,
+                                                          bool                     orientationCorrection)
+{
+  Devel::PixelBuffer pixelBuffer;
+  if(url.IsBufferResource())
+  {
+    const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(url.GetUrl());
+    if(encodedImageBuffer)
+    {
+      pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection);
+    }
+  }
+  else
+  {
+    pixelBuffer = LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection);
+  }
+  return pixelBuffer;
+}
+
 TextureSet TextureManager::GetTextureSet(TextureId textureId)
 {
   TextureSet textureSet; // empty handle
@@ -759,7 +824,7 @@ std::string TextureManager::AddExternalEncodedImageBuffer(const EncodedImageBuff
     {
       // If same buffer added, increase reference count and return.
       elem.referenceCount++;
-      return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId));;
+      return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId));
     }
   }
   TextureManager::EncodedBufferTextureInfo info(GenerateUniqueTextureId(), encodedImageBuffer);
@@ -807,7 +872,7 @@ EncodedImageBuffer TextureManager::RemoveExternalEncodedImageBuffer(const std::s
       std::string location = VisualUrl::GetLocation(url);
       if(location.size() > 0u)
       {
-        TextureId id = std::stoi(location);
+        TextureId  id  = std::stoi(location);
         const auto end = mEncodedBufferTextures.end();
         for(auto iter = mEncodedBufferTextures.begin(); iter != end; ++iter)
         {
@@ -1049,7 +1114,10 @@ void TextureManager::PostLoad(TextureInfo& textureInfo, Devel::PixelBuffer& pixe
       // If there is a mask texture ID associated with this texture, then apply the mask
       // if it's already loaded. If it hasn't, and the mask is still loading,
       // wait for the mask to finish loading.
-      if(textureInfo.maskTextureId != INVALID_TEXTURE_ID)
+      // note, If the texture is already uploaded synchronously during loading,
+      // we don't need to apply mask.
+      if(textureInfo.loadState != LoadState::UPLOADED &&
+         textureInfo.maskTextureId != INVALID_TEXTURE_ID)
       {
         if(textureInfo.loadState == LoadState::MASK_APPLYING)
         {
@@ -1153,7 +1221,7 @@ void TextureManager::ApplyMask(TextureInfo& textureInfo, TextureId maskTextureId
 
 void TextureManager::UploadTexture(Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo)
 {
-  if(textureInfo.useAtlas != USE_ATLAS)
+  if(textureInfo.loadState != LoadState::UPLOADED && textureInfo.useAtlas != USE_ATLAS)
   {
     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "  TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId);
 
index 48eea78..7730d28 100644 (file)
@@ -75,9 +75,10 @@ public:
    */
   enum class StorageType : uint8_t
   {
-    KEEP_PIXEL_BUFFER,
-    RETURN_PIXEL_BUFFER,
-    UPLOAD_TO_TEXTURE
+    KEEP_PIXEL_BUFFER,    ///< Keep loaded pixel buffer inside of texture manager without making texture. This could be used for inside pixel process like mask image.
+    RETURN_PIXEL_BUFFER,  ///< Return loaded pixel buffer without making texture.
+                          ///  Because a pixel buffer cannot be used multiple texture, this pixel buffer only cached during loading, and is removed after loading is finished.
+    UPLOAD_TO_TEXTURE     ///< Loaded image will be uploaded to texture and the texture will be returned.
   };
 
   /**
@@ -177,7 +178,6 @@ public:
    *
    * @return                          The texture set containing the frame of animated image, or empty if still loading.
    */
-
   TextureSet LoadAnimatedImageTexture(Dali::AnimatedImageLoading animatedImageLoading,
                                       uint32_t                   frameIndex,
                                       Dali::SamplingMode::Type   samplingMode,
@@ -206,7 +206,6 @@ public:
    *
    * @return                          The pixel buffer containing the image, or empty if still loading.
    */
-
   Devel::PixelBuffer LoadPixelBuffer(const VisualUrl&                url,
                                      Dali::ImageDimensions           desiredSize,
                                      Dali::FittingMode::Type         fittingMode,
@@ -253,7 +252,6 @@ public:
    *
    * @return                          The texture set containing the image, or empty if still loading.
    */
-
   TextureSet LoadTexture(const VisualUrl&             url,
                          Dali::ImageDimensions        desiredSize,
                          Dali::FittingMode::Type      fittingMode,
@@ -293,6 +291,7 @@ public:
    * @param[in] orientationCorrection Whether to rotate image to match embedded orientation data
    * @param[in] reloadPolicy          Forces a reload of the texture even if already cached
    * @param[in,out] preMultiplyOnLoad     True if the image color should be multiplied by it's alpha. Set to false if the image has no alpha channel
+   * @param[in] synchronousLoading    true if the frame should be loaded synchronously
    * @return                          A TextureId to use as a handle to reference this Texture
    */
   TextureId RequestLoad(const VisualUrl&             url,
@@ -303,7 +302,8 @@ public:
                         TextureUploadObserver*       observer,
                         bool                         orientationCorrection,
                         TextureManager::ReloadPolicy reloadPolicy,
-                        MultiplyOnLoad&              preMultiplyOnLoad);
+                        MultiplyOnLoad&              preMultiplyOnLoad,
+                        bool                         synchronousLoading = false);
 
   /**
    * @brief Requests an image load of the given URL, when the texture has
@@ -335,6 +335,7 @@ public:
    * @param[in] reloadPolicy          Forces a reload of the texture even if already cached
    * @param[in] preMultiplyOnLoad     True if the image color should be multiplied by it's alpha. Set to false if the
    *                                  image has no alpha channel
+   * @param[in] synchronousLoading    true if the frame should be loaded synchronously
    * @return                          A TextureId to use as a handle to reference this Texture
    */
   TextureId RequestLoad(const VisualUrl&             url,
@@ -348,13 +349,15 @@ public:
                         TextureUploadObserver*       observer,
                         bool                         orientationCorrection,
                         TextureManager::ReloadPolicy reloadPolicy,
-                        MultiplyOnLoad&              preMultiplyOnLoad);
+                        MultiplyOnLoad&              preMultiplyOnLoad,
+                        bool                         synchronousLoading = false);
 
   /**
    * Requests a masking image to be loaded. This mask is not uploaded to GL,
    * instead, it is stored in CPU memory, and can be used for CPU blending.
    */
-  TextureId RequestMaskLoad(const VisualUrl& maskUrl);
+  TextureId RequestMaskLoad(const VisualUrl& maskUrl,
+                            bool             synchronousLoading = false);
 
   /**
    * @brief Remove a Texture from the TextureManager.
@@ -492,6 +495,7 @@ private:
    *                                  there is no alpha
    * @param[in] animatedImageLoading  The AnimatedImageLoading to load animated image
    * @param[in] frameIndex            The frame index of a frame to be loaded frame
+   * @param[in] synchronousLoading    true if the frame should be loaded synchronously
    * @return                          A TextureId to use as a handle to reference this Texture
    */
   TextureId RequestLoadInternal(
@@ -509,7 +513,8 @@ private:
     TextureManager::ReloadPolicy reloadPolicy,
     MultiplyOnLoad&              preMultiplyOnLoad,
     Dali::AnimatedImageLoading   animatedImageLoading,
-    uint32_t                     frameIndex);
+    uint32_t                     frameIndex,
+    bool                         synchronousLoading);
 
   /**
    * @brief Get the current state of a texture
@@ -519,6 +524,23 @@ private:
    */
   LoadState GetTextureStateInternal(TextureId textureId);
 
+  /**
+   * @brief Load a new image synchronously.
+   * @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] orientationCorrection Whether to use image metadata to rotate or flip the image,
+   *                                  e.g., from portrait to landscape
+   * @return PixelBuffer of loaded image.
+   */
+  Devel::PixelBuffer LoadImageSynchronously(const VisualUrl&         url,
+                                            const ImageDimensions    desiredSize,
+                                            FittingMode::Type        fittingMode,
+                                            Dali::SamplingMode::Type samplingMode,
+                                            bool                     orientationCorrection);
+
   typedef size_t TextureHash; ///< The type used to store the hash used for Texture caching.
 
   // Structs: