NativeImageSourceQueue demo 19/319719/8
authorEunki, Hong <eunkiki.hong@samsung.com>
Mon, 17 Feb 2025 03:07:38 +0000 (12:07 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Mon, 24 Feb 2025 05:28:52 +0000 (14:28 +0900)
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 <eunkiki.hong@samsung.com>
com.samsung.dali-demo.xml
examples-reel/dali-examples-reel.cpp
examples/native-image-source-queue/README.md [new file with mode: 0644]
examples/native-image-source-queue/native-image-source-queue-example.cpp [new file with mode: 0644]
examples/native-image-source-queue/native-image-source-queue.png [new file with mode: 0755]
resources/po/en_GB.po
resources/po/en_US.po
resources/po/ko.po
shared/dali-demo-strings.h

index 2e9381e81b80a537948cb296b13fddeb2c8f0629..d338e07f43fe8f16ef6af68fe0894d38b0e264f1 100644 (file)
        <ui-application appid="native-image-source.example" exec="/usr/apps/com.samsung.dali-demo/bin/native-image-source.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
                <label>Native Image Source</label>
        </ui-application>
+       <ui-application appid="native-image-source-queue.example" exec="/usr/apps/com.samsung.dali-demo/bin/native-image-source-queue.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
+               <label>Native Image Source Queue</label>
+       </ui-application>
        <ui-application appid="page-turn-view.example" exec="/usr/apps/com.samsung.dali-demo/bin/page-turn-view.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
                <label>Page Turn</label>
        </ui-application>
index 236c694ef625ee52b34e331561d33585cd86196b..4a5e80fc5eed467a47859a2a397b23d96db2c2b7 100644 (file)
@@ -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 (file)
index 0000000..52ca018
--- /dev/null
@@ -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 (file)
index 0000000..484da9f
--- /dev/null
@@ -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 <dali-toolkit/dali-toolkit.h>
+#include <dali/dali.h>
+#include <cstring>
+
+#include <dali/devel-api/adaptor-framework/native-image-source-devel.h>
+#include <dali/devel-api/adaptor-framework/native-image-source-queue.h>
+#include <dali/devel-api/rendering/renderer-devel.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/devel-api/threading/thread.h>
+
+#include <dali/integration-api/debug.h>
+
+// 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<float>(j) + 0.5f) / static_cast<float>(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<uint32_t>(value * 255.0f);
+    if(returnValue > 255u)
+    {
+      return 255u;
+    }
+
+    return static_cast<uint8_t>(returnValue);
+  }
+
+  inline static float RainbowOffset(float value)
+  {
+    // Ensure to make value range in [0.0f 1.0f)
+    value -= static_cast<int>(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<uint32_t>());
+      }
+      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<float>(frameCount) + 0.5f) / static_cast<float>(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 (executable)
index 0000000..d0927e0
Binary files /dev/null and b/examples/native-image-source-queue/native-image-source-queue.png differ
index c6660ac563dc0c02fd2178f5c554d51661222484..1704476068d82fffe87d8accc97900f924ee3f07 100755 (executable)
@@ -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"
 
index f619dcaef1691dcd3b4d0e289349c52b04416e9e..4c7f601be6e9ca99397a107b40e7b5062558ec89 100755 (executable)
@@ -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"
 
index 320c9dbee2f0b4f3b805922c8432b422b0fde86d..6a1a2e1d4964063f47f8e860cf31e743c967656f 100755 (executable)
@@ -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 "사이즈 조절"
 
index efad758ed6468cbe5067c20f35a871f5bb1ca12f..a84a3bb84f127955d6b43c9741a539bd5f84841d 100644 (file)
@@ -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"