/*
- * 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.
#include <dali/internal/system/common/capture-impl.h>
// EXTERNAL INCLUDES
-#include <fstream>
-#include <string.h>
-#include <dali/devel-api/common/stage.h>
+#include <dali/integration-api/debug.h>
#include <dali/public-api/common/vector-wrapper.h>
#include <dali/public-api/render-tasks/render-task-list.h>
-#include <dali/integration-api/debug.h>
+#include <string.h>
+#include <fstream>
// INTERNAL INCLUDES
-#include <dali/integration-api/adaptor-framework/adaptor.h>
+#include <dali/devel-api/adaptor-framework/bitmap-saver.h>
#include <dali/devel-api/adaptor-framework/native-image-source-devel.h>
-
-namespace
-{
-unsigned int TIME_OUT_DURATION = 1000;
-}
+#include <dali/devel-api/adaptor-framework/window-devel.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
+#include <dali/internal/adaptor/common/adaptor-impl.h>
+#include <dali/internal/graphics/gles/egl-graphics.h>
namespace Dali
{
-
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 ),
+: mQuality(DEFAULT_QUALITY),
mTimer(),
mPath(),
- mNativeImageSourcePtr( NULL ),
- mFileSave( false )
+ mNativeImageSourcePtr(NULL),
+ mFileSave(false),
+ mUseDefaultCamera(true),
+ mSceneOffCameraAfterCaptureFinished(false),
+ mIsNativeImageSourcePossible(true)
{
}
-Capture::Capture( Dali::CameraActor cameraActor )
-: mQuality( DEFAULT_QUALITY ),
- mCameraActor( cameraActor ),
+Capture::Capture(Dali::CameraActor cameraActor)
+: mQuality(DEFAULT_QUALITY),
+ mCameraActor(cameraActor),
mTimer(),
mPath(),
- mNativeImageSourcePtr( NULL ),
- mFileSave( false )
+ mNativeImageSourcePtr(NULL),
+ mFileSave(false),
+ mUseDefaultCamera(!cameraActor),
+ mSceneOffCameraAfterCaptureFinished(false),
+ mIsNativeImageSourcePossible(true)
{
}
Capture::~Capture()
{
DeleteNativeImageSource();
+ mTexture.Reset();
}
CapturePtr Capture::New()
return pWorker;
}
-CapturePtr Capture::New( Dali::CameraActor cameraActor )
+CapturePtr Capture::New(Dali::CameraActor cameraActor)
{
- CapturePtr pWorker = new Capture( cameraActor );
+ CapturePtr pWorker = new Capture(cameraActor);
return pWorker;
}
-void Capture::Start( Dali::Actor source, const Dali::Vector2& size, const std::string &path, const Dali::Vector4& clearColor, const uint32_t quality )
+void Capture::Start(Dali::Actor source, const Dali::Vector2& position, const Dali::Vector2& size, const std::string& path, const Dali::Vector4& clearColor, const uint32_t quality)
{
mQuality = quality;
- Start( source, size, path, clearColor );
+ Start(source, position, size, path, clearColor);
}
-void Capture::Start( Dali::Actor source, const Dali::Vector2& size, const std::string &path, const Dali::Vector4& clearColor )
+void Capture::Start(Dali::Actor source, const Dali::Vector2& position, const Dali::Vector2& size, const std::string& path, const Dali::Vector4& clearColor)
{
- DALI_ASSERT_ALWAYS(path.size() > 4 && "Path is invalid.");
+ if(!source)
+ {
+ return;
+ }
// Increase the reference count focely to avoid application mistake.
Reference();
mPath = path;
- if( mPath.size() > 0 )
+ if(!mPath.empty())
{
mFileSave = true;
}
- DALI_ASSERT_ALWAYS(source && "Source is NULL.");
-
UnsetResources();
- SetupResources( size, clearColor, source );
+ SetupResources(position, size, clearColor, source);
+}
+
+void Capture::SetImageQuality(uint32_t quality)
+{
+ mQuality = quality;
}
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( Dali::Actor source, const Dali::Vector4& clearColor )
+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.");
-
- mSource = source;
-
- // Check the original parent about source.
- mParent = mSource.GetParent();
+ if(!source)
+ {
+ DALI_LOG_ERROR("Source is empty\n");
+ return;
+ }
- Dali::Stage stage = Dali::Stage::GetCurrent();
- Dali::Size stageSize = stage.GetSize();
+ Dali::Window window = DevelWindow::Get(source);
+ if(!window)
+ {
+ DALI_LOG_ERROR("The source is not added on the scene\n");
+ return;
+ }
- // Add to stage for rendering the source. If source isn't on the stage then it never be rendered.
- stage.Add( mSource );
+ mSource = source;
- if( !mCameraActor )
+ if(!mCameraActor)
{
- mCameraActor = Dali::CameraActor::New( stageSize );
- mCameraActor.SetProperty( Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
- mCameraActor.SetProperty( Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
+ 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);
+ Vector2 positionTransition = position + size / 2;
+ mCameraActor.SetProperty(Dali::Actor::Property::POSITION, Vector3(positionTransition.x, positionTransition.y, cameraDefaultZPosition));
+ mCameraActor.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+ mCameraActor.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
}
- stage.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 = stage.GetRenderTaskList();
- mRenderTask = taskList.CreateTask();
- mRenderTask.SetRefreshRate( Dali::RenderTask::REFRESH_ONCE );
- mRenderTask.SetSourceActor( source );
- mRenderTask.SetCameraActor( mCameraActor );
- mRenderTask.SetScreenToFrameBufferFunction( Dali::RenderTask::FULLSCREEN_FRAMEBUFFER_FUNCTION );
- mRenderTask.SetFrameBuffer( mFrameBuffer );
- mRenderTask.SetClearColor( clearColor );
- mRenderTask.SetClearEnabled( true );
- mRenderTask.SetProperty( Dali::RenderTask::Property::REQUIRES_SYNC, true );
- mRenderTask.FinishedSignal().Connect( this, &Capture::OnRenderFinished );
- mRenderTask.GetCameraActor().SetInvertYAxis( true );
+ Dali::RenderTaskList taskList = window.GetRenderTaskList();
+ mRenderTask = taskList.CreateTask();
+ mRenderTask.SetRefreshRate(Dali::RenderTask::REFRESH_ONCE);
+ mRenderTask.SetSourceActor(source);
+ mRenderTask.SetCameraActor(mCameraActor);
+ mRenderTask.SetScreenToFrameBufferFunction(Dali::RenderTask::FULLSCREEN_FRAMEBUFFER_FUNCTION);
+ mRenderTask.SetFrameBuffer(mFrameBuffer);
+ mRenderTask.SetClearColor(clearColor);
+ mRenderTask.SetClearEnabled(true);
+ 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 );
+ mTimer = Dali::Timer::New(TIME_OUT_DURATION);
+ mTimer.TickSignal().Connect(this, &Capture::OnTimeOut);
mTimer.Start();
}
void Capture::UnsetRenderTask()
{
- DALI_ASSERT_ALWAYS(mCameraActor && "CameraActor is NULL.");
+ mTimer.Reset();
- if( mParent )
+ if(mSceneOffCameraAfterCaptureFinished && mCameraActor)
{
- // Restore the parent of source.
- mParent.Add( mSource );
- mParent.Reset();
+ if(!mUseDefaultCamera)
+ {
+ DALI_LOG_ERROR("Camera is disconnected from window now.\n");
+ }
+ mSceneOffCameraAfterCaptureFinished = false;
+ mCameraActor.Unparent();
+ mCameraActor.Reset();
}
- else
+
+ if(mRenderTask)
{
- mSource.Unparent();
+ Dali::Window window = DevelWindow::Get(mSource);
+ Dali::RenderTaskList taskList = window.GetRenderTaskList();
+ taskList.RemoveTask(mRenderTask);
+ mRenderTask.Reset();
}
-
mSource.Reset();
-
- mTimer.Reset();
-
- mCameraActor.Unparent();
- mCameraActor.Reset();
-
- DALI_ASSERT_ALWAYS( mRenderTask && "RenderTask is NULL." );
-
- Dali::RenderTaskList taskList = Dali::Stage::GetCurrent().GetRenderTaskList();
- taskList.RemoveTask( mRenderTask );
- mRenderTask.Reset();
}
bool Capture::IsRenderTaskSetup()
return mCameraActor && mRenderTask;
}
-void Capture::SetupResources( const Dali::Vector2& size, const Dali::Vector4& clearColor, Dali::Actor source )
+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();
- SetupRenderTask( source, clearColor );
+ SetupRenderTask(position, size, source, clearColor);
}
void Capture::UnsetResources()
{
- if( IsRenderTaskSetup() )
+ if(IsRenderTaskSetup())
{
UnsetRenderTask();
}
- if( IsFrameBufferCreated() )
+ if(IsFrameBufferCreated())
{
DeleteFrameBuffer();
}
}
-void Capture::OnRenderFinished( Dali::RenderTask& task )
+void Capture::OnRenderFinished(Dali::RenderTask& task)
{
Dali::Capture::FinishState state = Dali::Capture::FinishState::SUCCEEDED;
mTimer.Stop();
- if( mFileSave )
+ if(mFileSave)
{
- if( !SaveFile() )
+ 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() );
}
}
- Dali::Capture handle( this );
- mFinishedSignal.Emit( handle, state );
+ Dali::Capture handle(this);
+ mFinishedSignal.Emit(handle, state);
UnsetResources();
{
Dali::Capture::FinishState state = Dali::Capture::FinishState::FAILED;
- Dali::Capture handle( this );
- mFinishedSignal.Emit( handle, state );
+ Dali::Capture handle(this);
+ mFinishedSignal.Emit(handle, state);
UnsetResources();
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
+} // End of namespace Adaptor
-} // End of namespace Internal
+} // End of namespace Internal
-} // End of namespace Dali
+} // End of namespace Dali