[Tizen] Support to get raw pixel informations of framebuffer for old driver device
[platform/core/uifw/dali-adaptor.git] / dali / internal / system / common / capture-impl.cpp
index dfd48e2..56ef77f 100644 (file)
@@ -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.
 #include <fstream>
 
 // INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/bitmap-saver.h>
 #include <dali/devel-api/adaptor-framework/native-image-source-devel.h>
 #include <dali/devel-api/adaptor-framework/window-devel.h>
 #include <dali/integration-api/adaptor-framework/adaptor.h>
-
-namespace
-{
-unsigned int TIME_OUT_DURATION = 1000;
-}
+#include <dali/internal/adaptor/common/adaptor-impl.h>
+#include <dali/internal/graphics/gles/egl-graphics.h>
 
 namespace Dali
 {
@@ -41,12 +39,21 @@ namespace Internal
 {
 namespace Adaptor
 {
+namespace
+{
+constexpr int32_t  GL_VERSION_NATIVE_IMAGE_SOURCE_AVAILABLE = 30;
+constexpr uint32_t TIME_OUT_DURATION                        = 1000;
+} // namespace
+
 Capture::Capture()
 : mQuality(DEFAULT_QUALITY),
   mTimer(),
   mPath(),
   mNativeImageSourcePtr(NULL),
-  mFileSave(false)
+  mFileSave(false),
+  mUseDefaultCamera(true),
+  mSceneOffCameraAfterCaptureFinished(false),
+  mIsNativeImageSourcePossible(true)
 {
 }
 
@@ -56,13 +63,17 @@ Capture::Capture(Dali::CameraActor cameraActor)
   mTimer(),
   mPath(),
   mNativeImageSourcePtr(NULL),
-  mFileSave(false)
+  mFileSave(false),
+  mUseDefaultCamera(!cameraActor),
+  mSceneOffCameraAfterCaptureFinished(false),
+  mIsNativeImageSourcePossible(true)
 {
 }
 
 Capture::~Capture()
 {
   DeleteNativeImageSource();
+  mTexture.Reset();
 }
 
 CapturePtr Capture::New()
@@ -87,6 +98,11 @@ void Capture::Start(Dali::Actor source, const Dali::Vector2& position, const Dal
 
 void Capture::Start(Dali::Actor source, const Dali::Vector2& position, const Dali::Vector2& size, const std::string& path, const Dali::Vector4& clearColor)
 {
+  if(!source)
+  {
+    return;
+  }
+
   // Increase the reference count focely to avoid application mistake.
   Reference();
 
@@ -96,8 +112,6 @@ void Capture::Start(Dali::Actor source, const Dali::Vector2& position, const Dal
     mFileSave = true;
   }
 
-  DALI_ASSERT_ALWAYS(source && "Source is NULL.");
-
   UnsetResources();
   SetupResources(position, size, clearColor, source);
 }
@@ -112,68 +126,115 @@ Dali::NativeImageSourcePtr Capture::GetNativeImageSource() const
   return mNativeImageSourcePtr;
 }
 
-Dali::Capture::CaptureFinishedSignalType& Capture::FinishedSignal()
+Dali::Texture Capture::GetTexture()
 {
-  return mFinishedSignal;
+  return mTexture;
 }
 
-void Capture::CreateNativeImageSource(const Vector2& size)
+Dali::Devel::PixelBuffer Capture::GetCapturedBuffer()
 {
-  Dali::Adaptor& adaptor = Dali::Adaptor::Get();
-
-  DALI_ASSERT_ALWAYS(adaptor.IsAvailable() && "Dali::Adaptor is not available.");
-
-  DALI_ASSERT_ALWAYS(!mNativeImageSourcePtr && "NativeImageSource is already created.");
+  if(!mPixelBuffer || (mPixelBuffer && !mPixelBuffer.GetBuffer()))
+  {
+    uint32_t             width, height;
+    Dali::Pixel::Format  pixelFormat;
+    if(mIsNativeImageSourcePossible)
+    {
+      std::vector<uint8_t> buffer;
+      if(!mNativeImageSourcePtr || !mNativeImageSourcePtr->GetPixels(buffer, width, height, pixelFormat))
+      {
+        return Dali::Devel::PixelBuffer();
+      }
+      mPixelBuffer = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
+      memcpy(mPixelBuffer.GetBuffer(), &buffer[0], width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat));
+    }
+    else
+    {
+      if(!mFrameBuffer || !mTexture)
+      {
+        DALI_LOG_ERROR("Capture is not started yet.");
+        return Dali::Devel::PixelBuffer();
+      }
+      uint8_t* buffer = mFrameBuffer.GetRenderedBuffer();
+      if(buffer == nullptr)
+      {
+        DALI_LOG_ERROR("Capture is not finished.");
+        return Dali::Devel::PixelBuffer();
+      }
+      width = mTexture.GetWidth();
+      height = mTexture.GetHeight();
+      pixelFormat = mTexture.GetPixelFormat();
+      mPixelBuffer = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
+      memcpy(mPixelBuffer.GetBuffer(), buffer, width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat));
+    }
+  }
+  return mPixelBuffer;
+}
 
-  // create the NativeImageSource object with our surface
-  mNativeImageSourcePtr = Dali::NativeImageSource::New(size.width, size.height, Dali::NativeImageSource::COLOR_DEPTH_DEFAULT);
+Dali::Capture::CaptureFinishedSignalType& Capture::FinishedSignal()
+{
+  return mFinishedSignal;
 }
 
-void Capture::DeleteNativeImageSource()
+void Capture::CreateTexture(const Vector2& size)
 {
-  mNativeImageSourcePtr.Reset();
+  if(mIsNativeImageSourcePossible)
+  {
+    if(!mNativeImageSourcePtr)
+    {
+      mNativeImageSourcePtr = Dali::NativeImageSource::New(size.width, size.height, Dali::NativeImageSource::COLOR_DEPTH_DEFAULT);
+      mTexture              = Dali::Texture::New(*mNativeImageSourcePtr);
+    }
+  }
+  else
+  {
+    mTexture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, unsigned(size.width), unsigned(size.height));
+  }
 }
 
-bool Capture::IsNativeImageSourceCreated()
+void Capture::DeleteNativeImageSource()
 {
-  return mNativeImageSourcePtr;
+  if(mNativeImageSourcePtr)
+  {
+    mNativeImageSourcePtr.Reset();
+  }
 }
 
 void Capture::CreateFrameBuffer()
 {
-  DALI_ASSERT_ALWAYS(mNativeImageSourcePtr && "NativeImageSource is NULL.");
-
-  DALI_ASSERT_ALWAYS(!mFrameBuffer && "FrameBuffer is already created.");
-
-  mNativeTexture = Dali::Texture::New(*mNativeImageSourcePtr);
-
-  // Create a FrameBuffer object with depth attachments.
-  mFrameBuffer = Dali::FrameBuffer::New(mNativeTexture.GetWidth(), mNativeTexture.GetHeight(), Dali::FrameBuffer::Attachment::DEPTH);
-  // Add a color attachment to the FrameBuffer object.
-  mFrameBuffer.AttachColorTexture(mNativeTexture);
+  if(!mFrameBuffer)
+  {
+    // Create a FrameBuffer object with depth attachments.
+    mFrameBuffer = Dali::FrameBuffer::New(mTexture.GetWidth(), mTexture.GetHeight(), Dali::FrameBuffer::Attachment::DEPTH);
+    // Add a color attachment to the FrameBuffer object.
+    mFrameBuffer.AttachColorTexture(mTexture);
+  }
 }
 
 void Capture::DeleteFrameBuffer()
 {
-  DALI_ASSERT_ALWAYS(mFrameBuffer && "FrameBuffer is NULL.");
-
-  mFrameBuffer.Reset();
-  mNativeTexture.Reset();
+  if(mFrameBuffer)
+  {
+    mFrameBuffer.Reset();
+  }
 }
 
 bool Capture::IsFrameBufferCreated()
 {
-  return mFrameBuffer;
+  return static_cast<bool>(mFrameBuffer);
 }
 
 void Capture::SetupRenderTask(const Dali::Vector2& position, const Dali::Vector2& size, Dali::Actor source, const Dali::Vector4& clearColor)
 {
-  DALI_ASSERT_ALWAYS(source && "Source is empty.");
+  if(!source)
+  {
+    DALI_LOG_ERROR("Source is empty\n");
+    return;
+  }
 
   Dali::Window window = DevelWindow::Get(source);
   if(!window)
   {
-    DALI_LOG_ERROR("The source is not added on the window\n");
+    DALI_LOG_ERROR("The source is not added on the scene\n");
     return;
   }
 
@@ -181,7 +242,8 @@ void Capture::SetupRenderTask(const Dali::Vector2& position, const Dali::Vector2
 
   if(!mCameraActor)
   {
-    mCameraActor = Dali::CameraActor::New(size);
+    mUseDefaultCamera = true;
+    mCameraActor      = Dali::CameraActor::New(size);
     // Because input position and size are for 2 dimentional area,
     // default z-directional position of the camera is required to be used for the new camera position.
     float   cameraDefaultZPosition = mCameraActor.GetProperty<float>(Dali::Actor::Property::POSITION_Z);
@@ -191,11 +253,22 @@ void Capture::SetupRenderTask(const Dali::Vector2& position, const Dali::Vector2
     mCameraActor.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
   }
 
-  window.Add(mCameraActor);
-
-  DALI_ASSERT_ALWAYS(mFrameBuffer && "Framebuffer is NULL.");
+  // Camera must be scene on. Add camera to window.
+  if(!mCameraActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
+  {
+    if(!mUseDefaultCamera)
+    {
+      DALI_LOG_ERROR("Camera must be on scene. Camera is connected to window now.\n");
+    }
+    window.Add(mCameraActor);
+    mSceneOffCameraAfterCaptureFinished = true;
+  }
 
-  DALI_ASSERT_ALWAYS(!mRenderTask && "RenderTask is already created.");
+  if(!mFrameBuffer)
+  {
+    DALI_LOG_ERROR("Frame buffer is not created.\n");
+    return;
+  }
 
   Dali::RenderTaskList taskList = window.GetRenderTaskList();
   mRenderTask                   = taskList.CreateTask();
@@ -209,6 +282,10 @@ void Capture::SetupRenderTask(const Dali::Vector2& position, const Dali::Vector2
   mRenderTask.SetProperty(Dali::RenderTask::Property::REQUIRES_SYNC, true);
   mRenderTask.FinishedSignal().Connect(this, &Capture::OnRenderFinished);
   mRenderTask.GetCameraActor().SetInvertYAxis(true);
+  if(!mIsNativeImageSourcePossible)
+  {
+    mFrameBuffer.CaptureRenderedResult();
+  }
 
   mTimer = Dali::Timer::New(TIME_OUT_DURATION);
   mTimer.TickSignal().Connect(this, &Capture::OnTimeOut);
@@ -217,19 +294,26 @@ void Capture::SetupRenderTask(const Dali::Vector2& position, const Dali::Vector2
 
 void Capture::UnsetRenderTask()
 {
-  DALI_ASSERT_ALWAYS(mCameraActor && "CameraActor is NULL.");
-
   mTimer.Reset();
 
-  mCameraActor.Unparent();
-  mCameraActor.Reset();
-
-  DALI_ASSERT_ALWAYS(mRenderTask && "RenderTask is NULL.");
+  if(mSceneOffCameraAfterCaptureFinished && mCameraActor)
+  {
+    if(!mUseDefaultCamera)
+    {
+      DALI_LOG_ERROR("Camera is disconnected from window now.\n");
+    }
+    mSceneOffCameraAfterCaptureFinished = false;
+    mCameraActor.Unparent();
+    mCameraActor.Reset();
+  }
 
-  Dali::Window         window   = DevelWindow::Get(mSource);
-  Dali::RenderTaskList taskList = window.GetRenderTaskList();
-  taskList.RemoveTask(mRenderTask);
-  mRenderTask.Reset();
+  if(mRenderTask)
+  {
+    Dali::Window         window   = DevelWindow::Get(mSource);
+    Dali::RenderTaskList taskList = window.GetRenderTaskList();
+    taskList.RemoveTask(mRenderTask);
+    mRenderTask.Reset();
+  }
   mSource.Reset();
 }
 
@@ -240,7 +324,17 @@ bool Capture::IsRenderTaskSetup()
 
 void Capture::SetupResources(const Dali::Vector2& position, const Dali::Vector2& size, const Dali::Vector4& clearColor, Dali::Actor source)
 {
-  CreateNativeImageSource(size);
+  Dali::Internal::Adaptor::Adaptor& adaptor     = Internal::Adaptor::Adaptor::GetImplementation(Internal::Adaptor::Adaptor::Get());
+  GraphicsInterface*                graphics    = &adaptor.GetGraphicsInterface();
+  auto                              eglGraphics = static_cast<EglGraphics*>(graphics);
+
+  if(eglGraphics->GetEglImplementation().GetGlesVersion() < GL_VERSION_NATIVE_IMAGE_SOURCE_AVAILABLE)
+  {
+    DALI_LOG_ERROR("GLES is 2.0, we can't use native image source \n");
+    mIsNativeImageSourcePossible = false;
+  }
+
+  CreateTexture(size);
 
   CreateFrameBuffer();
 
@@ -270,8 +364,8 @@ void Capture::OnRenderFinished(Dali::RenderTask& task)
   {
     if(!SaveFile())
     {
+      DALI_LOG_ERROR("Fail to Capture Path[%s]\n", mPath.c_str());
       state = Dali::Capture::FinishState::FAILED;
-      DALI_LOG_ERROR("Fail to Capture Path[%s]", mPath.c_str());
     }
   }
 
@@ -301,9 +395,20 @@ bool Capture::OnTimeOut()
 
 bool Capture::SaveFile()
 {
-  DALI_ASSERT_ALWAYS(mNativeImageSourcePtr && "mNativeImageSourcePtr is NULL");
+  if(mIsNativeImageSourcePossible)
+  {
+    if(mNativeImageSourcePtr)
+    {
+      return Dali::DevelNativeImageSource::EncodeToFile(*mNativeImageSourcePtr, mPath, mQuality);
+    }
+  }
+  else
+  {
+    uint8_t* buffer = mFrameBuffer.GetRenderedBuffer();
+    return Dali::EncodeToFile(buffer, mPath, Dali::Pixel::RGBA8888, mTexture.GetWidth(), mTexture.GetHeight(), mQuality);
+  }
 
-  return Dali::DevelNativeImageSource::EncodeToFile(*mNativeImageSourcePtr, mPath, mQuality);
+  return false;
 }
 
 } // End of namespace Adaptor