Masking support for external textures 38/292338/8
authorsunghyun kim <scholb.kim@samsung.com>
Thu, 4 May 2023 02:13:14 +0000 (11:13 +0900)
committersunghyun kim <scholb.kim@samsung.com>
Mon, 15 May 2023 09:51:29 +0000 (18:51 +0900)
Masking support for external textures as well.
For masking, only GPU masking is available.
Support for both asynchronous/synchronous.

Change-Id: Ifdd18f0d90911ba849b5ff9cf1d8255f40ce12e9

automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp
dali-toolkit/devel-api/visuals/image-visual-properties-devel.h
dali-toolkit/internal/texture-manager/texture-manager-impl.cpp
dali-toolkit/internal/visuals/image/image-visual.cpp

index 43c7530..271246f 100644 (file)
@@ -429,8 +429,8 @@ int UtcDaliImageVisualWithFrameBufferPreMultipliedAlpha01(void)
   ToolkitTestApplication application;
   tet_infoline("Use FrameBuffer as url");
 
-  uint32_t width(64);
-  uint32_t height(64);
+  uint32_t    width(64);
+  uint32_t    height(64);
   FrameBuffer frameBuffer = Dali::FrameBuffer::New(width, height, FrameBuffer::Attachment::NONE);
 
   DALI_TEST_CHECK(frameBuffer);
@@ -474,8 +474,8 @@ int UtcDaliImageVisualWithFrameBufferPreMultipliedAlpha02(void)
   ToolkitTestApplication application;
   tet_infoline("Use FrameBuffer as url");
 
-  uint32_t width(64);
-  uint32_t height(64);
+  uint32_t    width(64);
+  uint32_t    height(64);
   FrameBuffer frameBuffer = Dali::FrameBuffer::New(width, height, FrameBuffer::Attachment::NONE);
 
   DALI_TEST_CHECK(frameBuffer);
@@ -616,6 +616,112 @@ int UtcDaliImageVisualWithPixelDataPreMultipliedAlpha(void)
   END_TEST;
 }
 
+int UtcDaliImageVisualWithPixelDataMasking(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Load external texture with mask");
+
+  TestGlAbstraction& gl           = application.GetGlAbstraction();
+  TraceCallStack&    textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  uint32_t width(64);
+  uint32_t height(64);
+  uint32_t bufferSize = width * height * Pixel::GetBytesPerPixel(Pixel::RGBA8888);
+
+  uint8_t*  buffer    = reinterpret_cast<uint8_t*>(malloc(bufferSize));
+  PixelData pixelData = PixelData::New(buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE);
+
+  DALI_TEST_CHECK(pixelData);
+
+  ImageUrl    imageUrl = Dali::Toolkit::Image::GenerateUrl(pixelData, true);
+  std::string url      = imageUrl.GetUrl();
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Property::Map propertyMap;
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, url);
+  propertyMap.Insert(ImageVisual::Property::ALPHA_MASK_URL, TEST_MASK_IMAGE_FILE_NAME);
+
+  Visual::Base visual = factory.CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  Property::Map testMap;
+  visual.CreatePropertyMap(testMap);
+  DALI_TEST_EQUALS(*testMap.Find(ImageVisual::Property::ALPHA_MASK_URL), Property::Value(TEST_MASK_IMAGE_FILE_NAME), TEST_LOCATION);
+
+  DummyControl      actor     = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+  application.GetScene().Add(actor);
+  application.SendNotification();
+  application.Render(16);
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+  DALI_TEST_EQUALS(textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION);
+
+  dummyImpl.UnregisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1);
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliImageVisualWithPixelDataMaskingSynchronously(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Load synchronously external texture with mask");
+
+  uint32_t width(64);
+  uint32_t height(64);
+  uint32_t bufferSize = width * height * Pixel::GetBytesPerPixel(Pixel::RGBA8888);
+
+  uint8_t*  buffer    = reinterpret_cast<uint8_t*>(malloc(bufferSize));
+  PixelData pixelData = PixelData::New(buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE);
+
+  DALI_TEST_CHECK(pixelData);
+
+  ImageUrl    imageUrl = Dali::Toolkit::Image::GenerateUrl(pixelData, true);
+  std::string url      = imageUrl.GetUrl();
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK(factory);
+
+  Property::Map propertyMap;
+  propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE);
+  propertyMap.Insert(ImageVisual::Property::URL, url);
+  propertyMap.Insert(ImageVisual::Property::ALPHA_MASK_URL, TEST_MASK_IMAGE_FILE_NAME);
+  propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
+
+  Visual::Base visual = factory.CreateVisual(propertyMap);
+  DALI_TEST_CHECK(visual);
+
+  Property::Map testMap;
+  visual.CreatePropertyMap(testMap);
+  DALI_TEST_EQUALS(*testMap.Find(ImageVisual::Property::ALPHA_MASK_URL), Property::Value(TEST_MASK_IMAGE_FILE_NAME), TEST_LOCATION);
+
+  DummyControl      actor     = DummyControl::New();
+  DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+  dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 0u, TEST_LOCATION);
+
+  application.GetScene().Add(actor);
+
+  DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(16);
+
+  END_TEST;
+}
+
 int UtcDaliImageVisualWithNativeImage(void)
 {
   ToolkitTestApplication application;
index 7deaf7a..24cc113 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_DEVEL_API_VISUALS_IMAGE_VISUAL_PROPERTIES_DEVEL_H
 
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -150,6 +150,9 @@ enum Type
   /**
    * @brief Whether to apply mask in loading time or rendering time.
    * @details Name "maskingType", type PlayState::Type (Property::INTEGER).
+   * In general, MASKING_ON_LOADING is the default behavior.
+   * However, if the visual uses an external texture, only MASKING_ON_RENDERING is possible.
+   * So we change its value to MASKING_ON_RENDERING even if the visual sets the MASKING_TYPE as MASKING_ON_LOADING when it uses external texture.
    * @note It is used in the ImageVisual and AnimatedImageVisual. The default is MASKING_ON_LOADING.
    */
   MASKING_TYPE = ORIENTATION_CORRECTION + 12
index 4666cc5..99620f0 100644 (file)
@@ -318,7 +318,25 @@ TextureSet TextureManager::LoadTexture(
           // TODO : Should we seperate input and output value?
           preMultiplyOnLoad = externalTextureInfo.preMultiplied ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
         }
-        return externalTextureInfo.textureSet;
+
+        TextureId alphaMaskId = INVALID_TEXTURE_ID;
+        if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
+        {
+          maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, StorageType::KEEP_TEXTURE, synchronousLoading);
+          alphaMaskId            = maskInfo->mAlphaMaskId;
+          textureId              = RequestLoad(url, alphaMaskId, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad, synchronousLoading);
+
+          if(synchronousLoading)
+          {
+            auto textureSet = GetTextureSet(textureId);
+            textureSet.SetTexture(MASK_TEXTURE_INDEX, GetTextureSet(alphaMaskId).GetTexture(TEXTURE_INDEX));
+            return textureSet;
+          }
+        }
+        else
+        {
+          return externalTextureInfo.textureSet;
+        }
       }
     }
   }
@@ -566,6 +584,15 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
   textureInfo.storageType           = storageType;
   textureInfo.orientationCorrection = orientationCorrection;
 
+  // the case using external texture has already been loaded texture, so change its status to WAITING_FOR_MASK.
+  if(url.GetProtocolType() == VisualUrl::TEXTURE)
+  {
+    if(textureInfo.loadState != LoadState::UPLOADED)
+    {
+      textureInfo.loadState = TextureManager::LoadState::WAITING_FOR_MASK;
+    }
+  }
+
   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureInfo loadState:%s\n", GET_LOAD_STATE_STRING(textureInfo.loadState));
 
   // Force reloading of texture by setting loadState unless already loading or cancelled.
@@ -643,58 +670,73 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
     if(!(textureInfo.loadState == TextureManager::LoadState::UPLOADED ||
          textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED))
     {
-      std::vector<Devel::PixelBuffer> pixelBuffers;
-      LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection, loadYuvPlanes, pixelBuffers);
-
-      if(pixelBuffers.empty())
-      {
-        // If pixelBuffer loading is failed in synchronously, call RequestRemove() method.
-        RequestRemove(textureId, nullptr);
-        return INVALID_TEXTURE_ID;
-      }
-
-      if(storageType == StorageType::KEEP_PIXEL_BUFFER) // For the mask image loading.
+      if(url.GetProtocolType() == VisualUrl::TEXTURE)
       {
-        textureInfo.pixelBuffer = pixelBuffers[0]; // Store the pixel data
-        textureInfo.loadState   = LoadState::LOAD_FINISHED;
+        // Get external textureSet from cacheManager.
+        std::string location = textureInfo.url.GetLocation();
+        if(!location.empty())
+        {
+          TextureId id                  = std::stoi(location);
+          auto      externalTextureInfo = mTextureCacheManager.GetExternalTextureInfo(id);
+          textureInfo.textures.push_back(externalTextureInfo.textureSet.GetTexture(0));
+          textureInfo.loadState = LoadState::UPLOADED;
+        }
       }
-      else // For the image loading.
+      else
       {
-        Texture maskTexture;
-        if(maskTextureId != INVALID_TEXTURE_ID)
+        std::vector<Devel::PixelBuffer> pixelBuffers;
+        LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection, loadYuvPlanes, pixelBuffers);
+
+        if(pixelBuffers.empty())
+        {
+          // If pixelBuffer loading is failed in synchronously, call RequestRemove() method.
+          RequestRemove(textureId, nullptr);
+          return INVALID_TEXTURE_ID;
+        }
+
+        if(storageType == StorageType::KEEP_PIXEL_BUFFER) // For the mask image loading.
+        {
+          textureInfo.pixelBuffer = pixelBuffers[0]; // Store the pixel data
+          textureInfo.loadState   = LoadState::LOAD_FINISHED;
+        }
+        else // For the image loading.
         {
-          TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(maskTextureId);
-          if(maskCacheIndex != INVALID_CACHE_INDEX)
+          Texture maskTexture;
+          if(maskTextureId != INVALID_TEXTURE_ID)
           {
-            if(mTextureCacheManager[maskCacheIndex].storageType == StorageType::KEEP_TEXTURE)
+            TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(maskTextureId);
+            if(maskCacheIndex != INVALID_CACHE_INDEX)
             {
-              if(!mTextureCacheManager[maskCacheIndex].textures.empty())
+              if(mTextureCacheManager[maskCacheIndex].storageType == StorageType::KEEP_TEXTURE)
               {
-                maskTexture = mTextureCacheManager[maskCacheIndex].textures[0];
-              }
-            }
-            else if(mTextureCacheManager[maskCacheIndex].storageType == StorageType::KEEP_PIXEL_BUFFER)
-            {
-              Devel::PixelBuffer maskPixelBuffer = mTextureCacheManager[maskCacheIndex].pixelBuffer;
-              if(maskPixelBuffer)
-              {
-                pixelBuffers[0].ApplyMask(maskPixelBuffer, contentScale, cropToMask);
+                if(!mTextureCacheManager[maskCacheIndex].textures.empty())
+                {
+                  maskTexture = mTextureCacheManager[maskCacheIndex].textures[0];
+                }
               }
-              else
+              else if(mTextureCacheManager[maskCacheIndex].storageType == StorageType::KEEP_PIXEL_BUFFER)
               {
-                DALI_LOG_ERROR("Mask image cached invalid pixel buffer!\n");
+                Devel::PixelBuffer maskPixelBuffer = mTextureCacheManager[maskCacheIndex].pixelBuffer;
+                if(maskPixelBuffer)
+                {
+                  pixelBuffers[0].ApplyMask(maskPixelBuffer, contentScale, cropToMask);
+                }
+                else
+                {
+                  DALI_LOG_ERROR("Mask image cached invalid pixel buffer!\n");
+                }
               }
             }
+            else
+            {
+              DALI_LOG_ERROR("Mask image is not stored in cache.\n");
+            }
           }
-          else
-          {
-            DALI_LOG_ERROR("Mask image is not stored in cache.\n");
-          }
-        }
-        PreMultiply(pixelBuffers[0], preMultiplyOnLoad);
+          PreMultiply(pixelBuffers[0], preMultiplyOnLoad);
 
-        // Upload texture
-        UploadTextures(pixelBuffers, textureInfo);
+          // Upload texture
+          UploadTextures(pixelBuffers, textureInfo);
+        }
       }
     }
   }
@@ -1153,10 +1195,24 @@ void TextureManager::CheckForWaitingTexture(TextureManager::TextureInfo& maskTex
       {
         if(maskTextureInfo.storageType == StorageType::KEEP_TEXTURE)
         {
-          // Upload image texture. textureInfo.loadState will be UPLOADED.
-          std::vector<Devel::PixelBuffer> pixelBuffers;
-          pixelBuffers.push_back(textureInfo.pixelBuffer);
-          UploadTextures(pixelBuffers, textureInfo);
+          if(textureInfo.url.GetProtocolType() == VisualUrl::TEXTURE)
+          {
+            // Get external textureSet from cacheManager.
+            std::string location = textureInfo.url.GetLocation();
+            if(!location.empty())
+            {
+              TextureId id                  = std::stoi(location);
+              auto      externalTextureInfo = mTextureCacheManager.GetExternalTextureInfo(id);
+              textureInfo.textures.push_back(externalTextureInfo.textureSet.GetTexture(0));
+            }
+          }
+          else
+          {
+            // Upload image texture. textureInfo.loadState will be UPLOADED.
+            std::vector<Devel::PixelBuffer> pixelBuffers;
+            pixelBuffers.push_back(textureInfo.pixelBuffer);
+            UploadTextures(pixelBuffers, textureInfo);
+          }
 
           // Increase reference counts for notify required textureId.
           // Now we can assume that we don't remove & re-assign this textureId
index 2f51b7a..8adcb32 100644 (file)
@@ -424,7 +424,16 @@ void ImageVisual::DoSetProperty(Property::Index index, const Property::Value& va
       if(value.Get(maskingType))
       {
         AllocateMaskData();
-        mMaskingData->mPreappliedMasking = Toolkit::DevelImageVisual::MaskingType::Type(maskingType) == Toolkit::DevelImageVisual::MaskingType::MASKING_ON_LOADING ? true : false;
+        if(mImageUrl.IsValid() && mImageUrl.GetProtocolType() == VisualUrl::TEXTURE)
+        {
+          // For external textures, only gpu masking is available.
+          // Therefore, MASKING_TYPE is set to MASKING_ON_RENDERING forcelly.
+          mMaskingData->mPreappliedMasking = false;
+        }
+        else
+        {
+          mMaskingData->mPreappliedMasking = Toolkit::DevelImageVisual::MaskingType::Type(maskingType) == Toolkit::DevelImageVisual::MaskingType::MASKING_ON_LOADING ? true : false;
+        }
       }
       break;
     }
@@ -461,6 +470,10 @@ void ImageVisual::AllocateMaskData()
   if(!mMaskingData)
   {
     mMaskingData.reset(new TextureManager::MaskingData());
+    if(mImageUrl.IsValid() && mImageUrl.GetProtocolType() == VisualUrl::TEXTURE)
+    {
+      mMaskingData->mPreappliedMasking = false;
+    }
   }
 }