X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fvisuals%2Fanimated-image%2Fanimated-image-visual.cpp;h=382d52be83d775fec9eed00de954c6133db4773b;hb=HEAD;hp=9693cc834ffa57bc63ced05006c9403064e7cd3c;hpb=4f6a1937f912ecec09d2ba09e2cbeedae881061e;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git 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 9693cc8..d94e0b5 100644 --- a/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp +++ b/dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2024 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. @@ -16,11 +16,13 @@ */ // CLASS HEADER -#include "animated-image-visual.h" +#include // EXTERNAL INCLUDES #include +#include #include +#include #include // INTERNAL INCLUDES @@ -31,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +49,28 @@ namespace Internal { namespace { +const int CUSTOM_PROPERTY_COUNT(5); // ltr, wrap, pixel area, crop to mask, mask texture ratio + +// fitting modes +DALI_ENUM_TO_STRING_TABLE_BEGIN(FITTING_MODE) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, SHRINK_TO_FIT) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, SCALE_TO_FILL) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, FIT_WIDTH) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, FIT_HEIGHT) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, DEFAULT) +DALI_ENUM_TO_STRING_TABLE_END(FITTING_MODE) + +// sampling modes +DALI_ENUM_TO_STRING_TABLE_BEGIN(SAMPLING_MODE) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, BOX) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, NEAREST) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, LINEAR) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, BOX_THEN_NEAREST) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, BOX_THEN_LINEAR) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, NO_FILTER) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, DONT_CARE) +DALI_ENUM_TO_STRING_TABLE_END(SAMPLING_MODE) + // stop behavior DALI_ENUM_TO_STRING_TABLE_BEGIN(STOP_BEHAVIOR) DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::DevelImageVisual::StopBehavior, CURRENT_FRAME) @@ -61,8 +86,27 @@ DALI_ENUM_TO_STRING_TABLE_BEGIN(WRAP_MODE) DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, MIRRORED_REPEAT) DALI_ENUM_TO_STRING_TABLE_END(WRAP_MODE) -const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f); -constexpr auto LOOP_FOREVER = -1; +// load policies +DALI_ENUM_TO_STRING_TABLE_BEGIN(LOAD_POLICY) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::LoadPolicy, IMMEDIATE) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::LoadPolicy, ATTACHED) +DALI_ENUM_TO_STRING_TABLE_END(LOAD_POLICY) + +// release policies +DALI_ENUM_TO_STRING_TABLE_BEGIN(RELEASE_POLICY) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, DETACHED) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, DESTROYED) + DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, NEVER) +DALI_ENUM_TO_STRING_TABLE_END(RELEASE_POLICY) + +static constexpr uint32_t SINGLE_IMAGE_COUNT = 1u; +static constexpr uint32_t FIRST_FRAME_INDEX = 0u; +static constexpr uint16_t MINIMUM_CACHESIZE = 1; +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; + +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"); @@ -74,18 +118,15 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, " * * | New * | DoSetProperties() - * | LoadFirstBatch() - * | new cache - * | cache->LoadBatch() + * | OnInitialize() + * | CreateImageCache() * | * | DoSetOnScene() * | PrepareTextureSet() * | cache->FirstFrame() - * | CreateRenderer() (Doesn't become ready until first frame loads) - * | StartFirstFrame() * | * | FrameReady(textureSet) - * | start first frame: + * | StartFirstFrame: * | actor.AddRenderer * | start timer * | mRenderer.SetTextures(textureSet) @@ -95,8 +136,7 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, " * | if front frame is ready, * | mRenderer.SetTextures( front frame's texture ) * | else - * | mWaitingForTexture=true - * | cache->LoadBatch() + * | Waiting for frame ready. * | * | FrameReady(textureSet) * | mRenderer.SetTextures(textureSet) @@ -106,15 +146,10 @@ Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, " AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, const Property::Map& properties) { - AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory)); + AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory, ImageDimensions())); visual->InitializeAnimatedImage(imageUrl); visual->SetProperties(properties); - if(visual->mFrameCount > 0) - { - visual->LoadFirstBatch(); - } - visual->Initialize(); return visual; @@ -122,7 +157,7 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const Property::Array& imageUrls, const Property::Map& properties) { - AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory)); + AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory, ImageDimensions())); visual->mImageUrls = new ImageCache::UrlList(); visual->mImageUrls->reserve(imageUrls.Count()); @@ -136,26 +171,16 @@ AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache visual->mFrameCount = imageUrls.Count(); visual->SetProperties(properties); - if(visual->mFrameCount > 0) - { - visual->LoadFirstBatch(); - } - visual->Initialize(); return visual; } -AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl) +AnimatedImageVisualPtr AnimatedImageVisual::New(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, const VisualUrl& imageUrl, ImageDimensions size) { - AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory)); + AnimatedImageVisualPtr visual(new AnimatedImageVisual(factoryCache, shaderFactory, size)); visual->InitializeAnimatedImage(imageUrl); - if(visual->mFrameCount > 0) - { - visual->LoadFirstBatch(); - } - visual->Initialize(); return visual; @@ -165,11 +190,59 @@ void AnimatedImageVisual::InitializeAnimatedImage(const VisualUrl& imageUrl) { mImageUrl = imageUrl; mAnimatedImageLoading = AnimatedImageLoading::New(imageUrl.GetUrl(), imageUrl.IsLocalResource()); - mFrameCount = mAnimatedImageLoading.GetImageCount(); + + // If we fail to load the animated image, we will try to load as a normal image. + if(!mAnimatedImageLoading) + { + mImageUrls = new ImageCache::UrlList(); + mImageUrls->reserve(SINGLE_IMAGE_COUNT); + + for(unsigned int i = 0; i < SINGLE_IMAGE_COUNT; ++i) + { + ImageCache::UrlStore urlStore; + urlStore.mTextureId = TextureManager::INVALID_TEXTURE_ID; + urlStore.mUrl = imageUrl; + mImageUrls->push_back(urlStore); + } + mFrameCount = SINGLE_IMAGE_COUNT; + } +} + +void AnimatedImageVisual::CreateImageCache() +{ + DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::CreateImageCache() batchSize:%d cacheSize:%d\n", mBatchSize, mCacheSize); + + TextureManager& textureManager = mFactoryCache.GetTextureManager(); + + if(mAnimatedImageLoading) + { + mImageCache = new RollingAnimatedImageCache(textureManager, mDesiredSize, mFittingMode, mSamplingMode, mAnimatedImageLoading, mMaskingData, *this, mCacheSize, mBatchSize, mWrapModeU, mWrapModeV, IsSynchronousLoadingRequired(), IsPreMultipliedAlphaEnabled()); + } + else if(mImageUrls) + { + // Ensure the batch size and cache size are no bigger than the number of URLs, + // and that the cache is at least as big as the batch size. + uint16_t numUrls = mImageUrls->size(); + uint16_t batchSize = std::max(std::min(mBatchSize, numUrls), MINIMUM_CACHESIZE); + uint16_t cacheSize = std::max(std::min(std::max(batchSize, mCacheSize), numUrls), MINIMUM_CACHESIZE); + if(cacheSize < numUrls) + { + mImageCache = new RollingImageCache(textureManager, mDesiredSize, mFittingMode, mSamplingMode, *mImageUrls, mMaskingData, *this, cacheSize, batchSize, mFrameDelay, IsPreMultipliedAlphaEnabled()); + } + else + { + mImageCache = new FixedImageCache(textureManager, mDesiredSize, mFittingMode, mSamplingMode, *mImageUrls, mMaskingData, *this, batchSize, mFrameDelay, IsPreMultipliedAlphaEnabled()); + } + } + + if(DALI_UNLIKELY(!mImageCache)) + { + DALI_LOG_ERROR("mImageCache is null\n"); + } } -AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory) -: Visual::Base(factoryCache, Visual::FittingMode::FIT_KEEP_ASPECT_RATIO, Toolkit::Visual::ANIMATED_IMAGE), +AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, ImageVisualShaderFactory& shaderFactory, ImageDimensions desiredSize) +: Visual::Base(factoryCache, Visual::FittingMode::DONT_CARE, Toolkit::Visual::ANIMATED_IMAGE), mFrameDelayTimer(), mPlacementActor(), mImageVisualShaderFactory(shaderFactory), @@ -177,42 +250,99 @@ AnimatedImageVisual::AnimatedImageVisual(VisualFactoryCache& factoryCache, Image mImageUrl(), mAnimatedImageLoading(), mFrameIndexForJumpTo(0), + mCurrentFrameIndex(FIRST_FRAME_INDEX), mImageUrls(NULL), mImageCache(NULL), mCacheSize(2), mBatchSize(2), mFrameDelay(100), mLoopCount(LOOP_FOREVER), - mCurrentLoopIndex(0), - mUrlIndex(0), + mCurrentLoopIndex(FIRST_LOOP), + mLoadPolicy(Toolkit::ImageVisual::LoadPolicy::ATTACHED), + mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED), + mMaskingData(), + mDesiredSize(desiredSize), mFrameCount(0), mImageSize(), + mActionStatus(DevelAnimatedImageVisual::Action::PLAY), mWrapModeU(WrapMode::DEFAULT), mWrapModeV(WrapMode::DEFAULT), - mActionStatus(DevelAnimatedImageVisual::Action::PLAY), + mFittingMode(FittingMode::VISUAL_FITTING), + mSamplingMode(SamplingMode::BOX_THEN_LINEAR), mStopBehavior(DevelImageVisual::StopBehavior::CURRENT_FRAME), mStartFirstFrame(false), mIsJumpTo(false) { + EnablePreMultipliedAlpha(mFactoryCache.GetPreMultiplyOnLoad()); } AnimatedImageVisual::~AnimatedImageVisual() { + // AnimatedImageVisual destroyed so remove texture unless ReleasePolicy is set to never release + // If this is animated image, clear cache. Else if this is single frame image, this is affected be release policy. + if(mFrameCount > SINGLE_IMAGE_COUNT || mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER) + { + if(DALI_LIKELY(mImageCache)) + { + mImageCache->ClearCache(); + } + } delete mImageCache; delete mImageUrls; } void AnimatedImageVisual::GetNaturalSize(Vector2& naturalSize) { + if(mDesiredSize.GetWidth() > 0 && mDesiredSize.GetHeight() > 0) + { + if(mImpl->mRenderer) + { + auto textureSet = mImpl->mRenderer.GetTextures(); + if(textureSet && textureSet.GetTextureCount()) + { + auto texture = textureSet.GetTexture(0); + if(texture) + { + Dali::Vector2 textureSize; + textureSize.x = texture.GetWidth(); + textureSize.y = texture.GetHeight(); + if(textureSize != Vector2::ZERO) + { + naturalSize = textureSize; + return; + } + } + } + } + + naturalSize.x = mDesiredSize.GetWidth(); + naturalSize.y = mDesiredSize.GetHeight(); + return; + } + + naturalSize = Vector2::ZERO; if(mImageSize.GetWidth() == 0 && mImageSize.GetHeight() == 0) { - if(mImageUrl.IsValid()) + if(mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid() && + mMaskingData->mCropToMask) + { + 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() && mAnimatedImageLoading) { mImageSize = mAnimatedImageLoading.GetImageSize(); } else if(mImageUrls && mImageUrls->size() > 0) { - mImageSize = Dali::GetClosestImageSize((*mImageUrls)[0].mUrl); + mImageSize = Dali::GetClosestImageSize((*mImageUrls)[0].mUrl.GetUrl()); } } @@ -238,7 +368,7 @@ void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const Property::Array urls; for(unsigned int i = 0; i < mImageUrls->size(); ++i) { - urls.Add((*mImageUrls)[i].mUrl); + urls.Add((*mImageUrls)[i].mUrl.GetUrl()); } Property::Value value(const_cast(urls)); map.Insert(Toolkit::ImageVisual::Property::URL, value); @@ -253,14 +383,49 @@ void AnimatedImageVisual::DoCreatePropertyMap(Property::Map& map) const map.Insert(Toolkit::ImageVisual::Property::FRAME_DELAY, static_cast(mFrameDelay)); map.Insert(Toolkit::DevelImageVisual::Property::LOOP_COUNT, static_cast(mLoopCount)); map.Insert(Toolkit::DevelImageVisual::Property::CURRENT_FRAME_NUMBER, (mImageCache) ? static_cast(mImageCache->GetCurrentFrameIndex()) : -1); - map.Insert(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, (mImageCache) ? static_cast(mImageCache->GetTotalFrameCount()) : -1); + + // This returns -1 until the loading is finished. + auto frameCount = int32_t(mFrameCount); + if(mImageCache && frameCount == 0) + { + frameCount = mImageCache->GetTotalFrameCount(); + + if(frameCount <= int32_t(SINGLE_IMAGE_COUNT) && mAnimatedImageLoading && mAnimatedImageLoading.HasLoadingSucceeded()) + { + frameCount = int32_t(mAnimatedImageLoading.GetImageCount()); + } + else + { + frameCount = -1; + } + } + + map.Insert(Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, static_cast(frameCount)); map.Insert(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mStopBehavior); + + if(mMaskingData != nullptr) + { + 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); + map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy); + map.Insert(Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode); + map.Insert(Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode); + map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth()); + map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight()); } void AnimatedImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const { - // Do nothing + map.Clear(); + map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::ANIMATED_IMAGE); + map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth()); + map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight()); } void AnimatedImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes) @@ -293,7 +458,8 @@ void AnimatedImageVisual::OnDoAction(const Dali::Property::Index actionId, const { // STOP reset functionality will actually be done in a future change // Stop will be executed on next timer tick - mActionStatus = DevelAnimatedImageVisual::Action::STOP; + mActionStatus = DevelAnimatedImageVisual::Action::STOP; + mCurrentLoopIndex = FIRST_LOOP; if(IsOnScene()) { DisplayNextFrame(); @@ -368,8 +534,57 @@ void AnimatedImageVisual::DoSetProperties(const Property::Map& propertyMap) { DoSetProperty(Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, keyValue.second); } + else if(keyValue.first == ALPHA_MASK_URL) + { + DoSetProperty(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, keyValue.second); + } + else if(keyValue.first == MASK_CONTENT_SCALE_NAME) + { + DoSetProperty(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, keyValue.second); + } + else if(keyValue.first == CROP_TO_MASK_NAME) + { + 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); + } + else if(keyValue.first == RELEASE_POLICY_NAME) + { + DoSetProperty(Toolkit::ImageVisual::Property::RELEASE_POLICY, keyValue.second); + } + else if(keyValue.first == SYNCHRONOUS_LOADING) + { + DoSetProperty(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second); + } + else if(keyValue.first == IMAGE_FITTING_MODE) + { + DoSetProperty(Toolkit::ImageVisual::Property::FITTING_MODE, keyValue.second); + } + else if(keyValue.first == IMAGE_SAMPLING_MODE) + { + DoSetProperty(Toolkit::ImageVisual::Property::SAMPLING_MODE, keyValue.second); + } + else if(keyValue.first == IMAGE_DESIRED_WIDTH) + { + DoSetProperty(Toolkit::ImageVisual::Property::DESIRED_WIDTH, keyValue.second); + } + else if(keyValue.first == IMAGE_DESIRED_HEIGHT) + { + DoSetProperty(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, keyValue.second); + } } } + // Load image immediately if LOAD_POLICY requires it + if(mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::IMMEDIATE) + { + PrepareTextureSet(); + } } void AnimatedImageVisual::DoSetProperty(Property::Index index, @@ -449,6 +664,10 @@ void AnimatedImageVisual::DoSetProperty(Property::Index index, if(value.Get(frameDelay)) { mFrameDelay = frameDelay; + if(DALI_LIKELY(mImageCache)) + { + mImageCache->SetInterval(static_cast(mFrameDelay)); + } } break; } @@ -479,11 +698,115 @@ void AnimatedImageVisual::DoSetProperty(Property::Index index, value.Get(sync); if(sync) { - mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING; + mImpl->mFlags |= Visual::Base::Impl::IS_SYNCHRONOUS_RESOURCE_LOADING; + } + else + { + mImpl->mFlags &= ~Visual::Base::Impl::IS_SYNCHRONOUS_RESOURCE_LOADING; + } + break; + } + + case Toolkit::ImageVisual::Property::ALPHA_MASK_URL: + { + std::string alphaUrl = ""; + if(value.Get(alphaUrl)) + { + AllocateMaskData(); + mMaskingData->mAlphaMaskUrl = alphaUrl; + } + break; + } + + case Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE: + { + float scale = 1.0f; + if(value.Get(scale)) + { + AllocateMaskData(); + mMaskingData->mContentScaleFactor = scale; + } + break; + } + + case Toolkit::ImageVisual::Property::CROP_TO_MASK: + { + bool crop = false; + if(value.Get(crop)) + { + AllocateMaskData(); + mMaskingData->mCropToMask = crop; + } + 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; + Scripting::GetEnumerationProperty(value, RELEASE_POLICY_TABLE, RELEASE_POLICY_TABLE_COUNT, releasePolicy); + mReleasePolicy = Toolkit::ImageVisual::ReleasePolicy::Type(releasePolicy); + break; + } + + case Toolkit::ImageVisual::Property::LOAD_POLICY: + { + int loadPolicy = 0; + Scripting::GetEnumerationProperty(value, LOAD_POLICY_TABLE, LOAD_POLICY_TABLE_COUNT, loadPolicy); + mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type(loadPolicy); + break; + } + + case Toolkit::ImageVisual::Property::FITTING_MODE: + { + int fittingMode = 0; + Scripting::GetEnumerationProperty(value, FITTING_MODE_TABLE, FITTING_MODE_TABLE_COUNT, fittingMode); + mFittingMode = Dali::FittingMode::Type(fittingMode); + break; + } + + case Toolkit::ImageVisual::Property::SAMPLING_MODE: + { + int samplingMode = 0; + Scripting::GetEnumerationProperty(value, SAMPLING_MODE_TABLE, SAMPLING_MODE_TABLE_COUNT, samplingMode); + mSamplingMode = Dali::SamplingMode::Type(samplingMode); + break; + } + + case Toolkit::ImageVisual::Property::DESIRED_WIDTH: + { + float desiredWidth = 0.0f; + if(value.Get(desiredWidth)) + { + mDesiredSize.SetWidth(desiredWidth); } else { - mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING; + DALI_LOG_ERROR("AnimatedImageVisual: desiredWidth property has incorrect type\n"); + } + break; + } + + case Toolkit::ImageVisual::Property::DESIRED_HEIGHT: + { + float desiredHeight = 0.0f; + if(value.Get(desiredHeight)) + { + mDesiredSize.SetHeight(desiredHeight); + } + else + { + DALI_LOG_ERROR("AnimatedImageVisual: desiredHeight property has incorrect type\n"); } break; } @@ -492,23 +815,17 @@ void AnimatedImageVisual::DoSetProperty(Property::Index index, void AnimatedImageVisual::DoSetOnScene(Actor& actor) { - mPlacementActor = actor; - TextureSet textureSet = PrepareTextureSet(); + mStartFirstFrame = true; + mPlacementActor = actor; + PrepareTextureSet(); - // Loading animated image file is failed. - if(!mImageCache || - (mAnimatedImageLoading && !mAnimatedImageLoading.HasLoadingSucceeded())) - { - textureSet = SetLoadingFailed(); - } + actor.InheritedVisibilityChangedSignal().Connect(this, &AnimatedImageVisual::OnControlInheritedVisibilityChanged); - if(textureSet) // if the image loading is successful - { - StartFirstFrame(textureSet); - } - else + Window window = DevelWindow::Get(actor); + if(window) { - mStartFirstFrame = true; + mPlacementWindow = window; + DevelWindow::VisibilityChangedSignal(window).Connect(this, &AnimatedImageVisual::OnWindowVisibilityChanged); } } @@ -523,15 +840,38 @@ void AnimatedImageVisual::DoSetOffScene(Actor& actor) } actor.RemoveRenderer(mImpl->mRenderer); + if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED) + { + if(DALI_LIKELY(mImageCache)) + { + mImageCache->ClearCache(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas + } + mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING; + + TextureSet textureSet = TextureSet::New(); + mImpl->mRenderer.SetTextures(textureSet); + } + mPlacementActor.Reset(); - mStartFirstFrame = false; + mStartFirstFrame = false; + mCurrentFrameIndex = FIRST_FRAME_INDEX; + mCurrentLoopIndex = FIRST_LOOP; + + actor.InheritedVisibilityChangedSignal().Disconnect(this, &AnimatedImageVisual::OnControlInheritedVisibilityChanged); + + Window window = mPlacementWindow.GetHandle(); + if(window) + { + DevelWindow::VisibilityChangedSignal(window).Disconnect(this, &AnimatedImageVisual::OnWindowVisibilityChanged); + mPlacementWindow.Reset(); + } } void AnimatedImageVisual::OnSetTransform() { if(mImpl->mRenderer) { - mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT); + mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT); } } @@ -544,17 +884,35 @@ void AnimatedImageVisual::UpdateShader() } } +Shader AnimatedImageVisual::GenerateShader() const +{ + 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, + ImageVisualShaderFeatureBuilder() + .ApplyDefaultTextureWrapMode(defaultWrapMode) + .EnableRoundedCorner(IsRoundedCornerRequired()) + .EnableBorderline(IsBorderlineRequired()) + .EnableAlphaMaskingOnRendering(requiredAlphaMaskingOnRendering)); + return shader; +} + void AnimatedImageVisual::OnInitialize() { + CreateImageCache(); + bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE; Shader shader = GenerateShader(); Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY); - mImpl->mRenderer = Renderer::New(geometry, shader); + mImpl->mRenderer = DecoratedVisualRenderer::New(geometry, shader); + mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT); // Register transform properties - mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT); + mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT); if(!defaultWrapMode) // custom wrap mode { @@ -567,63 +925,20 @@ void AnimatedImageVisual::OnInitialize() { mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea); } -} -void AnimatedImageVisual::LoadFirstBatch() -{ - // Ensure the batch size and cache size are no bigger than the number of URLs, - // and that the cache is at least as big as the batch size. - uint16_t numUrls = 0; - uint16_t batchSize = 1; - uint16_t cacheSize = 1; - - if(mImageUrls) + if(mMaskingData) { - numUrls = mImageUrls->size(); + mImpl->mRenderer.RegisterProperty(CROP_TO_MASK_NAME, static_cast(mMaskingData->mCropToMask)); } - else - { - numUrls = mFrameCount; - } - - batchSize = std::min(mBatchSize, numUrls); - cacheSize = std::min(std::max(batchSize, mCacheSize), numUrls); - DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::LoadFirstBatch() batchSize:%d cacheSize:%d\n", batchSize, cacheSize); - - mUrlIndex = 0; - TextureManager& textureManager = mFactoryCache.GetTextureManager(); - - if(mAnimatedImageLoading) - { - mImageCache = new RollingAnimatedImageCache(textureManager, mAnimatedImageLoading, mFrameCount, *this, cacheSize, batchSize, IsSynchronousLoadingRequired()); - } - else if(mImageUrls) - { - if(batchSize > 0 && cacheSize > 0) - { - if(cacheSize < numUrls) - { - mImageCache = new RollingImageCache(textureManager, *mImageUrls, *this, cacheSize, batchSize); - } - else - { - mImageCache = new FixedImageCache(textureManager, *mImageUrls, *this, batchSize); - } - } - else - { - mImageCache = new RollingImageCache(textureManager, *mImageUrls, *this, 1, 1); - } - } - - if(!mImageCache) - { - DALI_LOG_ERROR("mImageCache is null\n"); - } + // Enable PreMultipliedAlpha if it need premultiplied + auto preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader + ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD + : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + EnablePreMultipliedAlpha(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD); } -void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet) +void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet, uint32_t firstInterval) { DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::StartFirstFrame()\n"); @@ -631,6 +946,7 @@ void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet) if(mImpl->mRenderer) { mImpl->mRenderer.SetTextures(textureSet); + CheckMaskTexture(); Actor actor = mPlacementActor.GetHandle(); if(actor) @@ -640,39 +956,40 @@ void AnimatedImageVisual::StartFirstFrame(TextureSet& textureSet) } } - if(mFrameCount > 1) + if(mImpl->mResourceStatus != Toolkit::Visual::ResourceStatus::FAILED) { - int frameDelay = mImageCache->GetFrameInterval(0); - if(frameDelay == 0u) + if(mFrameCount > SINGLE_IMAGE_COUNT) { - frameDelay = mFrameDelay; // from URL array + mFrameDelayTimer = Timer::New(firstInterval); + mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame); + mFrameDelayTimer.Start(); } - mFrameDelayTimer = Timer::New(frameDelay); - mFrameDelayTimer.TickSignal().Connect(this, &AnimatedImageVisual::DisplayNextFrame); - mFrameDelayTimer.Start(); - } - if(mImpl->mResourceStatus != Toolkit::Visual::ResourceStatus::FAILED) - { DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::READY)\n"); ResourceReady(Toolkit::Visual::ResourceStatus::READY); } + + mCurrentFrameIndex = FIRST_FRAME_INDEX; } -TextureSet AnimatedImageVisual::PrepareTextureSet() +void AnimatedImageVisual::PrepareTextureSet() { TextureSet textureSet; - if(mImageCache) + if(DALI_LIKELY(mImageCache)) { textureSet = mImageCache->FirstFrame(); } + else + { + // preMultiplied should be false because broken image don't premultiply alpha on load + FrameReady(TextureSet(), 0, false); + } + // Check whether synchronous loading is true or false for the first frame. if(textureSet) { SetImageSize(textureSet); } - - return textureSet; } void AnimatedImageVisual::SetImageSize(TextureSet& textureSet) @@ -685,28 +1002,54 @@ 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); + } + } } } -void AnimatedImageVisual::FrameReady(TextureSet textureSet) +void AnimatedImageVisual::FrameReady(TextureSet textureSet, uint32_t interval, bool preMultiplied) { + EnablePreMultipliedAlpha(preMultiplied); + // When image visual requested to load new frame to mImageCache and it is failed. - if(!textureSet) + if(!mImageCache || !textureSet) { textureSet = SetLoadingFailed(); } - SetImageSize(textureSet); if(mStartFirstFrame) { - StartFirstFrame(textureSet); + if(DALI_LIKELY(mImageCache)) + { + mFrameCount = mImageCache->GetTotalFrameCount(); + } + StartFirstFrame(textureSet, interval); } else { if(mImpl->mRenderer) { + if(mFrameDelayTimer && interval > 0u) + { + mFrameDelayTimer.SetInterval(interval); + } mImpl->mRenderer.SetTextures(textureSet); + CheckMaskTexture(); } } } @@ -716,9 +1059,8 @@ bool AnimatedImageVisual::DisplayNextFrame() TextureSet textureSet; bool continueTimer = false; - if(mImageCache) + if(DALI_LIKELY(mImageCache)) { - bool nextFrame = false; uint32_t frameIndex = mImageCache->GetCurrentFrameIndex(); if(mIsJumpTo) @@ -732,10 +1074,10 @@ bool AnimatedImageVisual::DisplayNextFrame() } else if(mActionStatus == DevelAnimatedImageVisual::Action::STOP) { - frameIndex = 0; + mCurrentLoopIndex = FIRST_LOOP; if(mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME) { - frameIndex = 0; + frameIndex = FIRST_FRAME_INDEX; } else if(mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME) { @@ -748,13 +1090,12 @@ bool AnimatedImageVisual::DisplayNextFrame() } else { - if(mFrameCount > 1) + if(mFrameCount > SINGLE_IMAGE_COUNT) { - nextFrame = true; frameIndex++; if(frameIndex >= mFrameCount) { - frameIndex %= mFrameCount; + frameIndex = FIRST_FRAME_INDEX; ++mCurrentLoopIndex; } @@ -765,38 +1106,25 @@ bool AnimatedImageVisual::DisplayNextFrame() return DisplayNextFrame(); } } - - unsigned int delay = mImageCache->GetFrameInterval(frameIndex); - if(delay > 0u) - { - if(mFrameDelayTimer.GetInterval() != delay) - { - mFrameDelayTimer.SetInterval(delay); - } - } } DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex); - if(nextFrame) - { - textureSet = mImageCache->NextFrame(); - } - else - { - textureSet = mImageCache->Frame(frameIndex); - } - - continueTimer = (mActionStatus == DevelAnimatedImageVisual::Action::PLAY) ? true : false; - } + textureSet = mImageCache->Frame(frameIndex); - if(textureSet) - { - SetImageSize(textureSet); - if(mImpl->mRenderer) + if(textureSet) { - mImpl->mRenderer.SetTextures(textureSet); + SetImageSize(textureSet); + if(mImpl->mRenderer) + { + mImpl->mRenderer.SetTextures(textureSet); + CheckMaskTexture(); + } + mFrameDelayTimer.SetInterval(mImageCache->GetFrameInterval(frameIndex)); } + + mCurrentFrameIndex = frameIndex; + continueTimer = (mActionStatus == DevelAnimatedImageVisual::Action::PLAY && textureSet) ? true : false; } return continueTimer; @@ -807,14 +1135,19 @@ TextureSet AnimatedImageVisual::SetLoadingFailed() DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "ResourceReady(ResourceStatus::FAILED)\n"); ResourceReady(Toolkit::Visual::ResourceStatus::FAILED); - Actor actor = mPlacementActor.GetHandle(); + Actor actor = mPlacementActor.GetHandle(); Vector2 imageSize = Vector2::ZERO; if(actor) { imageSize = actor.GetProperty(Actor::Property::SIZE).Get(); } - mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize); - TextureSet textureSet = mImpl->mRenderer.GetTextures(); + + TextureSet textureSet; + if(DALI_LIKELY(mImpl->mRenderer)) + { + mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize); + textureSet = mImpl->mRenderer.GetTextures(); + } if(mFrameDelayTimer) { @@ -827,17 +1160,50 @@ TextureSet AnimatedImageVisual::SetLoadingFailed() return textureSet; } -Shader AnimatedImageVisual::GenerateShader() const +void AnimatedImageVisual::AllocateMaskData() { - bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE; - Shader shader; - shader = mImageVisualShaderFactory.GetShader( - mFactoryCache, - TextureAtlas::DISABLED, - defaultWrapMode ? DefaultTextureWrapMode::APPLY : DefaultTextureWrapMode::DO_NOT_APPLY, - IsRoundedCornerRequired() ? RoundedCorner::ENABLED : RoundedCorner::DISABLED, - IsBorderlineRequired() ? Borderline::ENABLED : Borderline::DISABLED); - return shader; + if(!mMaskingData) + { + mMaskingData.reset(new TextureManager::MaskingData()); + } +} + +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(); + } + } +} + +void AnimatedImageVisual::OnControlInheritedVisibilityChanged(Actor actor, bool visible) +{ + if(!visible && mActionStatus != DevelAnimatedImageVisual::Action::STOP) + { + mActionStatus = DevelAnimatedImageVisual::Action::STOP; + DisplayNextFrame(); + DALI_LOG_INFO(gAnimImgLogFilter, Debug::Verbose, "AnimatedImageVisual::OnControlInheritedVisibilityChanged: invisibile. Pause animation [%p]\n", this); + } +} + +void AnimatedImageVisual::OnWindowVisibilityChanged(Window window, bool visible) +{ + if(!visible && mActionStatus != DevelAnimatedImageVisual::Action::STOP) + { + mActionStatus = DevelAnimatedImageVisual::Action::STOP; + DisplayNextFrame(); + DALI_LOG_INFO(gAnimImgLogFilter, Debug::Verbose, "AnimatedImageVisual::OnWindowVisibilityChanged: invisibile. Pause animation [%p]\n", this); + } } } // namespace Internal