From: seungho Date: Tue, 14 Dec 2021 08:20:09 +0000 (+0900) Subject: [Tizen] GPU Alpha Masking for Animated Image Visual X-Git-Tag: accepted/tizen/6.5/unified/20220104.122930~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F97%2F267997%2F15;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git [Tizen] GPU Alpha Masking for Animated Image Visual Change-Id: I1780deec4964ae76656fc83a23781870b07b180c Signed-off-by: seungho --- 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 413b556..0cfb9f6 100644 --- a/dali-toolkit/devel-api/visuals/image-visual-properties-devel.h +++ b/dali-toolkit/devel-api/visuals/image-visual-properties-devel.h @@ -145,7 +145,16 @@ enum Type * @details Name "redrawInScalingDown", type Property::BOOLEAN. * @note It is used in the AnimatedVectorImageVisual. The default is true. */ - REDRAW_IN_SCALING_DOWN + REDRAW_IN_SCALING_DOWN = ORIENTATION_CORRECTION + 11, + + /** + * @brief Whether to apply mask in loading time or not. + * @details Name "preappliedMask", type Property::BOOLEAN. + * If it is true, mask image is applied on the texture in loading time with CPU. + * If it is false, mask image is applied in runtime in shader. + * @note It is used in the ImageVisual and AnimatedImageVisual. The default is true. + */ + PREAPPLIED_MASK = ORIENTATION_CORRECTION + 12 }; } //namespace Property diff --git a/dali-toolkit/internal/graphics/shaders/image-visual-shader.frag b/dali-toolkit/internal/graphics/shaders/image-visual-shader.frag index 703c649..c2324e7 100644 --- a/dali-toolkit/internal/graphics/shaders/image-visual-shader.frag +++ b/dali-toolkit/internal/graphics/shaders/image-visual-shader.frag @@ -4,6 +4,9 @@ #ifndef IS_REQUIRED_BORDERLINE #define IS_REQUIRED_BORDERLINE 0 #endif +#ifndef IS_REQUIRED_ALPHA_MASKING +#define IS_REQUIRED_ALPHA_MASKING 0 +#endif #ifndef ATLAS_DEFAULT_WARP #define ATLAS_DEFAULT_WARP 0 #endif @@ -22,6 +25,12 @@ INPUT mediump vec4 vCornerRadius; #endif uniform sampler2D sTexture; + +#if IS_REQUIRED_ALPHA_MASKING +uniform sampler2D sMaskTexture; +INPUT mediump vec2 vMaskTexCoord; +#endif + #if ATLAS_DEFAULT_WARP uniform mediump vec4 uAtlasRect; #elif ATLAS_CUSTOM_WARP @@ -228,6 +237,12 @@ void main() lowp vec4 textureColor = TEXTURE( sTexture, texCoord ) * vec4( mixColor, 1.0 ) * uColor; +#if IS_REQUIRED_ALPHA_MASKING + mediump float maskAlpha = TEXTURE(sMaskTexture, vMaskTexCoord).a; + textureColor.a *= maskAlpha; + textureColor.rgb *= mix(1.0, maskAlpha, preMultipliedAlpha); +#endif + #if IS_REQUIRED_ROUNDED_CORNER || IS_REQUIRED_BORDERLINE // skip most potential calculate for performance if(abs(vPosition.x) < vOptRectSize.x && abs(vPosition.y) < vOptRectSize.y) diff --git a/dali-toolkit/internal/graphics/shaders/image-visual-shader.vert b/dali-toolkit/internal/graphics/shaders/image-visual-shader.vert index 21d4084..de8290b 100644 --- a/dali-toolkit/internal/graphics/shaders/image-visual-shader.vert +++ b/dali-toolkit/internal/graphics/shaders/image-visual-shader.vert @@ -4,6 +4,9 @@ #ifndef IS_REQUIRED_BORDERLINE #define IS_REQUIRED_BORDERLINE 0 #endif +#ifndef IS_REQUIRED_ALPHA_MASKING +#define IS_REQUIRED_ALPHA_MASKING 0 +#endif INPUT mediump vec2 aPosition; OUTPUT mediump vec2 vTexCoord; @@ -34,6 +37,13 @@ uniform mediump float borderlineOffset; uniform mediump vec4 cornerRadius; uniform mediump float cornerRadiusPolicy; #endif + +#if IS_REQUIRED_ALPHA_MASKING +OUTPUT mediump vec2 vMaskTexCoord; +uniform lowp float cropToMask; +uniform mediump vec2 maskTextureRatio; +#endif + uniform mediump vec2 extraSize; vec4 ComputeVertexPosition() @@ -68,7 +78,19 @@ vec4 ComputeVertexPosition() mediump vec2 vPosition = aPosition * visualSize; #endif - vTexCoord = pixelArea.xy + pixelArea.zw * (vPosition.xy / max(vec2(1.0), visualSize) + vec2(0.5)); + +vec4 finalPixelArea = pixelArea; +#if IS_REQUIRED_ALPHA_MASKING + finalPixelArea = mix(pixelArea, + vec4( + vec2(0.5) + (pixelArea.xy - vec2(0.5)) * maskTextureRatio, + pixelArea.zw * maskTextureRatio + ), + cropToMask); + vMaskTexCoord = pixelArea.xy + pixelArea.zw * (vPosition.xy / max(vec2(1.0), visualSize) + vec2(0.5)); +#endif + vTexCoord = finalPixelArea.xy + finalPixelArea.zw * (vPosition.xy / max(vec2(1.0), visualSize) + vec2(0.5)); + return vec4(vPosition + anchorPoint * visualSize + (visualOffset + origin) * uSize.xy, 0.0, 1.0); } diff --git a/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp b/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp index e830602..5025862 100644 --- a/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp +++ b/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp @@ -81,6 +81,8 @@ static constexpr Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f); static constexpr auto LOOP_FOREVER = -1; static constexpr auto FIRST_LOOP = 0u; +const char* const MASK_TEXTURE_RATIO_NAME("maskTextureRatio"); + #if defined(DEBUG_ENABLED) Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE"); #endif @@ -244,23 +246,24 @@ AnimatedImageVisual::~AnimatedImageVisual() void AnimatedImageVisual::GetNaturalSize(Vector2& naturalSize) { + naturalSize = Vector2::ZERO; if(mImageSize.GetWidth() == 0 && mImageSize.GetHeight() == 0) { - if(mImageUrl.IsValid()) + if(mImpl->mRenderer) { - if(mAnimatedImageLoading.HasLoadingSucceeded()) + auto textureSet = mImpl->mRenderer.GetTextures(); + if(textureSet) { - mImageSize = mAnimatedImageLoading.GetImageSize(); - } - else - { - mImageSize = Dali::GetClosestImageSize(mImageUrl.GetUrl()); + auto texture = textureSet.GetTexture(0); + if(texture) + { + SetImageSize(textureSet); + naturalSize.x = texture.GetWidth(); + naturalSize.y = texture.GetHeight(); + return; + } } } - else if(mImageUrls && mImageUrls->size() > 0) - { - mImageSize = Dali::GetClosestImageSize((*mImageUrls)[0].mUrl); - } if(mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid() && mMaskingData->mCropToMask) @@ -268,11 +271,28 @@ void AnimatedImageVisual::GetNaturalSize(Vector2& naturalSize) ImageDimensions dimensions = Dali::GetClosestImageSize(mMaskingData->mAlphaMaskUrl.GetUrl()); if(dimensions != ImageDimensions(0, 0)) { + mImageSize = dimensions; naturalSize.x = dimensions.GetWidth(); naturalSize.y = dimensions.GetHeight(); } return; } + + if(mImageUrl.IsValid()) + { + if(mAnimatedImageLoading.HasLoadingSucceeded()) + { + mImageSize = mAnimatedImageLoading.GetImageSize(); + } + else + { + mImageSize = Dali::GetClosestImageSize(mImageUrl.GetUrl()); + } + } + else if(mImageUrls && mImageUrls->size() > 0) + { + mImageSize = Dali::GetClosestImageSize((*mImageUrls)[0].mUrl); + } } naturalSize.width = mImageSize.GetWidth(); @@ -321,6 +341,7 @@ void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const map.Insert(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, mMaskingData->mAlphaMaskUrl.GetUrl()); map.Insert(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, mMaskingData->mContentScaleFactor); map.Insert(Toolkit::ImageVisual::Property::CROP_TO_MASK, mMaskingData->mCropToMask); + map.Insert(Toolkit::DevelImageVisual::Property::PREAPPLIED_MASK, mMaskingData->mPreappliedMasking); } map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy); @@ -450,6 +471,10 @@ void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap) { DoSetProperty(Toolkit::ImageVisual::Property::CROP_TO_MASK, keyValue.second); } + else if(keyValue.first == PREAPPLIED_MASK_NAME) + { + DoSetProperty(Toolkit::DevelImageVisual::Property::PREAPPLIED_MASK, keyValue.second); + } else if(keyValue.first == LOAD_POLICY_NAME) { DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second); @@ -624,6 +649,17 @@ void AnimatedImageVisual::DoSetProperty(Property::Index index, break; } + case Toolkit::DevelImageVisual::Property::PREAPPLIED_MASK: + { + bool preappliedMask = true; + if(value.Get(preappliedMask)) + { + AllocateMaskData(); + mMaskingData->mPreappliedMasking = preappliedMask; + } + break; + } + case Toolkit::ImageVisual::Property::RELEASE_POLICY: { int releasePolicy = 0; @@ -730,6 +766,11 @@ void AnimatedImageVisual::OnInitialize() { mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea); } + + if(mMaskingData) + { + mImpl->mRenderer.RegisterProperty(CROP_TO_MASK_NAME, static_cast(mMaskingData->mCropToMask)); + } } void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval) @@ -792,6 +833,22 @@ void AnimatedImageVisual::SetImageSize(TextureSet& textureSet) mImageSize.SetWidth(texture.GetWidth()); mImageSize.SetHeight(texture.GetHeight()); } + + if(textureSet.GetTextureCount() > 1u && mMaskingData && mMaskingData->mCropToMask) + { + Texture maskTexture = textureSet.GetTexture(1); + if(maskTexture) + { + mImageSize.SetWidth(std::min(static_cast(mImageSize.GetWidth() * mMaskingData->mContentScaleFactor), maskTexture.GetWidth())); + mImageSize.SetHeight(std::min(static_cast(mImageSize.GetHeight() * mMaskingData->mContentScaleFactor), maskTexture.GetHeight())); + + float textureWidth = std::max(static_cast(texture.GetWidth() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1); + float textureHeight = std::max(static_cast(texture.GetHeight() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1); + Vector2 textureRatio(std::min(static_cast(maskTexture.GetWidth()), textureWidth) / textureWidth, + std::min(static_cast(maskTexture.GetHeight()), textureHeight) / textureHeight); + mImpl->mRenderer.RegisterProperty(MASK_TEXTURE_RATIO_NAME, textureRatio); + } + } } } @@ -923,15 +980,16 @@ TextureSet AnimatedImageVisual::SetLoadingFailed() Shader AnimatedImageVisual::GenerateShader() const { - bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE; + bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE; + bool requiredAlphaMasking = (mMaskingData) ? !mMaskingData->mPreappliedMasking : false; Shader shader; shader = mImageVisualShaderFactory.GetShader( mFactoryCache, ImageVisualShaderFeature::FeatureBuilder() - .ApplyDefaultTextureWrapMode(defaultWrapMode) - .EnableRoundedCorner(IsRoundedCornerRequired()) - .EnableBorderline(IsBorderlineRequired()) - ); + .ApplyDefaultTextureWrapMode(defaultWrapMode) + .EnableRoundedCorner(IsRoundedCornerRequired()) + .EnableBorderline(IsBorderlineRequired()) + .EnableAlphaMasking(requiredAlphaMasking)); return shader; } diff --git a/dali-toolkit/internal/visuals/image-visual-shader-factory.cpp b/dali-toolkit/internal/visuals/image-visual-shader-factory.cpp index f5753fa..8e7e304 100644 --- a/dali-toolkit/internal/visuals/image-visual-shader-factory.cpp +++ b/dali-toolkit/internal/visuals/image-visual-shader-factory.cpp @@ -43,6 +43,28 @@ static std::string gFragmentShaderNoAtlas; const int NATIVE_SHADER_TYPE_OFFSET = VisualFactoryCache::ShaderType::NATIVE_IMAGE_SHADER - VisualFactoryCache::ShaderType::IMAGE_SHADER; +// enum of required list when we select shader +enum class ImageVisualRequireFlag : uint32_t +{ + DEFAULT = 0, + ROUNDED_CORNER = 1 << 0, + BORDERLINE = 1 << 1, + ALPHA_MASKING = 1 << 2, +}; + +static constexpr auto SHADER_TYPE_COUNT = 8u; +VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[SHADER_TYPE_COUNT] = +{ + VisualFactoryCache::IMAGE_SHADER, + VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER, + VisualFactoryCache::IMAGE_SHADER_BORDERLINE, + VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE, + VisualFactoryCache::IMAGE_SHADER_MASKING, + VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER_MASKING, + VisualFactoryCache::IMAGE_SHADER_BORDERLINE_MASKING, + VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING +}; + } // unnamed namespace namespace ImageVisualShaderFeature @@ -72,6 +94,11 @@ FeatureBuilder& FeatureBuilder::SetTextureForFragmentShaderCheck(const Dali::Tex mTexture = texture; return *this; } +FeatureBuilder& FeatureBuilder::EnableAlphaMasking(bool enableAlphaMasking) +{ + mAlphaMasking = (enableAlphaMasking ? AlphaMasking::ENABLED : AlphaMasking::DISABLED); + return *this; +} } // namespace ImageVisualShaderFeature ImageVisualShaderFactory::ImageVisualShaderFactory() @@ -92,9 +119,10 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, con const auto& defaultTextureWrapping = featureBuilder.mDefaultTextureWrapMode; const auto& roundedCorner = featureBuilder.mRoundedCorner; const auto& borderline = featureBuilder.mBorderline; + const auto& alphaMasking = featureBuilder.mAlphaMasking; const auto& changeFragmentShader = (featureBuilder.mTexture && DevelTexture::IsNative(featureBuilder.mTexture)) - ? ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE - : ImageVisualShaderFeature::ChangeFragmentShader::DONT_CHANGE; + ? ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE + : ImageVisualShaderFeature::ChangeFragmentShader::DONT_CHANGE; if(atlasing == ImageVisualShaderFeature::TextureAtlas::ENABLED) { @@ -109,24 +137,20 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, con } else { + uint32_t shaderTypeFlag = static_cast(ImageVisualRequireFlag::DEFAULT); if(roundedCorner == ImageVisualShaderFeature::RoundedCorner::ENABLED) { - if(borderline == ImageVisualShaderFeature::Borderline::ENABLED) - { - shaderType = VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE; - } - else - { - shaderType = VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER; - } + shaderTypeFlag |= static_cast(ImageVisualRequireFlag::ROUNDED_CORNER); } - else + if(borderline == ImageVisualShaderFeature::Borderline::ENABLED) { - if(borderline == ImageVisualShaderFeature::Borderline::ENABLED) - { - shaderType = VisualFactoryCache::IMAGE_SHADER_BORDERLINE; - } + shaderTypeFlag |= static_cast(ImageVisualRequireFlag::BORDERLINE); + } + if(alphaMasking == ImageVisualShaderFeature::AlphaMasking::ENABLED) + { + shaderTypeFlag |= static_cast(ImageVisualRequireFlag::ALPHA_MASKING); } + shaderType = SHADER_TYPE_TABLE[shaderTypeFlag]; } if(changeFragmentShader == ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE && @@ -164,6 +188,11 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, con vertexShaderPrefixList += "#define IS_REQUIRED_BORDERLINE 1\n"; fragmentShaderPrefixList += "#define IS_REQUIRED_BORDERLINE 1\n"; } + if(alphaMasking == ImageVisualShaderFeature::AlphaMasking::ENABLED) + { + vertexShaderPrefixList += "#define IS_REQUIRED_ALPHA_MASKING 1\n"; + fragmentShaderPrefixList += "#define IS_REQUIRED_ALPHA_MASKING 1\n"; + } } std::string vertexShader = std::string(Dali::Shader::GetVertexShaderPrefix() + vertexShaderPrefixList + SHADER_IMAGE_VISUAL_SHADER_VERT.data()); diff --git a/dali-toolkit/internal/visuals/image-visual-shader-factory.h b/dali-toolkit/internal/visuals/image-visual-shader-factory.h index 1459614..1e1974d 100644 --- a/dali-toolkit/internal/visuals/image-visual-shader-factory.h +++ b/dali-toolkit/internal/visuals/image-visual-shader-factory.h @@ -29,7 +29,6 @@ namespace Toolkit { namespace Internal { - /** * ImageVisualShaderFeature contains feature lists what image visual shader need to know. */ @@ -96,6 +95,18 @@ enum Type }; } // namespace ChangeFragmentShader +namespace AlphaMasking +{ +/** + * @brief Whether use runtime alpha masking in shader, or not + */ +enum Type +{ + DISABLED = 0, ///< Image visual doesn't use runtime alpha masking + ENABLED ///< Image visual uses runtime alpha masking +}; +} // namespace AlphaMasking + /** * @brief Collection of current image visual feature. Only use for ImageVisualShaderFactory::GetShader() */ @@ -106,6 +117,7 @@ struct FeatureBuilder mDefaultTextureWrapMode(DefaultTextureWrapMode::APPLY), mRoundedCorner(RoundedCorner::DISABLED), mBorderline(Borderline::DISABLED), + mAlphaMasking(AlphaMasking::DISABLED), mTexture() { } @@ -115,15 +127,17 @@ struct FeatureBuilder FeatureBuilder& EnableRoundedCorner(bool enableRoundedCorner); FeatureBuilder& EnableBorderline(bool enableBorderline); FeatureBuilder& SetTextureForFragmentShaderCheck(const Dali::Texture& texture); + FeatureBuilder& EnableAlphaMasking(bool enableAlphaMasking); TextureAtlas::Type mTextureAtlas : 2; ///< Whether use texture with atlas, or not. default as TextureAtlas::DISABLED DefaultTextureWrapMode::Type mDefaultTextureWrapMode : 2; ///< Whether apply to texture wraping in default, or not. default as DefaultTextureWrapMode::APPLY RoundedCorner::Type mRoundedCorner : 2; ///< Whether use rounded corner, or not. default as RoundedCorner::DISABLED Borderline::Type mBorderline : 2; ///< Whether use borderline, or not. default as Borderline::DISABLED + AlphaMasking::Type mAlphaMasking : 2; ///< Whether use runtime alpha masking, or not. default as AlphaMasking::DISABLED Dali::Texture mTexture; ///< Texture to check whether we need to change fragment shader or not }; -} // namespace ImageVisualShaderFactoryFeature +} // namespace ImageVisualShaderFeature /** * ImageVisualShaderFactory is an object that provides and shares shaders between image visuals @@ -131,7 +145,6 @@ struct FeatureBuilder class ImageVisualShaderFactory { public: - /** * @brief Constructor */ @@ -174,7 +187,6 @@ protected: ImageVisualShaderFactory& operator=(const ImageVisualShaderFactory& rhs); private: - /** * @brief Cached information whether native image should change fragment shader. * Default it is ChangeFragmentShader::UNDECIDED. diff --git a/dali-toolkit/internal/visuals/texture-manager-impl.cpp b/dali-toolkit/internal/visuals/texture-manager-impl.cpp index e694c57..9171032 100644 --- a/dali-toolkit/internal/visuals/texture-manager-impl.cpp +++ b/dali-toolkit/internal/visuals/texture-manager-impl.cpp @@ -117,7 +117,8 @@ TextureManager::MaskingData::MaskingData() : mAlphaMaskUrl(), mAlphaMaskId(INVALID_TEXTURE_ID), mContentScaleFactor(1.0f), - mCropToMask(true) + mCropToMask(true), + mPreappliedMasking(true) { } @@ -169,12 +170,22 @@ TextureSet TextureManager::LoadAnimatedImageTexture(Dali::AnimatedImageLoading a } else { + Texture maskTexture; if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid()) { Devel::PixelBuffer maskPixelBuffer = LoadImageFromFile(maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true); if(maskPixelBuffer) { - pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask); + if(!maskInfo->mPreappliedMasking) + { + PixelData maskPixelData = Devel::PixelBuffer::Convert(maskPixelBuffer); // takes ownership of buffer + Texture maskTexture = Texture::New(Dali::TextureType::TEXTURE_2D, maskPixelData.GetPixelFormat(), maskPixelData.GetWidth(), maskPixelData.GetHeight()); + maskTexture.Upload(maskPixelData); + } + else + { + pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask); + } } } @@ -185,6 +196,10 @@ TextureSet TextureManager::LoadAnimatedImageTexture(Dali::AnimatedImageLoading a texture.Upload(pixelData); textureSet = TextureSet::New(); textureSet.SetTexture(0u, texture); + if(maskTexture) + { + textureSet.SetTexture(1u, maskTexture); + } } } } @@ -195,7 +210,7 @@ TextureSet TextureManager::LoadAnimatedImageTexture(Dali::AnimatedImageLoading a bool cropToMask = false; if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid()) { - maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl); + maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, maskInfo->mPreappliedMasking ? StorageType::KEEP_PIXEL_BUFFER : StorageType::KEEP_TEXTURE); alphaMaskId = maskInfo->mAlphaMaskId; contentScaleFactor = maskInfo->mContentScaleFactor; cropToMask = maskInfo->mCropToMask; @@ -206,11 +221,11 @@ TextureSet TextureManager::LoadAnimatedImageTexture(Dali::AnimatedImageLoading a textureId = RequestLoadInternal(animatedImageLoading.GetUrl(), alphaMaskId, contentScaleFactor, + cropToMask, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, TextureManager::NO_ATLAS, - cropToMask, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, @@ -275,7 +290,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, false); + RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, false, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false); } return pixelBuffer; @@ -396,7 +411,7 @@ TextureSet TextureManager::LoadTexture(const VisualUrl& url, } else { - maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl); + maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, maskInfo->mPreappliedMasking ? StorageType::KEEP_PIXEL_BUFFER : StorageType::KEEP_TEXTURE); textureId = RequestLoad(url, maskInfo->mAlphaMaskId, maskInfo->mContentScaleFactor, @@ -454,7 +469,7 @@ TextureManager::TextureId TextureManager::RequestLoad( TextureManager::ReloadPolicy reloadPolicy, TextureManager::MultiplyOnLoad& preMultiplyOnLoad) { - return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, true); + return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, false, desiredSize, fittingMode, samplingMode, useAtlas, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, true); } TextureManager::TextureId TextureManager::RequestLoad( @@ -471,25 +486,25 @@ TextureManager::TextureId TextureManager::RequestLoad( TextureManager::ReloadPolicy reloadPolicy, TextureManager::MultiplyOnLoad& preMultiplyOnLoad) { - return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, true); + return RequestLoadInternal(url, maskTextureId, contentScale, cropToMask, desiredSize, fittingMode, samplingMode, useAtlas, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, true); } -TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl) +TextureManager::TextureId TextureManager::RequestMaskLoad(const VisualUrl& maskUrl, StorageType storageType) { // 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, true); + return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, false, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, storageType, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, true); } TextureManager::TextureId TextureManager::RequestLoadInternal( const VisualUrl& url, TextureId maskTextureId, float contentScale, + bool cropToMask, const ImageDimensions desiredSize, FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode, UseAtlas useAtlas, - bool cropToMask, StorageType storageType, TextureUploadObserver* observer, bool orientationCorrection, @@ -507,7 +522,8 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( textureHash = GenerateHash(url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId); // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision. - cacheIndex = FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad, (animatedImageLoading) ? true : false); + cacheIndex = FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, + storageType, maskTextureId, preMultiplyOnLoad, (animatedImageLoading) ? true : false); } TextureManager::TextureId textureId = INVALID_TEXTURE_ID; @@ -1151,8 +1167,36 @@ void TextureManager::PostLoad(TextureInfo& textureInfo, Devel::PixelBuffer& pixe } else if(maskLoadState == LoadState::LOAD_FINISHED) { - // Send New Task to Thread - ApplyMask(textureInfo, textureInfo.maskTextureId); + int32_t maskCacheIndex = GetCacheIndexFromId(textureInfo.maskTextureId); + if(maskCacheIndex != INVALID_CACHE_INDEX) + { + TextureInfo& maskTextureInfo(mTextureInfoContainer[maskCacheIndex]); + if(maskTextureInfo.storageType == StorageType::KEEP_PIXEL_BUFFER) + { + // Send New Task to Thread + ApplyMask(textureInfo, textureInfo.maskTextureId); + } + } + } + else if(maskLoadState == LoadState::UPLOADED) + { + int32_t maskCacheIndex = GetCacheIndexFromId(textureInfo.maskTextureId); + if(maskCacheIndex != INVALID_CACHE_INDEX) + { + TextureInfo& maskTextureInfo(mTextureInfoContainer[maskCacheIndex]); + if(maskTextureInfo.storageType == StorageType::KEEP_TEXTURE) + { + // Upload image texture. textureInfo.loadState will be UPLOADED. + UploadTexture(textureInfo.pixelBuffer, textureInfo); + if(maskTextureInfo.textureSet.GetTextureCount() > 0u) + { + Texture maskTexture = maskTextureInfo.textureSet.GetTexture(0u); + textureInfo.textureSet.SetTexture(1u, maskTexture); + } + // notify mask texture set. + NotifyObservers(textureInfo, true); + } + } } } } @@ -1171,7 +1215,7 @@ void TextureManager::PostLoad(TextureInfo& textureInfo, Devel::PixelBuffer& pixe { NotifyObservers(textureInfo, true); } - else + else // for the StorageType::KEEP_PIXEL_BUFFER and StorageType::KEEP_TEXTURE { // Check if there was another texture waiting for this load to complete // (e.g. if this was an image mask, and its load is on a different thread) @@ -1189,10 +1233,16 @@ void TextureManager::PostLoad(TextureInfo& textureInfo, Devel::PixelBuffer& pixe void TextureManager::CheckForWaitingTexture(TextureInfo& maskTextureInfo) { + if(maskTextureInfo.loadState == LoadState::LOAD_FINISHED && + maskTextureInfo.storageType == StorageType::KEEP_TEXTURE) + { + // Upload mask texture. textureInfo.loadState will be UPLOADED. + UploadTexture(maskTextureInfo.pixelBuffer, maskTextureInfo); + } + // Search the cache, checking if any texture has this texture id as a // maskTextureId: const unsigned int size = mTextureInfoContainer.size(); - for(unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex) { if(mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId && @@ -1202,11 +1252,30 @@ void TextureManager::CheckForWaitingTexture(TextureInfo& maskTextureInfo) if(maskTextureInfo.loadState == LoadState::LOAD_FINISHED) { - // Send New Task to Thread - ApplyMask(textureInfo, maskTextureInfo.textureId); + if(maskTextureInfo.storageType == StorageType::KEEP_PIXEL_BUFFER) + { + // Send New Task to Thread + ApplyMask(textureInfo, maskTextureInfo.textureId); + } + } + else if (maskTextureInfo.loadState == LoadState::UPLOADED) + { + if(maskTextureInfo.storageType == StorageType::KEEP_TEXTURE) + { + // Upload image texture. textureInfo.loadState will be UPLOADED. + UploadTexture(textureInfo.pixelBuffer, textureInfo); + if(maskTextureInfo.textureSet.GetTextureCount() > 0u) + { + Texture maskTexture = maskTextureInfo.textureSet.GetTexture(0u); + textureInfo.textureSet.SetTexture(1u, maskTexture); + } + // notify mask texture set. + NotifyObservers(textureInfo, true); + } } else { + // for the load fail. textureInfo.pixelBuffer.Reset(); textureInfo.loadState = LoadState::LOAD_FAILED; NotifyObservers(textureInfo, false); @@ -1432,6 +1501,7 @@ int TextureManager::FindCachedTexture( const FittingMode::Type fittingMode, const Dali::SamplingMode::Type samplingMode, const bool useAtlas, + StorageType storageType, TextureId maskTextureId, TextureManager::MultiplyOnLoad preMultiplyOnLoad, bool isAnimatedImage) @@ -1453,6 +1523,7 @@ int TextureManager::FindCachedTexture( (maskTextureId == textureInfo.maskTextureId) && (size == textureInfo.desiredSize) && (isAnimatedImage == textureInfo.isAnimatedImageFormat) && + (storageType == textureInfo.storageType) && ((size.GetWidth() == 0 && size.GetHeight() == 0) || (fittingMode == textureInfo.fittingMode && samplingMode == textureInfo.samplingMode))) diff --git a/dali-toolkit/internal/visuals/texture-manager-impl.h b/dali-toolkit/internal/visuals/texture-manager-impl.h index 0da2ba2..823fc0d 100644 --- a/dali-toolkit/internal/visuals/texture-manager-impl.h +++ b/dali-toolkit/internal/visuals/texture-manager-impl.h @@ -77,6 +77,7 @@ public: { KEEP_PIXEL_BUFFER, RETURN_PIXEL_BUFFER, + KEEP_TEXTURE, UPLOAD_TO_TEXTURE }; @@ -133,6 +134,7 @@ public: TextureManager::TextureId mAlphaMaskId; float mContentScaleFactor; bool mCropToMask; + bool mPreappliedMasking; }; using MaskingDataPointer = std::unique_ptr; @@ -360,7 +362,7 @@ public: * 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, StorageType storageType); /** * @brief Remove a Texture from the TextureManager. @@ -480,14 +482,14 @@ private: * @param[in] maskTextureId The texture id of an image to use as a mask. If no mask is required, then set * to INVALID_TEXTURE_ID * @param[in] contentScale The scaling factor to apply to the content when masking + * @param[in] cropToMask Whether to crop the target after masking, or scale the mask to the image before + * masking. * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic * @param[in] fittingMode The FittingMode to use * @param[in] samplingMode The SamplingMode to use * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be * loaded, and marked successful, but "useAtlasing" will be set to false in the * "UploadCompleted" callback from the TextureManagerUploadObserver. - * @param[in] cropToMask Whether to crop the target after masking, or scale the mask to the image before - * masking. * @param[in] storageType, Whether the pixel data is stored in the cache or uploaded to the GPU * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" * virtual. @@ -504,11 +506,11 @@ private: const VisualUrl& url, TextureId maskTextureId, float contentScale, + bool cropToMask, const ImageDimensions desiredSize, FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode, UseAtlas useAtlas, - bool cropToMask, StorageType storageType, TextureUploadObserver* observer, bool orientationCorrection, @@ -516,7 +518,7 @@ private: MultiplyOnLoad& preMultiplyOnLoad, Dali::AnimatedImageLoading animatedImageLoading, uint32_t frameIndex, - bool useCache); + bool useCache); /** * @brief Get the current state of a texture @@ -793,6 +795,7 @@ private: * @param[in] fittingMode The FittingMode to use * @param[in] samplingMode The SamplingMode to use * @param[in] useAtlas True if atlased + * @param[in] storageType, Whether the pixel data is stored in the cache or uploaded to the GPU * @param[in] maskTextureId Optional texture ID to use to mask this image * @param[in] preMultiplyOnLoad If the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha. * @param[in] isAnimatedImage True if the texture is from animated image. @@ -805,6 +808,7 @@ private: const FittingMode::Type fittingMode, const Dali::SamplingMode::Type samplingMode, const bool useAtlas, + StorageType storageType, TextureId maskTextureId, MultiplyOnLoad preMultiplyOnLoad, bool isAnimatedImage); diff --git a/dali-toolkit/internal/visuals/visual-factory-cache.h b/dali-toolkit/internal/visuals/visual-factory-cache.h index deae0b0..3dbbb6f 100644 --- a/dali-toolkit/internal/visuals/visual-factory-cache.h +++ b/dali-toolkit/internal/visuals/visual-factory-cache.h @@ -84,12 +84,20 @@ public: IMAGE_SHADER_ROUNDED_CORNER, IMAGE_SHADER_BORDERLINE, IMAGE_SHADER_ROUNDED_BORDERLINE, + IMAGE_SHADER_MASKING, + IMAGE_SHADER_ROUNDED_CORNER_MASKING, + IMAGE_SHADER_BORDERLINE_MASKING, + IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING, IMAGE_SHADER_ATLAS_DEFAULT_WRAP, IMAGE_SHADER_ATLAS_CUSTOM_WRAP, NATIVE_IMAGE_SHADER, NATIVE_IMAGE_SHADER_ROUNDED_CORNER, NATIVE_IMAGE_SHADER_BORDERLINE, NATIVE_IMAGE_SHADER_ROUNDED_BORDERLINE, + NATIVE_IMAGE_SHADER_MASKING, + NATIVE_IMAGE_SHADER_ROUNDED_CORNER_MASKING, + NATIVE_IMAGE_SHADER_BORDERLINE_MASKING, + NATIVE_IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING, NINE_PATCH_SHADER, NINE_PATCH_MASK_SHADER, TEXT_SHADER_MULTI_COLOR_TEXT, diff --git a/dali-toolkit/internal/visuals/visual-string-constants.cpp b/dali-toolkit/internal/visuals/visual-string-constants.cpp index 53bcd1c..579ad4a 100644 --- a/dali-toolkit/internal/visuals/visual-string-constants.cpp +++ b/dali-toolkit/internal/visuals/visual-string-constants.cpp @@ -122,6 +122,7 @@ const char* const IMAGE_DESIRED_WIDTH("desiredWidth"); const char* const IMAGE_DESIRED_HEIGHT("desiredHeight"); const char* const ALPHA_MASK_URL("alphaMaskUrl"); const char* const REDRAW_IN_SCALING_DOWN_NAME("redrawInScalingDown"); +const char* const PREAPPLIED_MASK_NAME("preappliedMask"); // Text visual const char* const TEXT_PROPERTY("text"); diff --git a/dali-toolkit/internal/visuals/visual-string-constants.h b/dali-toolkit/internal/visuals/visual-string-constants.h index 470ded9..da65661 100644 --- a/dali-toolkit/internal/visuals/visual-string-constants.h +++ b/dali-toolkit/internal/visuals/visual-string-constants.h @@ -106,6 +106,7 @@ extern const char* const IMAGE_DESIRED_WIDTH; extern const char* const IMAGE_DESIRED_HEIGHT; extern const char* const ALPHA_MASK_URL; extern const char* const REDRAW_IN_SCALING_DOWN_NAME; +extern const char* const PREAPPLIED_MASK_NAME; // Text visual extern const char* const TEXT_PROPERTY;