(Capture) Don't scene-off camera when we use inputed camera
[platform/core/uifw/dali-adaptor.git] / dali / internal / system / common / capture-impl.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/system/common/capture-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/common/vector-wrapper.h>
24 #include <dali/public-api/render-tasks/render-task-list.h>
25 #include <string.h>
26 #include <fstream>
27
28 // INTERNAL INCLUDES
29 #include <dali/devel-api/adaptor-framework/bitmap-saver.h>
30 #include <dali/devel-api/adaptor-framework/native-image-source-devel.h>
31 #include <dali/devel-api/adaptor-framework/window-devel.h>
32 #include <dali/integration-api/adaptor-framework/adaptor.h>
33 #include <dali/internal/adaptor/common/adaptor-impl.h>
34 #include <dali/internal/graphics/gles/egl-graphics.h>
35
36 namespace Dali
37 {
38 namespace Internal
39 {
40 namespace Adaptor
41 {
42 namespace
43 {
44 constexpr int32_t  GL_VERSION_NATIVE_IMAGE_SOURCE_AVAILABLE = 30;
45 constexpr uint32_t TIME_OUT_DURATION                        = 1000;
46 } // namespace
47
48 Capture::Capture()
49 : mQuality(DEFAULT_QUALITY),
50   mTimer(),
51   mPath(),
52   mNativeImageSourcePtr(NULL),
53   mFileSave(false),
54   mUseDefaultCamera(true),
55   mSceneOffCameraAfterCaptureFinished(false)
56 {
57 }
58
59 Capture::Capture(Dali::CameraActor cameraActor)
60 : mQuality(DEFAULT_QUALITY),
61   mCameraActor(cameraActor),
62   mTimer(),
63   mPath(),
64   mNativeImageSourcePtr(NULL),
65   mFileSave(false),
66   mUseDefaultCamera(!cameraActor),
67   mSceneOffCameraAfterCaptureFinished(false)
68 {
69 }
70
71 Capture::~Capture()
72 {
73   DeleteNativeImageSource();
74   mTexture.Reset();
75 }
76
77 CapturePtr Capture::New()
78 {
79   CapturePtr pWorker = new Capture();
80
81   return pWorker;
82 }
83
84 CapturePtr Capture::New(Dali::CameraActor cameraActor)
85 {
86   CapturePtr pWorker = new Capture(cameraActor);
87
88   return pWorker;
89 }
90
91 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)
92 {
93   mQuality = quality;
94   Start(source, position, size, path, clearColor);
95 }
96
97 void Capture::Start(Dali::Actor source, const Dali::Vector2& position, const Dali::Vector2& size, const std::string& path, const Dali::Vector4& clearColor)
98 {
99   if(!source)
100   {
101     return;
102   }
103
104   // Increase the reference count focely to avoid application mistake.
105   Reference();
106
107   mPath = path;
108   if(!mPath.empty())
109   {
110     mFileSave = true;
111   }
112
113   UnsetResources();
114   SetupResources(position, size, clearColor, source);
115 }
116
117 void Capture::SetImageQuality(uint32_t quality)
118 {
119   mQuality = quality;
120 }
121
122 Dali::NativeImageSourcePtr Capture::GetNativeImageSource() const
123 {
124   return mNativeImageSourcePtr;
125 }
126
127 Dali::Devel::PixelBuffer Capture::GetCapturedBuffer()
128 {
129   if(!mPixelBuffer || (mPixelBuffer && !mPixelBuffer.GetBuffer()))
130   {
131     std::vector<uint8_t> buffer;
132     uint32_t             width, height;
133     Dali::Pixel::Format  pixelFormat;
134     if(!mNativeImageSourcePtr->GetPixels(buffer, width, height, pixelFormat))
135     {
136       return Dali::Devel::PixelBuffer();
137     }
138     mPixelBuffer = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
139     memcpy(mPixelBuffer.GetBuffer(), &buffer[0], width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat));
140   }
141   return mPixelBuffer;
142 }
143
144 Dali::Capture::CaptureFinishedSignalType& Capture::FinishedSignal()
145 {
146   return mFinishedSignal;
147 }
148
149 void Capture::CreateTexture(const Vector2& size)
150 {
151   if(!mNativeImageSourcePtr)
152   {
153     mNativeImageSourcePtr = Dali::NativeImageSource::New(size.width, size.height, Dali::NativeImageSource::COLOR_DEPTH_DEFAULT);
154     mTexture              = Dali::Texture::New(*mNativeImageSourcePtr);
155   }
156 }
157
158 void Capture::DeleteNativeImageSource()
159 {
160   if(mNativeImageSourcePtr)
161   {
162     mNativeImageSourcePtr.Reset();
163   }
164 }
165
166 void Capture::CreateFrameBuffer()
167 {
168   if(!mFrameBuffer)
169   {
170     // Create a FrameBuffer object with depth attachments.
171     mFrameBuffer = Dali::FrameBuffer::New(mTexture.GetWidth(), mTexture.GetHeight(), Dali::FrameBuffer::Attachment::DEPTH);
172     // Add a color attachment to the FrameBuffer object.
173     mFrameBuffer.AttachColorTexture(mTexture);
174   }
175 }
176
177 void Capture::DeleteFrameBuffer()
178 {
179   if(mFrameBuffer)
180   {
181     mFrameBuffer.Reset();
182   }
183 }
184
185 bool Capture::IsFrameBufferCreated()
186 {
187   return static_cast<bool>(mFrameBuffer);
188 }
189
190 void Capture::SetupRenderTask(const Dali::Vector2& position, const Dali::Vector2& size, Dali::Actor source, const Dali::Vector4& clearColor)
191 {
192   if(!source)
193   {
194     DALI_LOG_ERROR("Source is empty\n");
195     return;
196   }
197
198   Dali::Window window = DevelWindow::Get(source);
199   if(!window)
200   {
201     DALI_LOG_ERROR("The source is not added on the scene\n");
202     return;
203   }
204
205   mSource = source;
206
207   if(!mCameraActor)
208   {
209     mUseDefaultCamera = true;
210     mCameraActor      = Dali::CameraActor::New(size);
211     // Because input position and size are for 2 dimentional area,
212     // default z-directional position of the camera is required to be used for the new camera position.
213     float   cameraDefaultZPosition = mCameraActor.GetProperty<float>(Dali::Actor::Property::POSITION_Z);
214     Vector2 positionTransition     = position + size / 2;
215     mCameraActor.SetProperty(Dali::Actor::Property::POSITION, Vector3(positionTransition.x, positionTransition.y, cameraDefaultZPosition));
216     mCameraActor.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
217     mCameraActor.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
218   }
219
220   // Camera must be scene on. Add camera to window.
221   if(!mCameraActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
222   {
223     if(!mUseDefaultCamera)
224     {
225       DALI_LOG_ERROR("Camera must be on scene. Camera is connected to window now.\n");
226     }
227     window.Add(mCameraActor);
228     mSceneOffCameraAfterCaptureFinished = true;
229   }
230
231   if(!mFrameBuffer)
232   {
233     DALI_LOG_ERROR("Frame buffer is not created.\n");
234     return;
235   }
236
237   Dali::RenderTaskList taskList = window.GetRenderTaskList();
238   mRenderTask                   = taskList.CreateTask();
239   mRenderTask.SetRefreshRate(Dali::RenderTask::REFRESH_ONCE);
240   mRenderTask.SetSourceActor(source);
241   mRenderTask.SetCameraActor(mCameraActor);
242   mRenderTask.SetScreenToFrameBufferFunction(Dali::RenderTask::FULLSCREEN_FRAMEBUFFER_FUNCTION);
243   mRenderTask.SetFrameBuffer(mFrameBuffer);
244   mRenderTask.SetClearColor(clearColor);
245   mRenderTask.SetClearEnabled(true);
246   mRenderTask.SetProperty(Dali::RenderTask::Property::REQUIRES_SYNC, true);
247   mRenderTask.FinishedSignal().Connect(this, &Capture::OnRenderFinished);
248   mRenderTask.GetCameraActor().SetInvertYAxis(true);
249
250   mTimer = Dali::Timer::New(TIME_OUT_DURATION);
251   mTimer.TickSignal().Connect(this, &Capture::OnTimeOut);
252   mTimer.Start();
253 }
254
255 void Capture::UnsetRenderTask()
256 {
257   mTimer.Reset();
258
259   if(mSceneOffCameraAfterCaptureFinished && mCameraActor)
260   {
261     if(!mUseDefaultCamera)
262     {
263       DALI_LOG_ERROR("Camera is disconnected from window now.\n");
264     }
265     mSceneOffCameraAfterCaptureFinished = false;
266     mCameraActor.Unparent();
267     mCameraActor.Reset();
268   }
269
270   if(mRenderTask)
271   {
272     Dali::Window         window   = DevelWindow::Get(mSource);
273     Dali::RenderTaskList taskList = window.GetRenderTaskList();
274     taskList.RemoveTask(mRenderTask);
275     mRenderTask.Reset();
276   }
277   mSource.Reset();
278 }
279
280 bool Capture::IsRenderTaskSetup()
281 {
282   return mCameraActor && mRenderTask;
283 }
284
285 void Capture::SetupResources(const Dali::Vector2& position, const Dali::Vector2& size, const Dali::Vector4& clearColor, Dali::Actor source)
286 {
287   CreateTexture(size);
288
289   CreateFrameBuffer();
290
291   SetupRenderTask(position, size, source, clearColor);
292 }
293
294 void Capture::UnsetResources()
295 {
296   if(IsRenderTaskSetup())
297   {
298     UnsetRenderTask();
299   }
300
301   if(IsFrameBufferCreated())
302   {
303     DeleteFrameBuffer();
304   }
305 }
306
307 void Capture::OnRenderFinished(Dali::RenderTask& task)
308 {
309   Dali::Capture::FinishState state = Dali::Capture::FinishState::SUCCEEDED;
310
311   mTimer.Stop();
312
313   if(mFileSave)
314   {
315     if(!SaveFile())
316     {
317       DALI_LOG_ERROR("Fail to Capture Path[%s]\n", mPath.c_str());
318       state = Dali::Capture::FinishState::FAILED;
319     }
320   }
321
322   Dali::Capture handle(this);
323   mFinishedSignal.Emit(handle, state);
324
325   UnsetResources();
326
327   // Decrease the reference count forcely. It is increased at Start().
328   Unreference();
329 }
330
331 bool Capture::OnTimeOut()
332 {
333   Dali::Capture::FinishState state = Dali::Capture::FinishState::FAILED;
334
335   Dali::Capture handle(this);
336   mFinishedSignal.Emit(handle, state);
337
338   UnsetResources();
339
340   // Decrease the reference count forcely. It is increased at Start().
341   Unreference();
342
343   return false;
344 }
345
346 bool Capture::SaveFile()
347 {
348   if(mNativeImageSourcePtr)
349   {
350     return Dali::DevelNativeImageSource::EncodeToFile(*mNativeImageSourcePtr, mPath, mQuality);
351   }
352   return false;
353 }
354
355 } // End of namespace Adaptor
356
357 } // End of namespace Internal
358
359 } // End of namespace Dali