Adds SceneView Capture 16/310516/20
authorSeungho Baek <sbsh.baek@samsung.com>
Tue, 30 Apr 2024 07:39:13 +0000 (16:39 +0900)
committerSeungho Baek <sbsh.baek@samsung.com>
Wed, 29 May 2024 06:31:40 +0000 (15:31 +0900)
Change-Id: I9f6dc69f5bf8d53082997c089325398f1b95e10a
Signed-off-by: Seungho Baek <sbsh.baek@samsung.com>
automated-tests/src/dali-scene3d/utc-Dali-SceneView.cpp
dali-scene3d/internal/controls/scene-view/scene-view-impl.cpp
dali-scene3d/internal/controls/scene-view/scene-view-impl.h
dali-scene3d/public-api/controls/scene-view/scene-view.cpp
dali-scene3d/public-api/controls/scene-view/scene-view.h

index 06ff712..4541eee 100644 (file)
  */
 
 #include <dali-toolkit-test-suite-utils.h>
+#include <toolkit-event-thread-callback.h>
+#include <toolkit-timer.h>
+
 #include <dali-toolkit/dali-toolkit.h>
 #include <stdlib.h>
 #include <iostream>
 
 #include <dali-scene3d/public-api/controls/model/model.h>
 #include <dali-scene3d/public-api/controls/scene-view/scene-view.h>
-#include <toolkit-event-thread-callback.h>
+
 
 using namespace Dali;
 using namespace Dali::Toolkit;
@@ -1195,3 +1198,296 @@ int UtcDaliSceneViewMasking(void)
 
   END_TEST;
 }
+
+namespace
+{
+static bool                                   gCaptureFinishedCalled{false};
+static int32_t                                gCaptureId{-1};
+static Toolkit::ImageUrl                      gCapturedImageUrl;
+static Scene3D::SceneView::CaptureFinishState gCaptureFinishState{Scene3D::SceneView::CaptureFinishState::FAILED};
+
+void OnCaptureFinished(Scene3D::SceneView sceneView, Scene3D::SceneView::CaptureResult& captureResult)
+{
+  gCaptureFinishedCalled = true;
+  gCaptureId             = captureResult.captureId;
+  gCapturedImageUrl      = captureResult.imageUrl;
+  gCaptureFinishState    = captureResult.state;
+}
+
+static int32_t                                             gCapturedCount{0};
+static std::vector<int32_t>                                gCaptureIds;
+static std::vector<Toolkit::ImageUrl>                      gCapturedImageUrls;
+static std::vector<Scene3D::SceneView::CaptureFinishState> gCaptureFinishStates;
+
+void OnCaptureMultipleFinished(Scene3D::SceneView sceneView, Scene3D::SceneView::CaptureResult& captureResult)
+{
+  gCapturedCount++;
+  gCaptureIds.push_back(captureResult.captureId);
+  gCapturedImageUrls.push_back(captureResult.imageUrl);
+  gCaptureFinishStates.push_back(captureResult.state);
+}
+} // namespace
+
+int UtcDaliSceneViewCapture01(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.CaptureFinishedSignal().Connect(OnCaptureFinished);
+  view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
+
+  application.GetScene().Add(view);
+
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Model modelView1 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  view.Add(modelView1);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  CameraActor camera = Dali::CameraActor::New();
+  camera.SetProperty(Dali::Actor::Property::NAME, "camera");
+  camera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  camera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  camera.SetFieldOfView(0.5f);
+  camera.SetNearClippingPlane(1.0f);
+  camera.SetFarClippingPlane(5000.0f);
+  camera.SetProperty(Dali::Actor::Property::POSITION, Vector3(20, 30, 40));
+
+  view.Add(camera);
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+  gCaptureFinishState = Scene3D::SceneView::CaptureFinishState::FAILED;
+  int32_t captureId = view.Capture(camera, Vector2(300, 300));
+
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureId, captureId, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCapturedImageUrl.GetUrl().empty(), false, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureFinishState, Scene3D::SceneView::CaptureFinishState::SUCCEEDED, TEST_LOCATION);
+
+  Toolkit::ImageUrl tempImageUrl = gCapturedImageUrl;
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+  gCaptureFinishState = Scene3D::SceneView::CaptureFinishState::FAILED;
+  int32_t captureId2 = view.Capture(camera, Vector2(400, 400));
+
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, true, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(captureId, captureId2, 0.1f, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureId, captureId2, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCapturedImageUrl.GetUrl().empty(), false, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(gCapturedImageUrl, tempImageUrl, 0.1f, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureFinishState, Scene3D::SceneView::CaptureFinishState::SUCCEEDED, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewCaptureCancel(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.CaptureFinishedSignal().Connect(OnCaptureFinished);
+  view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
+
+  application.GetScene().Add(view);
+
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Model modelView1 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  view.Add(modelView1);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  CameraActor camera = Dali::CameraActor::New();
+  camera.SetProperty(Dali::Actor::Property::NAME, "camera");
+  camera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  camera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  camera.SetFieldOfView(0.5f);
+  camera.SetNearClippingPlane(1.0f);
+  camera.SetFarClippingPlane(5000.0f);
+  camera.SetProperty(Dali::Actor::Property::POSITION, Vector3(20, 30, 40));
+
+  view.Add(camera);
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+  gCaptureFinishState = Scene3D::SceneView::CaptureFinishState::FAILED;
+  int32_t captureId = view.Capture(camera, Vector2(300, 300));
+
+  view.Unparent();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureId, captureId, TEST_LOCATION);
+  DALI_TEST_EQUALS(!!gCapturedImageUrl, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureFinishState, Scene3D::SceneView::CaptureFinishState::FAILED, TEST_LOCATION);
+
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+  gCaptureFinishState = Scene3D::SceneView::CaptureFinishState::FAILED;
+
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, false, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewCaptureFailed(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.CaptureFinishedSignal().Connect(OnCaptureFinished);
+  view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
+
+  application.GetScene().Add(view);
+
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Model modelView1 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  view.Add(modelView1);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  CameraActor camera = Dali::CameraActor::New();
+  camera.SetProperty(Dali::Actor::Property::NAME, "camera");
+  camera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  camera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  camera.SetFieldOfView(0.5f);
+  camera.SetNearClippingPlane(1.0f);
+  camera.SetFarClippingPlane(5000.0f);
+  camera.SetProperty(Dali::Actor::Property::POSITION, Vector3(20, 30, 40));
+
+  view.Add(camera);
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+  gCaptureFinishState = Scene3D::SceneView::CaptureFinishState::FAILED;
+  int32_t captureId = view.Capture(camera, Vector2(300, 300));
+
+  Test::EmitGlobalTimerSignal();
+  Test::EmitGlobalTimerSignal();
+  Test::EmitGlobalTimerSignal();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureId, captureId, TEST_LOCATION);
+  DALI_TEST_EQUALS(!!gCapturedImageUrl, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureFinishState, Scene3D::SceneView::CaptureFinishState::FAILED, TEST_LOCATION);
+
+  gCaptureFinishedCalled = false;
+  gCaptureId = -1;
+  gCapturedImageUrl.Reset();
+  gCaptureFinishState = Scene3D::SceneView::CaptureFinishState::FAILED;
+
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+
+  DALI_TEST_EQUALS(gCaptureFinishedCalled, false, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliSceneViewCapture02(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView view = Scene3D::SceneView::New();
+  view.CaptureFinishedSignal().Connect(OnCaptureMultipleFinished);
+  view.SetProperty(Dali::Actor::Property::SIZE, Vector2(100, 100));
+
+  application.GetScene().Add(view);
+
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Model modelView1 = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  view.Add(modelView1);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  CameraActor camera = Dali::CameraActor::New();
+  camera.SetProperty(Dali::Actor::Property::NAME, "camera");
+  camera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  camera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  camera.SetFieldOfView(0.5f);
+  camera.SetNearClippingPlane(1.0f);
+  camera.SetFarClippingPlane(5000.0f);
+  camera.SetProperty(Dali::Actor::Property::POSITION, Vector3(20, 30, 40));
+
+  view.Add(camera);
+
+  gCapturedCount = 0;
+  gCaptureIds.clear();
+  gCapturedImageUrls.clear();
+  gCaptureFinishStates.clear();
+  int32_t captureId = view.Capture(camera, Vector2(300, 300));
+  int32_t captureId2 = view.Capture(camera, Vector2(300, 300));
+
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+  application.Render();
+  application.SendNotification();
+
+  DALI_TEST_EQUALS(gCapturedCount, 2, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureIds.size(), 2, TEST_LOCATION);
+  auto idIter1 = std::find(gCaptureIds.begin(), gCaptureIds.end(), captureId);
+  bool isIter1 = idIter1 != gCaptureIds.end();
+  DALI_TEST_EQUALS(isIter1, true, TEST_LOCATION);
+  auto idIter2 = std::find(gCaptureIds.begin(), gCaptureIds.end(), captureId2);
+  bool isIter2 = idIter2 != gCaptureIds.end();
+  DALI_TEST_EQUALS(isIter2, true, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(gCapturedImageUrls.size(), 2, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(gCapturedImageUrls[0], gCapturedImageUrls[1], 0.1f, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureFinishStates[0], Scene3D::SceneView::CaptureFinishState::SUCCEEDED, TEST_LOCATION);
+  DALI_TEST_EQUALS(gCaptureFinishStates[1], Scene3D::SceneView::CaptureFinishState::SUCCEEDED, TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
index 7fc62c1..efd89a7 100644 (file)
@@ -26,6 +26,7 @@
 #include <dali-toolkit/public-api/image-loader/image-url.h>
 #include <dali-toolkit/public-api/image-loader/image.h>
 #include <dali/devel-api/actors/camera-actor-devel.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
 #include <dali/devel-api/adaptor-framework/window-devel.h>
 #include <dali/devel-api/common/stage.h>
 #include <dali/devel-api/rendering/frame-buffer-devel.h>
@@ -42,8 +43,6 @@
 #include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
 #include <dali-scene3d/internal/light/light-impl.h>
 
-#include <dali/integration-api/debug.h>
-
 using namespace Dali;
 
 namespace Dali
@@ -61,7 +60,6 @@ BaseHandle Create()
 
 // Setup properties, signals and actions using the type-registry.
 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::SceneView, Toolkit::Control, Create);
-
 DALI_PROPERTY_REGISTRATION(Scene3D, SceneView, "AlphaMaskUrl", STRING, ALPHA_MASK_URL)
 DALI_PROPERTY_REGISTRATION(Scene3D, SceneView, "MaskContentScale", FLOAT, MASK_CONTENT_SCALE)
 DALI_PROPERTY_REGISTRATION(Scene3D, SceneView, "CropToMask", BOOLEAN, CROP_TO_MASK)
@@ -70,6 +68,7 @@ DALI_TYPE_REGISTRATION_END()
 Property::Index    RENDERING_BUFFER        = Dali::Toolkit::Control::CONTROL_PROPERTY_END_INDEX + 1;
 constexpr int32_t  DEFAULT_ORIENTATION     = 0;
 constexpr int32_t  INVALID_INDEX           = -1;
+constexpr int32_t  INVALID_CAPTURE_ID      = -1;
 constexpr uint32_t MAXIMUM_SIZE_SHADOW_MAP = 2048;
 
 constexpr int32_t SCENE_ORDER_INDEX  = 100;
@@ -171,7 +170,8 @@ void SetShadowLightConstraint(Dali::CameraActor selectedCamera, Dali::CameraActo
 
   // Compute ViewProjectionMatrix and store it to "tempViewProjectionMatrix" property
   auto       tempViewProjectionMatrixIndex = shadowLightCamera.RegisterProperty("tempViewProjectionMatrix", Matrix::IDENTITY);
-  Constraint projectionMatrixConstraint    = Constraint::New<Matrix>(shadowLightCamera, tempViewProjectionMatrixIndex, [](Matrix& output, const PropertyInputContainer& inputs) {
+  Constraint projectionMatrixConstraint    = Constraint::New<Matrix>(shadowLightCamera, tempViewProjectionMatrixIndex, [](Matrix& output, const PropertyInputContainer& inputs)
+                                                                  {
     Matrix worldMatrix  = inputs[0]->GetMatrix();
     float  tangentFov_2 = tanf(inputs[4]->GetFloat());
     float  nearDistance = inputs[5]->GetFloat();
@@ -284,8 +284,7 @@ void SetShadowLightConstraint(Dali::CameraActor selectedCamera, Dali::CameraActo
     projMatrix[14] = -(near + far) / deltaZ;
     projMatrix[15] = 1.0f;
 
-    output = output * shadowCameraViewMatrix;
-  });
+    output = output * shadowCameraViewMatrix; });
   projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::Actor::Property::WORLD_MATRIX});
   projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::CameraActor::Property::PROJECTION_MODE});
   projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::DevelCameraActor::Property::PROJECTION_DIRECTION});
@@ -641,7 +640,8 @@ void SceneView::SetShadow(Scene3D::Light light)
     return;
   }
 
-  auto foundLight = std::find_if(mLights.begin(), mLights.end(), [light](std::pair<Scene3D::Light, bool> lightEntity) -> bool { return (lightEntity.second && lightEntity.first == light); });
+  auto foundLight = std::find_if(mLights.begin(), mLights.end(), [light](std::pair<Scene3D::Light, bool> lightEntity) -> bool
+                                 { return (lightEntity.second && lightEntity.first == light); });
 
   if(foundLight == mLights.end())
   {
@@ -847,6 +847,100 @@ Quaternion SceneView::GetSkyboxOrientation() const
   return mSkyboxOrientation;
 }
 
+int32_t SceneView::Capture(Dali::CameraActor camera, const Vector2& size)
+{
+  if(size.x <= 0.0f || size.y <= 0.0f)
+  {
+    DALI_LOG_ERROR("The width and height should be positive.\n");
+    return INVALID_CAPTURE_ID;
+  }
+
+  uint32_t width = unsigned(size.width);
+  uint32_t height = unsigned(size.height);
+  if(width > Dali::GetMaxTextureSize() || height > Dali::GetMaxTextureSize())
+  {
+    DALI_LOG_ERROR("The input size is too large.\n");
+    return INVALID_CAPTURE_ID;
+  }
+
+  if(!mRootLayer.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
+  {
+    DALI_LOG_ERROR("Current SceneView is not connected on scene tree\n");
+    return INVALID_CAPTURE_ID;
+  }
+
+  if(!camera.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
+  {
+    mRootLayer.Add(camera);
+  }
+
+  std::shared_ptr<CaptureData> captureData = std::make_shared<CaptureData>();
+  captureData->mCaptureId                  = mCaptureId;
+  captureData->mCaptureTexture             = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, width, height);
+  captureData->mCaptureFrameBuffer         = Dali::FrameBuffer::New(captureData->mCaptureTexture.GetWidth(), captureData->mCaptureTexture.GetHeight(), Dali::FrameBuffer::Attachment::DEPTH_STENCIL);
+  DevelFrameBuffer::SetMultiSamplingLevel(captureData->mCaptureFrameBuffer, mFrameBufferMultiSamplingLevel);
+  captureData->mCaptureFrameBuffer.AttachColorTexture(captureData->mCaptureTexture);
+
+  RenderTaskList taskList   = mSceneHolder.GetRenderTaskList();
+  captureData->mCaptureTask = taskList.CreateTask();
+  captureData->mCaptureTask.SetSourceActor(mRootLayer);
+  captureData->mCaptureTask.SetExclusive(true);
+  captureData->mCaptureTask.SetCullMode(false);
+  captureData->mCaptureTask.SetOrderIndex(SCENE_ORDER_INDEX + 1);
+  captureData->mCaptureTask.SetCameraActor(camera);
+  captureData->mCaptureTask.SetFrameBuffer(captureData->mCaptureFrameBuffer);
+  captureData->mCaptureTask.SetClearEnabled(true);
+  captureData->mCaptureTask.SetClearColor(Color::TRANSPARENT);
+  captureData->mCaptureTask.SetRefreshRate(Dali::RenderTask::REFRESH_ONCE);
+
+  captureData->mCaptureInvertCamera = Dali::CameraActor::New(size);
+  captureData->mCaptureInvertCamera.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+  captureData->mCaptureInvertCamera.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  captureData->mCaptureInvertCamera.SetProperty(Dali::Actor::Property::POSITION_X, size.x / 2.0f);
+  captureData->mCaptureInvertCamera.SetProperty(Dali::Actor::Property::POSITION_Y, size.y / 2.0f);
+
+  captureData->mCaptureUrl       = Dali::Toolkit::Image::GenerateUrl(captureData->mCaptureFrameBuffer, 0u);
+  captureData->mCaptureImageView = Dali::Toolkit::ImageView::New(captureData->mCaptureUrl.GetUrl());
+  captureData->mCaptureImageView.SetProperty(Dali::Actor::Property::SIZE, size);
+  captureData->mCaptureImageView.Add(captureData->mCaptureInvertCamera);
+
+  Window window = DevelWindow::Get(Self());
+  window.Add(captureData->mCaptureImageView);
+
+  captureData->mCaptureInvertTexture     = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, width, height);
+  captureData->mCaptureInvertFrameBuffer = Dali::FrameBuffer::New(captureData->mCaptureInvertTexture.GetWidth(), captureData->mCaptureInvertTexture.GetHeight(), Dali::FrameBuffer::Attachment::DEPTH_STENCIL);
+  captureData->mCaptureInvertFrameBuffer.AttachColorTexture(captureData->mCaptureInvertTexture);
+
+  captureData->mCaptureInvertTask = taskList.CreateTask();
+  captureData->mCaptureInvertTask.SetSourceActor(captureData->mCaptureImageView);
+  captureData->mCaptureInvertTask.SetExclusive(true);
+  captureData->mCaptureInvertTask.SetCullMode(false);
+  captureData->mCaptureInvertTask.SetOrderIndex(SCENE_ORDER_INDEX + 2);
+  captureData->mCaptureInvertTask.SetCameraActor(captureData->mCaptureInvertCamera);
+  captureData->mCaptureInvertTask.SetFrameBuffer(captureData->mCaptureInvertFrameBuffer);
+  captureData->mCaptureInvertTask.SetClearEnabled(true);
+  captureData->mCaptureInvertTask.SetClearColor(Color::TRANSPARENT);
+  captureData->mCaptureInvertTask.SetRefreshRate(Dali::RenderTask::REFRESH_ONCE);
+  captureData->mCaptureInvertTask.FinishedSignal().Connect(this, &SceneView::OnCaptureFinished);
+
+  captureData->mStartTick = mTimerTickCount;
+
+  mCaptureContainer.push_back(std::make_pair(captureData->mCaptureInvertTask, captureData));
+
+  if(!mCaptureTimer)
+  {
+    mCaptureTimer = Dali::Timer::New(1000);
+    mCaptureTimer.TickSignal().Connect(this, &SceneView::OnTimeOut);
+    mCaptureTimer.Start();
+  }
+  return mCaptureId++;
+}
+
+Dali::Scene3D::SceneView::CaptureFinishedSignalType& SceneView::CaptureFinishedSignal()
+{
+  return mCaptureFinishedSignal;
+}
+
 Dali::Scene3D::Loader::ShaderManagerPtr SceneView::GetShaderManager() const
 {
   return mShaderManager;
@@ -1027,6 +1121,39 @@ void SceneView::OnSceneDisconnection()
   }
   mWindow.Reset();
 
+  auto                                                                   self = Self();
+  Dali::Scene3D::SceneView                                               handle(Dali::Scene3D::SceneView::DownCast(self));
+  std::vector<std::pair<Dali::RenderTask, std::shared_ptr<CaptureData>>> tempContainer(mCaptureContainer);
+  for(auto&& capture : tempContainer)
+  {
+    Dali::Scene3D::SceneView::CaptureResult result{capture.second->mCaptureId, Dali::Toolkit::ImageUrl(), Dali::Scene3D::SceneView::CaptureFinishState::FAILED};
+    mCaptureFinishedSignal.Emit(handle, result);
+  }
+  tempContainer.clear();
+
+  for(auto && capture : mCaptureContainer)
+  {
+    if(mSceneHolder)
+    {
+      RenderTaskList taskList = mSceneHolder.GetRenderTaskList();
+      taskList.RemoveTask(capture.second->mCaptureTask);
+      taskList.RemoveTask(capture.second->mCaptureInvertTask);
+    }
+    capture.second->mCaptureTask.Reset();
+    capture.second->mCaptureInvertTask.Reset();
+    capture.second->mCaptureTexture.Reset();
+    capture.second->mCaptureInvertTexture.Reset();
+    capture.second->mCaptureFrameBuffer.Reset();
+    capture.second->mCaptureInvertFrameBuffer.Reset();
+    capture.second->mCaptureUrl.Reset();
+    capture.second->mCaptureImageView.Unparent();
+    capture.second->mCaptureImageView.Reset();
+    capture.second->mCaptureInvertCamera.Unparent();
+    capture.second->mCaptureInvertCamera.Reset();
+  }
+  mCaptureContainer.clear();
+
+
   if(mSceneHolder)
   {
     if(mRenderTask)
@@ -1419,6 +1546,85 @@ void SceneView::UpdateShadowMapBuffer(uint32_t shadowMapSize)
   }
 }
 
+void SceneView::OnCaptureFinished(Dali::RenderTask& task)
+{
+  int32_t                 captureId = INVALID_CAPTURE_ID;
+  Dali::Toolkit::ImageUrl imageUrl;
+
+  auto iter = std::find_if(mCaptureContainer.begin(), mCaptureContainer.end(), [task](std::pair<Dali::RenderTask, std::shared_ptr<CaptureData>> item)
+                           { return item.first == task; });
+
+  if(iter != mCaptureContainer.end())
+  {
+    captureId               = iter->second->mCaptureId;
+    imageUrl                = Dali::Toolkit::ImageUrl::New(iter->second->mCaptureInvertTexture);
+    if(mSceneHolder)
+    {
+      RenderTaskList taskList = mSceneHolder.GetRenderTaskList();
+      taskList.RemoveTask(iter->second->mCaptureTask);
+      taskList.RemoveTask(iter->second->mCaptureInvertTask);
+    }
+    iter->second->mCaptureTexture.Reset();
+    iter->second->mCaptureInvertTexture.Reset();
+    iter->second->mCaptureFrameBuffer.Reset();
+    iter->second->mCaptureInvertFrameBuffer.Reset();
+    iter->second->mCaptureUrl.Reset();
+    iter->second->mCaptureImageView.Unparent();
+    iter->second->mCaptureImageView.Reset();
+    mCaptureContainer.erase(iter);
+
+    auto                     self = Self();
+    Dali::Scene3D::SceneView handle(Dali::Scene3D::SceneView::DownCast(self));
+
+    Dali::Scene3D::SceneView::CaptureResult result{captureId, imageUrl, Dali::Scene3D::SceneView::CaptureFinishState::SUCCEEDED};
+    mCaptureFinishedSignal.Emit(handle, result);
+  }
+
+  if(mCaptureContainer.empty() && mCaptureTimer)
+  {
+    mCaptureTimer.Stop();
+    mCaptureTimer.Reset();
+    mTimerTickCount = 0;
+  }
+}
+
+bool SceneView::OnTimeOut()
+{
+  mTimerTickCount++;
+  auto                     self = Self();
+  Dali::Scene3D::SceneView handle(Dali::Scene3D::SceneView::DownCast(self));
+  std::vector<std::pair<Dali::RenderTask, std::shared_ptr<CaptureData>>> tempContainer;
+  for(auto&& capture : mCaptureContainer)
+  {
+    if(capture.second->mStartTick + 1 < mTimerTickCount)
+    {
+      tempContainer.push_back(capture);
+    }
+  }
+
+  for(auto&& capture : tempContainer)
+  {
+    Dali::Scene3D::SceneView::CaptureResult result{capture.second->mCaptureId, Dali::Toolkit::ImageUrl(), Dali::Scene3D::SceneView::CaptureFinishState::FAILED};
+    mCaptureFinishedSignal.Emit(handle, result);
+  }
+
+  int32_t tickCount = mTimerTickCount;
+  auto it = std::remove_if(mCaptureContainer.begin(), mCaptureContainer.end(), [tickCount](std::pair<Dali::RenderTask, std::shared_ptr<CaptureData>> item) {
+    return item.second->mStartTick + 1 < tickCount;
+  });
+  mCaptureContainer.erase(it, mCaptureContainer.end());
+  mCaptureContainer.shrink_to_fit();
+
+  if(mCaptureContainer.empty() && mCaptureTimer)
+  {
+    mCaptureTimer.Stop();
+    mCaptureTimer.Reset();
+    mTimerTickCount = 0;
+  }
+
+  return !mCaptureContainer.empty();
+}
+
 } // namespace Internal
 } // namespace Scene3D
 } // namespace Dali
index 29661ef..47ff5e7 100644 (file)
 // EXTERNAL INCLUDES
 #include <dali-toolkit/internal/visuals/image/image-visual.h>
 #include <dali-toolkit/public-api/controls/control-impl.h>
+#include <dali-toolkit/public-api/controls/image-view/image-view.h>
 #include <dali/integration-api/adaptor-framework/scene-holder.h>
 #include <dali/integration-api/ordered-set.h>
 #include <dali/public-api/actors/camera-actor.h>
 #include <dali/public-api/actors/layer.h>
+#include <dali/public-api/adaptor-framework/timer.h>
 #include <dali/public-api/adaptor-framework/window.h>
 #include <dali/public-api/animation/animation.h>
+#include <dali/public-api/capture/capture.h>
 #include <dali/public-api/object/weak-handle.h>
 #include <dali/public-api/render-tasks/render-task.h>
 #include <dali/public-api/rendering/frame-buffer.h>
@@ -242,6 +245,16 @@ public:
   Quaternion GetSkyboxOrientation() const;
 
   /**
+   * @copydoc SceneView::Capture()
+   */
+  int32_t Capture(Dali::CameraActor camera, const Vector2& size);
+
+  /**
+   * @copydoc SceneView::FinishedSignal
+   */
+  Dali::Scene3D::SceneView::CaptureFinishedSignalType& CaptureFinishedSignal();
+
+  /**
    * @brief Retrieves ShaderManager of this SceneView.
    * @return ShaderManager of this SceneView.
    */
@@ -430,25 +443,61 @@ private:
    */
   void UpdateShadowMapBuffer(uint32_t shadowMapSize);
 
+  /**
+   * @brief CaptureFinished Callback that is called the capture rendering is finished.
+   * @param[in] task RenderTask that draws requested capture scene.
+   */
+  void OnCaptureFinished(Dali::RenderTask& task);
+
+  /**
+   * @brief Time out Callback to handle the case each capture request is not finished for long time.
+   * @return True if the timer needs to go on.
+   */
+  bool OnTimeOut();
+
 private:
+  /**
+   * Data to store Capture related objects.
+   */
+  struct CaptureData
+  {
+    int32_t                  mStartTick;
+    int32_t                  mCaptureId;                // Unique Key to distinguish requested Captures.
+    Dali::Toolkit::ImageUrl  mCaptureUrl;               // URL for first captured buffer, but it is Y-inverted.
+    Dali::Toolkit::ImageView mCaptureImageView;         // ImageView to draw first capture buffer to be transfered as input for invert.
+    Dali::RenderTask         mCaptureTask;              // RenderTask that is used to capture first buffer.
+    Dali::Texture            mCaptureTexture;           // First Captured texture, but it is Y-inverted.
+    Dali::FrameBuffer        mCaptureFrameBuffer;       // First Captured FBO, but it is Y-inverted.
+    Dali::CameraActor        mCaptureInvertCamera;      // CameraActor to invert first captured buffer by second pass.
+    Dali::RenderTask         mCaptureInvertTask;        // RenderTask to invert first captured buffer.
+    Dali::Texture            mCaptureInvertTexture;     // Result texture of second pass. This is final Texture result.
+    Dali::FrameBuffer        mCaptureInvertFrameBuffer; // FBO for firnal Texture result
+  };
+
   Toolkit::Visual::Base mVisual;
 
   /////////////////////////////////////////////////////////////
   // FrameBuffer and Rendertask to render child objects as a 3D Scene
-  Dali::WeakHandle<Dali::Window> mWindow;
-  Integration::SceneHolder       mSceneHolder;
-  CameraActor                    mDefaultCamera;
-  CameraActor                    mSelectedCamera;
-  std::vector<CameraActor>       mCameras;
-  Dali::FrameBuffer              mFrameBuffer;
-  Dali::Texture                  mTexture;
-  Dali::RenderTask               mRenderTask;
-  Layer                          mRootLayer;
-  int32_t                        mWindowOrientation;
-  Dali::Actor                    mSkybox;
-  Quaternion                     mSkyboxOrientation;
-  float                          mSkyboxIntensity{1.0f};
-  uint8_t                        mFrameBufferMultiSamplingLevel{0u};
+  Dali::WeakHandle<Dali::Window>                      mWindow;
+  Integration::SceneHolder                            mSceneHolder;
+  CameraActor                                         mDefaultCamera;
+  CameraActor                                         mSelectedCamera;
+  std::vector<CameraActor>                            mCameras;
+  Dali::FrameBuffer                                   mFrameBuffer;
+  Dali::Texture                                       mTexture;
+  Dali::RenderTask                                    mRenderTask;
+  Layer                                               mRootLayer;
+  int32_t                                             mWindowOrientation;
+  Dali::Actor                                         mSkybox;
+  Quaternion                                          mSkyboxOrientation;
+  float                                               mSkyboxIntensity{1.0f};
+  uint8_t                                             mFrameBufferMultiSamplingLevel{0u};
+  Dali::Scene3D::SceneView::CaptureFinishedSignalType mCaptureFinishedSignal;
+
+  int32_t                                                                mCaptureId{0};     // Capture ID for requested capture, this is incrementally increasing.
+  std::vector<std::pair<Dali::RenderTask, std::shared_ptr<CaptureData>>> mCaptureContainer; // Container that stores CaptureData until the Capture is finished.
+  Dali::Timer                                                            mCaptureTimer;     // Timer to check the capture is time out or not.
+  int32_t                                                                mTimerTickCount{0};
 
   Dali::Integration::OrderedSet<Scene3D::Internal::LightObserver, false> mLightObservers; ///< The set of items to be notified when light properties change. (not owned)
 
index 8a1efb9..aa4172b 100644 (file)
@@ -192,6 +192,16 @@ Quaternion SceneView::GetSkyboxOrientation() const
   return GetImpl(*this).GetSkyboxOrientation();
 }
 
+int32_t SceneView::Capture(Dali::CameraActor camera, const Vector2& size)
+{
+  return GetImpl(*this).Capture(camera, size);
+}
+
+SceneView::CaptureFinishedSignalType& SceneView::CaptureFinishedSignal()
+{
+  return GetImpl(*this).CaptureFinishedSignal();
+}
+
 } // namespace Scene3D
 
 } // namespace Dali
index ff3d1c2..79e9751 100644 (file)
@@ -20,6 +20,7 @@
 
 // EXTERNAL INCLUDES
 #include <dali-toolkit/public-api/controls/control.h>
+#include <dali-toolkit/public-api/image-loader/image-url.h>
 #include <dali/public-api/actors/camera-actor.h>
 #include <dali/public-api/common/dali-common.h>
 
@@ -149,7 +150,31 @@ public:
     };
   };
 
+  /**
+   * @brief The enumerations used for checking capture success
+   * @SINCE_2_3.25
+   */
+  enum class CaptureFinishState
+  {
+    SUCCEEDED, ///< Succeeded to capture
+    FAILED     ///< Failed to capture by time out
+  };
+
 public:
+  struct CaptureResult
+  {
+    int32_t                 captureId;
+    Dali::Toolkit::ImageUrl imageUrl;
+    CaptureFinishState      state;
+  };
+
+  /**
+   * @brief Typedef for capture finished signals sent by this class.
+   *
+   * @SINCE_2_3.25
+   */
+  typedef Signal<void(SceneView, CaptureResult&)> CaptureFinishedSignalType;
+
   /**
    * @brief Create an initialized SceneView.
    *
@@ -484,6 +509,26 @@ public:
    */
   Quaternion GetSkyboxOrientation() const;
 
+  /**
+   * @brief Requests to capture this SceneView with the Camera.
+   *
+   * @SINCE_2_3.25
+   * @param[in] camera CameraActor to be used for capture.
+   * @param[in] size captured size.
+   * @note The camera is required to be added in this SceneView. (Not need to be a selected camera)
+   * @note If the SceneView is disconnected from Scene, the left capture requests are canceled.
+   * @return capture id that id unique value to distinguish each requiest.
+   */
+  int32_t Capture(Dali::CameraActor camera, const Vector2& size);
+
+  /**
+   * @brief Get capture finished signal.
+   *
+   * @SINCE_2_3.25
+   * @return finished signal instance.
+   */
+  CaptureFinishedSignalType& CaptureFinishedSignal();
+
 public: // Not intended for application developers
   /// @cond internal
   /**