From a37d95582a6e302346e98bca3606296d137470b6 Mon Sep 17 00:00:00 2001 From: sunghyun kim Date: Thu, 4 May 2023 11:13:14 +0900 Subject: [PATCH] Masking support for external textures Masking support for external textures as well. For masking, only GPU masking is available. Support for both asynchronous/synchronous. Change-Id: Ifdd18f0d90911ba849b5ff9cf1d8255f40ce12e9 --- .../src/dali-toolkit/utc-Dali-ImageVisual.cpp | 114 +++++++++++++++- .../visuals/image-visual-properties-devel.h | 5 +- .../texture-manager/texture-manager-impl.cpp | 144 ++++++++++++++------- .../internal/visuals/image/image-visual.cpp | 15 ++- 4 files changed, 228 insertions(+), 50 deletions(-) diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp index 43c7530..271246f 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp @@ -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(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(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(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(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; diff --git a/dali-toolkit/devel-api/visuals/image-visual-properties-devel.h b/dali-toolkit/devel-api/visuals/image-visual-properties-devel.h index 7deaf7a..24cc113 100644 --- a/dali-toolkit/devel-api/visuals/image-visual-properties-devel.h +++ b/dali-toolkit/devel-api/visuals/image-visual-properties-devel.h @@ -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 diff --git a/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp b/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp index 4666cc5..99620f0 100644 --- a/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp +++ b/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp @@ -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 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 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 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 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 diff --git a/dali-toolkit/internal/visuals/image/image-visual.cpp b/dali-toolkit/internal/visuals/image/image-visual.cpp index 2f51b7a..8adcb32 100644 --- a/dali-toolkit/internal/visuals/image/image-visual.cpp +++ b/dali-toolkit/internal/visuals/image/image-visual.cpp @@ -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; + } } } -- 2.7.4