From 397f4aeed26dcfbe111ecaae6ddb4edaa6214d03 Mon Sep 17 00:00:00 2001 From: Heeyong Song Date: Thu, 16 Jun 2022 13:46:29 +0900 Subject: [PATCH] [Tizen] Support YUV decoding for JPEG Change-Id: Ibf098d991e21813c0a388c66e7420a51f628706d --- .../resources/gallery-small-1-yuv420.jpg | Bin 0 -> 8259 bytes .../utc-Dali-TextureManager.cpp | 15 +- .../toolkit-environment-variable.cpp | 25 +- .../toolkit-environment-variable.h | 9 +- .../src/dali-toolkit/utc-Dali-ImageVisual.cpp | 135 ++++++++++- .../image-loader/async-image-loader-devel.cpp | 4 +- .../graphics/shaders/image-visual-shader.frag | 22 ++ .../image-loader/async-image-loader-impl.cpp | 12 +- .../image-loader/async-image-loader-impl.h | 15 +- .../internal/image-loader/image-atlas-impl.cpp | 2 +- .../internal/image-loader/image-load-thread.cpp | 81 ++++--- .../internal/image-loader/image-load-thread.h | 36 +-- .../texture-async-loading-helper.cpp | 7 +- .../texture-manager/texture-async-loading-helper.h | 4 +- .../texture-manager/texture-manager-impl.cpp | 265 ++++++++++++++------- .../texture-manager/texture-manager-impl.h | 36 ++- .../texture-manager/texture-manager-type.h | 7 +- .../visuals/image-visual-shader-factory.cpp | 42 +++- .../internal/visuals/image-visual-shader-factory.h | 20 +- .../internal/visuals/image/image-visual.cpp | 20 +- dali-toolkit/internal/visuals/image/image-visual.h | 1 + .../internal/visuals/svg/svg-rasterize-thread.cpp | 8 +- .../internal/visuals/visual-factory-cache.h | 4 + .../public-api/image-loader/async-image-loader.cpp | 8 +- 24 files changed, 568 insertions(+), 210 deletions(-) create mode 100644 automated-tests/resources/gallery-small-1-yuv420.jpg diff --git a/automated-tests/resources/gallery-small-1-yuv420.jpg b/automated-tests/resources/gallery-small-1-yuv420.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c1362a62f4c6e296e33beab32b6b598ba3b2a2fa GIT binary patch literal 8259 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, Dali::Devel::PixelBuffer()); + std::vector pixelBuffers; + pixelBuffers.push_back(pixelBuffer); + textureManager.AsyncLoadComplete(textureId, pixelBuffers); + std::vector maskBuffers; + textureManager.AsyncLoadComplete(maskInfo->mAlphaMaskId, maskBuffers); textureManager.Remove(maskInfo->mAlphaMaskId, nullptr); textureManager.Remove(textureId, &observer); } @@ -1216,7 +1219,8 @@ int UtcTextureManagerRemoveDuringApplyMasking(void) // ApplyMask event come back, and do nothing. // CAPTION : HARD-CODING. { - textureManager.AsyncLoadComplete(textureId1, Dali::Devel::PixelBuffer()); + std::vector pixelBuffers; + textureManager.AsyncLoadComplete(textureId1, pixelBuffers); textureManager.Remove(maskInfo->mAlphaMaskId, nullptr); } @@ -1230,7 +1234,8 @@ int UtcTextureManagerRemoveDuringApplyMasking(void) // CAPTION : HARD-CODING. { - textureManager.AsyncLoadComplete(textureId2, Dali::Devel::PixelBuffer()); + std::vector pixelBuffers; + textureManager.AsyncLoadComplete(textureId2, pixelBuffers); textureManager.Remove(textureId2, &observer2); } @@ -1239,4 +1244,4 @@ int UtcTextureManagerRemoveDuringApplyMasking(void) DALI_TEST_EQUALS(observer2.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION); END_TEST; -} \ No newline at end of file +} diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-environment-variable.cpp b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-environment-variable.cpp index f02bfb4..5d8d143 100644 --- a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-environment-variable.cpp +++ b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-environment-variable.cpp @@ -19,26 +19,31 @@ // EXTERNAL INCLUDE #include +#include namespace Dali { - namespace EnvironmentVariable { - namespace { -const char * gReturnValue = NULL; -} +const char* gReturnValue = NULL; +std::string gEnvironmentVariableName; +std::string gEnvironmentVariableValue; +} // namespace -const char * GetEnvironmentVariable( const char * variable ) +const char* GetEnvironmentVariable(const char* variable) { + if(gEnvironmentVariableName == variable) + { + return gEnvironmentVariableValue.c_str(); + } return gReturnValue; } -void SetTestingEnvironmentVariable( bool testing) +void SetTestingEnvironmentVariable(bool testing) { - if( testing ) + if(testing) { gReturnValue = "1"; } @@ -48,6 +53,12 @@ void SetTestingEnvironmentVariable( bool testing) } } +void SetTestEnvironmentVariable(const char* variable, const char* value) +{ + gEnvironmentVariableName = variable; + gEnvironmentVariableValue = value; +} + } // namespace EnvironmentVariable } // namespace Dali diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-environment-variable.h b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-environment-variable.h index b1ebae0..43b0758 100644 --- a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-environment-variable.h +++ b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-environment-variable.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_ENVIRONMENT_VARIABLE_H /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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,17 +23,16 @@ namespace Dali { - namespace EnvironmentVariable { +const char* GetEnvironmentVariable(const char* variable); -const char * GetEnvironmentVariable( const char * variable ); +void SetTestingEnvironmentVariable(bool); -void SetTestingEnvironmentVariable( bool ); +void SetTestEnvironmentVariable(const char* variable, const char* value); } // namespace EnvironmentVariable } // namespace Dali - #endif // DALI_TOOLKIT_ENVIRONMENT_VARIABLE_H diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp index 3f4fdba..c399993 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-ImageVisual.cpp @@ -20,6 +20,7 @@ #include +#include #include #include @@ -60,6 +61,9 @@ const char* TEST_INVALID_FILE_NAME = TEST_RESOURCE_DIR "/invalid.jpg"; const char* TEST_REMOTE_INVALID_FILE_NAME = "https://www.tizen.org/invalid.png"; const char* TEST_MASK_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/mask.png"; const char* TEST_ROTATED_IMAGE = TEST_RESOURCE_DIR "/keyboard-Landscape.jpg"; +const char* TEST_YUV420_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/gallery-small-1-yuv420.jpg"; + +constexpr auto LOAD_IMAGE_YUV_PLANES_ENV = "DALI_LOAD_IMAGE_YUV_PLANES_ENV"; bool gResourceReadySignalFired = false; std::vector gReadyIds = {}; @@ -1694,7 +1698,7 @@ int UtcDaliImageVisualAlphaMask02(void) application.Render(); DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION); - Renderer renderer = actor.GetRendererAt(0u); + Renderer renderer = actor.GetRendererAt(0u); TextureSet textures = renderer.GetTextures(); DALI_TEST_CHECK(textures); DALI_TEST_EQUALS(textures.GetTextureCount(), 2u, TEST_LOCATION); @@ -1753,7 +1757,7 @@ int UtcDaliImageVisualAlphaMask03(void) application.Render(); DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION); - Renderer renderer = actor.GetRendererAt(0u); + Renderer renderer = actor.GetRendererAt(0u); TextureSet textures = renderer.GetTextures(); DALI_TEST_CHECK(textures); DALI_TEST_EQUALS(textures.GetTextureCount(), 1u, TEST_LOCATION); @@ -1865,7 +1869,7 @@ int UtcDaliImageVisualSynchronousLoadAlphaMask02(void) application.Render(); DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION); - Renderer renderer = actor.GetRendererAt(0u); + Renderer renderer = actor.GetRendererAt(0u); TextureSet textures = renderer.GetTextures(); DALI_TEST_CHECK(textures); DALI_TEST_EQUALS(textures.GetTextureCount(), 2u, TEST_LOCATION); @@ -2921,3 +2925,128 @@ int UtcDaliImageVisualLoadReady01(void) END_TEST; } + +int UtcDaliImageVisualLoadImagePlanes01(void) +{ + EnvironmentVariable::SetTestEnvironmentVariable(LOAD_IMAGE_YUV_PLANES_ENV, "1"); + + ToolkitTestApplication application; + + VisualFactory factory = VisualFactory::Get(); + DALI_TEST_CHECK(factory); + + Property::Map propertyMap; + propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE); + propertyMap.Insert(ImageVisual::Property::URL, TEST_YUV420_IMAGE_FILE_NAME); + + Visual::Base visual = factory.CreateVisual(propertyMap); + DALI_TEST_CHECK(visual); + + DummyControl actor = DummyControl::New(); + DummyControlImpl& dummyImpl = static_cast(actor.GetImplementation()); + dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual); + actor.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f)); + application.GetScene().Add(actor); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION); + + TestGlAbstraction& gl = application.GetGlAbstraction(); + TraceCallStack& textureTrace = gl.GetTextureTrace(); + textureTrace.Enable(true); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION); + DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION); + DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 3, TEST_LOCATION); + + Renderer renderer = actor.GetRendererAt(0); + auto preMultipliedAlpha = renderer.GetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA); + DALI_TEST_EQUALS(preMultipliedAlpha, false, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliImageVisualLoadImagePlanes02(void) +{ + EnvironmentVariable::SetTestEnvironmentVariable(LOAD_IMAGE_YUV_PLANES_ENV, "1"); + + ToolkitTestApplication application; + + VisualFactory factory = VisualFactory::Get(); + DALI_TEST_CHECK(factory); + + // Alpha masking case - not support yuv planes + Property::Map propertyMap; + propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE); + propertyMap.Insert(ImageVisual::Property::URL, TEST_YUV420_IMAGE_FILE_NAME); + propertyMap.Insert(ImageVisual::Property::ALPHA_MASK_URL, TEST_MASK_IMAGE_FILE_NAME); + + Visual::Base visual = factory.CreateVisual(propertyMap); + DALI_TEST_CHECK(visual); + + DummyControl actor = DummyControl::New(); + DummyControlImpl& dummyImpl = static_cast(actor.GetImplementation()); + dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual); + actor.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f)); + application.GetScene().Add(actor); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(3), true, TEST_LOCATION); + + TestGlAbstraction& gl = application.GetGlAbstraction(); + TraceCallStack& textureTrace = gl.GetTextureTrace(); + textureTrace.Enable(true); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION); + DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION); + DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 1, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliImageVisualLoadImagePlanes03(void) +{ + EnvironmentVariable::SetTestEnvironmentVariable(LOAD_IMAGE_YUV_PLANES_ENV, "1"); + + ToolkitTestApplication application; + + VisualFactory factory = VisualFactory::Get(); + DALI_TEST_CHECK(factory); + + TestGlAbstraction& gl = application.GetGlAbstraction(); + TraceCallStack& textureTrace = gl.GetTextureTrace(); + textureTrace.Enable(true); + + Property::Map propertyMap; + propertyMap.Insert(Toolkit::Visual::Property::TYPE, Visual::IMAGE); + propertyMap.Insert(ImageVisual::Property::URL, TEST_YUV420_IMAGE_FILE_NAME); + propertyMap.Insert(ImageVisual::Property::SYNCHRONOUS_LOADING, true); + + Visual::Base visual = factory.CreateVisual(propertyMap); + DALI_TEST_CHECK(visual); + + DummyControl actor = DummyControl::New(); + DummyControlImpl& dummyImpl = static_cast(actor.GetImplementation()); + dummyImpl.RegisterVisual(Control::CONTROL_PROPERTY_END_INDEX + 1, visual); + actor.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f)); + application.GetScene().Add(actor); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(actor.GetRendererCount(), 1u, TEST_LOCATION); + DALI_TEST_EQUALS(actor.IsResourceReady(), true, TEST_LOCATION); + DALI_TEST_EQUALS(textureTrace.CountMethod("GenTextures"), 3, TEST_LOCATION); + + END_TEST; +} diff --git a/dali-toolkit/devel-api/image-loader/async-image-loader-devel.cpp b/dali-toolkit/devel-api/image-loader/async-image-loader-devel.cpp index e73a53c..cf47c70 100644 --- a/dali-toolkit/devel-api/image-loader/async-image-loader-devel.cpp +++ b/dali-toolkit/devel-api/image-loader/async-image-loader-devel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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,7 +39,7 @@ uint32_t Load(AsyncImageLoader asyncImageLoader, bool orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad) { - return GetImplementation(asyncImageLoader).Load(Toolkit::Internal::VisualUrl(url), dimensions, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad); + return GetImplementation(asyncImageLoader).Load(Toolkit::Internal::VisualUrl(url), dimensions, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad, false); } uint32_t ApplyMask(AsyncImageLoader asyncImageLoader, diff --git a/dali-toolkit/internal/graphics/shaders/image-visual-shader.frag b/dali-toolkit/internal/graphics/shaders/image-visual-shader.frag index 3694280..c292b0e 100644 --- a/dali-toolkit/internal/graphics/shaders/image-visual-shader.frag +++ b/dali-toolkit/internal/graphics/shaders/image-visual-shader.frag @@ -9,6 +9,10 @@ INPUT mediump vec4 vCornerRadius; #endif uniform sampler2D sTexture; +#ifdef IS_REQUIRED_YUV_TO_RGB +uniform sampler2D sTextureU; +uniform sampler2D sTextureV; +#endif #ifdef IS_REQUIRED_ALPHA_MASKING uniform sampler2D sMaskTexture; @@ -208,6 +212,20 @@ mediump float calculateCornerOpacity() } #endif +#ifdef IS_REQUIRED_YUV_TO_RGB +lowp vec3 ConvertYuvToRgb(mediump vec2 texCoord) +{ + lowp float y = texture(sTexture, texCoord).r; + lowp float u = texture(sTextureU, texCoord).r - 0.5; + lowp float v = texture(sTextureV, texCoord).r - 0.5; + lowp vec3 rgb; + rgb.r = y + (1.403 * v); + rgb.g = y - (0.344 * u) - (0.714 * v); + rgb.b = y + (1.770 * u); + return rgb; +} +#endif + void main() { #ifdef ATLAS_DEFAULT_WARP @@ -219,7 +237,11 @@ void main() mediump vec2 texCoord = vTexCoord; #endif +#ifdef IS_REQUIRED_YUV_TO_RGB + lowp vec4 textureColor = vec4(ConvertYuvToRgb(texCoord), 1.0) * vec4( mixColor, 1.0 ) * uColor; +#else lowp vec4 textureColor = TEXTURE( sTexture, texCoord ) * vec4( mixColor, 1.0 ) * uColor; +#endif #ifdef IS_REQUIRED_ALPHA_MASKING mediump float maskAlpha = TEXTURE(sMaskTexture, vMaskTexCoord).a; diff --git a/dali-toolkit/internal/image-loader/async-image-loader-impl.cpp b/dali-toolkit/internal/image-loader/async-image-loader-impl.cpp index fc48c18..880db04 100644 --- a/dali-toolkit/internal/image-loader/async-image-loader-impl.cpp +++ b/dali-toolkit/internal/image-loader/async-image-loader-impl.cpp @@ -65,14 +65,15 @@ uint32_t AsyncImageLoader::Load(const VisualUrl& url, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection, - DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad) + DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad, + bool loadPlanes) { if(!mIsLoadThreadStarted) { mLoadThread.Start(); mIsLoadThreadStarted = true; } - mLoadThread.AddTask(new LoadingTask(++mLoadTaskId, url, dimensions, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad)); + mLoadThread.AddTask(new LoadingTask(++mLoadTaskId, url, dimensions, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad, loadPlanes)); return mLoadTaskId; } @@ -136,15 +137,14 @@ void AsyncImageLoader::ProcessLoadedImage() { if(mPixelBufferLoadedSignal.GetConnectionCount() > 0) { - std::vector pixelBuffers{next->pixelBuffer}; - mPixelBufferLoadedSignal.Emit(next->id, pixelBuffers); + mPixelBufferLoadedSignal.Emit(next->id, next->pixelBuffers); } else if(mLoadedSignal.GetConnectionCount() > 0) { PixelData pixelData; - if(next->pixelBuffer) + if(!next->pixelBuffers.empty()) { - pixelData = Devel::PixelBuffer::Convert(next->pixelBuffer); + pixelData = Devel::PixelBuffer::Convert(next->pixelBuffers[0]); } mLoadedSignal.Emit(next->id, pixelData); } diff --git a/dali-toolkit/internal/image-loader/async-image-loader-impl.h b/dali-toolkit/internal/image-loader/async-image-loader-impl.h index aa2efc9..34496b1 100644 --- a/dali-toolkit/internal/image-loader/async-image-loader-impl.h +++ b/dali-toolkit/internal/image-loader/async-image-loader-impl.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_ASYNC_IMAGE_LOADER_IMPL_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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. @@ -53,14 +53,23 @@ public: DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad); /** - * @copydoc Toolkit::AsyncImageLoader::Load( const std::string&, ImageDimensions, FittingMode::Type, SamplingMode::Type, bool , DevelAsyncImageLoader::PreMultiplyOnLoad ) + * @brief Starts an image loading task. + * @param[in] url The URL of the image file to load + * @param[in] dimensions The width and height to fit the loaded image to + * @param[in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter + * @param[in] samplingMode The filtering method used when sampling pixels from the input image while fitting it to desired size + * @param[in] orientationCorrection Reorient the image to respect any orientation metadata in its header + * @param[in] preMultiplyOnLoad ON if the image color should be multiplied by it's alpha. Set to OFF if there is no alpha or if the image need to be applied alpha mask. + * @param[in] loadPlanes true to load image planes or false to load bitmap image. + * @return The loading task id */ uint32_t Load(const VisualUrl& url, ImageDimensions dimensions, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection, - DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad); + DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad, + bool loadPlanes); /** * @brief Starts an image loading task by encoded image buffer. diff --git a/dali-toolkit/internal/image-loader/image-atlas-impl.cpp b/dali-toolkit/internal/image-loader/image-atlas-impl.cpp index 08b4cd0..be946ab 100644 --- a/dali-toolkit/internal/image-loader/image-atlas-impl.cpp +++ b/dali-toolkit/internal/image-loader/image-atlas-impl.cpp @@ -160,7 +160,7 @@ bool ImageAtlas::Upload(Vector4& textureRect, uint32_t packPositionY = 0; if(mPacker.Pack(dimensions.GetWidth(), dimensions.GetHeight(), packPositionX, packPositionY)) { - uint32_t loadId = GetImplementation(mAsyncLoader).Load(url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad::OFF); + uint32_t loadId = GetImplementation(mAsyncLoader).Load(url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad::OFF, false); mLoadingTaskInfoContainer.PushBack(new LoadingTaskInfo(loadId, packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight(), atlasUploadObserver)); // apply the half pixel correction textureRect.x = (static_cast(packPositionX) + 0.5f) / mWidth; // left diff --git a/dali-toolkit/internal/image-loader/image-load-thread.cpp b/dali-toolkit/internal/image-loader/image-load-thread.cpp index c0959f6..deb0178 100644 --- a/dali-toolkit/internal/image-loader/image-load-thread.cpp +++ b/dali-toolkit/internal/image-loader/image-load-thread.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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,11 +19,11 @@ #include "image-load-thread.h" // EXTERNAL INCLUDES -#include #include #include #include #include +#include namespace Dali { @@ -32,83 +32,85 @@ namespace Toolkit namespace Internal { LoadingTask::LoadingTask(uint32_t id, Dali::AnimatedImageLoading animatedImageLoading, uint32_t frameIndex, DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad) -: pixelBuffer(), - url(), +: url(), encodedImageBuffer(), id(id), dimensions(), fittingMode(), samplingMode(), - orientationCorrection(), preMultiplyOnLoad(preMultiplyOnLoad), - isMaskTask(false), maskPixelBuffer(), contentScale(1.0f), - cropToMask(false), animatedImageLoading(animatedImageLoading), - frameIndex(frameIndex) + frameIndex(frameIndex), + orientationCorrection(), + isMaskTask(false), + cropToMask(false), + loadPlanes(false) { } -LoadingTask::LoadingTask(uint32_t id, const VisualUrl& url, ImageDimensions dimensions, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad) -: pixelBuffer(), - url(url), +LoadingTask::LoadingTask(uint32_t id, const VisualUrl& url, ImageDimensions dimensions, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad, bool loadPlanes) +: url(url), encodedImageBuffer(), id(id), dimensions(dimensions), fittingMode(fittingMode), samplingMode(samplingMode), - orientationCorrection(orientationCorrection), preMultiplyOnLoad(preMultiplyOnLoad), - isMaskTask(false), maskPixelBuffer(), contentScale(1.0f), - cropToMask(false), animatedImageLoading(), - frameIndex(0u) + frameIndex(0u), + orientationCorrection(orientationCorrection), + isMaskTask(false), + cropToMask(false), + loadPlanes(loadPlanes) { } LoadingTask::LoadingTask(uint32_t id, const EncodedImageBuffer& encodedImageBuffer, ImageDimensions dimensions, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad) -: pixelBuffer(), - url(), +: url(), encodedImageBuffer(encodedImageBuffer), id(id), dimensions(dimensions), fittingMode(fittingMode), samplingMode(samplingMode), - orientationCorrection(orientationCorrection), preMultiplyOnLoad(preMultiplyOnLoad), - isMaskTask(false), maskPixelBuffer(), contentScale(1.0f), - cropToMask(false), animatedImageLoading(), - frameIndex(0u) + frameIndex(0u), + orientationCorrection(orientationCorrection), + isMaskTask(false), + cropToMask(false), + loadPlanes(false) { } LoadingTask::LoadingTask(uint32_t id, Devel::PixelBuffer pixelBuffer, Devel::PixelBuffer maskPixelBuffer, float contentScale, bool cropToMask, DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad) -: pixelBuffer(pixelBuffer), - url(""), +: url(""), encodedImageBuffer(), id(id), dimensions(), fittingMode(), samplingMode(), - orientationCorrection(), preMultiplyOnLoad(preMultiplyOnLoad), - isMaskTask(true), maskPixelBuffer(maskPixelBuffer), contentScale(contentScale), - cropToMask(cropToMask), animatedImageLoading(), - frameIndex(0u) + frameIndex(0u), + orientationCorrection(), + isMaskTask(true), + cropToMask(cropToMask), + loadPlanes(false) { + pixelBuffers.push_back(pixelBuffer); } void LoadingTask::Load() { + Devel::PixelBuffer pixelBuffer; if(animatedImageLoading) { pixelBuffer = animatedImageLoading.LoadFrame(frameIndex); @@ -119,14 +121,26 @@ void LoadingTask::Load() } else if(url.IsValid() && url.IsLocalResource()) { - pixelBuffer = Dali::LoadImageFromFile(url.GetUrl(), dimensions, fittingMode, samplingMode, orientationCorrection); + if(loadPlanes) + { + Dali::LoadImagePlanesFromFile(url.GetUrl(), pixelBuffers, dimensions, fittingMode, samplingMode, orientationCorrection); + } + else + { + pixelBuffer = Dali::LoadImageFromFile(url.GetUrl(), dimensions, fittingMode, samplingMode, orientationCorrection); + } } else if(url.IsValid()) { pixelBuffer = Dali::DownloadImageSynchronously(url.GetUrl(), dimensions, fittingMode, samplingMode, orientationCorrection); } - if(!pixelBuffer) + if(pixelBuffer) + { + pixelBuffers.push_back(pixelBuffer); + } + + if(pixelBuffers.empty()) { DALI_LOG_ERROR("LoadingTask::Load: Loading is failed: %s\n", url.GetUrl().c_str()); } @@ -134,16 +148,19 @@ void LoadingTask::Load() void LoadingTask::ApplyMask() { - pixelBuffer.ApplyMask(maskPixelBuffer, contentScale, cropToMask); + if(!pixelBuffers.empty()) + { + pixelBuffers[0].ApplyMask(maskPixelBuffer, contentScale, cropToMask); + } } void LoadingTask::MultiplyAlpha() { - if(pixelBuffer && Pixel::HasAlpha(pixelBuffer.GetPixelFormat())) + if(!pixelBuffers.empty() && Pixel::HasAlpha(pixelBuffers[0].GetPixelFormat())) { if(preMultiplyOnLoad == DevelAsyncImageLoader::PreMultiplyOnLoad::ON) { - pixelBuffer.MultiplyColorByAlpha(); + pixelBuffers[0].MultiplyColorByAlpha(); } } } diff --git a/dali-toolkit/internal/image-loader/image-load-thread.h b/dali-toolkit/internal/image-loader/image-load-thread.h index 319f750..520d2b2 100644 --- a/dali-toolkit/internal/image-loader/image-load-thread.h +++ b/dali-toolkit/internal/image-loader/image-load-thread.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_IMAGE_LOAD_THREAD_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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. @@ -26,10 +26,10 @@ #include #include #include +#include #include #include #include -#include namespace Dali { @@ -63,6 +63,7 @@ struct LoadingTask * @param [in] samplingMode The filtering method used when sampling pixels from the input image while fitting it to desired size. * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header. * @param [in] preMultiplyOnLoad ON if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha or if the image need to be applied alpha mask. + * @param [in] loadPlanes true to load image planes or false to load bitmap image. */ LoadingTask(uint32_t id, const VisualUrl& url, @@ -70,7 +71,8 @@ struct LoadingTask FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection, - DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad); + DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad, + bool loadPlanes); /** * Constructor. @@ -129,23 +131,25 @@ private: LoadingTask& operator=(const LoadingTask& queue); public: - Devel::PixelBuffer pixelBuffer; ///< pixelBuffer handle after successful load - ///< or pixelBuffer to be masked image in the mask task - VisualUrl url; ///< url of the image to load - EncodedImageBuffer encodedImageBuffer; ///< encoded buffer of the image to load - uint32_t id; ///< The unique id associated with this task. - ImageDimensions dimensions; ///< dimensions to load - FittingMode::Type fittingMode; ///< fitting options - SamplingMode::Type samplingMode; ///< sampling options - bool orientationCorrection : 1; ///< if orientation correction is needed - DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad; //< if the image's color should be multiplied by it's alpha - - bool isMaskTask; ///< whether this task is for mask or not + std::vector pixelBuffers{}; ///< pixelBuffer handle after successful load + ///< or pixelBuffer to be masked image in the mask task + VisualUrl url; ///< url of the image to load + EncodedImageBuffer encodedImageBuffer; ///< encoded buffer of the image to load + uint32_t id; ///< The unique id associated with this task. + ImageDimensions dimensions; ///< dimensions to load + FittingMode::Type fittingMode; ///< fitting options + SamplingMode::Type samplingMode; ///< sampling options + DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad; ///< if the image's color should be multiplied by it's alpha + Devel::PixelBuffer maskPixelBuffer; ///< pixelBuffer of mask image float contentScale; ///< The factor to scale the content - bool cropToMask; ///< Whether to crop the content to the mask size Dali::AnimatedImageLoading animatedImageLoading; uint32_t frameIndex; + + bool orientationCorrection : 1; ///< if orientation correction is needed + bool isMaskTask : 1; ///< whether this task is for mask or not + bool cropToMask : 1; ///< Whether to crop the content to the mask size + bool loadPlanes : 1; ///< Whether to load image planes }; /** diff --git a/dali-toolkit/internal/texture-manager/texture-async-loading-helper.cpp b/dali-toolkit/internal/texture-manager/texture-async-loading-helper.cpp index 0d88f47..8f0982e 100644 --- a/dali-toolkit/internal/texture-manager/texture-async-loading-helper.cpp +++ b/dali-toolkit/internal/texture-manager/texture-async-loading-helper.cpp @@ -56,7 +56,8 @@ void TextureAsyncLoadingHelper::Load(const TextureManager::TextureId& const Dali::FittingMode::Type& fittingMode, const Dali::SamplingMode::Type& samplingMode, const bool& orientationCorrection, - const DevelAsyncImageLoader::PreMultiplyOnLoad& preMultiplyOnLoad) + const DevelAsyncImageLoader::PreMultiplyOnLoad& preMultiplyOnLoad, + const bool& loadYuvPlanes) { mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId)); if(DALI_UNLIKELY(url.IsBufferResource())) @@ -66,7 +67,7 @@ void TextureAsyncLoadingHelper::Load(const TextureManager::TextureId& } else { - auto id = GetImplementation(mLoader).Load(url, desiredSize, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad); + auto id = GetImplementation(mLoader).Load(url, desiredSize, fittingMode, samplingMode, orientationCorrection, preMultiplyOnLoad, loadYuvPlanes); mLoadingInfoContainer.back().loadId = id; } } @@ -112,7 +113,7 @@ void TextureAsyncLoadingHelper::AsyncLoadComplete(uint32_t if(loadingInfo.loadId == id) { // Call TextureManager::AsyncLoadComplete - mTextureManager.AsyncLoadComplete(loadingInfo.textureId, pixelBuffers[0]); + mTextureManager.AsyncLoadComplete(loadingInfo.textureId, pixelBuffers); } mLoadingInfoContainer.pop_front(); diff --git a/dali-toolkit/internal/texture-manager/texture-async-loading-helper.h b/dali-toolkit/internal/texture-manager/texture-async-loading-helper.h index 688aefb..d8830f7 100644 --- a/dali-toolkit/internal/texture-manager/texture-async-loading-helper.h +++ b/dali-toolkit/internal/texture-manager/texture-async-loading-helper.h @@ -82,6 +82,7 @@ public: * @param[in] orientationCorrection Whether to use image metadata to rotate or flip the image, * e.g., from portrait to landscape * @param[in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha or if the image need to be applied alpha mask. + * @param[in] loadYuvPlanes True if the image should be loaded as yuv planes */ void Load(const TextureManager::TextureId& textureId, const VisualUrl& url, @@ -89,7 +90,8 @@ public: const Dali::FittingMode::Type& fittingMode, const Dali::SamplingMode::Type& samplingMode, const bool& orientationCorrection, - const DevelAsyncImageLoader::PreMultiplyOnLoad& preMultiplyOnLoad); + const DevelAsyncImageLoader::PreMultiplyOnLoad& preMultiplyOnLoad, + const bool& loadYuvPlanes); /** * @brief Apply mask diff --git a/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp b/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp index 92031ae..8d9c72f 100644 --- a/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp +++ b/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp @@ -39,6 +39,7 @@ constexpr auto DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS = size_t{8u}; constexpr auto NUMBER_OF_LOCAL_LOADER_THREADS_ENV = "DALI_TEXTURE_LOCAL_THREADS"; constexpr auto NUMBER_OF_REMOTE_LOADER_THREADS_ENV = "DALI_TEXTURE_REMOTE_THREADS"; +constexpr auto LOAD_IMAGE_YUV_PLANES_ENV = "DALI_LOAD_IMAGE_YUV_PLANES_ENV"; size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue) { @@ -60,6 +61,12 @@ size_t GetNumberOfRemoteLoaderThreads() return GetNumberOfThreads(NUMBER_OF_REMOTE_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS); } +bool NeedToLoadYuvPlanes() +{ + auto loadYuvPlanesString = Dali::EnvironmentVariable::GetEnvironmentVariable(LOAD_IMAGE_YUV_PLANES_ENV); + bool loadYuvPlanes = loadYuvPlanesString ? std::atoi(loadYuvPlanesString) : false; + return loadYuvPlanes; +} } // namespace namespace Dali @@ -126,7 +133,8 @@ TextureManager::TextureManager() mLifecycleObservers(), mLoadQueue(), mRemoveQueue(), - mQueueLoadFlag(false) + mQueueLoadFlag(false), + mLoadYuvPlanes(NeedToLoadYuvPlanes()) { // Initialize the AddOn RenderingAddOn::Get(); @@ -337,22 +345,24 @@ TextureSet TextureManager::LoadTexture( : false; if(synchronousAtlasAvaliable) { - Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection); + std::vector pixelBuffers; + LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection, false, pixelBuffers); - if(pixelBuffer && maskInfo && maskInfo->mAlphaMaskUrl.IsValid()) + if(!pixelBuffers.empty() && maskInfo && maskInfo->mAlphaMaskUrl.IsValid()) { - Devel::PixelBuffer maskPixelBuffer = LoadImageSynchronously(maskInfo->mAlphaMaskUrl, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true); - if(maskPixelBuffer) + std::vector maskPixelBuffers; + LoadImageSynchronously(maskInfo->mAlphaMaskUrl, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true, false, maskPixelBuffers); + if(!maskPixelBuffers.empty()) { - pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask); + pixelBuffers[0].ApplyMask(maskPixelBuffers[0], maskInfo->mContentScaleFactor, maskInfo->mCropToMask); } } PixelData data; - if(pixelBuffer) + if(!pixelBuffers.empty()) { - PreMultiply(pixelBuffer, preMultiplyOnLoad); - data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer + PreMultiply(pixelBuffers[0], preMultiplyOnLoad); + data = Devel::PixelBuffer::Convert(pixelBuffers[0]); // takes ownership of buffer if(data) { @@ -529,8 +539,10 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( const std::uint32_t& frameIndex, const bool& synchronousLoading) { - TextureHash textureHash = INITIAL_HASH_NUMBER; - TextureCacheIndex cacheIndex = INVALID_CACHE_INDEX; + TextureHash textureHash = INITIAL_HASH_NUMBER; + TextureCacheIndex cacheIndex = INVALID_CACHE_INDEX; + bool loadYuvPlanes = (mLoadYuvPlanes && maskTextureId == INVALID_TEXTURE_ID && storageType == StorageType::UPLOAD_TO_TEXTURE); + if(storageType != StorageType::RETURN_PIXEL_BUFFER) { textureHash = mTextureCacheManager.GenerateHash(url, desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, cropToMask, frameIndex); @@ -564,7 +576,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( bool preMultiply = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD); // Cache new texutre, and get cacheIndex. - cacheIndex = mTextureCacheManager.AppendCache(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex)); + cacheIndex = mTextureCacheManager.AppendCache(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex, loadYuvPlanes)); DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d, frameindex=%d premultiply=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, frameIndex, preMultiply); } @@ -646,9 +658,10 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( if(!(textureInfo.loadState == TextureManager::LoadState::UPLOADED || textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED)) { - Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection); + std::vector pixelBuffers; + LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection, loadYuvPlanes, pixelBuffers); - if(!pixelBuffer) + if(pixelBuffers.empty()) { // If pixelBuffer loading is failed in synchronously, call Remove() method. Remove(textureId, nullptr); @@ -657,7 +670,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( if(storageType == StorageType::KEEP_PIXEL_BUFFER) // For the mask image loading. { - textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data + textureInfo.pixelBuffer = pixelBuffers[0]; // Store the pixel data textureInfo.loadState = LoadState::LOAD_FINISHED; } else // For the image loading. @@ -681,7 +694,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( Devel::PixelBuffer maskPixelBuffer = mTextureCacheManager[maskCacheIndex].pixelBuffer; if(maskPixelBuffer) { - pixelBuffer.ApplyMask(maskPixelBuffer, contentScale, cropToMask); + pixelBuffers[0].ApplyMask(maskPixelBuffer, contentScale, cropToMask); } else { @@ -694,10 +707,10 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( DALI_LOG_ERROR("Mask image is not stored in cache.\n"); } } - PreMultiply(pixelBuffer, preMultiplyOnLoad); + PreMultiply(pixelBuffers[0], preMultiplyOnLoad); // Upload texture - UploadTexture(pixelBuffer, textureInfo); + UploadTextures(pixelBuffers, textureInfo); if(maskTexture && textureInfo.textureSet) { textureInfo.textureSet.SetTexture(1u, maskTexture); @@ -739,12 +752,14 @@ void TextureManager::Remove(const TextureManager::TextureId& textureId, TextureU } } -Devel::PixelBuffer TextureManager::LoadImageSynchronously( - const VisualUrl& url, - const Dali::ImageDimensions& desiredSize, - const Dali::FittingMode::Type& fittingMode, - const Dali::SamplingMode::Type& samplingMode, - const bool& orientationCorrection) +void TextureManager::LoadImageSynchronously( + const VisualUrl& url, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const bool& orientationCorrection, + const bool& loadYuvPlanes, + std::vector& pixelBuffers) { Devel::PixelBuffer pixelBuffer; if(url.IsBufferResource()) @@ -757,9 +772,20 @@ Devel::PixelBuffer TextureManager::LoadImageSynchronously( } else { - pixelBuffer = LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection); + if(loadYuvPlanes) + { + Dali::LoadImagePlanesFromFile(url.GetUrl(), pixelBuffers, desiredSize, fittingMode, samplingMode, orientationCorrection); + } + else + { + pixelBuffer = Dali::LoadImageFromFile(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection); + } + } + + if(pixelBuffer) + { + pixelBuffers.push_back(pixelBuffer); } - return pixelBuffer; } void TextureManager::AddObserver(TextureManager::LifecycleObserver& observer) @@ -853,7 +879,7 @@ void TextureManager::LoadTexture(TextureManager::TextureInfo& textureInfo, Textu } else { - loadingHelperIt->Load(textureInfo.textureId, textureInfo.url, textureInfo.desiredSize, textureInfo.fittingMode, textureInfo.samplingMode, textureInfo.orientationCorrection, premultiplyOnLoad); + loadingHelperIt->Load(textureInfo.textureId, textureInfo.url, textureInfo.desiredSize, textureInfo.fittingMode, textureInfo.samplingMode, textureInfo.orientationCorrection, premultiplyOnLoad, textureInfo.loadYuvPlanes); } } ObserveTexture(textureInfo, observer); @@ -912,7 +938,7 @@ void TextureManager::ObserveTexture(TextureManager::TextureInfo& textureInfo, } } -void TextureManager::AsyncLoadComplete(const TextureManager::TextureId& textureId, Devel::PixelBuffer pixelBuffer) +void TextureManager::AsyncLoadComplete(const TextureManager::TextureId& textureId, std::vector& pixelBuffers) { TextureCacheIndex cacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureId); DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( textureId:%d CacheIndex:%d )\n", textureId, cacheIndex.GetIndex()); @@ -925,7 +951,7 @@ void TextureManager::AsyncLoadComplete(const TextureManager::TextureId& textureI if(textureInfo.loadState != LoadState::CANCELLED) { // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified) - PostLoad(textureInfo, pixelBuffer); + PostLoad(textureInfo, pixelBuffers); } else { @@ -934,95 +960,112 @@ void TextureManager::AsyncLoadComplete(const TextureManager::TextureId& textureI } } -void TextureManager::PostLoad(TextureManager::TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer) +void TextureManager::PostLoad(TextureManager::TextureInfo& textureInfo, std::vector& pixelBuffers) { // Was the load successful? - if(pixelBuffer && (pixelBuffer.GetWidth() != 0) && (pixelBuffer.GetHeight() != 0)) + if(!pixelBuffers.empty()) { - // No atlas support for now - textureInfo.useAtlas = UseAtlas::NO_ATLAS; - textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied(); - - if(textureInfo.storageType == StorageType::UPLOAD_TO_TEXTURE) + if(pixelBuffers.size() == 1) { - // If there is a mask texture ID associated with this texture, then apply the mask - // if it's already loaded. If it hasn't, and the mask is still loading, - // wait for the mask to finish loading. - // note, If the texture is already uploaded synchronously during loading, - // we don't need to apply mask. - if(textureInfo.loadState != LoadState::UPLOADED && - textureInfo.maskTextureId != INVALID_TEXTURE_ID) + Devel::PixelBuffer pixelBuffer = pixelBuffers[0]; + if(pixelBuffer && (pixelBuffer.GetWidth() != 0) && (pixelBuffer.GetHeight() != 0)) { - if(textureInfo.loadState == LoadState::MASK_APPLYING) - { - textureInfo.loadState = LoadState::MASK_APPLIED; - UploadTexture(pixelBuffer, textureInfo); - NotifyObservers(textureInfo, true); - } - else + // No atlas support for now + textureInfo.useAtlas = UseAtlas::NO_ATLAS; + textureInfo.preMultiplied = pixelBuffer.IsAlphaPreMultiplied(); + + if(textureInfo.storageType == StorageType::UPLOAD_TO_TEXTURE) { - LoadState maskLoadState = mTextureCacheManager.GetTextureStateInternal(textureInfo.maskTextureId); - textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily - if(maskLoadState == LoadState::LOADING) - { - textureInfo.loadState = LoadState::WAITING_FOR_MASK; - } - else if(maskLoadState == LoadState::LOAD_FINISHED || maskLoadState == LoadState::UPLOADED) + // If there is a mask texture ID associated with this texture, then apply the mask + // if it's already loaded. If it hasn't, and the mask is still loading, + // wait for the mask to finish loading. + // note, If the texture is already uploaded synchronously during loading, + // we don't need to apply mask. + if(textureInfo.loadState != LoadState::UPLOADED && + textureInfo.maskTextureId != INVALID_TEXTURE_ID) { - // Send New Task to Thread - TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureInfo.maskTextureId); - if(maskCacheIndex != INVALID_CACHE_INDEX) + if(textureInfo.loadState == LoadState::MASK_APPLYING) + { + textureInfo.loadState = LoadState::MASK_APPLIED; + UploadTexture(pixelBuffer, textureInfo); + NotifyObservers(textureInfo, true); + } + else { - TextureInfo& maskTextureInfo(mTextureCacheManager[maskCacheIndex]); - if(maskTextureInfo.storageType == StorageType::KEEP_PIXEL_BUFFER) + LoadState maskLoadState = mTextureCacheManager.GetTextureStateInternal(textureInfo.maskTextureId); + textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily + if(maskLoadState == LoadState::LOADING) { - // Send New Task to Thread - ApplyMask(textureInfo, textureInfo.maskTextureId); + textureInfo.loadState = LoadState::WAITING_FOR_MASK; } - else if(maskTextureInfo.storageType == StorageType::KEEP_TEXTURE) + else if(maskLoadState == LoadState::LOAD_FINISHED || maskLoadState == LoadState::UPLOADED) { - // Upload image texture. textureInfo.loadState will be UPLOADED. - UploadTexture(textureInfo.pixelBuffer, textureInfo); - if(maskTextureInfo.textureSet.GetTextureCount() > 0u) + // Send New Task to Thread + TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureInfo.maskTextureId); + if(maskCacheIndex != INVALID_CACHE_INDEX) { - Texture maskTexture = maskTextureInfo.textureSet.GetTexture(0u); - textureInfo.textureSet.SetTexture(1u, maskTexture); + TextureInfo& maskTextureInfo(mTextureCacheManager[maskCacheIndex]); + if(maskTextureInfo.storageType == StorageType::KEEP_PIXEL_BUFFER) + { + // Send New Task to Thread + ApplyMask(textureInfo, textureInfo.maskTextureId); + } + else if(maskTextureInfo.storageType == StorageType::KEEP_TEXTURE) + { + // Upload image texture. textureInfo.loadState will be UPLOADED. + UploadTexture(textureInfo.pixelBuffer, textureInfo); + if(maskTextureInfo.textureSet.GetTextureCount() > 0u) + { + Texture maskTexture = maskTextureInfo.textureSet.GetTexture(0u); + textureInfo.textureSet.SetTexture(1u, maskTexture); + } + // notify mask texture set. + NotifyObservers(textureInfo, true); + } } - // notify mask texture set. + } + else // maskLoadState == LoadState::LOAD_FAILED + { + // Url texture load success, But alpha mask texture load failed. Run as normal image upload. + DALI_LOG_ERROR("Alpha mask image loading failed! Image will not be masked\n"); + UploadTexture(pixelBuffer, textureInfo); NotifyObservers(textureInfo, true); } } } - else // maskLoadState == LoadState::LOAD_FAILED + else { - // Url texture load success, But alpha mask texture load failed. Run as normal image upload. - DALI_LOG_ERROR("Alpha mask image loading failed! Image will not be masked\n"); UploadTexture(pixelBuffer, textureInfo); NotifyObservers(textureInfo, true); } } - } - else - { - UploadTexture(pixelBuffer, textureInfo); - NotifyObservers(textureInfo, true); + else + { + textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data + textureInfo.loadState = LoadState::LOAD_FINISHED; + + if(textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER) + { + NotifyObservers(textureInfo, true); + } + else // for the StorageType::KEEP_PIXEL_BUFFER and StorageType::KEEP_TEXTURE + { + // Check if there was another texture waiting for this load to complete + // (e.g. if this was an image mask, and its load is on a different thread) + CheckForWaitingTexture(textureInfo); + } + } } } else { - textureInfo.pixelBuffer = pixelBuffer; // Store the pixel data - textureInfo.loadState = LoadState::LOAD_FINISHED; + // YUV case + // No atlas support for now + textureInfo.useAtlas = UseAtlas::NO_ATLAS; + textureInfo.preMultiplied = false; - if(textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER) - { - NotifyObservers(textureInfo, true); - } - else // for the StorageType::KEEP_PIXEL_BUFFER and StorageType::KEEP_TEXTURE - { - // Check if there was another texture waiting for this load to complete - // (e.g. if this was an image mask, and its load is on a different thread) - CheckForWaitingTexture(textureInfo); - } + UploadTextures(pixelBuffers, textureInfo); + NotifyObservers(textureInfo, true); } } else @@ -1149,6 +1192,46 @@ void TextureManager::UploadTexture(Devel::PixelBuffer& pixelBuffer, TextureManag textureInfo.loadState = LoadState::UPLOADED; } +void TextureManager::UploadTextures(std::vector& pixelBuffers, TextureManager::TextureInfo& textureInfo) +{ + if(!pixelBuffers.empty() && textureInfo.loadState != LoadState::UPLOADED && textureInfo.useAtlas != UseAtlas::USE_ATLAS) + { + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, " TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId); + + // Check if this pixelBuffer is premultiplied + textureInfo.preMultiplied = pixelBuffers[0].IsAlphaPreMultiplied(); + + auto& renderingAddOn = RenderingAddOn::Get(); + if(renderingAddOn.IsValid()) + { + renderingAddOn.CreateGeometry(textureInfo.textureId, pixelBuffers[0]); + } + + if(!textureInfo.textureSet) + { + textureInfo.textureSet = TextureSet::New(); + } + + uint32_t index = 0; + for(auto&& pixelBuffer : pixelBuffers) + { + Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight()); + + PixelData pixelData = Devel::PixelBuffer::Convert(pixelBuffer); + texture.Upload(pixelData); + textureInfo.textureSet.SetTexture(index++, texture); + } + + pixelBuffers.clear(); + } + + // Update the load state. + // Note: This is regardless of success as we care about whether a + // load attempt is in progress or not. If unsuccessful, a broken + // image is still loaded. + textureInfo.loadState = LoadState::UPLOADED; +} + void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, const bool& success) { TextureId textureId = textureInfo.textureId; diff --git a/dali-toolkit/internal/texture-manager/texture-manager-impl.h b/dali-toolkit/internal/texture-manager/texture-manager-impl.h index 4248904..887eb74 100644 --- a/dali-toolkit/internal/texture-manager/texture-manager-impl.h +++ b/dali-toolkit/internal/texture-manager/texture-manager-impl.h @@ -492,14 +492,18 @@ private: * @param[in] samplingMode The SamplingMode to use * @param[in] orientationCorrection Whether to use image metadata to rotate or flip the image, * e.g., from portrait to landscape + * @param[in] loadYuvPlanes True if the image should be loaded as yuv planes + * @param[out] pixelBuffers The image pixelBuffer * @return PixelBuffer of loaded image. */ - Devel::PixelBuffer LoadImageSynchronously( - const VisualUrl& url, - const Dali::ImageDimensions& desiredSize, - const Dali::FittingMode::Type& fittingMode, - const Dali::SamplingMode::Type& samplingMode, - const bool& orientationCorrection); + void LoadImageSynchronously( + const VisualUrl& url, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, + const Dali::SamplingMode::Type& samplingMode, + const bool& orientationCorrection, + const bool& loadYuvPlanes, + std::vector& pixelBuffers); private: // Load and queue @@ -559,11 +563,11 @@ private: /** * @brief Performs Post-Load steps including atlasing. - * @param[in] textureInfo The struct associated with this Texture - * @param[in] pixelBuffer The image pixelBuffer + * @param[in] textureInfo The struct associated with this Texture + * @param[in] pixelBuffers The image pixelBuffer * @return True if successful */ - void PostLoad(TextureManager::TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer); + void PostLoad(TextureManager::TextureInfo& textureInfo, std::vector& pixelBuffers); /** * Check if there is a texture waiting to be masked. If there @@ -588,6 +592,13 @@ private: void UploadTexture(Devel::PixelBuffer& pixelBuffer, TextureManager::TextureInfo& textureInfo); /** + * Upload the texture specified in pixelBuffer to the appropriate location + * @param[in] pixelBuffers The image data to upload + * @param[in] textureInfo The texture info containing the location to store the data to. + */ + void UploadTextures(std::vector& pixelBuffers, TextureManager::TextureInfo& textureInfo); + + /** * Notify the current observers that the texture upload is complete, * then remove the observers from the list. * @param[in] textureInfo The struct associated with this Texture @@ -607,10 +618,10 @@ public: /** * @brief Common method to handle loading completion. * TextureAsyncLoadingHelper will call this API After async loading finished. - * @param[in] textureId The ID of the texture load complete. - * @param[in] pixelBuffer The loaded image data + * @param[in] textureId The ID of the texture load complete. + * @param[in] pixelBuffers The loaded image data */ - void AsyncLoadComplete(const TextureManager::TextureId& textureId, Devel::PixelBuffer pixelBuffer); + void AsyncLoadComplete(const TextureManager::TextureId& textureId, std::vector& pixelBuffers); private: /** @@ -640,6 +651,7 @@ private: // Member Variables: Dali::Vector mLoadQueue; ///< Queue of textures to load after NotifyObservers Dali::Vector mRemoveQueue; ///< Queue of textures to remove after NotifyObservers bool mQueueLoadFlag; ///< Flag that causes Load Textures to be queued. + bool mLoadYuvPlanes; ///< A global flag to specify if the image should be loaded as yuv planes }; } // namespace Internal diff --git a/dali-toolkit/internal/texture-manager/texture-manager-type.h b/dali-toolkit/internal/texture-manager/texture-manager-type.h index 567d876..98c5baa 100644 --- a/dali-toolkit/internal/texture-manager/texture-manager-type.h +++ b/dali-toolkit/internal/texture-manager/texture-manager-type.h @@ -243,7 +243,8 @@ struct TextureInfo const bool& orientationCorrection, const bool& preMultiplyOnLoad, const Dali::AnimatedImageLoading& animatedImageLoading, - const std::uint32_t& frameIndex) + const std::uint32_t& frameIndex, + const bool& loadYuvPlanes) : url(url), desiredSize(desiredSize), useSize(desiredSize), @@ -266,7 +267,8 @@ struct TextureInfo cropToMask(cropToMask), orientationCorrection(true), preMultiplyOnLoad(preMultiplyOnLoad), - preMultiplied(false) + preMultiplied(false), + loadYuvPlanes(loadYuvPlanes) { isAnimatedImageFormat = (animatedImageLoading) ? true : false; } @@ -305,6 +307,7 @@ struct TextureInfo bool preMultiplyOnLoad : 1; ///< True if the image's color should be multiplied by it's alpha bool preMultiplied : 1; ///< True if the image's color was multiplied by it's alpha bool isAnimatedImageFormat : 1; ///< true if the image is requested from animated image visual. + bool loadYuvPlanes : 1; ///< true if the image should be loaded as yuv planes }; } // namespace TextureManagerType diff --git a/dali-toolkit/internal/visuals/image-visual-shader-factory.cpp b/dali-toolkit/internal/visuals/image-visual-shader-factory.cpp index ceb5848..728c219 100644 --- a/dali-toolkit/internal/visuals/image-visual-shader-factory.cpp +++ b/dali-toolkit/internal/visuals/image-visual-shader-factory.cpp @@ -46,13 +46,14 @@ const int NATIVE_SHADER_TYPE_OFFSET = VisualFactoryCache::ShaderType::NATIVE_IMA // enum of required list when we select shader enum class ImageVisualRequireFlag : uint32_t { - DEFAULT = 0, - ROUNDED_CORNER = 1 << 0, - BORDERLINE = 1 << 1, - ALPHA_MASKING = 1 << 2, + DEFAULT = 0, + ROUNDED_CORNER = 1 << 0, + BORDERLINE = 1 << 1, + ALPHA_MASKING = 1 << 2, + COLOR_CONVERSION = 1 << 3, }; -static constexpr auto SHADER_TYPE_COUNT = 8u; +static constexpr auto SHADER_TYPE_COUNT = 12u; VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[SHADER_TYPE_COUNT] = { VisualFactoryCache::IMAGE_SHADER, @@ -62,8 +63,11 @@ VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[SHADER_TYPE_COUNT] = VisualFactoryCache::IMAGE_SHADER_MASKING, VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER_MASKING, VisualFactoryCache::IMAGE_SHADER_BORDERLINE_MASKING, - VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING}; - + VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING, + VisualFactoryCache::IMAGE_SHADER_YUV_TO_RGB, + VisualFactoryCache::IMAGE_SHADER_ROUNDED_CORNER_YUV_TO_RGB, + VisualFactoryCache::IMAGE_SHADER_BORDERLINE_YUV_TO_RGB, + VisualFactoryCache::IMAGE_SHADER_ROUNDED_BORDERLINE_YUV_TO_RGB}; } // unnamed namespace namespace ImageVisualShaderFeature @@ -73,31 +77,42 @@ FeatureBuilder& FeatureBuilder::EnableTextureAtlas(bool enableAtlas) mTextureAtlas = (enableAtlas ? TextureAtlas::ENABLED : TextureAtlas::DISABLED); return *this; } + FeatureBuilder& FeatureBuilder::ApplyDefaultTextureWrapMode(bool applyDefaultTextureWrapMode) { mDefaultTextureWrapMode = (applyDefaultTextureWrapMode ? DefaultTextureWrapMode::APPLY : DefaultTextureWrapMode::DO_NOT_APPLY); return *this; } + FeatureBuilder& FeatureBuilder::EnableRoundedCorner(bool enableRoundedCorner) { mRoundedCorner = (enableRoundedCorner ? RoundedCorner::ENABLED : RoundedCorner::DISABLED); return *this; } + FeatureBuilder& FeatureBuilder::EnableBorderline(bool enableBorderline) { mBorderline = (enableBorderline ? Borderline::ENABLED : Borderline::DISABLED); return *this; } + FeatureBuilder& FeatureBuilder::SetTextureForFragmentShaderCheck(const Dali::Texture& texture) { mTexture = texture; return *this; } + FeatureBuilder& FeatureBuilder::EnableAlphaMaskingOnRendering(bool enableAlphaMaskingOnRendering) { mAlphaMaskingOnRendering = (enableAlphaMaskingOnRendering ? AlphaMaskingOnRendering::ENABLED : AlphaMaskingOnRendering::DISABLED); return *this; } + +FeatureBuilder& FeatureBuilder::EnableYuvToRgb(bool enableYuvToRgb) +{ + mColorConversion = (enableYuvToRgb ? ColorConversion::YUV_TO_RGB : ColorConversion::DONT_NEED); + return *this; +} } // namespace ImageVisualShaderFeature ImageVisualShaderFactory::ImageVisualShaderFactory() @@ -119,9 +134,10 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, con const auto& roundedCorner = featureBuilder.mRoundedCorner; const auto& borderline = featureBuilder.mBorderline; const auto& alphaMaskingOnRendering = featureBuilder.mAlphaMaskingOnRendering; + const auto& colorConversion = featureBuilder.mColorConversion; const auto& changeFragmentShader = (featureBuilder.mTexture && DevelTexture::IsNative(featureBuilder.mTexture)) - ? ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE - : ImageVisualShaderFeature::ChangeFragmentShader::DONT_CHANGE; + ? ImageVisualShaderFeature::ChangeFragmentShader::NEED_CHANGE + : ImageVisualShaderFeature::ChangeFragmentShader::DONT_CHANGE; if(atlasing == ImageVisualShaderFeature::TextureAtlas::ENABLED) { @@ -149,6 +165,10 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, con { shaderTypeFlag |= static_cast(ImageVisualRequireFlag::ALPHA_MASKING); } + else if(colorConversion == ImageVisualShaderFeature::ColorConversion::YUV_TO_RGB) // Not support gpu masking and color conversion at the same time now + { + shaderTypeFlag |= static_cast(ImageVisualRequireFlag::COLOR_CONVERSION); + } shaderType = SHADER_TYPE_TABLE[shaderTypeFlag]; } @@ -192,6 +212,10 @@ Shader ImageVisualShaderFactory::GetShader(VisualFactoryCache& factoryCache, con vertexShaderPrefixList += "#define IS_REQUIRED_ALPHA_MASKING\n"; fragmentShaderPrefixList += "#define IS_REQUIRED_ALPHA_MASKING\n"; } + else if(colorConversion == ImageVisualShaderFeature::ColorConversion::YUV_TO_RGB) + { + fragmentShaderPrefixList += "#define IS_REQUIRED_YUV_TO_RGB\n"; + } } std::string vertexShader = std::string(Dali::Shader::GetVertexShaderPrefix() + vertexShaderPrefixList + SHADER_IMAGE_VISUAL_SHADER_VERT.data()); diff --git a/dali-toolkit/internal/visuals/image-visual-shader-factory.h b/dali-toolkit/internal/visuals/image-visual-shader-factory.h index e0070ed..7dfefbc 100644 --- a/dali-toolkit/internal/visuals/image-visual-shader-factory.h +++ b/dali-toolkit/internal/visuals/image-visual-shader-factory.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_IMAGE_VISUAL_SHADER_FACTORY_H /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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. @@ -29,7 +29,6 @@ namespace Toolkit { namespace Internal { - /** * ImageVisualShaderFeature contains feature lists what image visual shader need to know. */ @@ -108,6 +107,18 @@ enum Type }; } // namespace AlphaMaskingOnRendering +namespace ColorConversion +{ +/** + * @brief Whether the color format conversion is needed or not + */ +enum Type +{ + DONT_NEED = 0, ///< Not need to convert + YUV_TO_RGB ///< Need yuv to rgb conversion +}; +} // namespace ColorConversion + /** * @brief Collection of current image visual feature. Only use for ImageVisualShaderFactory::GetShader() */ @@ -119,6 +130,7 @@ struct FeatureBuilder mRoundedCorner(RoundedCorner::DISABLED), mBorderline(Borderline::DISABLED), mAlphaMaskingOnRendering(AlphaMaskingOnRendering::DISABLED), + mColorConversion(ColorConversion::DONT_NEED), mTexture() { } @@ -129,12 +141,14 @@ struct FeatureBuilder FeatureBuilder& EnableBorderline(bool enableBorderline); FeatureBuilder& SetTextureForFragmentShaderCheck(const Dali::Texture& texture); FeatureBuilder& EnableAlphaMaskingOnRendering(bool enableAlphaMaskingOnRendering); + FeatureBuilder& EnableYuvToRgb(bool enableYuvToRgb); TextureAtlas::Type mTextureAtlas : 2; ///< Whether use texture with atlas, or not. default as TextureAtlas::DISABLED DefaultTextureWrapMode::Type mDefaultTextureWrapMode : 2; ///< Whether apply to texture wraping in default, or not. default as DefaultTextureWrapMode::APPLY RoundedCorner::Type mRoundedCorner : 2; ///< Whether use rounded corner, or not. default as RoundedCorner::DISABLED Borderline::Type mBorderline : 2; ///< Whether use borderline, or not. default as Borderline::DISABLED AlphaMaskingOnRendering::Type mAlphaMaskingOnRendering : 2; ///< Whether use runtime alpha masking, or not. default as AlphaMaskingOnRendering::DISABLED + ColorConversion::Type mColorConversion : 2; ///< Whether the color format conversion is needed or not Dali::Texture mTexture; ///< Texture to check whether we need to change fragment shader or not }; @@ -146,7 +160,6 @@ struct FeatureBuilder class ImageVisualShaderFactory { public: - /** * @brief Constructor */ @@ -189,7 +202,6 @@ protected: ImageVisualShaderFactory& operator=(const ImageVisualShaderFactory& rhs); private: - /** * @brief Cached information whether native image should change fragment shader. * Default it is ChangeFragmentShader::UNDECIDED. diff --git a/dali-toolkit/internal/visuals/image/image-visual.cpp b/dali-toolkit/internal/visuals/image/image-visual.cpp index ef71786..58e2ef9 100644 --- a/dali-toolkit/internal/visuals/image/image-visual.cpp +++ b/dali-toolkit/internal/visuals/image/image-visual.cpp @@ -684,7 +684,15 @@ void ImageVisual::InitializeRenderer() mImpl->mRenderer.SetTextures(mTextures); ComputeTextureSize(); CheckMaskTexture(); - if(DevelTexture::IsNative(mTextures.GetTexture(0))) + + bool needToUpdateShader = DevelTexture::IsNative(mTextures.GetTexture(0)); + if(mTextures.GetTextureCount() == 3) + { + //TODO: need to check pixel format yuv. Now we can't get the format of the texture. + mNeedYuvToRgb = true; + needToUpdateShader = true; + } + if(needToUpdateShader) { UpdateShader(); } @@ -906,6 +914,13 @@ void ImageVisual::LoadComplete(bool loadingSuccess, TextureInformation textureIn mImpl->mRenderer.SetTextures(textureInformation.textureSet); ComputeTextureSize(); CheckMaskTexture(); + + if(textureInformation.textureSet.GetTextureCount() == 3) + { + //TODO: need to check pixel format yuv. Now we can't get the format of the texture. + mNeedYuvToRgb = true; + UpdateShader(); + } } if(actor) @@ -1068,7 +1083,8 @@ Shader ImageVisual::GenerateShader() const .EnableRoundedCorner(IsRoundedCornerRequired()) .EnableBorderline(IsBorderlineRequired()) .SetTextureForFragmentShaderCheck(useNativeImage ? mTextures.GetTexture(0) : Dali::Texture()) - .EnableAlphaMaskingOnRendering(requiredAlphaMaskingOnRendering)); + .EnableAlphaMaskingOnRendering(requiredAlphaMaskingOnRendering) + .EnableYuvToRgb(mNeedYuvToRgb)); } else { diff --git a/dali-toolkit/internal/visuals/image/image-visual.h b/dali-toolkit/internal/visuals/image/image-visual.h index 17d5a6e..307ab19 100644 --- a/dali-toolkit/internal/visuals/image/image-visual.h +++ b/dali-toolkit/internal/visuals/image/image-visual.h @@ -346,6 +346,7 @@ private: 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. }; } // namespace Internal diff --git a/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.cpp b/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.cpp index c6e0a2c..0b039df 100644 --- a/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.cpp +++ b/dali-toolkit/internal/visuals/svg/svg-rasterize-thread.cpp @@ -236,11 +236,15 @@ void SvgRasterizeThread::RemoveTask(SvgVisual* visual) ConditionalWait::ScopedLock lock(mConditionalWait); if(!mRasterizeTasks.empty()) { - for(std::vector::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it) + for(std::vector::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt;) { if((*it) && (*it)->GetSvgVisual() == visual) { - mRasterizeTasks.erase(it); + it = mRasterizeTasks.erase(it); + } + else + { + it++; } } } diff --git a/dali-toolkit/internal/visuals/visual-factory-cache.h b/dali-toolkit/internal/visuals/visual-factory-cache.h index 193799c..52dbf61 100644 --- a/dali-toolkit/internal/visuals/visual-factory-cache.h +++ b/dali-toolkit/internal/visuals/visual-factory-cache.h @@ -90,6 +90,10 @@ public: IMAGE_SHADER_ROUNDED_BORDERLINE_MASKING, IMAGE_SHADER_ATLAS_DEFAULT_WRAP, IMAGE_SHADER_ATLAS_CUSTOM_WRAP, + IMAGE_SHADER_YUV_TO_RGB, + IMAGE_SHADER_ROUNDED_CORNER_YUV_TO_RGB, + IMAGE_SHADER_BORDERLINE_YUV_TO_RGB, + IMAGE_SHADER_ROUNDED_BORDERLINE_YUV_TO_RGB, NATIVE_IMAGE_SHADER, NATIVE_IMAGE_SHADER_ROUNDED_CORNER, NATIVE_IMAGE_SHADER_BORDERLINE, diff --git a/dali-toolkit/public-api/image-loader/async-image-loader.cpp b/dali-toolkit/public-api/image-loader/async-image-loader.cpp index 3cbfc60..97ebf62 100644 --- a/dali-toolkit/public-api/image-loader/async-image-loader.cpp +++ b/dali-toolkit/public-api/image-loader/async-image-loader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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. @@ -59,12 +59,12 @@ AsyncImageLoader AsyncImageLoader::New() uint32_t AsyncImageLoader::Load(const std::string& url) { - return GetImplementation(*this).Load(Toolkit::Internal::VisualUrl(url), ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true, DevelAsyncImageLoader::PreMultiplyOnLoad::OFF); + return GetImplementation(*this).Load(Toolkit::Internal::VisualUrl(url), ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true, DevelAsyncImageLoader::PreMultiplyOnLoad::OFF, false); } uint32_t AsyncImageLoader::Load(const std::string& url, ImageDimensions dimensions) { - return GetImplementation(*this).Load(Toolkit::Internal::VisualUrl(url), dimensions, FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true, DevelAsyncImageLoader::PreMultiplyOnLoad::OFF); + return GetImplementation(*this).Load(Toolkit::Internal::VisualUrl(url), dimensions, FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true, DevelAsyncImageLoader::PreMultiplyOnLoad::OFF, false); } uint32_t AsyncImageLoader::Load(const std::string& url, @@ -73,7 +73,7 @@ uint32_t AsyncImageLoader::Load(const std::string& url, SamplingMode::Type samplingMode, bool orientationCorrection) { - return GetImplementation(*this).Load(Toolkit::Internal::VisualUrl(url), dimensions, fittingMode, samplingMode, orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad::OFF); + return GetImplementation(*this).Load(Toolkit::Internal::VisualUrl(url), dimensions, fittingMode, samplingMode, orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad::OFF, false); } bool AsyncImageLoader::Cancel(uint32_t loadingTaskId) -- 2.7.4