From: Eunki, Hong Date: Mon, 17 Feb 2025 03:07:38 +0000 (+0900) Subject: NativeImageSourceQueue demo X-Git-Tag: dali_2.4.8~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=d81037fc9bee0ba58253f7b8cf6b4479ac00f953;p=platform%2Fcore%2Fuifw%2Fdali-demo.git NativeImageSourceQueue demo Let we implement NativeImageSourceQueue using demo application. If NativeImageSourceQueue is not supported, it will show random image. If NativeImageSourceQueue supproted, it will show rainbow image, which will update frame at worker thread. Change-Id: I8b2c70e75553cd3360f00c8beb6b95f8a88c5e99 Signed-off-by: Eunki, Hong --- diff --git a/com.samsung.dali-demo.xml b/com.samsung.dali-demo.xml index 2e9381e81..d338e07f4 100644 --- a/com.samsung.dali-demo.xml +++ b/com.samsung.dali-demo.xml @@ -226,6 +226,9 @@ + + + diff --git a/examples-reel/dali-examples-reel.cpp b/examples-reel/dali-examples-reel.cpp index 236c694ef..4a5e80fc5 100644 --- a/examples-reel/dali-examples-reel.cpp +++ b/examples-reel/dali-examples-reel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Samsung Electronics Co., Ltd. + * Copyright (c) 2025 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. @@ -82,6 +82,7 @@ int DALI_EXPORT_API main(int argc, char** argv) demo.AddExample(Example("mesh-morph.example", DALI_DEMO_STR_TITLE_MESH_MORPH)); demo.AddExample(Example("motion-stretch.example", DALI_DEMO_STR_TITLE_MOTION_STRETCH)); demo.AddExample(Example("native-image-source.example", DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE)); + demo.AddExample(Example("native-image-source-queue.example", DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE_QUEUE)); demo.AddExample(Example("popup.example", DALI_DEMO_STR_TITLE_POPUP)); demo.AddExample(Example("pivot.example", DALI_DEMO_STR_TITLE_PIVOT)); demo.AddExample(Example("primitive-shapes.example", DALI_DEMO_STR_TITLE_PRIMITIVE_SHAPES)); diff --git a/examples/native-image-source-queue/README.md b/examples/native-image-source-queue/README.md new file mode 100644 index 000000000..52ca018f4 --- /dev/null +++ b/examples/native-image-source-queue/README.md @@ -0,0 +1,12 @@ +# Native Image Source Queue Example + +Test for `NativeImageSource` and `NativeImageSourceQueue` works well. + +Left half is `NativeImageSource` with static image. +Right half is `NativeImageSourceQueue` with dynamic image. + +This demo test each image buffer write at custom thread, and render result applied well. + +> Warning : This demo application works only for Tizen target. + +![](./native-image-source-queue.png) diff --git a/examples/native-image-source-queue/native-image-source-queue-example.cpp b/examples/native-image-source-queue/native-image-source-queue-example.cpp new file mode 100644 index 000000000..484da9fc5 --- /dev/null +++ b/examples/native-image-source-queue/native-image-source-queue-example.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2025 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +// INTERNAL INCLUDES +#include "shared/utility.h" + +using namespace Dali; +using namespace Dali::Toolkit; + +namespace +{ +class NativeImageRenderThread : public Dali::Thread +{ +public: + NativeImageRenderThread() + : Thread(), + mMutex(), + mDestroyed(false) + { + } + + ~NativeImageRenderThread() + { + } + + void Finalize() + { + Dali::Mutex::ScopedLock lock(mMutex); + mDestroyed = true; + } + + void Run() + { + while(true) + { + { + Dali::Mutex::ScopedLock lock(mMutex); + if(mDestroyed) + { + break; + } + } + + RenderToNative(); + } + } + +protected: + static void FillRainbowBuffer(uint8_t* buffer, uint32_t width, uint32_t height, uint32_t strideBytes, float frameOffset = 0.0f) + { + static const uint32_t bytesPerPixel = 4; + + // Write something to buffer! + for(uint32_t i = 0; i < height; ++i) + { + for(uint32_t j = 0; j < width; ++j) + { + const uint32_t index = i * strideBytes + j * bytesPerPixel; + const float widthOffset = ((static_cast(j) + 0.5f) / static_cast(width)); + + buffer[index + 0u] = FloatToColor(RainbowOffset(frameOffset + widthOffset + 0.0f / 3.0f)); + buffer[index + 1u] = FloatToColor(RainbowOffset(frameOffset + widthOffset + 1.0f / 3.0f)); + buffer[index + 2u] = FloatToColor(RainbowOffset(frameOffset + widthOffset + 2.0f / 3.0f)); + buffer[index + 3u] = FloatToColor(0.1f + 0.9f * widthOffset); + } + } + } + +private: + inline static uint8_t FloatToColor(float value) + { + if(value < 0.0f) + { + return 0u; + } + uint32_t returnValue = static_cast(value * 255.0f); + if(returnValue > 255u) + { + return 255u; + } + + return static_cast(returnValue); + } + + inline static float RainbowOffset(float value) + { + // Ensure to make value range in [0.0f 1.0f) + value -= static_cast(value); + + while(value < 0.0f) + { + value += 1.0f; + } + while(value >= 1.0f) + { + value -= 1.0f; + } + + if(value < 1.0f / 6.0f) + { + return value * 6.0f; + } + if(value < 3.0f / 6.0f) + { + return 1.0f; + } + if(value < 4.0f / 6.0f) + { + return 4.0f - value * 6.0f; + } + return 0.0f; + } + + virtual void RenderToNative() = 0; + +private: + Dali::Mutex mMutex; + + bool mDestroyed; +}; + +class NativeImageSourceRenderThread : public NativeImageRenderThread +{ +public: + NativeImageSourceRenderThread(NativeImageSourcePtr nativeSource) + : NativeImageRenderThread(), + mNativeSource(nativeSource) + { + } + +private: + void RenderToNative() override + { + if(mNativeSource) + { + uint32_t width, height, strideBytes; + uint8_t* buffer = DevelNativeImageSource::AcquireBuffer(*mNativeSource, width, height, strideBytes); + + if(buffer != nullptr) + { + FillRainbowBuffer(buffer, width, height, strideBytes); + + // Don't forget to call ReleaseBuffer + DevelNativeImageSource::ReleaseBuffer(*mNativeSource, Rect()); + } + else + { + DALI_LOG_ERROR("AcquireBuffer not supported! Ignore to render thread\n"); + } + + // DevNote : Since Acquire + Release make render thread block. + // let we just finalize NativeImageSource thread now. + Finalize(); + } + } + +private: + NativeImageSourcePtr mNativeSource; +}; + +class NativeImageSourceQueueRenderThread : public NativeImageRenderThread +{ +public: + NativeImageSourceQueueRenderThread(NativeImageSourceQueuePtr nativeQueue) + : NativeImageRenderThread(), + mNativeQueue(nativeQueue) + { + } + +private: + void RenderToNative() override + { + if(mNativeQueue) + { + if(!mNativeQueue->CanDequeueBuffer()) + { + // Ignore last source and try again + mNativeQueue->IgnoreSourceImage(); + if(!mNativeQueue->CanDequeueBuffer()) + { + // Invalid to render! Ignore. + return; + } + } + uint32_t width, height, strideBytes; + uint8_t* buffer = mNativeQueue->DequeueBuffer(width, height, strideBytes); + + if(buffer != nullptr) + { + static uint32_t frameCount = 0; + static const uint32_t frameCountMax = 1000; + + const float frameOffset = ((static_cast(frameCount) + 0.5f) / static_cast(frameCountMax)); + + FillRainbowBuffer(buffer, width, height, strideBytes, frameOffset); + + frameCount = (frameCount + 1) % frameCountMax; + + // Don't forget to call EnqueueBuffer + mNativeQueue->EnqueueBuffer(buffer); + } + } + } + +private: + NativeImageSourceQueuePtr mNativeQueue; +}; + +} // namespace + +// This example shows how to create and use a NativeImageSourceQueueController as the target of the render task. +// +class NativeImageSourceQueueController : public ConnectionTracker +{ +public: + NativeImageSourceQueueController(Application& application) + : mApplication(application), + mNativeSource(), + mNativeQueue(), + mSourceRenderThreadPtr(nullptr), + mQueueRenderThreadPtr(nullptr), + mNativeSourceSupported(false), + mNativeSourceQueueSupported(false) + { + // Connect to the Application's Init signal + mApplication.InitSignal().Connect(this, &NativeImageSourceQueueController::Create); + } + + ~NativeImageSourceQueueController() + { + TerminateRenderThread(); + } + + // The Init signal is received once (only) during the Application lifetime + void Create(Application& application) + { + // Get a handle to the window + Window window = application.GetWindow(); + window.SetBackgroundColor(Color::WHITE); + + window.KeyEventSignal().Connect(this, &NativeImageSourceQueueController::OnKeyEvent); + + mNativeSource = NativeImageSource::New(123, 345, NativeImageSource::ColorDepth::COLOR_DEPTH_32); + + // Check whether current platform support NativeImageSource or not. + if(mNativeSource && !mNativeSource->GetNativeImageSource().Empty()) + { + mNativeSourceSupported = true; + + mSourceRenderThreadPtr = new NativeImageSourceRenderThread(mNativeSource); + mSourceRenderThreadPtr->Start(); + } + + mNativeQueue = NativeImageSourceQueue::New(123, 345, NativeImageSourceQueue::ColorFormat::RGBA8888); + + // Check whether current platform support NativeImageSourceQueue or not. + if(mNativeQueue && !mNativeQueue->GetNativeImageSourceQueue().Empty()) + { + mNativeSourceQueueSupported = true; + + mQueueRenderThreadPtr = new NativeImageSourceQueueRenderThread(mNativeQueue); + mQueueRenderThreadPtr->Start(); + } + + TextLabel supportedLabel = TextLabel::New(); + supportedLabel.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + supportedLabel.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + supportedLabel.SetProperty(TextLabel::Property::TEXT, + std::string("Support NativeImageSource ") + std::string(mNativeSourceSupported ? "T" : "F") + std::string(" Queue ") + std::string(mNativeSourceQueueSupported ? "T" : "F")); + window.Add(supportedLabel); + + if(mNativeSource) + { + ImageUrl nativeImageUrl = Image::GenerateUrl(mNativeSource, false); + + ImageView nativeImageViewer = ImageView::New(nativeImageUrl.GetUrl()); + nativeImageViewer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + nativeImageViewer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + nativeImageViewer.SetProperty(Actor::Property::SIZE, Vector2(123.0f, 345.0f)); + nativeImageViewer.SetProperty(Actor::Property::POSITION, Vector2(-(123.0f * 0.5f + 1.0f), 0.0f)); + + window.Add(nativeImageViewer); + + // Trick to make render native image always. + Renderer nativeImageRenderer = nativeImageViewer.GetRendererAt(0u); + nativeImageRenderer.SetProperty(DevelRenderer::Property::RENDERING_BEHAVIOR, DevelRenderer::Rendering::CONTINUOUSLY); + } + if(mNativeQueue) + { + ImageUrl nativeImageUrl = Image::GenerateUrl(mNativeQueue, false); + + ImageView nativeImageViewer = ImageView::New(nativeImageUrl.GetUrl()); + nativeImageViewer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + nativeImageViewer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + nativeImageViewer.SetProperty(Actor::Property::SIZE, Vector2(123.0f, 345.0f)); + nativeImageViewer.SetProperty(Actor::Property::POSITION, Vector2(123.0f * 0.5f + 1.0f, 0.0f)); + + window.Add(nativeImageViewer); + + // Trick to make render native image always. + Renderer nativeImageRenderer = nativeImageViewer.GetRendererAt(0u); + nativeImageRenderer.SetProperty(DevelRenderer::Property::RENDERING_BEHAVIOR, DevelRenderer::Rendering::CONTINUOUSLY); + } + } + + void OnKeyEvent(const KeyEvent& event) + { + if(event.GetState() == KeyEvent::DOWN) + { + if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK)) + { + TerminateRenderThread(); + mApplication.Quit(); + } + } + } + + void TerminateRenderThread() + { + if(mSourceRenderThreadPtr) + { + mSourceRenderThreadPtr->Finalize(); + mSourceRenderThreadPtr->Join(); + delete mSourceRenderThreadPtr; + mSourceRenderThreadPtr = nullptr; + } + if(mQueueRenderThreadPtr) + { + mQueueRenderThreadPtr->Finalize(); + mQueueRenderThreadPtr->Join(); + delete mQueueRenderThreadPtr; + mQueueRenderThreadPtr = nullptr; + } + } + +private: + Application& mApplication; + + NativeImageSourcePtr mNativeSource; + NativeImageSourceQueuePtr mNativeQueue; + + NativeImageSourceRenderThread* mSourceRenderThreadPtr; + NativeImageSourceQueueRenderThread* mQueueRenderThreadPtr; + + bool mNativeSourceSupported; + bool mNativeSourceQueueSupported; +}; + +int DALI_EXPORT_API main(int argc, char** argv) +{ + Application application = Application::New(&argc, &argv); + NativeImageSourceQueueController test(application); + application.MainLoop(); + return 0; +} diff --git a/examples/native-image-source-queue/native-image-source-queue.png b/examples/native-image-source-queue/native-image-source-queue.png new file mode 100755 index 000000000..d0927e0dd Binary files /dev/null and b/examples/native-image-source-queue/native-image-source-queue.png differ diff --git a/resources/po/en_GB.po b/resources/po/en_GB.po index c6660ac56..170447606 100755 --- a/resources/po/en_GB.po +++ b/resources/po/en_GB.po @@ -178,6 +178,9 @@ msgstr "Motion Stretch" msgid "DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE" msgstr "Native Image Source" +msgid "DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE_QUEUE" +msgstr "Native Image Source Queue" + msgid "DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE" msgstr "Negotiate Size" diff --git a/resources/po/en_US.po b/resources/po/en_US.po index f619dcaef..4c7f601be 100755 --- a/resources/po/en_US.po +++ b/resources/po/en_US.po @@ -187,6 +187,9 @@ msgstr "Motion Stretch" msgid "DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE" msgstr "Native Image Source" +msgid "DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE_QUEUE" +msgstr "Native Image Source Queue" + msgid "DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE" msgstr "Negotiate Size" diff --git a/resources/po/ko.po b/resources/po/ko.po index 320c9dbee..6a1a2e1d4 100755 --- a/resources/po/ko.po +++ b/resources/po/ko.po @@ -121,6 +121,9 @@ msgstr "늘이기" msgid "DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE" msgstr "네이티브 이미지 소스" +msgid "DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE_QUEUE" +msgstr "네이티브 이미지 소스 큐" + msgid "DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE" msgstr "사이즈 조절" diff --git a/shared/dali-demo-strings.h b/shared/dali-demo-strings.h index efad758ed..a84a3bb84 100644 --- a/shared/dali-demo-strings.h +++ b/shared/dali-demo-strings.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Samsung Electronics Co., Ltd. + * Copyright (c) 2025 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. @@ -100,6 +100,7 @@ extern "C" #define DALI_DEMO_STR_TITLE_MOTION_BLUR dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_MOTION_BLUR") #define DALI_DEMO_STR_TITLE_MOTION_STRETCH dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_MOTION_STRETCH") #define DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE") +#define DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE_QUEUE dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE_QUEUE") #define DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE") #define DALI_DEMO_STR_TITLE_PAGE_TURN dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PAGE_TURN") #define DALI_DEMO_STR_TITLE_PARTICLES dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PARTICLES") @@ -228,6 +229,7 @@ extern "C" #define DALI_DEMO_STR_TITLE_MOTION_BLUR "Motion Blur" #define DALI_DEMO_STR_TITLE_MOTION_STRETCH "Motion Stretch" #define DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE "Native Image Source" +#define DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE_QUEUE "Native Image Source Queue" #define DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE "Negotiate Size" #define DALI_DEMO_STR_TITLE_PAGE_TURN "Page Turn" #define DALI_DEMO_STR_TITLE_PARTICLES "Particles"