const char* TEST_ANIMATED_VECTOR_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/insta_camera.json";
const char* TEST_WEBP_FILE_NAME = TEST_RESOURCE_DIR "/dali-logo.webp";
+const char* TEST_OVERWRITABLE_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/overwritable-image.jpg";
+
void TestUrl(ImageView imageView, const std::string url)
{
Property::Value value = imageView.GetProperty(imageView.GetPropertyIndex("image"));
DALI_TEST_EQUALS(urlActual, url, TEST_LOCATION);
}
+void OverwriteImage(const char* sourceFilename)
+{
+ FILE* fpOut = fopen(TEST_OVERWRITABLE_IMAGE_FILE_NAME, "wb");
+ DALI_TEST_CHECK(fpOut);
+ if(fpOut)
+ {
+ FILE* fpIn = fopen(sourceFilename, "rb");
+ if(fpIn)
+ {
+ fseek(fpIn, 0, SEEK_END);
+ size_t size = ftell(fpIn);
+
+ tet_printf("Open %s success! file size : %zu byte\n", sourceFilename, size);
+ Dali::Vector<uint8_t> data;
+ data.Resize(size);
+ fseek(fpIn, 0, SEEK_SET);
+ size_t realSize = fread(data.Begin(), sizeof(uint8_t), size, fpIn);
+ fclose(fpIn);
+ data.Resize(realSize);
+
+ // Overwrite
+ fwrite(data.Begin(), sizeof(uint8_t), size, fpOut);
+ }
+ else
+ {
+ tet_printf("Open %s failed! write invalid\n", sourceFilename);
+ fprintf(fpOut, "invalid\n");
+ }
+ fclose(fpOut);
+ }
+}
+
} // namespace
int UtcDaliImageViewNewP(void)
END_TEST;
}
+
+int UtcDaliImageViewImageLoadFailureAndReload01(void)
+{
+ tet_infoline("Try to load invalid image first, and then reload after that image valid.");
+ ToolkitTestApplication application;
+
+ gResourceReadySignalFired = false;
+
+ // Make overwritable image invalid first.
+ OverwriteImage("");
+
+ ImageView imageView = ImageView::New(TEST_OVERWRITABLE_IMAGE_FILE_NAME);
+ imageView.SetProperty(Actor::Property::SIZE, Vector2(100.f, 100.f));
+ imageView.ResourceReadySignal().Connect(&ResourceReadySignal);
+
+ application.GetScene().Add(imageView);
+ application.SendNotification();
+ application.Render(16);
+
+ // loading started, this waits for the loader thread
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(gResourceReadySignalFired, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(imageView.IsResourceReady(), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(imageView.GetVisualResourceStatus(ImageView::Property::IMAGE), Visual::ResourceStatus::FAILED, TEST_LOCATION);
+
+ gResourceReadySignalFired = false;
+
+ // Make overwritable image valid now.
+ OverwriteImage(gImage_34_RGBA);
+
+ // Reload the image
+ Property::Map attributes;
+ DevelControl::DoAction(imageView, ImageView::Property::IMAGE, DevelImageVisual::Action::RELOAD, attributes);
+ application.SendNotification();
+ application.Render(16);
+
+ // loading started, this waits for the loader thread
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(gResourceReadySignalFired, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(imageView.IsResourceReady(), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(imageView.GetVisualResourceStatus(ImageView::Property::IMAGE), Visual::ResourceStatus::READY, TEST_LOCATION);
+
+ // Make overwritable image invalid end of test (for clean).
+ OverwriteImage("");
+
+ gResourceReadySignalFired = false;
+
+ END_TEST;
+}
+
+int UtcDaliImageViewImageLoadFailureAndReload02(void)
+{
+ tet_infoline("Try to load invalid image first, and then reload after that image valid.");
+ tet_infoline("This case, broken image was n-patch. So we should check whether Geometry / Shader changed after reload");
+ ToolkitTestApplication application;
+
+ Toolkit::StyleManager styleManager = Toolkit::StyleManager::Get();
+ DevelStyleManager::SetBrokenImageUrl(styleManager, DevelStyleManager::BrokenImageType::SMALL, TEST_BROKEN_IMAGE_S);
+ DevelStyleManager::SetBrokenImageUrl(styleManager, DevelStyleManager::BrokenImageType::NORMAL, TEST_BROKEN_IMAGE_M);
+ DevelStyleManager::SetBrokenImageUrl(styleManager, DevelStyleManager::BrokenImageType::LARGE, TEST_BROKEN_IMAGE_L);
+
+ gResourceReadySignalFired = false;
+
+ // Make overwritable image invalid first.
+ OverwriteImage("");
+
+ ImageView imageView = ImageView::New(TEST_OVERWRITABLE_IMAGE_FILE_NAME);
+ imageView.SetProperty(Actor::Property::SIZE, Vector2(100.f, 100.f));
+ imageView.ResourceReadySignal().Connect(&ResourceReadySignal);
+
+ application.GetScene().Add(imageView);
+ application.SendNotification();
+ application.Render(16);
+
+ // loading started, this waits for the loader thread
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(gResourceReadySignalFired, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(imageView.IsResourceReady(), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(imageView.GetVisualResourceStatus(ImageView::Property::IMAGE), Visual::ResourceStatus::FAILED, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(imageView.GetRendererCount(), 1u, TEST_LOCATION);
+ Geometry brokenGeometry = imageView.GetRendererAt(0u).GetGeometry();
+ Shader brokenShader = imageView.GetRendererAt(0u).GetShader();
+ DALI_TEST_CHECK(brokenGeometry);
+ DALI_TEST_CHECK(brokenShader);
+
+ gResourceReadySignalFired = false;
+
+ // Make overwritable image valid now.
+ OverwriteImage(gImage_34_RGBA);
+
+ // Reload the image
+ Property::Map attributes;
+ DevelControl::DoAction(imageView, ImageView::Property::IMAGE, DevelImageVisual::Action::RELOAD, attributes);
+ application.SendNotification();
+ application.Render(16);
+
+ // loading started, this waits for the loader thread
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(gResourceReadySignalFired, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(imageView.IsResourceReady(), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(imageView.GetVisualResourceStatus(ImageView::Property::IMAGE), Visual::ResourceStatus::READY, TEST_LOCATION);
+
+ // Check whether we don't use n-patch shader and geometry in this case.
+ DALI_TEST_EQUALS(imageView.GetRendererCount(), 1u, TEST_LOCATION);
+ DALI_TEST_CHECK(brokenGeometry != imageView.GetRendererAt(0u).GetGeometry());
+ DALI_TEST_CHECK(brokenShader != imageView.GetRendererAt(0u).GetShader());
+
+ // Make overwritable image invalid end of test (for clean).
+ OverwriteImage("");
+
+ gResourceReadySignalFired = false;
+
+ END_TEST;
+}
\ No newline at end of file
imageSize = mPlacementActorSize;
}
+ mUseBrokenImageRenderer = true;
mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
Texture brokenImage = mImpl->mRenderer.GetTextures().GetTexture(0);
naturalSize.x = brokenImage.GetWidth();
void ImageVisual::OnInitialize()
{
- Geometry geometry;
-
- // Get the geometry
- if(mImpl->mCustomShader)
- {
- geometry = CreateGeometry(mFactoryCache, mImpl->mCustomShader->mGridSize);
- }
- else // Get any geometry associated with the texture
- {
- TextureManager& textureManager = mFactoryCache.GetTextureManager();
-
- uint32_t firstElementCount{0u};
- uint32_t secondElementCount{0u};
- geometry = textureManager.GetRenderGeometry(mTextureId, firstElementCount, secondElementCount);
-
- if(!firstElementCount && !secondElementCount) // Otherwise use quad
- {
- geometry = CreateGeometry(mFactoryCache, ImageDimensions(1, 1));
- }
- }
-
// Increase reference count of External Resources :
// EncodedImageBuffer or ExternalTextures.
// Reference count will be decreased at destructor of the visual.
textureManager.UseExternalResource(mImageUrl.GetUrl());
}
- Shader shader = GenerateShader();
+ // Generate geometry and shader. Note that we should check AddOn when generate geometry, due to LoadPolicy::IMMEDIATE case
+ Geometry geometry = GenerateGeometry(mTextureId, true);
+ Shader shader = GenerateShader();
// Create the renderer
mImpl->mRenderer = DecoratedVisualRenderer::New(geometry, shader);
ComputeTextureSize();
CheckMaskTexture();
- bool needToUpdateShader = DevelTexture::IsNative(mTextures.GetTexture(0));
+ bool needToUpdateShader = DevelTexture::IsNative(mTextures.GetTexture(0)) || mUseBrokenImageRenderer;
if(mTextures.GetTextureCount() == 3)
{
UpdateShader();
}
mTextures.Reset(); // Visual should not keep a handle to the texture after this point.
+
+ if(DALI_UNLIKELY(mUseBrokenImageRenderer))
+ {
+ // We need to re-generate geometry only if it was broken image before, and result changed after Reload.
+ auto geometry = GenerateGeometry(mTextureId, true);
+
+ // Update geometry only if we need.
+ if(geometry)
+ {
+ mImpl->mRenderer.SetGeometry(geometry);
+ }
+ }
+
+ // We don't use broken image anymore.
+ mUseBrokenImageRenderer = false;
}
if(attemptAtlasing) // the texture is packed inside atlas
void ImageVisual::LoadComplete(bool loadingSuccess, TextureInformation textureInformation)
{
Toolkit::Visual::ResourceStatus resourceStatus;
+
if(mImpl->mRenderer)
{
EnablePreMultipliedAlpha(textureInformation.preMultiplied);
ComputeTextureSize();
CheckMaskTexture();
+ bool needToUpdateShader = mUseBrokenImageRenderer;
+
if(textureInformation.textureSet.GetTextureCount() == 3)
{
if(textureInformation.textureSet.GetTexture(0).GetPixelFormat() == Pixel::L8 && textureInformation.textureSet.GetTexture(1).GetPixelFormat() == Pixel::CHROMINANCE_U && textureInformation.textureSet.GetTexture(2).GetPixelFormat() == Pixel::CHROMINANCE_V)
{
- mNeedYuvToRgb = true;
- UpdateShader();
+ mNeedYuvToRgb = true;
+ needToUpdateShader = true;
}
}
+ if(needToUpdateShader)
+ {
+ UpdateShader();
+ }
+
if(actor)
{
actor.AddRenderer(mImpl->mRenderer);
// reset the weak handle so that the renderer only get added to actor once
mPlacementActor.Reset();
}
+
+ auto geometry = GenerateGeometry(textureInformation.textureId, mUseBrokenImageRenderer);
+
+ if(DALI_UNLIKELY(geometry))
+ {
+ // Rare cases. If load successed image don't use quad geometry (i.e. Show some n-patch broken image, and call Reload(), and success)
+ // or If given texture use AddOn,
+ // then we need to make to use quad geometry and update shader agian.
+ mImpl->mRenderer.SetGeometry(geometry);
+ }
+
+ // We don't use broken image anymore.
+ mUseBrokenImageRenderer = false;
}
}
mLoadState = TextureManager::LoadState::LOAD_FAILED;
}
- // use geometry if needed
- if(loadingSuccess)
- {
- uint32_t firstElementCount{0u};
- uint32_t secondElementCount{0u};
- auto geometry = mFactoryCache.GetTextureManager().GetRenderGeometry(mTextureId, firstElementCount, secondElementCount);
- if(mImpl->mRenderer && geometry)
- {
- mImpl->mRenderer.SetGeometry(geometry);
- Dali::DevelRenderer::DrawCommand drawCommand{};
- drawCommand.drawType = DevelRenderer::DrawType::INDEXED;
-
- if(firstElementCount)
- {
- drawCommand.firstIndex = 0;
- drawCommand.elementCount = firstElementCount;
- drawCommand.queue = DevelRenderer::RENDER_QUEUE_OPAQUE;
- DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand);
- }
-
- if(secondElementCount)
- {
- drawCommand.firstIndex = firstElementCount;
- drawCommand.elementCount = secondElementCount;
- drawCommand.queue = DevelRenderer::RENDER_QUEUE_TRANSPARENT;
- DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand);
- }
- }
- }
-
// Signal to observers ( control ) that resources are ready. Must be all resources.
ResourceReady(resourceStatus);
}
}
}
+ mUseBrokenImageRenderer = true;
mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
if(actor)
{
}
}
+Geometry ImageVisual::GenerateGeometry(TextureManager::TextureId textureId, bool createForce)
+{
+ Geometry geometry;
+ if(Stage::IsInstalled())
+ {
+ if(mImpl->mCustomShader)
+ {
+ if(createForce)
+ {
+ geometry = CreateGeometry(mFactoryCache, mImpl->mCustomShader->mGridSize);
+ }
+ }
+ else
+ {
+ uint32_t firstElementCount{0u};
+ uint32_t secondElementCount{0u};
+
+ geometry = mFactoryCache.GetTextureManager().GetRenderGeometry(textureId, firstElementCount, secondElementCount);
+ if(geometry)
+ {
+ if(mImpl->mRenderer)
+ {
+ Dali::DevelRenderer::DrawCommand drawCommand{};
+ drawCommand.drawType = DevelRenderer::DrawType::INDEXED;
+
+ if(firstElementCount)
+ {
+ drawCommand.firstIndex = 0;
+ drawCommand.elementCount = firstElementCount;
+ drawCommand.queue = DevelRenderer::RENDER_QUEUE_OPAQUE;
+ DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand);
+ }
+
+ if(secondElementCount)
+ {
+ drawCommand.firstIndex = firstElementCount;
+ drawCommand.elementCount = secondElementCount;
+ drawCommand.queue = DevelRenderer::RENDER_QUEUE_TRANSPARENT;
+ DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand);
+ }
+ }
+ }
+ else if(createForce)
+ {
+ // Create default quad geometry now
+ geometry = CreateGeometry(mFactoryCache, ImageDimensions(1, 1));
+ }
+ }
+ }
+
+ return geometry;
+}
+
} // namespace Internal
} // namespace Toolkit
#define DALI_TOOLKIT_INTERNAL_IMAGE_VISUAL_H
/*
- * Copyright (c) 2023 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.
*/
void ResetFastTrackLoadingTask();
+ /**
+ * @brief Update geometry information and get the generated result.
+ *
+ * @param[in] textureId Id of texture. It will be used when we use AddOn.
+ * @param[in] createForce True if we need to create geometry forcely. False if we don't re-generate geometry.
+ * @return Generated geometry, or empty handle if we don't need to update geometry.
+ */
+ Geometry GenerateGeometry(TextureManager::TextureId textureId, bool createForce);
+
private:
Vector4 mPixelArea;
Property::Index mPixelAreaIndex;
Dali::Toolkit::ImageVisual::ReleasePolicy::Type mReleasePolicy;
Vector4 mAtlasRect;
Dali::ImageDimensions mAtlasRectSize;
- TextureManager::LoadState mLoadState; ///< The texture loading state
- bool mAttemptAtlasing; ///< If true will attempt atlasing, otherwise create unique texture
- bool mOrientationCorrection; ///< true if the image will have it's orientation corrected.
- bool mNeedYuvToRgb{false}; ///< true if we need to convert yuv to rgb.
- bool mNeedUnifiedYuvAndRgb{false}; ///< true if we need to support both yuv and rgb.
- bool mEnableBrokenImage{true}; ///< true if enable broken image.
- bool mUseFastTrackUploading{false}; ///< True if we use fast tack feature.
- bool mRendererAdded{false}; ///< True if renderer added into actor.
+ TextureManager::LoadState mLoadState; ///< The texture loading state
+ bool mAttemptAtlasing; ///< If true will attempt atlasing, otherwise create unique texture
+ bool mOrientationCorrection; ///< true if the image will have it's orientation corrected.
+ bool mNeedYuvToRgb{false}; ///< true if we need to convert yuv to rgb.
+ bool mNeedUnifiedYuvAndRgb{false}; ///< true if we need to support both yuv and rgb.
+ bool mEnableBrokenImage{true}; ///< true if enable broken image.
+ bool mUseFastTrackUploading{false}; ///< True if we use fast tack feature.
+ bool mRendererAdded{false}; ///< True if renderer added into actor.
+ bool mUseBrokenImageRenderer{false}; ///< True if renderer changed as broken image.
};
} // namespace Internal