/*
- * Copyright (c) 2021 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.
mCroppedWidth(0),
mCroppedHeight(0),
mBorder(0, 0, 0, 0),
- mLoadingState(LoadingState::LOADING),
+ mLoadingState(LoadingState::NOT_STARTED),
+ mRenderingMap{nullptr},
mPreMultiplyOnLoad(false),
- mRenderingMap{nullptr}
+ mObserverNotifying(false)
{
}
{
RenderingAddOn::Get().DestroyNPatch(mRenderingMap);
}
+ mObserverList.Clear();
+ mQueuedObservers.Clear();
}
void NPatchData::SetId(const NPatchDataId id)
void NPatchData::AddObserver(TextureUploadObserver* textureObserver)
{
- mObserverList.PushBack(textureObserver);
+ if(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)
{
- for(uint32_t index = 0; index < mObserverList.Count(); ++index)
+ if(textureObserver)
{
- if(textureObserver == mObserverList[index])
+ for(uint32_t index = 0; index < mObserverList.Count(); ++index)
{
- mObserverList.Erase(mObserverList.begin() + index);
- break;
+ if(textureObserver == mObserverList[index])
+ {
+ textureObserver->DestructionSignal().Disconnect(this, &NPatchData::ObserverDestroyed);
+ mObserverList.Erase(mObserverList.begin() + index);
+ break;
+ }
}
}
}
return mObserverList.Count();
}
-void NPatchData::SetUrl(const std::string url)
+void NPatchData::SetUrl(const VisualUrl& url)
{
mUrl = url;
}
-std::string NPatchData::GetUrl() const
+VisualUrl NPatchData::GetUrl() const
{
return mUrl;
}
mLoadingState = LoadingState::LOAD_COMPLETE;
}
-void NPatchData::LoadComplete(bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied)
+void NPatchData::NotifyObserver(TextureUploadObserver* observer, const bool& loadSuccess)
+{
+ observer->LoadComplete(
+ loadSuccess,
+ TextureUploadObserver::TextureInformation(
+ TextureUploadObserver::ReturnType::TEXTURE,
+ static_cast<TextureManager::TextureId>(mId), ///< Note : until end of NPatchLoader::Load, npatch-visual don't know the id of data.
+ mTextureSet,
+ mUrl.GetUrl(),
+ mPreMultiplyOnLoad));
+}
+
+void NPatchData::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
{
+ NPatchDataPtr self = this; // Keep reference until this API finished
+
if(loadSuccess)
{
- SetLoadedNPatchData(pixelBuffer, preMultiplied);
+ if(mLoadingState != LoadingState::LOAD_COMPLETE)
+ {
+ // If mLoadingState is LOAD_FAILED, just re-set (It can be happened when sync loading is failed, but async loading is succeeded).
+ SetLoadedNPatchData(textureInformation.pixelBuffer, textureInformation.preMultiplied);
+ }
}
else
{
- mLoadingState = LoadingState::LOAD_FAILED;
+ if(mLoadingState == LoadingState::LOADING)
+ {
+ mLoadingState = LoadingState::LOAD_FAILED;
+ }
+ // If mLoadingState is already LOAD_COMPLETE, we can use uploaded texture (It can be happened when sync loading is succeeded, but async loading is failed).
+ else if(mLoadingState == LoadingState::LOAD_COMPLETE)
+ {
+ loadSuccess = true;
+ }
}
- 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];
- observer->UploadComplete(loadSuccess, TextureManager::INVALID_TEXTURE_ID, mTextureSet, false, Vector4(), preMultiplied);
+ TextureUploadObserver* observer = *(mObserverList.End() - 1u);
+ mObserverList.Erase(mObserverList.End() - 1u);
+
+ observer->DestructionSignal().Disconnect(this, &NPatchData::ObserverDestroyed);
+
+ NotifyObserver(observer, loadSuccess);
+ }
+
+ 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;
+ }
+ }
}
}