/*
- * 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.
#include <dali-toolkit/internal/texture-manager/texture-upload-observer.h>
#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
#include <dali-toolkit/internal/visuals/visual-factory-impl.h> ///< For VisualFactory's member TextureManager.
+#include <dali-toolkit/public-api/image-loader/image-url.h>
+#include <dali-toolkit/public-api/image-loader/image.h>
#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
#include <test-encoded-image-buffer.h>
namespace
{
-const char* TEST_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/gallery-small-1.jpg";
-const char* TEST_MASK_FILE_NAME = TEST_RESOURCE_DIR "/mask.png";
+const char* TEST_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/gallery-small-1.jpg";
+const char* TEST_IMAGE_2_FILE_NAME = TEST_RESOURCE_DIR "/icon-delete.png";
+const char* TEST_IMAGE_3_FILE_NAME = TEST_RESOURCE_DIR "/icon-edit.png";
+const char* TEST_IMAGE_4_FILE_NAME = TEST_RESOURCE_DIR "/application-icon-20.png";
+const char* TEST_MASK_FILE_NAME = TEST_RESOURCE_DIR "/mask.png";
class TestObserver : public Dali::Toolkit::TextureUploadObserver
{
mTextureSet = textureInformation.textureSet;
// Remove during LoadComplete
- mTextureManagerPtr->Remove(textureInformation.textureId, nullptr);
+ mTextureManagerPtr->RequestRemove(textureInformation.textureId, nullptr);
// ...And generate string which using texture id.
mGeneratedExternalUrl = mTextureManagerPtr->AddExternalTexture(mTextureSet);
TextureManager* mTextureManagerPtr; // Keep the pointer of texture manager.
};
+class TestObserverWithCustomFunction : public TestObserver
+{
+public:
+ TestObserverWithCustomFunction()
+ : TestObserver(),
+ mSignals{},
+ mData{nullptr},
+ mKeepSignal{false}
+ {
+ }
+
+ virtual void LoadComplete(bool loadSuccess, TextureInformation textureInformation) override
+ {
+ if(textureInformation.returnType == TextureUploadObserver::ReturnType::TEXTURE)
+ {
+ mCompleteType = CompleteType::UPLOAD_COMPLETE;
+ }
+ else
+ {
+ mCompleteType = CompleteType::LOAD_COMPLETE;
+ }
+ mLoaded = loadSuccess;
+ mObserverCalled = true;
+ mTextureSet = textureInformation.textureSet;
+
+ // Execute signals.
+ for(size_t i = 0; i < mSignals.size(); i++)
+ {
+ mSignals[i](mData);
+ }
+
+ // Clear signals.
+ if(!mKeepSignal)
+ {
+ mSignals.clear();
+ }
+ }
+
+ void ConnectFunction(std::function<void(void*)> signal)
+ {
+ mSignals.push_back(signal);
+ }
+
+public:
+ std::vector<std::function<void(void*)>> mSignals;
+ void* mData;
+ bool mKeepSignal;
+};
+
} // namespace
int UtcTextureManagerRequestLoad(void)
END_TEST;
}
+int UtcTextureManagerExternalTexture(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("UtcTextureManagerExternalTexture check TextureManager using external texture works well");
+
+ auto visualFactory = Toolkit::VisualFactory::Get();
+ auto& textureManager = GetImplementation(visualFactory).GetTextureManager(); // Use VisualFactory's texture manager
+
+ TestObserver observer1;
+ TestObserver observer2;
+
+ auto textureId1(TextureManager::INVALID_TEXTURE_ID);
+ auto textureId2(TextureManager::INVALID_TEXTURE_ID);
+ std::string maskname(TEST_MASK_FILE_NAME);
+ TextureManager::MaskingDataPointer maskInfo = nullptr;
+ maskInfo.reset(new TextureManager::MaskingData());
+ maskInfo->mAlphaMaskUrl = maskname;
+ maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID;
+ maskInfo->mCropToMask = true;
+ maskInfo->mContentScaleFactor = 1.0f;
+ Vector4 atlasRect(0.f, 0.f, 1.f, 1.f);
+ Dali::ImageDimensions atlasRectSize(0, 0);
+ bool synchronousLoading(false);
+ bool atlasingStatus(false);
+ bool loadingStatus(false);
+ auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+ ImageAtlasManagerPtr atlasManager = nullptr;
+ Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+ uint32_t width(64);
+ uint32_t height(64);
+ uint32_t bufferSize = width * height * Pixel::GetBytesPerPixel(Pixel::RGBA8888);
+
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(malloc(bufferSize));
+ PixelData pixelData = PixelData::New(buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE);
+
+ DALI_TEST_CHECK(pixelData);
+
+ Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::Image::GenerateUrl(pixelData, true);
+ std::string url = imageUrl.GetUrl();
+
+ TextureSet texture1 = textureManager.LoadTexture(
+ url,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo,
+ synchronousLoading,
+ textureId1,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer1,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ TextureSet texture2 = textureManager.LoadTexture(
+ url,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo,
+ synchronousLoading,
+ textureId2,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer2,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mObserverCalled, false, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(observer1.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(observer2.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mObserverCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(textureId1 == textureId2, true, TEST_LOCATION);
+
+ texture1 = textureManager.LoadTexture(
+ url,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo,
+ synchronousLoading,
+ textureId1,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer1,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ texture2 = textureManager.LoadTexture(
+ url,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo,
+ synchronousLoading,
+ textureId2,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer2,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS(textureId1 == textureId2, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(texture1 != texture2, true, TEST_LOCATION);
+
+ END_TEST;
+}
+
int UtcTextureManagerEncodedImageBufferReferenceCount(void)
{
ToolkitTestApplication application;
application.SendNotification();
application.Render();
- // CAPTION : HARD-CODING for coverage. If you are a good boy, Do not follow this code.
+ // CAPTION : HARD-CODING for coverage.
{
Dali::Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(
filename,
textureManager.AsyncLoadComplete(textureId, pixelBuffers);
std::vector<Devel::PixelBuffer> maskBuffers;
textureManager.AsyncLoadComplete(maskInfo->mAlphaMaskId, maskBuffers);
- textureManager.Remove(maskInfo->mAlphaMaskId, nullptr);
- textureManager.Remove(textureId, &observer);
+ textureManager.RequestRemove(maskInfo->mAlphaMaskId, nullptr);
+ textureManager.RequestRemove(textureId, &observer);
}
application.SendNotification();
Texture asyncTexture2 = asyncTextureSet2.GetTexture(0u);
DALI_TEST_CHECK(syncTexture);
DALI_TEST_CHECK(asyncTexture2);
- DALI_TEST_CHECK(asyncTexture2 == syncTexture); // check loaded two texture is same.
+ DALI_TEST_CHECK(asyncTexture2 == syncTexture); // check loaded two texture is same.
// observer is called synchronously because the texture is cached.
DALI_TEST_EQUALS(asyncObserver2.mLoaded, true, TEST_LOCATION);
DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION);
// Remove current textureId1. and request new texture again.
- textureManager.Remove(textureId1, &observer1);
+ textureManager.RequestRemove(textureId1, &observer1);
auto textureId2 = textureManager.RequestLoad(
filename,
ImageDimensions(),
{
std::vector<Devel::PixelBuffer> pixelBuffers;
textureManager.AsyncLoadComplete(textureId2, pixelBuffers);
- textureManager.Remove(textureId2, &observer2);
+ textureManager.RequestRemove(textureId2, &observer2);
}
DALI_TEST_EQUALS(observer2.mLoaded, false, TEST_LOCATION); ///< Note that we call AsyncLoadComplete hardly with empty pixelbuffer.
END_TEST;
}
+
+int UtcTextureManagerMaskCacheTest(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("UtcTextureManagerMaskCacheTest");
+
+ TextureManager textureManager; // Create new texture manager
+
+ TestObserver observer1;
+ TestObserver observer2;
+
+ std::string filename(TEST_IMAGE_FILE_NAME);
+ std::string filename2(TEST_IMAGE_2_FILE_NAME);
+ std::string maskname(TEST_MASK_FILE_NAME);
+ TextureManager::MaskingDataPointer maskInfo = nullptr;
+ maskInfo.reset(new TextureManager::MaskingData());
+ maskInfo->mAlphaMaskUrl = maskname;
+ maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID;
+ maskInfo->mCropToMask = true;
+ maskInfo->mContentScaleFactor = 1.0f;
+
+ TextureManager::MaskingDataPointer maskInfo2 = nullptr;
+ maskInfo2.reset(new TextureManager::MaskingData());
+ maskInfo2->mAlphaMaskUrl = maskname;
+ maskInfo2->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID;
+ maskInfo2->mCropToMask = true;
+ maskInfo2->mContentScaleFactor = 1.0f;
+
+ auto textureId1(TextureManager::INVALID_TEXTURE_ID);
+ auto textureId2(TextureManager::INVALID_TEXTURE_ID);
+ Vector4 atlasRect(0.f, 0.f, 1.f, 1.f);
+ Dali::ImageDimensions atlasRectSize(0, 0);
+ bool synchronousLoading(false);
+ bool atlasingStatus(false);
+ bool loadingStatus(false);
+ auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+ ImageAtlasManagerPtr atlasManager = nullptr;
+ Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+ textureManager.LoadTexture(
+ filename,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo,
+ synchronousLoading,
+ textureId1,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer1,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ // Load image and mask image.
+ // Now, LoadState become MASK_APPLYING
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+ tet_printf("Current textureId1:%d's state become MASK_APPLYING\n", static_cast<int>(textureId1));
+
+ textureManager.LoadTexture(
+ filename2,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo2,
+ synchronousLoading,
+ textureId2,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer2,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ application.SendNotification();
+ application.Render();
+
+ // Load image2 + image1 apply mask + image2 apply mask = total 3 event trigger required.
+ // Note that we use cached mask image.
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(observer1.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mObserverCalled, true, TEST_LOCATION);
+
+ try
+ {
+ // Remove textureId1 first, and then remove textureId2. Check whether segfault occured.
+ textureManager.RequestRemove(textureId1, &observer1);
+
+ application.SendNotification();
+ application.Render();
+
+ textureManager.RequestRemove(textureId2, &observer2);
+
+ application.SendNotification();
+ application.Render();
+
+ TestObserver observer3;
+ maskInfo.reset(new TextureManager::MaskingData());
+ maskInfo->mAlphaMaskUrl = maskname;
+ maskInfo->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID;
+ maskInfo->mCropToMask = true;
+ maskInfo->mContentScaleFactor = 1.0f;
+
+ textureManager.LoadTexture(
+ filename,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo,
+ synchronousLoading,
+ textureId1,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer3,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ DALI_TEST_EQUALS(observer3.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3.mObserverCalled, false, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ // Load image and mask image.
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(observer3.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3.mObserverCalled, false, TEST_LOCATION);
+
+ // Apply mask.
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(observer3.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3.mObserverCalled, true, TEST_LOCATION);
+ }
+ catch(...)
+ {
+ DALI_TEST_CHECK(false);
+ }
+
+ END_TEST;
+}
+
+int UtcTextureManagerRemoveDuringGPUMasking(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("UtcTextureManagerRemoveDuringGPUMasking");
+ tet_infoline("Request 3 different GPU masking image.");
+ tet_infoline("Control to mask image load last. and then, check execute result.");
+
+ TextureManager textureManager; // Create new texture manager
+
+ TestObserverWithCustomFunction observer1;
+ TestObserverWithCustomFunction observer2;
+ TestObserverWithCustomFunction* observer3 = new TestObserverWithCustomFunction(); // Deleted in observer1 loaded signal
+ TestObserver observer4;
+
+ std::string filename1(TEST_IMAGE_FILE_NAME);
+ std::string filename2(TEST_IMAGE_2_FILE_NAME);
+ std::string filename3(TEST_IMAGE_3_FILE_NAME);
+ std::string filename4(TEST_IMAGE_4_FILE_NAME);
+
+ auto textureId1(TextureManager::INVALID_TEXTURE_ID);
+ auto textureId2(TextureManager::INVALID_TEXTURE_ID);
+ auto textureId3(TextureManager::INVALID_TEXTURE_ID);
+ auto textureId4(TextureManager::INVALID_TEXTURE_ID);
+
+ std::string maskname(TEST_MASK_FILE_NAME);
+ TextureManager::MaskingDataPointer maskInfo[3] = {nullptr, nullptr, nullptr};
+ for(int i = 0; i < 3; i++)
+ {
+ maskInfo[i].reset(new TextureManager::MaskingData());
+ maskInfo[i]->mAlphaMaskUrl = maskname;
+ maskInfo[i]->mAlphaMaskId = TextureManager::INVALID_TEXTURE_ID;
+ maskInfo[i]->mCropToMask = true;
+ maskInfo[i]->mPreappliedMasking = false; // To make GPU masking
+ maskInfo[i]->mContentScaleFactor = 1.0f;
+ }
+ Vector4 atlasRect(0.f, 0.f, 1.f, 1.f);
+ Dali::ImageDimensions atlasRectSize(0, 0);
+ bool synchronousLoading(false);
+ bool atlasingStatus(false);
+ bool loadingStatus(false);
+ auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+ ImageAtlasManagerPtr atlasManager = nullptr;
+ Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+ // Request image 1, 2, 3 with GPU masking
+ textureManager.LoadTexture(
+ filename1,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo[0],
+ synchronousLoading,
+ textureId1,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer1,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ textureManager.LoadTexture(
+ filename2,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo[1],
+ synchronousLoading,
+ textureId2,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ &observer2,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ textureManager.LoadTexture(
+ filename3,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ maskInfo[2],
+ synchronousLoading,
+ textureId3,
+ atlasRect,
+ atlasRectSize,
+ atlasingStatus,
+ loadingStatus,
+ observer3,
+ atlasUploadObserver,
+ atlasManager,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mObserverCalled, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3->mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3->mObserverCalled, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer4.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer4.mObserverCalled, false, TEST_LOCATION);
+
+ // Check we use cached mask image
+ DALI_TEST_CHECK(maskInfo[0]->mAlphaMaskId != TextureManager::INVALID_TEXTURE_ID);
+ DALI_TEST_EQUALS(maskInfo[0]->mAlphaMaskId, maskInfo[1]->mAlphaMaskId, TEST_LOCATION);
+ DALI_TEST_EQUALS(maskInfo[0]->mAlphaMaskId, maskInfo[2]->mAlphaMaskId, TEST_LOCATION);
+
+ // Connect observer1 custom function
+ struct CustomData1
+ {
+ TextureManager* textureManagerPtr{nullptr};
+ TextureManager::TextureId removeTextureId{TextureManager::INVALID_TEXTURE_ID};
+ TestObserver* removeTextureObserver{nullptr};
+ };
+ CustomData1 data1;
+ data1.textureManagerPtr = &textureManager;
+ data1.removeTextureId = textureId3;
+ data1.removeTextureObserver = observer3;
+
+ observer1.mData = &data1;
+ observer1.ConnectFunction(
+ [](void* data) {
+ DALI_TEST_CHECK(data);
+ CustomData1 data1 = *(CustomData1*)data;
+
+ DALI_TEST_CHECK(data1.textureManagerPtr);
+ DALI_TEST_CHECK(data1.removeTextureId != TextureManager::INVALID_TEXTURE_ID);
+ DALI_TEST_CHECK(data1.removeTextureObserver);
+
+ // Remove textureId3.
+ data1.textureManagerPtr->RequestRemove(data1.removeTextureId, data1.removeTextureObserver);
+
+ // Destroy observer3
+ delete data1.removeTextureObserver;
+ });
+
+ // Connect observer2 custom function
+ struct CustomData2
+ {
+ TextureManager* textureManagerPtr{nullptr};
+ std::string addTextureUrl{};
+ TextureManager::TextureId* addTextureIdPtr{nullptr};
+ TestObserver* addTextureObserver{nullptr};
+ };
+ CustomData2 data2;
+ data2.textureManagerPtr = &textureManager;
+ data2.addTextureUrl = filename4;
+ data2.addTextureIdPtr = &textureId4;
+ data2.addTextureObserver = &observer4;
+
+ observer2.mData = &data2;
+ observer2.ConnectFunction(
+ [](void* data) {
+ DALI_TEST_CHECK(data);
+ CustomData2 data2 = *(CustomData2*)data;
+
+ DALI_TEST_CHECK(data2.textureManagerPtr);
+ DALI_TEST_CHECK(!data2.addTextureUrl.empty());
+ DALI_TEST_CHECK(data2.addTextureIdPtr);
+ DALI_TEST_CHECK(data2.addTextureObserver);
+
+ auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+
+ // Load textureId4
+ (*data2.addTextureIdPtr) = data2.textureManagerPtr->RequestLoad(
+ data2.addTextureUrl,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ TextureManager::UseAtlas::NO_ATLAS,
+ data2.addTextureObserver,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+ });
+
+ // Connect observer3 custom function
+ struct CustomData3
+ {
+ TestObserver* self{nullptr};
+ bool* observerLoadedPtr{nullptr};
+ bool* observerCalleddPtr{nullptr};
+ };
+ CustomData3 data3;
+ bool observer3Loaded = false;
+ bool observer3Called = false;
+ data3.self = observer3;
+ data3.observerLoadedPtr = &observer3Loaded;
+ data3.observerCalleddPtr = &observer3Called;
+
+ observer3->mData = &data3;
+ observer3->ConnectFunction(
+ [](void* data) {
+ DALI_TEST_CHECK(data);
+ CustomData3 data3 = *(CustomData3*)data;
+
+ DALI_TEST_CHECK(data3.self);
+ DALI_TEST_CHECK(data3.observerLoadedPtr);
+ DALI_TEST_CHECK(data3.observerCalleddPtr);
+
+ *data3.observerLoadedPtr = data3.self->mLoaded;
+ *data3.observerCalleddPtr = data3.self->mObserverCalled;
+ });
+
+ application.SendNotification();
+ application.Render();
+
+ tet_printf("Id info - mask : {%d}, 1 : {%d}, 2 : {%d}, 3 : {%d}, 4 : {%d}\n", static_cast<int>(maskInfo[0]->mAlphaMaskId), static_cast<int>(textureId1), static_cast<int>(textureId2), static_cast<int>(textureId3), static_cast<int>(textureId4));
+
+ // CAPTION : HARD-CODING.
+ {
+ // Complete async load 1, 2, 3.
+ std::vector<Devel::PixelBuffer> pixelBuffers;
+
+ pixelBuffers.clear();
+ pixelBuffers.push_back(Devel::PixelBuffer::New(1, 1, Pixel::Format::RGB888));
+ textureManager.AsyncLoadComplete(textureId1, pixelBuffers);
+ pixelBuffers.clear();
+ pixelBuffers.push_back(Devel::PixelBuffer::New(1, 1, Pixel::Format::RGB888));
+ textureManager.AsyncLoadComplete(textureId2, pixelBuffers);
+ pixelBuffers.clear();
+ pixelBuffers.push_back(Devel::PixelBuffer::New(1, 1, Pixel::Format::RGB888));
+ textureManager.AsyncLoadComplete(textureId3, pixelBuffers);
+
+ // Ensure textureId3 remove request processed.
+
+ DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mObserverCalled, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3Loaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3Called, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer4.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer4.mObserverCalled, false, TEST_LOCATION);
+
+ // Complete mask load.
+ pixelBuffers.clear();
+ pixelBuffers.push_back(Devel::PixelBuffer::New(1, 1, Pixel::Format::L8));
+ textureManager.AsyncLoadComplete(maskInfo[0]->mAlphaMaskId, pixelBuffers);
+
+ tet_printf("Id info after observer notify - mask : {%d}, 1 : {%d}, 2 : {%d}, 3 : {%d}, 4 : {%d}\n", static_cast<int>(maskInfo[0]->mAlphaMaskId), static_cast<int>(textureId1), static_cast<int>(textureId2), static_cast<int>(textureId3), static_cast<int>(textureId4));
+
+ // Check observer 1 and 2 called, but 3 and 4 not called.
+ DALI_TEST_EQUALS(observer1.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mObserverCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3Loaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3Called, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer4.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer4.mObserverCalled, false, TEST_LOCATION);
+
+ // Check textureId4
+ DALI_TEST_CHECK(textureId4 != TextureManager::INVALID_TEXTURE_ID);
+
+ // Complete 4.
+ pixelBuffers.clear();
+ pixelBuffers.push_back(Devel::PixelBuffer::New(1, 1, Pixel::Format::RGB888));
+ textureManager.AsyncLoadComplete(textureId4, pixelBuffers);
+
+ DALI_TEST_EQUALS(observer1.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2.mObserverCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3Loaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3Called, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer4.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer4.mObserverCalled, true, TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+int UtcTextureManagerDestroyObserverDuringObserve(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("UtcTextureManagerDestroyObserverDuringObserve");
+ tet_infoline("Request 3 different image.");
+ tet_infoline("Complete textureId1. After observer1 loaded done,");
+ tet_infoline(" - Remove and destroy observer2");
+ tet_infoline(" - Re-generate observer2 which has same address pointer with before.");
+ tet_infoline(" - Remove and Reqeust third file by observer3");
+ tet_infoline("Complete textureId2. and check old observer2 not emmited, and newly observer2 works.");
+ tet_infoline("Complete textureId3. and check observer3 comes");
+
+ TextureManager textureManager; // Create new texture manager
+
+ TestObserverWithCustomFunction observer1;
+ TestObserverWithCustomFunction* observer2 = new TestObserverWithCustomFunction(); // Deleted in observer1 loaded signal.
+ TestObserver observer3;
+
+ std::string filename1(TEST_IMAGE_FILE_NAME);
+ std::string filename2(TEST_IMAGE_2_FILE_NAME);
+ std::string filename3(TEST_IMAGE_3_FILE_NAME);
+ std::string filename4(TEST_IMAGE_4_FILE_NAME);
+
+ auto textureId1(TextureManager::INVALID_TEXTURE_ID);
+ auto textureId2(TextureManager::INVALID_TEXTURE_ID);
+ auto textureId3(TextureManager::INVALID_TEXTURE_ID);
+ auto textureId4(TextureManager::INVALID_TEXTURE_ID);
+
+ // Dummy reference value
+ auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+
+ // Request image 1, 2, 3.
+ textureId1 = textureManager.RequestLoad(
+ filename1,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ TextureManager::UseAtlas::NO_ATLAS,
+ &observer1,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ textureId2 = textureManager.RequestLoad(
+ filename2,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ TextureManager::UseAtlas::NO_ATLAS,
+ observer2,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ textureId3 = textureManager.RequestLoad(
+ filename3,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ TextureManager::UseAtlas::NO_ATLAS,
+ &observer3,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ struct CustomData1
+ {
+ TextureManager* textureManagerPtr{nullptr};
+ TextureManager::TextureId removeTextureId{TextureManager::INVALID_TEXTURE_ID};
+ TestObserverWithCustomFunction** removeTextureObserver{nullptr};
+ std::string resendFilename{};
+ TextureManager::TextureId resendTextureId{TextureManager::INVALID_TEXTURE_ID};
+ TestObserver* resendTextureObserver{nullptr};
+ std::string newlyFilename{};
+ TextureManager::TextureId* newlyTextureIdPtr{nullptr};
+ };
+ struct CustomData2
+ {
+ TextureManager* textureManagerPtr{nullptr};
+ TestObserver* self{nullptr};
+ bool* observerLoadedPtr{nullptr};
+ bool* observerCalledPtr{nullptr};
+ };
+
+ bool observer2Loaded = false;
+ bool observer2Called = false;
+ bool newObserver2Loaded = false;
+ bool newObserver2Called = false;
+ CustomData2 newData2; // Used on observer1 function
+
+ // Connect observer1 custom function
+ CustomData1 data1;
+ data1.textureManagerPtr = &textureManager;
+ data1.removeTextureId = textureId2;
+ data1.removeTextureObserver = &observer2;
+ data1.resendFilename = filename3;
+ data1.resendTextureId = textureId3;
+ data1.resendTextureObserver = &observer3;
+ data1.newlyFilename = filename2; // Same as observer2 filename
+ data1.newlyTextureIdPtr = &textureId4;
+
+ observer1.mData = &data1;
+ observer1.ConnectFunction(
+ [&](void* data) {
+ DALI_TEST_CHECK(data);
+ CustomData1 data1 = *(CustomData1*)data;
+
+ DALI_TEST_CHECK(data1.textureManagerPtr);
+ DALI_TEST_CHECK(data1.removeTextureId != TextureManager::INVALID_TEXTURE_ID);
+ DALI_TEST_CHECK(data1.removeTextureObserver);
+ DALI_TEST_CHECK(*data1.removeTextureObserver);
+ DALI_TEST_CHECK(!data1.resendFilename.empty());
+ DALI_TEST_CHECK(data1.resendTextureId != TextureManager::INVALID_TEXTURE_ID);
+ DALI_TEST_CHECK(data1.resendTextureObserver);
+ DALI_TEST_CHECK(!data1.newlyFilename.empty());
+ DALI_TEST_CHECK(data1.newlyTextureIdPtr);
+ DALI_TEST_CHECK(*data1.newlyTextureIdPtr == TextureManager::INVALID_TEXTURE_ID);
+
+ // Remove textureId2.
+ data1.textureManagerPtr->RequestRemove(data1.removeTextureId, *data1.removeTextureObserver);
+
+ auto removedObserver = *data1.removeTextureObserver;
+
+ // Destroy observer2.
+ delete removedObserver;
+
+ // Create new observer. Make we use same pointer if we can.
+ uint32_t maxTryCount = 100u;
+ uint32_t tryCount = 0u;
+
+ while(tryCount < maxTryCount)
+ {
+ *data1.removeTextureObserver = new TestObserverWithCustomFunction();
+ if(removedObserver == *data1.removeTextureObserver) break;
+ ++tryCount;
+ delete *data1.removeTextureObserver;
+ }
+
+ tet_printf("TryCount[%u] / Old observer2 : %p, newly observer2 : %p\n", tryCount, removedObserver, *data1.removeTextureObserver);
+
+ // Connect new observer2 custom function
+ newData2.textureManagerPtr = &textureManager;
+ newData2.self = (*data1.removeTextureObserver);
+ newData2.observerLoadedPtr = &newObserver2Loaded;
+ newData2.observerCalledPtr = &newObserver2Called;
+
+ (*data1.removeTextureObserver)->mData = &newData2;
+ (*data1.removeTextureObserver)->ConnectFunction([](void* data) {
+ DALI_TEST_CHECK(data);
+ CustomData2 data2 = *(CustomData2*)data;
+
+ tet_printf("New created observer running\n");
+
+ DALI_TEST_CHECK(data2.self);
+ DALI_TEST_CHECK(data2.observerLoadedPtr);
+ DALI_TEST_CHECK(data2.observerCalledPtr);
+
+ *data2.observerLoadedPtr = data2.self->mLoaded;
+ *data2.observerCalledPtr = data2.self->mObserverCalled;
+ });
+
+ // Dummy reference value
+ auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+
+ // Resend textureId3
+ data1.textureManagerPtr->RequestRemove(data1.resendTextureId, data1.resendTextureObserver);
+
+ TextureManager::TextureId tempId;
+ tempId = data1.textureManagerPtr->RequestLoad(
+ data1.resendFilename,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ TextureManager::UseAtlas::NO_ATLAS,
+ data1.resendTextureObserver,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ DALI_TEST_CHECK(tempId == data1.resendTextureId);
+
+ // Request new task
+
+ tempId = data1.textureManagerPtr->RequestLoad(
+ data1.newlyFilename,
+ ImageDimensions(),
+ FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR,
+ TextureManager::UseAtlas::NO_ATLAS,
+ *data1.removeTextureObserver,
+ true,
+ TextureManager::ReloadPolicy::CACHED,
+ preMultiply);
+
+ DALI_TEST_CHECK(tempId != TextureManager::INVALID_TEXTURE_ID);
+ *data1.newlyTextureIdPtr = tempId;
+ });
+
+ // Connect observer2 custom function
+ CustomData2 data2;
+ data2.textureManagerPtr = &textureManager;
+ data2.self = observer2;
+ data2.observerLoadedPtr = &observer2Loaded;
+ data2.observerCalledPtr = &observer2Called;
+
+ observer2->mData = &data2;
+ observer2->ConnectFunction(
+ [](void* data) {
+ DALI_TEST_CHECK(data);
+ CustomData2 data2 = *(CustomData2*)data;
+
+ tet_printf("Old created observer running. Something error occured!\n");
+
+ DALI_TEST_CHECK(data2.self);
+ DALI_TEST_CHECK(data2.observerLoadedPtr);
+ DALI_TEST_CHECK(data2.observerCalledPtr);
+
+ *data2.observerLoadedPtr = data2.self->mLoaded;
+ *data2.observerCalledPtr = data2.self->mObserverCalled;
+ });
+
+ application.SendNotification();
+ application.Render();
+
+ tet_printf("Id info - 1 : {%d}, 2 : {%d}, 3 : {%d}, 4 : {%d}\n", static_cast<int>(textureId1), static_cast<int>(textureId2), static_cast<int>(textureId3), static_cast<int>(textureId4));
+
+ DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2Loaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2Called, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(newObserver2Loaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(newObserver2Called, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3.mObserverCalled, false, TEST_LOCATION);
+
+ DALI_TEST_CHECK(textureId4 == TextureManager::INVALID_TEXTURE_ID);
+
+ // CAPTION : HARD-CODING.
+ // Run codes without exception.
+ try
+ {
+ tet_printf("Complete async load 1 first.\n");
+ std::vector<Devel::PixelBuffer> pixelBuffers;
+
+ pixelBuffers.clear();
+ pixelBuffers.push_back(Devel::PixelBuffer::New(1, 1, Pixel::Format::RGB888));
+ textureManager.AsyncLoadComplete(textureId1, pixelBuffers);
+
+ tet_printf("Now observer2 deleted, observer3 resended, observer2 re-created.\n");
+ DALI_TEST_EQUALS(observer1.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2Loaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2Called, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(newObserver2Loaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(newObserver2Called, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3.mLoaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3.mObserverCalled, false, TEST_LOCATION);
+
+ tet_printf("Id info - 1 : {%d}, 2 : {%d}, 3 : {%d}, 4 : {%d}\n", static_cast<int>(textureId1), static_cast<int>(textureId2), static_cast<int>(textureId3), static_cast<int>(textureId4));
+
+ DALI_TEST_CHECK(textureId4 == textureId2);
+
+ // Remove processor excute.
+ application.SendNotification();
+ application.Render();
+
+ tet_printf("Complete async load 2. Let we check old version observer2 ignored and newly observer2 loaded.\n");
+ pixelBuffers.clear();
+ pixelBuffers.push_back(Devel::PixelBuffer::New(1, 1, Pixel::Format::RGB888));
+ textureManager.AsyncLoadComplete(textureId2, pixelBuffers);
+
+ DALI_TEST_EQUALS(observer1.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2Loaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2Called, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(newObserver2Loaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(newObserver2Called, true, TEST_LOCATION);
+ // We don't check observer3 not loaded case because SendNotification can process AsyncTask.
+ //DALI_TEST_EQUALS(observer3.mLoaded, false, TEST_LOCATION);
+ //DALI_TEST_EQUALS(observer3.mObserverCalled, false, TEST_LOCATION);
+
+ tet_printf("Complete async load 3.\n");
+ pixelBuffers.clear();
+ pixelBuffers.push_back(Devel::PixelBuffer::New(1, 1, Pixel::Format::RGB888));
+ textureManager.AsyncLoadComplete(textureId3, pixelBuffers);
+
+ DALI_TEST_EQUALS(observer1.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer1.mObserverCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2Loaded, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer2Called, false, TEST_LOCATION);
+ DALI_TEST_EQUALS(newObserver2Loaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(newObserver2Called, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3.mLoaded, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(observer3.mObserverCalled, true, TEST_LOCATION);
+ }
+ catch(...)
+ {
+ DALI_TEST_CHECK(false);
+ }
+
+ END_TEST;
+}
\ No newline at end of file