[dali_2.3.43] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / system / common / capture-impl.cpp
1 /*
2  * Copyright (c) 2024 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
35 namespace Dali
36 {
37 namespace Internal
38 {
39 namespace Adaptor
40 {
41 namespace
42 {
43 static constexpr uint32_t ORDER_INDEX_CAPTURE_RENDER_TASK              = 1000;
44 constexpr int32_t         SHADER_VERSION_NATIVE_IMAGE_SOURCE_AVAILABLE = 300;
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 void Capture::SetExclusive(bool exclusive)
123 {
124   if(mIsExclusive != exclusive)
125   {
126     mIsExclusive = exclusive;
127     if(mRenderTask)
128     {
129       mRenderTask.SetExclusive(mIsExclusive);
130     }
131   }
132 }
133
134 bool Capture::IsExclusive() const
135 {
136   return mIsExclusive;
137 }
138
139 Dali::NativeImageSourcePtr Capture::GetNativeImageSource() const
140 {
141   return mNativeImageSourcePtr;
142 }
143
144 Dali::Texture Capture::GetTexture() const
145 {
146   return mTexture;
147 }
148
149 Dali::Devel::PixelBuffer Capture::GetCapturedBuffer()
150 {
151   if(!mPixelBuffer || (mPixelBuffer && !mPixelBuffer.GetBuffer()))
152   {
153     std::vector<uint8_t> buffer;
154     uint32_t             width, height;
155     Dali::Pixel::Format  pixelFormat;
156     if(!mNativeImageSourcePtr->GetPixels(buffer, width, height, pixelFormat))
157     {
158       return Dali::Devel::PixelBuffer();
159     }
160     mPixelBuffer = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
161     memcpy(mPixelBuffer.GetBuffer(), &buffer[0], width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat));
162   }
163   return mPixelBuffer;
164 }
165
166 Dali::Capture::CaptureFinishedSignalType& Capture::FinishedSignal()
167 {
168   return mFinishedSignal;
169 }
170
171 void Capture::CreateTexture(const Vector2& size)
172 {
173   if(mFileSave)
174   {
175     if(!mNativeImageSourcePtr)
176     {
177       mNativeImageSourcePtr = Dali::NativeImageSource::New(size.width, size.height, Dali::NativeImageSource::COLOR_DEPTH_DEFAULT);
178       mTexture              = Dali::Texture::New(*mNativeImageSourcePtr);
179     }
180   }
181   else
182   {
183     mTexture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, unsigned(size.width), unsigned(size.height));
184   }
185 }
186
187 void Capture::DeleteNativeImageSource()
188 {
189   if(mNativeImageSourcePtr)
190   {
191     mNativeImageSourcePtr.Reset();
192   }
193 }
194
195 void Capture::CreateFrameBuffer()
196 {
197   if(!mFrameBuffer)
198   {
199     // Create a FrameBuffer object with depth attachments.
200     mFrameBuffer = Dali::FrameBuffer::New(mTexture.GetWidth(), mTexture.GetHeight(), Dali::FrameBuffer::Attachment::DEPTH);
201     // Add a color attachment to the FrameBuffer object.
202     mFrameBuffer.AttachColorTexture(mTexture);
203   }
204 }
205
206 void Capture::DeleteFrameBuffer()
207 {
208   if(mFrameBuffer)
209   {
210     mFrameBuffer.Reset();
211   }
212 }
213
214 bool Capture::IsFrameBufferCreated()
215 {
216   return static_cast<bool>(mFrameBuffer);
217 }
218
219 void Capture::SetupRenderTask(const Dali::Vector2& position, const Dali::Vector2& size, Dali::Actor source, const Dali::Vector4& clearColor)
220 {
221   if(!source)
222   {
223     DALI_LOG_ERROR("Source is empty\n");
224     return;
225   }
226
227   Dali::Integration::SceneHolder sceneHolder = Dali::Integration::SceneHolder::Get(source);
228   if(!sceneHolder)
229   {
230     DALI_LOG_ERROR("The source is not added on the scene\n");
231     return;
232   }
233
234   mSource = source;
235
236   if(!mCameraActor)
237   {
238     mUseDefaultCamera = true;
239     mCameraActor      = Dali::CameraActor::New(size);
240     // Because input position and size are for 2 dimentional area,
241     // default z-directional position of the camera is required to be used for the new camera position.
242     float   cameraDefaultZPosition = mCameraActor.GetProperty<float>(Dali::Actor::Property::POSITION_Z);
243     Vector2 positionTransition     = position + size / 2;
244     mCameraActor.SetProperty(Dali::Actor::Property::POSITION, Vector3(positionTransition.x, positionTransition.y, cameraDefaultZPosition));
245     mCameraActor.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
246     mCameraActor.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
247   }
248
249   // Camera must be scene on. Add camera to window.
250   if(!mCameraActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
251   {
252     if(!mUseDefaultCamera)
253     {
254       DALI_LOG_ERROR("Camera must be on scene. Camera is connected to window now.\n");
255     }
256     sceneHolder.Add(mCameraActor);
257     mSceneOffCameraAfterCaptureFinished = true;
258   }
259
260   if(!mFrameBuffer)
261   {
262     DALI_LOG_ERROR("Frame buffer is not created.\n");
263     return;
264   }
265
266   mSceneHolderHandle            = sceneHolder;
267   Dali::RenderTaskList taskList = sceneHolder.GetRenderTaskList();
268   mRenderTask                   = taskList.CreateTask();
269   mRenderTask.SetOrderIndex(ORDER_INDEX_CAPTURE_RENDER_TASK);
270   mRenderTask.SetRefreshRate(Dali::RenderTask::REFRESH_ONCE);
271   mRenderTask.SetSourceActor(source);
272   mRenderTask.SetCameraActor(mCameraActor);
273   mRenderTask.SetScreenToFrameBufferFunction(Dali::RenderTask::FULLSCREEN_FRAMEBUFFER_FUNCTION);
274   mRenderTask.SetFrameBuffer(mFrameBuffer);
275   mRenderTask.SetClearColor(clearColor);
276   mRenderTask.SetClearEnabled(true);
277   mRenderTask.SetExclusive(mIsExclusive);
278   mRenderTask.SetProperty(Dali::RenderTask::Property::REQUIRES_SYNC, true);
279   mRenderTask.FinishedSignal().Connect(this, &Capture::OnRenderFinished);
280   mRenderTask.GetCameraActor().SetInvertYAxis(true);
281
282   mTimer = Dali::Timer::New(TIME_OUT_DURATION);
283   mTimer.TickSignal().Connect(this, &Capture::OnTimeOut);
284   mTimer.Start();
285 }
286
287 void Capture::UnsetRenderTask()
288 {
289   mTimer.Reset();
290
291   if(mSceneOffCameraAfterCaptureFinished && mCameraActor)
292   {
293     if(!mUseDefaultCamera)
294     {
295       DALI_LOG_ERROR("Camera is disconnected from window now.\n");
296     }
297     mSceneOffCameraAfterCaptureFinished = false;
298     mCameraActor.Unparent();
299     mCameraActor.Reset();
300   }
301
302   Dali::Integration::SceneHolder sceneHolder = mSceneHolderHandle.GetHandle();
303   if(mRenderTask && sceneHolder)
304   {
305     Dali::RenderTaskList taskList = sceneHolder.GetRenderTaskList();
306     taskList.RemoveTask(mRenderTask);
307   }
308   mRenderTask.Reset();
309   mSource.Reset();
310   mSceneHolderHandle.Reset();
311 }
312
313 bool Capture::IsRenderTaskSetup()
314 {
315   return mCameraActor && mRenderTask;
316 }
317
318 void Capture::SetupResources(const Dali::Vector2& position, const Dali::Vector2& size, const Dali::Vector4& clearColor, Dali::Actor source)
319 {
320   if(mFileSave && Dali::Shader::GetShaderLanguageVersion() < SHADER_VERSION_NATIVE_IMAGE_SOURCE_AVAILABLE)
321   {
322     DALI_LOG_ERROR("GLES is 2.0, we can't use native image source \n");
323     mFileSave = false;
324   }
325
326   CreateTexture(size);
327
328   CreateFrameBuffer();
329
330   SetupRenderTask(position, size, source, clearColor);
331 }
332
333 void Capture::UnsetResources()
334 {
335   if(IsRenderTaskSetup())
336   {
337     UnsetRenderTask();
338   }
339
340   if(IsFrameBufferCreated())
341   {
342     DeleteFrameBuffer();
343   }
344 }
345
346 void Capture::OnRenderFinished(Dali::RenderTask& task)
347 {
348   Dali::Capture::FinishState state = Dali::Capture::FinishState::SUCCEEDED;
349
350   mTimer.Stop();
351
352   if(mFileSave)
353   {
354     if(!SaveFile())
355     {
356       DALI_LOG_ERROR("Fail to Capture Path[%s]\n", mPath.c_str());
357       state = Dali::Capture::FinishState::FAILED;
358     }
359   }
360
361   Dali::Capture handle(this);
362   mFinishedSignal.Emit(handle, state);
363
364   UnsetResources();
365
366   // Decrease the reference count forcely. It is increased at Start().
367   Unreference();
368 }
369
370 bool Capture::OnTimeOut()
371 {
372   Dali::Capture::FinishState state = Dali::Capture::FinishState::FAILED;
373
374   Dali::Capture handle(this);
375   mFinishedSignal.Emit(handle, state);
376
377   UnsetResources();
378
379   // Decrease the reference count forcely. It is increased at Start().
380   Unreference();
381
382   return false;
383 }
384
385 bool Capture::SaveFile()
386 {
387   if(mNativeImageSourcePtr)
388   {
389     return Dali::DevelNativeImageSource::EncodeToFile(*mNativeImageSourcePtr, mPath, mQuality);
390   }
391
392   return false;
393 }
394
395 } // End of namespace Adaptor
396
397 } // End of namespace Internal
398
399 } // End of namespace Dali