.Add(ImageVisual::Property::ALPHA_MASK_URL, TEST_MASK_IMAGE_FILE_NAME)
.Add(ImageVisual::Property::MASK_CONTENT_SCALE, 1.6f)
.Add(ImageVisual::Property::CROP_TO_MASK, true)
+ .Add(DevelImageVisual::Property::MASKING_TYPE, DevelImageVisual::MaskingType::MASKING_ON_RENDERING)
.Add(DevelVisual::Property::CORNER_RADIUS, 22.2f)
.Add(DevelVisual::Property::CORNER_RADIUS_POLICY, Visual::Transform::Policy::ABSOLUTE)
.Add(DevelVisual::Property::BORDERLINE_WIDTH, 33.3f)
DALI_TEST_CHECK(value);
DALI_TEST_EQUALS(value->Get<bool>(), true, TEST_LOCATION);
+ value = resultMap.Find(DevelImageVisual::Property::MASKING_TYPE, Property::INTEGER);
+ DALI_TEST_CHECK(value);
+ DALI_TEST_CHECK(value->Get<int>() == DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
+
Vector2 naturalSize;
animatedImageVisual.GetNaturalSize(naturalSize);
DALI_TEST_EQUALS(naturalSize, Vector2(100, 100), TEST_LOCATION);
.Add("alphaMaskUrl", TEST_MASK_IMAGE_FILE_NAME)
.Add("maskContentScale", 1.6f)
.Add("cropToMask", true)
+ .Add(DevelImageVisual::Property::MASKING_TYPE, DevelImageVisual::MaskingType::MASKING_ON_RENDERING)
.Add("cornerRadius", Vector4(50.0f, 25.0f, 12.5f, 33.0f))
.Add("cornerRadiusPolicy", Visual::Transform::Policy::RELATIVE)
.Add("borderlineWidth", 20.0f)
DALI_TEST_CHECK(value);
DALI_TEST_EQUALS(value->Get<bool>(), true, TEST_LOCATION);
+ value = resultMap.Find(DevelImageVisual::Property::MASKING_TYPE, Property::INTEGER);
+ DALI_TEST_CHECK(value);
+ DALI_TEST_CHECK(value->Get<int>() == DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
+
END_TEST;
}
.Add("alphaMaskUrl", TEST_MASK_IMAGE_FILE_NAME)
.Add("maskContentScale", 1.6f)
.Add("cropToMask", true)
+ .Add(DevelImageVisual::Property::MASKING_TYPE, DevelImageVisual::MaskingType::MASKING_ON_RENDERING)
.Add("cornerRadius", 50.5f));
Property::Map resultMap;
DALI_TEST_CHECK(value);
DALI_TEST_EQUALS(value->Get<bool>(), true, TEST_LOCATION);
+ value = resultMap.Find(DevelImageVisual::Property::MASKING_TYPE, Property::INTEGER);
+ DALI_TEST_CHECK(value);
+ DALI_TEST_CHECK(value->Get<bool>() == DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
+
END_TEST;
}
END_TEST;
}
-int UtcDaliAnimatedImageVisualSynchronousLoadingWithAlphaMask(void)
+int UtcDaliAnimatedImageVisualSynchronousLoadingWithAlphaMask01(void)
{
ToolkitTestApplication application;
- TestGlAbstraction& gl = application.GetGlAbstraction();
+ tet_infoline("UtcDaliAnimatedImageVisualSynchronousLoadingWithAlphaMask01 for CPU Alpha Masking");
+ TestGlAbstraction& gl = application.GetGlAbstraction();
{
Property::Map propertyMap;
END_TEST;
}
+int UtcDaliAnimatedImageVisualSynchronousLoadingWithAlphaMask02(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("UtcDaliAnimatedImageVisualSynchronousLoadingWithAlphaMask02 for GPU Alpha Masking");
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+
+ {
+ Property::Map propertyMap;
+ propertyMap.Insert(Visual::Property::TYPE, Visual::ANIMATED_IMAGE);
+ propertyMap.Insert(ImageVisual::Property::URL, TEST_GIF_FILE_NAME);
+ propertyMap.Insert(ImageVisual::Property::BATCH_SIZE, 2);
+ propertyMap.Insert(ImageVisual::Property::CACHE_SIZE, 2);
+ propertyMap.Insert(ImageVisual::Property::FRAME_DELAY, 20);
+ propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true);
+ propertyMap.Insert(ImageVisual::Property::ALPHA_MASK_URL, TEST_MASK_IMAGE_FILE_NAME);
+ propertyMap.Insert(DevelImageVisual::Property::MASKING_TYPE, DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
+ propertyMap.Insert(DevelVisual::Property::CORNER_RADIUS, 0.23f);
+ propertyMap.Insert(DevelVisual::Property::CORNER_RADIUS_POLICY, Visual::Transform::Policy::ABSOLUTE);
+
+ VisualFactory factory = VisualFactory::Get();
+ Visual::Base visual = factory.CreateVisual(propertyMap);
+
+ 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 dummyControl = DummyControl::New(true);
+ Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+ dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+
+ dummyControl.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
+ application.GetScene().Add(dummyControl);
+
+ TraceCallStack& textureTrace = gl.GetTextureTrace();
+ textureTrace.Enable(true);
+
+ application.SendNotification();
+ application.Render(20);
+
+ // The first frame is loaded synchronously and load next batch with masking
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::GetTimerCount(), 1, TEST_LOCATION);
+ DALI_TEST_EQUALS(gl.GetNumGeneratedTextures(), 4, TEST_LOCATION);
+
+ dummyControl.Unparent();
+ }
+ tet_infoline("Test that removing the visual from stage deletes all textures");
+ application.SendNotification();
+ application.Render(16);
+ DALI_TEST_EQUALS(gl.GetNumGeneratedTextures(), 0, TEST_LOCATION);
+
+ END_TEST;
+}
+
int UtcDaliAnimatedImageVisualJumpToAction(void)
{
ToolkitTestApplication application;
int UtcDaliAnimatedImageVisualAnimatedImageWithAlphaMask01(void)
{
ToolkitTestApplication application;
- TestGlAbstraction& gl = application.GetGlAbstraction();
+ tet_infoline("UtcDaliAnimatedImageVisualAnimatedImageWithAlphaMask01 for CPU Alpha Masking");
+ TestGlAbstraction& gl = application.GetGlAbstraction();
{
Property::Map propertyMap;
END_TEST;
}
+int UtcDaliAnimatedImageVisualAnimatedImageWithAlphaMask02(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("UtcDaliAnimatedImageVisualAnimatedImageWithAlphaMask02 for GPU Alpha Masking");
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+
+ {
+ Property::Map propertyMap;
+ propertyMap.Insert(Visual::Property::TYPE, Visual::ANIMATED_IMAGE);
+ propertyMap.Insert(ImageVisual::Property::URL, TEST_GIF_FILE_NAME);
+ propertyMap.Insert(ImageVisual::Property::BATCH_SIZE, 2);
+ propertyMap.Insert(ImageVisual::Property::CACHE_SIZE, 4);
+ propertyMap.Insert(ImageVisual::Property::FRAME_DELAY, 20);
+ propertyMap.Insert(ImageVisual::Property::ALPHA_MASK_URL, TEST_MASK_IMAGE_FILE_NAME);
+ propertyMap.Insert(DevelImageVisual::Property::MASKING_TYPE, DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
+
+ VisualFactory factory = VisualFactory::Get();
+ Visual::Base visual = factory.CreateVisual(propertyMap);
+
+ DummyControl dummyControl = DummyControl::New(true);
+ Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+ dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+
+ dummyControl.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
+ application.GetScene().Add(dummyControl);
+
+ application.SendNotification();
+ application.Render();
+
+ // load two frame(batch size), load mask image, and request two masking
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render(20);
+
+ DALI_TEST_EQUALS(gl.GetLastGenTextureId(), 3, TEST_LOCATION);
+
+ dummyControl.Unparent();
+ }
+ tet_infoline("Test that removing the visual from stage deletes all textures");
+ application.SendNotification();
+ application.Render(20);
+ DALI_TEST_EQUALS(gl.GetNumGeneratedTextures(), 0, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliAnimatedImageVisualAnimatedImageWithAlphaMask03(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("UtcDaliAnimatedImageVisualAnimatedImageWithAlphaMask03 for GPU Alpha Masking with broken mask texture");
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+
+ {
+ Property::Map propertyMap;
+ propertyMap.Insert(Visual::Property::TYPE, Visual::ANIMATED_IMAGE);
+ propertyMap.Insert(ImageVisual::Property::URL, TEST_GIF_FILE_NAME);
+ propertyMap.Insert(ImageVisual::Property::BATCH_SIZE, 2);
+ propertyMap.Insert(ImageVisual::Property::CACHE_SIZE, 4);
+ propertyMap.Insert(ImageVisual::Property::FRAME_DELAY, 20);
+ propertyMap.Insert(ImageVisual::Property::ALPHA_MASK_URL, "");
+ propertyMap.Insert(DevelImageVisual::Property::MASKING_TYPE, DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
+
+ VisualFactory factory = VisualFactory::Get();
+ Visual::Base visual = factory.CreateVisual(propertyMap);
+
+ DummyControl dummyControl = DummyControl::New(true);
+ Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+ dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+
+ dummyControl.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
+ application.GetScene().Add(dummyControl);
+
+ application.SendNotification();
+ application.Render();
+
+ // load two frame(batch size), load mask image, and request two masking
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render(20);
+
+ DALI_TEST_EQUALS(gl.GetLastGenTextureId(), 3, TEST_LOCATION);
+
+ dummyControl.Unparent();
+ }
+ tet_infoline("Test that removing the visual from stage deletes all textures");
+ application.SendNotification();
+ application.Render(20);
+ DALI_TEST_EQUALS(gl.GetNumGeneratedTextures(), 0, TEST_LOCATION);
+
+ END_TEST;
+}
+
int UtcDaliAnimatedImageVisualMultiImage01(void)
{
ToolkitTestApplication application;
* @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 rendering time.
+ * @details Name "maskingType", type PlayState::Type (Property::INTEGER).
+ * @note It is used in the ImageVisual and AnimatedImageVisual. The default is MASKING_ON_LOADING.
+ */
+ MASKING_TYPE = ORIENTATION_CORRECTION + 12
};
} //namespace Property
} // namespace LoopingMode
+/**
+ * @brief Enumeration for what masking type is in.
+ */
+namespace MaskingType
+{
+enum Type
+{
+ MASKING_ON_RENDERING, ///< Alpha masking is applied for each rendering time. (On GPU)
+ MASKING_ON_LOADING ///< Alpha masking is applied when the image is loading. (On CPU)
+};
+
+}
+
} // namespace DevelImageVisual
} // namespace Toolkit
#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
#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
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)
#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;
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()
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);
}
const Dali::FittingMode::Type& fittingMode,
const Dali::SamplingMode::Type& samplingMode,
const TextureCacheManager::UseAtlas& useAtlas,
+ const StorageType& storageType,
const TextureCacheManager::TextureId& maskTextureId,
const bool& cropToMask,
const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad,
(cropToMask == textureInfo.cropToMask) &&
(size == textureInfo.desiredSize) &&
(isAnimatedImage == textureInfo.isAnimatedImageFormat) &&
+ (storageType == textureInfo.storageType) &&
(frameIndex == textureInfo.frameIndex) &&
((size.GetWidth() == 0 && size.GetHeight() == 0) ||
(fittingMode == textureInfo.fittingMode &&
* @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] cropToMask True if crop to mask
* @param[in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha.
const Dali::FittingMode::Type& fittingMode,
const Dali::SamplingMode::Type& samplingMode,
const TextureCacheManager::UseAtlas& useAtlas,
+ const StorageType& storageType,
const TextureCacheManager::TextureId& maskTextureId,
const bool& cropToMask,
const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad,
: mAlphaMaskUrl(),
mAlphaMaskId(INVALID_TEXTURE_ID),
mContentScaleFactor(1.0f),
- mCropToMask(true)
+ mCropToMask(true),
+ mPreappliedMasking(true),
+ mMaskImageLoadingFailed(false)
{
}
TextureManager::TextureManager()
: mTextureCacheManager(),
- mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]() { return TextureAsyncLoadingHelper(*this); }),
- mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]() { return TextureAsyncLoadingHelper(*this); }),
+ mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]()
+ { return TextureAsyncLoadingHelper(*this); }),
+ mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]()
+ { return TextureAsyncLoadingHelper(*this); }),
mLifecycleObservers(),
mLoadQueue(),
mRemoveQueue(),
}
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
+ maskTexture = Texture::New(Dali::TextureType::TEXTURE_2D, maskPixelData.GetPixelFormat(), maskPixelData.GetWidth(), maskPixelData.GetHeight());
+ maskTexture.Upload(maskPixelData);
+ }
+ else
+ {
+ pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask);
+ }
}
else
{
texture.Upload(pixelData);
textureSet = TextureSet::New();
textureSet.SetTexture(0u, texture);
+ if(maskTexture)
+ {
+ textureSet.SetTexture(1u, maskTexture);
+ }
}
}
}
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;
+ if(maskInfo->mPreappliedMasking)
+ {
+ contentScaleFactor = maskInfo->mContentScaleFactor;
+ cropToMask = maskInfo->mCropToMask;
+ }
}
- textureId = RequestLoadInternal(url, alphaMaskId, contentScaleFactor, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, UseAtlas::NO_ATLAS, cropToMask, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiplyOnLoad, animatedImageLoading, frameIndex, false);
+ textureId = RequestLoadInternal(url, alphaMaskId, contentScaleFactor, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, UseAtlas::NO_ATLAS, cropToMask, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiplyOnLoad, animatedImageLoading, frameIndex, false);
TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId);
if(loadState == TextureManager::LoadState::UPLOADED)
}
else
{
- maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, synchronousLoading);
+ maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, maskInfo->mPreappliedMasking ? StorageType::KEEP_PIXEL_BUFFER : StorageType::KEEP_TEXTURE, synchronousLoading);
textureId = RequestLoad(
url,
maskInfo->mAlphaMaskId,
TextureManager::TextureId TextureManager::RequestMaskLoad(
const VisualUrl& maskUrl,
+ StorageType storageType,
const 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, UseAtlas::NO_ATLAS, false, StorageType::KEEP_PIXEL_BUFFER, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
+ return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, UseAtlas::NO_ATLAS, false, storageType, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
}
TextureManager::TextureId TextureManager::RequestLoadInternal(
textureHash = mTextureCacheManager.GenerateHash(url, desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, cropToMask, frameIndex);
// Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
- cacheIndex = mTextureCacheManager.FindCachedTexture(textureHash, url, desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, cropToMask, preMultiplyOnLoad, (animatedImageLoading) ? true : false, frameIndex);
+ cacheIndex = mTextureCacheManager.FindCachedTexture(textureHash, url, desiredSize, fittingMode, samplingMode, useAtlas, storageType, maskTextureId, cropToMask, preMultiplyOnLoad, (animatedImageLoading) ? true : false, frameIndex);
}
TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
{
textureInfo.loadState = LoadState::WAITING_FOR_MASK;
}
- else if(maskLoadState == LoadState::LOAD_FINISHED)
+ else if(maskLoadState == LoadState::LOAD_FINISHED || maskLoadState == LoadState::UPLOADED)
{
// Send New Task to Thread
- ApplyMask(textureInfo, textureInfo.maskTextureId);
+ TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureInfo.maskTextureId);
+ if(maskCacheIndex != INVALID_CACHE_INDEX)
+ {
+ TextureInfo& maskTextureInfo(mTextureCacheManager[maskCacheIndex]);
+ if(maskTextureInfo.storageType == StorageType::KEEP_PIXEL_BUFFER)
+ {
+ // Send New Task to Thread
+ ApplyMask(textureInfo, textureInfo.maskTextureId);
+ }
+ else 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 // maskLoadState == LoadState::LOAD_FAILED
{
{
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)
else
{
textureInfo.loadState = LoadState::LOAD_FAILED;
- if(textureInfo.storageType != StorageType::KEEP_PIXEL_BUFFER)
- {
- NotifyObservers(textureInfo, false);
- }
- else // if(textureInfo.storageType == StorageType::KEEP_PIXEL_BUFFER) // image mask case
+ if(textureInfo.storageType == StorageType::KEEP_PIXEL_BUFFER || textureInfo.storageType == 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)
CheckForWaitingTexture(textureInfo);
}
+ else
+ {
+ NotifyObservers(textureInfo, false);
+ }
}
}
void TextureManager::CheckForWaitingTexture(TextureManager::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 std::size_t size = mTextureCacheManager.size();
- const bool maskLoadSuccess = maskTextureInfo.loadState == LoadState::LOAD_FINISHED ? true : false;
-
// TODO : Refactorize here to not iterate whole cached image.
for(TextureCacheIndex cacheIndex = TextureCacheIndex(TextureManagerType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, 0u); cacheIndex.GetIndex() < size; ++cacheIndex.detailValue.index)
{
{
TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]);
- if(maskLoadSuccess)
+ if(maskTextureInfo.loadState == LoadState::LOAD_FINISHED)
+ {
+ if(maskTextureInfo.storageType == StorageType::KEEP_PIXEL_BUFFER)
+ {
+ // Send New Task to Thread
+ ApplyMask(textureInfo, maskTextureInfo.textureId);
+ }
+ }
+ else if(maskTextureInfo.loadState == LoadState::UPLOADED)
{
- // Send New Task to Thread
- ApplyMask(textureInfo, maskTextureInfo.textureId);
+ 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
{
TextureManager::TextureId mAlphaMaskId;
float mContentScaleFactor;
bool mCropToMask;
+ bool mPreappliedMasking;
+ bool mMaskImageLoadingFailed;
};
using MaskingDataPointer = std::unique_ptr<MaskingData>;
* @brief 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.
* @param[in] maskUrl The URL of the mask image to load
+ * @param[in] storageType, Whether the pixel data is stored in the cache or uploaded to the GPU
* @param[in] synchronousLoading True if the frame should be loaded synchronously. If you skip this parameter,
* default is false.
* @return A TextureId to use as a handle to reference this mask Texture
*/
TextureId RequestMaskLoad(
const VisualUrl& maskUrl,
+ StorageType storageType,
const bool& synchronousLoading = false);
private:
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.
+ KEEP_TEXTURE, ///< Keep loaded texture inside of texture manager. This could be used for pixel processing like GPU masking.
UPLOAD_TO_TEXTURE ///< Loaded image will be uploaded to texture and the texture will be returned.
};
{
namespace
{
-const int CUSTOM_PROPERTY_COUNT(3); // ltr, wrap, pixel area,
+const int CUSTOM_PROPERTY_COUNT(10); // ltr, wrap, pixel area, crop to mask, mask texture ratio + border/corner
// stop behavior
DALI_ENUM_TO_STRING_TABLE_BEGIN(STOP_BEHAVIOR)
static constexpr auto LOOP_FOREVER = -1;
static constexpr auto FIRST_LOOP = 0u;
+const char* const MASK_TEXTURE_RATIO_NAME("maskTextureRatio");
+constexpr uint32_t TEXTURE_COUNT_FOR_GPU_ALPHA_MASK = 2u;
+
#if defined(DEBUG_ENABLED)
Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
#endif
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::MASKING_TYPE, mMaskingData->mPreappliedMasking ? DevelImageVisual::MaskingType::MASKING_ON_LOADING : DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
}
map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
{
DoSetProperty(Toolkit::ImageVisual::Property::CROP_TO_MASK, keyValue.second);
}
+ else if(keyValue.first == MASKING_TYPE_NAME)
+ {
+ DoSetProperty(Toolkit::DevelImageVisual::Property::MASKING_TYPE, keyValue.second);
+ }
else if(keyValue.first == LOAD_POLICY_NAME)
{
DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second);
break;
}
+ case Toolkit::DevelImageVisual::Property::MASKING_TYPE:
+ {
+ int maskingType = 0;
+ if(value.Get(maskingType))
+ {
+ AllocateMaskData();
+ mMaskingData->mPreappliedMasking = Toolkit::DevelImageVisual::MaskingType::Type(maskingType) == Toolkit::DevelImageVisual::MaskingType::MASKING_ON_LOADING ? true : false;
+ }
+ break;
+ }
+
case Toolkit::ImageVisual::Property::RELEASE_POLICY:
{
int releasePolicy = 0;
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 requiredAlphaMaskingOnRendering = (mMaskingData && !mMaskingData->mMaskImageLoadingFailed) ? !mMaskingData->mPreappliedMasking : false;
Shader shader;
shader = mImageVisualShaderFactory.GetShader(
mFactoryCache,
ImageVisualShaderFeature::FeatureBuilder()
.ApplyDefaultTextureWrapMode(defaultWrapMode)
.EnableRoundedCorner(IsRoundedCornerRequired())
- .EnableBorderline(IsBorderlineRequired()));
+ .EnableBorderline(IsBorderlineRequired())
+ .EnableAlphaMaskingOnRendering(requiredAlphaMaskingOnRendering));
return shader;
}
mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea);
}
+ if(mMaskingData)
+ {
+ mImpl->mRenderer.RegisterProperty(CROP_TO_MASK_NAME, static_cast<float>(mMaskingData->mCropToMask));
+ }
+
// Enable PreMultipliedAlpha if it need premultiplied
auto preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader
? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
if(mImpl->mRenderer)
{
mImpl->mRenderer.SetTextures(textureSet);
+ CheckMaskTexture();
Actor actor = mPlacementActor.GetHandle();
if(actor)
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<uint32_t>(mImageSize.GetWidth() * mMaskingData->mContentScaleFactor), maskTexture.GetWidth()));
+ mImageSize.SetHeight(std::min(static_cast<uint32_t>(mImageSize.GetHeight() * mMaskingData->mContentScaleFactor), maskTexture.GetHeight()));
+
+ float textureWidth = std::max(static_cast<float>(texture.GetWidth() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1);
+ float textureHeight = std::max(static_cast<float>(texture.GetHeight() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1);
+ Vector2 textureRatio(std::min(static_cast<float>(maskTexture.GetWidth()), textureWidth) / textureWidth,
+ std::min(static_cast<float>(maskTexture.GetHeight()), textureHeight) / textureHeight);
+ mImpl->mRenderer.RegisterProperty(MASK_TEXTURE_RATIO_NAME, textureRatio);
+ }
+ }
}
}
mFrameDelayTimer.SetInterval(interval);
}
mImpl->mRenderer.SetTextures(textureSet);
+ CheckMaskTexture();
}
}
}
if(mImpl->mRenderer)
{
mImpl->mRenderer.SetTextures(textureSet);
+ CheckMaskTexture();
}
mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex));
}
}
}
+void AnimatedImageVisual::CheckMaskTexture()
+{
+ if(mMaskingData && !mMaskingData->mPreappliedMasking)
+ {
+ bool maskLoadFailed = true;
+ TextureSet textures = mImpl->mRenderer.GetTextures();
+ if(textures && textures.GetTextureCount() >= TEXTURE_COUNT_FOR_GPU_ALPHA_MASK)
+ {
+ maskLoadFailed = false;
+ }
+ if(mMaskingData->mMaskImageLoadingFailed != maskLoadFailed)
+ {
+ mMaskingData->mMaskImageLoadingFailed = maskLoadFailed;
+ UpdateShader();
+ }
+ }
+}
+
} // namespace Internal
} // namespace Toolkit
*/
void AllocateMaskData();
+ /**
+ * @brief Check whether the mask texture is loaded or not.
+ * If MaskingType is MASKING_ON_LOADING and mask texture is failed to load, update shader.
+ */
+ void CheckMaskTexture();
+
// Undefined
AnimatedImageVisual(const AnimatedImageVisual& animatedImageVisual);
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
mTexture = texture;
return *this;
}
+FeatureBuilder& FeatureBuilder::EnableAlphaMaskingOnRendering(bool enableAlphaMaskingOnRendering)
+{
+ mAlphaMaskingOnRendering = (enableAlphaMaskingOnRendering ? AlphaMaskingOnRendering::ENABLED : AlphaMaskingOnRendering::DISABLED);
+ return *this;
+}
} // namespace ImageVisualShaderFeature
ImageVisualShaderFactory::ImageVisualShaderFactory()
Shader shader;
VisualFactoryCache::ShaderType shaderType = VisualFactoryCache::IMAGE_SHADER;
- const auto& atlasing = featureBuilder.mTextureAtlas;
- const auto& defaultTextureWrapping = featureBuilder.mDefaultTextureWrapMode;
- const auto& roundedCorner = featureBuilder.mRoundedCorner;
- const auto& borderline = featureBuilder.mBorderline;
- const auto& changeFragmentShader = (featureBuilder.mTexture && DevelTexture::IsNative(featureBuilder.mTexture))
- ? ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE
- : ImageVisualShaderFeature::ChangeFragmentShader::DONT_CHANGE;
+ const auto& atlasing = featureBuilder.mTextureAtlas;
+ const auto& defaultTextureWrapping = featureBuilder.mDefaultTextureWrapMode;
+ const auto& roundedCorner = featureBuilder.mRoundedCorner;
+ const auto& borderline = featureBuilder.mBorderline;
+ const auto& alphaMaskingOnRendering = featureBuilder.mAlphaMaskingOnRendering;
+ const auto& changeFragmentShader = (featureBuilder.mTexture && DevelTexture::IsNative(featureBuilder.mTexture))
+ ? ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE
+ : ImageVisualShaderFeature::ChangeFragmentShader::DONT_CHANGE;
if(atlasing == ImageVisualShaderFeature::TextureAtlas::ENABLED)
{
}
else
{
+ uint32_t shaderTypeFlag = static_cast<uint32_t>(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<uint32_t>(ImageVisualRequireFlag::ROUNDED_CORNER);
}
- else
+ if(borderline == ImageVisualShaderFeature::Borderline::ENABLED)
{
- if(borderline == ImageVisualShaderFeature::Borderline::ENABLED)
- {
- shaderType = VisualFactoryCache::IMAGE_SHADER_BORDERLINE;
- }
+ shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::BORDERLINE);
+ }
+ if(alphaMaskingOnRendering == ImageVisualShaderFeature::AlphaMaskingOnRendering::ENABLED)
+ {
+ shaderTypeFlag |= static_cast<uint32_t>(ImageVisualRequireFlag::ALPHA_MASKING);
}
+ shaderType = SHADER_TYPE_TABLE[shaderTypeFlag];
}
if(changeFragmentShader == ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE &&
vertexShaderPrefixList += "#define IS_REQUIRED_BORDERLINE 1\n";
fragmentShaderPrefixList += "#define IS_REQUIRED_BORDERLINE 1\n";
}
+ if(alphaMaskingOnRendering == ImageVisualShaderFeature::AlphaMaskingOnRendering::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());
};
} // namespace ChangeFragmentShader
+namespace AlphaMaskingOnRendering
+{
+/**
+ * @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 AlphaMaskingOnRendering
+
/**
* @brief Collection of current image visual feature. Only use for ImageVisualShaderFactory::GetShader()
*/
mDefaultTextureWrapMode(DefaultTextureWrapMode::APPLY),
mRoundedCorner(RoundedCorner::DISABLED),
mBorderline(Borderline::DISABLED),
+ mAlphaMaskingOnRendering(AlphaMaskingOnRendering::DISABLED),
mTexture()
{
}
FeatureBuilder& EnableRoundedCorner(bool enableRoundedCorner);
FeatureBuilder& EnableBorderline(bool enableBorderline);
FeatureBuilder& SetTextureForFragmentShaderCheck(const Dali::Texture& texture);
-
- 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
- Dali::Texture mTexture; ///< Texture to check whether we need to change fragment shader or not
+ FeatureBuilder& EnableAlphaMaskingOnRendering(bool enableAlphaMaskingOnRendering);
+
+ 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
+ AlphaMaskingOnRendering::Type mAlphaMaskingOnRendering : 2; ///< Whether use runtime alpha masking, or not. default as AlphaMaskingOnRendering::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
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,
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 MASKING_TYPE_NAME("maskingType");
// Text visual
const char* const TEXT_PROPERTY("text");
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 MASKING_TYPE_NAME;
// Text visual
extern const char* const TEXT_PROPERTY;