From: Eunki, Hong Date: Tue, 29 Aug 2023 06:52:47 +0000 (+0900) Subject: Guard NPatchData removal case during signal emit X-Git-Tag: dali_2.2.43~1^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=11c59a90d2751eedeb979707864580682ad9179f;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git Guard NPatchData removal case during signal emit It was possible that NPatchData removed during NotifyObserver. For more safety, let we make NPatchData as shared_ptr, instead of unique_ptr. And also, let we don't touch observer list container during NotifyObservers. And also, let we remove NPatchInfo at post processing. It will keep the life of NPatchData during NotifyObserver try to remove the NPatchInfo. Change-Id: Ieca33a2231df38d23966f96593506d67333cfcd0 Signed-off-by: Eunki, Hong --- diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp index ccc8eae..6fb05ab 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp @@ -71,6 +71,8 @@ const char* TEST_BROKEN_IMAGE_L = TEST_RESOURCE_DIR "/broken_l.9.png"; const char* TEST_BROKEN_IMAGE_01 = TEST_RESOURCE_DIR "/button-up.9.png"; const char* TEST_BROKEN_IMAGE_02 = TEST_RESOURCE_DIR "/heartsframe.9.png"; +const char* TEST_INVALID_NPATCH_FILE_NAME_01 = "invalid1.9.png"; + // resolution: 34*34, pixel format: RGBA8888 static const char* gImage_34_RGBA = TEST_RESOURCE_DIR "/icon-edit.png"; // resolution: 600*600, pixel format: RGB888 @@ -3672,6 +3674,45 @@ void OnResourceReadySignal07(Control control) } } +void OnResourceReadySignal08(Control control) +{ + gResourceReadySignalCounter++; + + if(gImageView1) + { + gImageView1.Unparent(); + gImageView1.Reset(); + } + if(gImageView2) + { + gImageView2.Unparent(); + gImageView2.Reset(); + } +} + +std::size_t gResourceReadySignal09Emitted = false; + +void OnResourceReadySignal09(Control control) +{ + gResourceReadySignalCounter++; + + if(gImageView1 && !gResourceReadySignal09Emitted) + { + gResourceReadySignal09Emitted = true; + gImageView1.ResourceReadySignal().Disconnect(&OnResourceReadySignal09); + + // Try to load cached invalid nine patch image. It will request load now. + gImageView1.SetImage(TEST_INVALID_NPATCH_FILE_NAME_01); + gImageView2.SetImage(TEST_INVALID_NPATCH_FILE_NAME_01); + + // Destroy all visuals immediatly. + gImageView1.Unparent(); + gImageView1.Reset(); + gImageView2.Unparent(); + gImageView2.Reset(); + } +} + } // namespace int UtcDaliImageViewSetImageOnResourceReadySignal01(void) @@ -4208,6 +4249,239 @@ int UtcDaliImageViewSetImageOnResourceReadySignal07(void) END_TEST; } +int UtcDaliImageViewSetImageOnResourceReadySignal08(void) +{ + tet_infoline("Test remove npatch images during resource ready"); + + ToolkitTestApplication application; + + gResourceReadySignalCounter = 0; + + Property::Map map; + map[Toolkit::ImageVisual::Property::URL] = TEST_BROKEN_IMAGE_M; + + // Clear image view for clear test + + if(gImageView1) + { + gImageView1.Reset(); + } + if(gImageView2) + { + gImageView2.Reset(); + } + + // Case 1 : Remove all images during resource ready. + try + { + gImageView1 = ImageView::New(); + gImageView1.SetProperty(Toolkit::ImageView::Property::IMAGE, map); + gImageView1.ResourceReadySignal().Connect(&OnResourceReadySignal08); + application.GetScene().Add(gImageView1); + + application.SendNotification(); + application.Render(); + + // Load gImageView1 + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); + DALI_TEST_EQUALS(gResourceReadySignalCounter, 1, TEST_LOCATION); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK(true); + } + catch(...) + { + // Exception should not happened + DALI_TEST_CHECK(false); + } + + // Clear cache. + application.SendNotification(); + application.Render(); + + gResourceReadySignalCounter = 0; + + // Case 2 : Remove all images when we use cached resource. + try + { + gImageView1 = ImageView::New(); + gImageView1.SetProperty(Toolkit::ImageView::Property::IMAGE, map); + gImageView1.ResourceReadySignal().Connect(&OnSimpleResourceReadySignal); + application.GetScene().Add(gImageView1); + + application.SendNotification(); + application.Render(); + + // Load gImageView1 + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); + DALI_TEST_EQUALS(gResourceReadySignalCounter, 1, TEST_LOCATION); + + application.SendNotification(); + application.Render(); + + gImageView2 = ImageView::New(); + gImageView2.SetProperty(Toolkit::ImageView::Property::IMAGE, map); + gImageView2.ResourceReadySignal().Connect(&OnResourceReadySignal08); + application.GetScene().Add(gImageView2); + DALI_TEST_EQUALS(gResourceReadySignalCounter, 2, TEST_LOCATION); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK(true); + } + catch(...) + { + // Exception should not happened + DALI_TEST_CHECK(false); + } + gResourceReadySignalCounter = 0; + + // Clear image view for clear test + + if(gImageView1) + { + gImageView1.Reset(); + } + if(gImageView2) + { + gImageView2.Reset(); + } + + END_TEST; +} + +int UtcDaliImageViewSetImageOnResourceReadySignal09(void) +{ + tet_infoline("Test load invalid npatch images during invalid resource ready"); + + ToolkitTestApplication application; + + gResourceReadySignalCounter = 0; + + Property::Map map; + map[Toolkit::ImageVisual::Property::URL] = TEST_INVALID_NPATCH_FILE_NAME_01; + + // Clear image view for clear test + + if(gImageView1) + { + gImageView1.Reset(); + } + if(gImageView2) + { + gImageView2.Reset(); + } + if(gImageView3) + { + gImageView3.Reset(); + } + + // Dummy view with npatch image + ImageView dummyView = ImageView::New(TEST_BROKEN_IMAGE_M); + application.GetScene().Add(dummyView); + + application.SendNotification(); + application.Render(); + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); + application.SendNotification(); + application.Render(); + + // Case 1 : Reload images during resource ready. + try + { + gResourceReadySignal09Emitted = false; + + gImageView1 = ImageView::New(); + gImageView1.SetProperty(Toolkit::ImageView::Property::IMAGE, map); + gImageView1.ResourceReadySignal().Connect(&OnResourceReadySignal09); + application.GetScene().Add(gImageView1); + + gImageView2 = ImageView::New(); + application.GetScene().Add(gImageView2); + + // Load TEST_INVALID_NPATCH_FILE_NAME_01 + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); + + // Load TEST_INVALID_NPATCH_FILE_NAME_01 one more times. + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK(true); + } + catch(...) + { + // Exception should not happened + DALI_TEST_CHECK(false); + } + + // Clear cache. + application.SendNotification(); + application.Render(); + + gResourceReadySignalCounter = 0; + + // Case 2 : Remove all images when we use cached resource. + try + { + gResourceReadySignal09Emitted = false; + + gImageView3 = ImageView::New(); + gImageView3.SetProperty(Toolkit::ImageView::Property::IMAGE, map); + gImageView3.ResourceReadySignal().Connect(&OnSimpleResourceReadySignal); + application.GetScene().Add(gImageView3); + + gImageView2 = ImageView::New(); + application.GetScene().Add(gImageView2); + + // Load gImageView2 + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); + + gImageView1 = ImageView::New(); + gImageView1.SetProperty(Toolkit::ImageView::Property::IMAGE, map); + gImageView1.ResourceReadySignal().Connect(&OnResourceReadySignal09); + application.GetScene().Add(gImageView1); + + // Load gImageView1 + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); + + // Load TEST_INVALID_NPATCH_FILE_NAME_01 one more times. + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK(true); + } + catch(...) + { + // Exception should not happened + DALI_TEST_CHECK(false); + } + gResourceReadySignalCounter = 0; + + // Clear image view for clear test + + if(gImageView1) + { + gImageView1.Reset(); + } + if(gImageView2) + { + gImageView2.Reset(); + } + if(gImageView3) + { + gImageView3.Reset(); + } + + END_TEST; +} + int UtcDaliImageViewUseSameUrlWithAnimatedImageVisual(void) { tet_infoline("Test multiple views with same image in animated image visual"); @@ -4247,6 +4521,11 @@ int UtcDaliImageViewNpatchImageCacheTest01(void) { imageView[index].Unparent(); } + + // Ensure remove npatch cache if required. + application.SendNotification(); + application.Render(); + imageView[index] = ImageView::New(nPatchImageUrl); imageView[index].SetProperty(Actor::Property::SIZE, Vector2(100.0f, 200.0f)); application.GetScene().Add(imageView[index]); diff --git a/dali-toolkit/internal/visuals/npatch-data.cpp b/dali-toolkit/internal/visuals/npatch-data.cpp index dc0a055..62c706e 100644 --- a/dali-toolkit/internal/visuals/npatch-data.cpp +++ b/dali-toolkit/internal/visuals/npatch-data.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,8 +39,9 @@ NPatchData::NPatchData() mCroppedHeight(0), mBorder(0, 0, 0, 0), mLoadingState(LoadingState::NOT_STARTED), + mRenderingMap{nullptr}, mPreMultiplyOnLoad(false), - mRenderingMap{nullptr} + mObserverNotifying(false) { } @@ -51,6 +52,8 @@ NPatchData::~NPatchData() { RenderingAddOn::Get().DestroyNPatch(mRenderingMap); } + mObserverList.Clear(); + mQueuedObservers.Clear(); } void NPatchData::SetId(const NPatchDataId id) @@ -65,7 +68,16 @@ NPatchData::NPatchDataId NPatchData::GetId() const void NPatchData::AddObserver(TextureUploadObserver* textureObserver) { - mObserverList.PushBack(textureObserver); + if(mObserverNotifying) + { + // Do not add it into observer list during observer notifying. + mQueuedObservers.PushBack(textureObserver); + } + else + { + mObserverList.PushBack(textureObserver); + } + textureObserver->DestructionSignal().Connect(this, &NPatchData::ObserverDestroyed); } void NPatchData::RemoveObserver(TextureUploadObserver* textureObserver) @@ -74,6 +86,7 @@ void NPatchData::RemoveObserver(TextureUploadObserver* textureObserver) { if(textureObserver == mObserverList[index]) { + textureObserver->DestructionSignal().Disconnect(this, &NPatchData::ObserverDestroyed); mObserverList.Erase(mObserverList.begin() + index); break; } @@ -259,13 +272,57 @@ void NPatchData::LoadComplete(bool loadSuccess, TextureInformation textureInform } } - for(uint32_t index = 0; index < mObserverList.Count(); ++index) + mObserverNotifying = true; + + // Reverse observer list that we can pop_back the observer. + std::reverse(mObserverList.Begin(), mObserverList.End()); + + while(mObserverList.Count() > 0u) { - TextureUploadObserver* observer = mObserverList[index]; + TextureUploadObserver* observer = *(mObserverList.End() - 1u); + mObserverList.Erase(mObserverList.End() - 1u); + + observer->DestructionSignal().Disconnect(this, &NPatchData::ObserverDestroyed); + NotifyObserver(observer, loadSuccess); } - mObserverList.Clear(); + mObserverNotifying = false; + + // Swap observer list what we queued during notify observer. + // If mQueuedObserver is not empty, it mean mLoadingState was LOAD_FAILED, and we try to re-load for this data. + // (If mLoadingState was LOAD_COMPLETE, NotifyObserver will be called directly. @todo : Should we fix this logic, matched with texture manager?) + // So LoadComplete will be called. + mObserverList.Swap(mQueuedObservers); +} + +void NPatchData::ObserverDestroyed(TextureUploadObserver* observer) +{ + for(auto iter = mObserverList.Begin(); iter != mObserverList.End();) + { + if(observer == (*iter)) + { + iter = mObserverList.Erase(iter); + } + else + { + ++iter; + } + } + if(mObserverNotifying) + { + for(auto iter = mQueuedObservers.Begin(); iter != mQueuedObservers.End();) + { + if(observer == (*iter)) + { + iter = mQueuedObservers.Erase(iter); + } + else + { + ++iter; + } + } + } } } // namespace Internal diff --git a/dali-toolkit/internal/visuals/npatch-data.h b/dali-toolkit/internal/visuals/npatch-data.h index cc520d8..3df4b29 100644 --- a/dali-toolkit/internal/visuals/npatch-data.h +++ b/dali-toolkit/internal/visuals/npatch-data.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_NPATCH_DATA_H /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,11 +33,11 @@ namespace Toolkit { namespace Internal { -class NPatchData : public Dali::Toolkit::TextureUploadObserver +class NPatchData : public ConnectionTracker, public Dali::Toolkit::TextureUploadObserver { public: - typedef int32_t NPatchDataId; ///< The NPatchDataId type. This is used as a handle to refer to a particular Npatch Data. - static const int INVALID_NPATCH_DATA_ID = -1; ///< Used to represent a null TextureId or error + typedef int32_t NPatchDataId; ///< The NPatchDataId type. This is used as a handle to refer to a particular Npatch Data. + static const NPatchDataId INVALID_NPATCH_DATA_ID = -1; ///< Used to represent a null TextureId or error /** * @brief Loading State of the NPatch image. @@ -269,22 +269,32 @@ private: */ void LoadComplete(bool loadSuccess, TextureInformation textureInformation) override; + /** + * This is called by the TextureUploadObserver when an observer is destroyed. + * We use the callback to know when to remove an observer from our notify list. + * @param[in] observer The observer that generated the callback + */ + void ObserverDestroyed(TextureUploadObserver* observer); + private: using ObserverListType = Dali::Vector; NPatchDataId mId; - ObserverListType mObserverList; ///< Container used to store all observer clients of this Texture - VisualUrl mUrl; ///< Url of the N-Patch - TextureSet mTextureSet; ///< Texture containing the cropped image - NPatchUtility::StretchRanges mStretchPixelsX; ///< X stretch pixels - NPatchUtility::StretchRanges mStretchPixelsY; ///< Y stretch pixels - std::size_t mHash; ///< Hash code for the Url - uint32_t mCroppedWidth; ///< Width of the cropped middle part of N-patch - uint32_t mCroppedHeight; ///< Height of the cropped middle part of N-patch - Rect mBorder; ///< The size of the border - LoadingState mLoadingState; ///< True if the data loading is completed - bool mPreMultiplyOnLoad; ///< Whether to multiply alpha into color channels on load - void* mRenderingMap; ///< NPatch rendering data + ObserverListType mObserverList; ///< Container used to store all observer clients of this Texture + ObserverListType mQueuedObservers; ///< Container observers when user try to add during notify observers + VisualUrl mUrl; ///< Url of the N-Patch + TextureSet mTextureSet; ///< Texture containing the cropped image + NPatchUtility::StretchRanges mStretchPixelsX; ///< X stretch pixels + NPatchUtility::StretchRanges mStretchPixelsY; ///< Y stretch pixels + std::size_t mHash; ///< Hash code for the Url + uint32_t mCroppedWidth; ///< Width of the cropped middle part of N-patch + uint32_t mCroppedHeight; ///< Height of the cropped middle part of N-patch + Rect mBorder; ///< The size of the border + LoadingState mLoadingState; ///< True if the data loading is completed + void* mRenderingMap; ///< NPatch rendering data + + bool mPreMultiplyOnLoad : 1; ///< Whether to multiply alpha into color channels on load + bool mObserverNotifying : 1; ///< Whether this NPatchData notifying observers or not. }; } // namespace Internal diff --git a/dali-toolkit/internal/visuals/npatch-loader.cpp b/dali-toolkit/internal/visuals/npatch-loader.cpp index 5b2b713..3e18482 100644 --- a/dali-toolkit/internal/visuals/npatch-loader.cpp +++ b/dali-toolkit/internal/visuals/npatch-loader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ // EXTERNAL HEADERS #include +#include #include namespace Dali @@ -39,24 +40,35 @@ constexpr auto UNINITIALIZED_ID = int32_t{0}; ///< uninitialised id, use to } // Anonymous namespace NPatchLoader::NPatchLoader() -: mCurrentNPatchDataId(0) +: mCurrentNPatchDataId(0), + mRemoveProcessorRegistered(false) { } NPatchLoader::~NPatchLoader() { + if(mRemoveProcessorRegistered && Adaptor::IsAvailable()) + { + Adaptor::Get().UnregisterProcessor(*this, true); + mRemoveProcessorRegistered = false; + } } NPatchData::NPatchDataId NPatchLoader::GenerateUniqueNPatchDataId() { + // Skip invalid id generation. + if(DALI_UNLIKELY(mCurrentNPatchDataId == NPatchData::INVALID_NPATCH_DATA_ID)) + { + mCurrentNPatchDataId = 0; + } return mCurrentNPatchDataId++; } -std::size_t NPatchLoader::Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect& border, bool& preMultiplyOnLoad, bool synchronousLoading) +NPatchData::NPatchDataId NPatchLoader::Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect& border, bool& preMultiplyOnLoad, bool synchronousLoading) { - NPatchData* data = GetNPatchData(url, border, preMultiplyOnLoad); + std::shared_ptr data = GetNPatchData(url, border, preMultiplyOnLoad); - DALI_ASSERT_ALWAYS(data && "NPatchData creation failed!"); + DALI_ASSERT_ALWAYS(data.get() && "NPatchData creation failed!"); if(data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE) { @@ -85,7 +97,7 @@ std::size_t NPatchLoader::Load(TextureManager& textureManager, TextureUploadObse auto preMultiplyOnLoading = preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(url, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, data, true, preMultiplyOnLoading); + Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(url, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, data.get(), true, preMultiplyOnLoading); if(pixelBuffer) { @@ -127,7 +139,18 @@ bool NPatchLoader::GetNPatchData(const NPatchData::NPatchDataId id, const NPatch return false; } -void NPatchLoader::Remove(std::size_t id, TextureUploadObserver* textureObserver) +void NPatchLoader::RequestRemove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver) +{ + mRemoveQueue.push_back({id, textureObserver}); + + if(!mRemoveProcessorRegistered && Adaptor::IsAvailable()) + { + mRemoveProcessorRegistered = true; + Adaptor::Get().RegisterProcessor(*this, true); + } +} + +void NPatchLoader::Remove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver) { int32_t cacheIndex = GetCacheIndexFromId(id); if(cacheIndex == INVALID_CACHE_INDEX) @@ -145,7 +168,22 @@ void NPatchLoader::Remove(std::size_t id, TextureUploadObserver* textureObserver } } -NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect& border, bool& preMultiplyOnLoad) +void NPatchLoader::Process(bool postProcessor) +{ + for(auto& iter : mRemoveQueue) + { + Remove(iter.first, iter.second); + } + mRemoveQueue.clear(); + + if(Adaptor::IsAvailable()) + { + Adaptor::Get().UnregisterProcessor(*this, true); + mRemoveProcessorRegistered = false; + } +} + +std::shared_ptr NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect& border, bool& preMultiplyOnLoad) { std::size_t hash = CalculateHash(url.GetUrl()); std::vector::size_type index = UNINITIALIZED_ID; @@ -164,7 +202,7 @@ NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect& b if(mCache[index].mData->GetBorder() == border) { mCache[index].mReferenceCount++; - return mCache[index].mData.get(); + return mCache[index].mData; } else { @@ -197,7 +235,7 @@ NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect& b // If this is new image loading, make new cache data if(infoPtr == nullptr) { - NPatchInfo info(new NPatchData()); + NPatchInfo info(std::make_shared()); info.mData->SetId(GenerateUniqueNPatchDataId()); info.mData->SetHash(hash); info.mData->SetUrl(url); @@ -210,7 +248,7 @@ NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect& b // Else if LOAD_COMPLETE, Same url but border is different - use the existing texture else if(infoPtr->mData->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE) { - NPatchInfo info(new NPatchData()); + NPatchInfo info(std::make_shared()); info.mData->SetId(GenerateUniqueNPatchDataId()); info.mData->SetHash(hash); @@ -245,7 +283,7 @@ NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect& b DALI_ASSERT_ALWAYS(infoPtr && "NPatchInfo creation failed!"); - return infoPtr->mData.get(); + return infoPtr->mData; } } // namespace Internal diff --git a/dali-toolkit/internal/visuals/npatch-loader.h b/dali-toolkit/internal/visuals/npatch-loader.h index 539009b..7e846e4 100644 --- a/dali-toolkit/internal/visuals/npatch-loader.h +++ b/dali-toolkit/internal/visuals/npatch-loader.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_NPATCH_LOADER_H /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,9 +19,11 @@ // EXTERNAL INCLUDES #include +#include #include -#include // for std::unique_ptr +#include // for std::shared_ptr #include +#include // for std::pair // INTERNAL INCLUDES #include @@ -44,7 +46,7 @@ namespace Internal * small space and there's not usually a lot of them. Usually N patches are specified in * toolkit default style and there is 1-2 per control that are shared across the whole application. */ -class NPatchLoader +class NPatchLoader : public Integration::Processor { public: /** @@ -69,16 +71,7 @@ public: * @param [in] synchronousLoading True if the image will be loaded in synchronous time. * @return id of the texture. */ - std::size_t Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect& border, bool& preMultiplyOnLoad, bool synchronousLoading); - - /** - * @brief Set loaded PixelBuffer and its information - * - * @param [in] id cache data id - * @param [in] pixelBuffer of loaded image - * @param [in] preMultiplied True if the image had pre-multiplied alpha applied - */ - void SetNPatchData(std::size_t id, Devel::PixelBuffer& pixelBuffer, bool preMultiplied); + NPatchData::NPatchDataId Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect& border, bool& preMultiplyOnLoad, bool synchronousLoading); /** * @brief Retrieve N patch data matching to an id @@ -89,21 +82,36 @@ public: bool GetNPatchData(const NPatchData::NPatchDataId id, const NPatchData*& data); /** - * @brief Remove a texture matching id. + * @brief Request remove a texture matching id. * Erase the observer from the observer list of cache if we need. - * This API decrease cached NPatchInfo reference. - * If the NPatchInfo reference become 0, the textureSet will be reset. * * @param [in] id cache data id * @param [in] textureObserver The NPatchVisual that requested loading. */ - void Remove(std::size_t id, TextureUploadObserver* textureObserver); + void RequestRemove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver); + +protected: // Implementation of Processor + /** + * @copydoc Dali::Integration::Processor::Process() + */ + void Process(bool postProcessor) override; private: NPatchData::NPatchDataId GenerateUniqueNPatchDataId(); int32_t GetCacheIndexFromId(const NPatchData::NPatchDataId id); + /** + * @brief Remove a texture matching id. + * Erase the observer from the observer list of cache if we need. + * This API decrease cached NPatchInfo reference. + * If the NPatchInfo reference become 0, the textureSet will be reset. + * + * @param [in] id cache data id + * @param [in] textureObserver The NPatchVisual that requested loading. + */ + void Remove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver); + private: /** * @brief Information of NPatchData @@ -111,7 +119,7 @@ private: */ struct NPatchInfo { - NPatchInfo(NPatchData* data) + NPatchInfo(std::shared_ptr data) : mData(data), mReferenceCount(1u) { @@ -137,7 +145,7 @@ private: NPatchInfo(const NPatchInfo& info) = delete; // Do not use copy constructor NPatchInfo& operator=(const NPatchInfo& info) = delete; // Do not use copy assign - std::unique_ptr mData; + std::shared_ptr mData; std::int16_t mReferenceCount; ///< The number of N-patch visuals that use this data. }; @@ -151,7 +159,7 @@ private: * image has no alpha channel * @return NPatchData pointer that Load function will used. */ - NPatchData* GetNPatchData(const VisualUrl& url, const Rect& border, bool& preMultiplyOnLoad); + std::shared_ptr GetNPatchData(const VisualUrl& url, const Rect& border, bool& preMultiplyOnLoad); protected: /** @@ -167,6 +175,10 @@ protected: private: NPatchData::NPatchDataId mCurrentNPatchDataId; std::vector mCache; + + std::vector> mRemoveQueue; ///< Queue of textures to remove at PostProcess. It will be cleared after PostProcess. + + bool mRemoveProcessorRegistered : 1; ///< Flag if remove processor registered or not. }; } // namespace Internal diff --git a/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp b/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp index d36eeb1..c9367a5 100644 --- a/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp +++ b/dali-toolkit/internal/visuals/npatch/npatch-visual.cpp @@ -239,7 +239,7 @@ void NPatchVisual::DoSetOffScene(Actor& actor) { if(mId != NPatchData::INVALID_NPATCH_DATA_ID) { - mLoader.Remove(mId, this); + mLoader.RequestRemove(mId, this); mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING; mId = NPatchData::INVALID_NPATCH_DATA_ID; } @@ -319,7 +319,7 @@ NPatchVisual::~NPatchVisual() { if(mId != NPatchData::INVALID_NPATCH_DATA_ID) { - mLoader.Remove(mId, this); + mLoader.RequestRemove(mId, this); mId = NPatchData::INVALID_NPATCH_DATA_ID; } if(mAuxiliaryTextureId != TextureManager::INVALID_TEXTURE_ID)