Return captured results with PixelBuffer
[platform/core/uifw/dali-adaptor.git] / dali / internal / system / common / capture-impl.cpp
1 /*
2  * Copyright (c) 2021 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 {
55 }
56
57 Capture::Capture(Dali::CameraActor cameraActor)
58 : mQuality(DEFAULT_QUALITY),
59   mCameraActor(cameraActor),
60   mTimer(),
61   mPath(),
62   mNativeImageSourcePtr(NULL),
63   mFileSave(false)
64 {
65 }
66
67 Capture::~Capture()
68 {
69   DeleteNativeImageSource();
70   mTexture.Reset();
71 }
72
73 CapturePtr Capture::New()
74 {
75   CapturePtr pWorker = new Capture();
76
77   return pWorker;
78 }
79
80 CapturePtr Capture::New(Dali::CameraActor cameraActor)
81 {
82   CapturePtr pWorker = new Capture(cameraActor);
83
84   return pWorker;
85 }
86
87 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)
88 {
89   mQuality = quality;
90   Start(source, position, size, path, clearColor);
91 }
92
93 void Capture::Start(Dali::Actor source, const Dali::Vector2& position, const Dali::Vector2& size, const std::string& path, const Dali::Vector4& clearColor)
94 {
95   if(!source)
96   {
97     return;
98   }
99
100   // Increase the reference count focely to avoid application mistake.
101   Reference();
102
103   mPath = path;
104   if(!mPath.empty())
105   {
106     mFileSave = true;
107   }
108
109   UnsetResources();
110   SetupResources(position, size, clearColor, source);
111 }
112
113 void Capture::SetImageQuality(uint32_t quality)
114 {
115   mQuality = quality;
116 }
117
118 Dali::NativeImageSourcePtr Capture::GetNativeImageSource() const
119 {
120   return mNativeImageSourcePtr;
121 }
122
123 Dali::Devel::PixelBuffer Capture::GetCapturedBuffer()
124 {
125   if(!mPixelBuffer || (mPixelBuffer && !mPixelBuffer.GetBuffer()))
126   {
127     std::vector<uint8_t> buffer;
128     uint32_t             width, height;
129     Dali::Pixel::Format  pixelFormat;
130     if(!mNativeImageSourcePtr->GetPixels(buffer, width, height, pixelFormat))
131     {
132       return Dali::Devel::PixelBuffer();
133     }
134     mPixelBuffer = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
135     memcpy(mPixelBuffer.GetBuffer(), &buffer[0], width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat));
136   }
137   return mPixelBuffer;
138 }
139
140 Dali::Capture::CaptureFinishedSignalType& Capture::FinishedSignal()
141 {
142   return mFinishedSignal;
143 }
144
145 void Capture::CreateTexture(const Vector2& size)
146 {
147   if(!mNativeImageSourcePtr)
148   {
149     mNativeImageSourcePtr = Dali::NativeImageSource::New(size.width, size.height, Dali::NativeImageSource::COLOR_DEPTH_DEFAULT);
150     mTexture              = Dali::Texture::New(*mNativeImageSourcePtr);
151   }
152 }
153
154 void Capture::DeleteNativeImageSource()
155 {
156   if(mNativeImageSourcePtr)
157   {
158     mNativeImageSourcePtr.Reset();
159   }
160 }
161
162 void Capture::CreateFrameBuffer()
163 {
164   if(!mFrameBuffer)
165   {
166     // Create a FrameBuffer object with depth attachments.
167     mFrameBuffer = Dali::FrameBuffer::New(mTexture.GetWidth(), mTexture.GetHeight(), Dali::FrameBuffer::Attachment::DEPTH);
168     // Add a color attachment to the FrameBuffer object.
169     mFrameBuffer.AttachColorTexture(mTexture);
170   }
171 }
172
173 void Capture::DeleteFrameBuffer()
174 {
175   if(mFrameBuffer)
176   {
177     mFrameBuffer.Reset();
178   }
179 }
180
181 bool Capture::IsFrameBufferCreated()
182 {
183   return mFrameBuffer;
184 }
185
186 void Capture::SetupRenderTask(const Dali::Vector2& position, const Dali::Vector2& size, Dali::Actor source, const Dali::Vector4& clearColor)
187 {
188   if(!source)
189   {
190     DALI_LOG_ERROR("Source is empty\n");
191     return;
192   }
193
194   Dali::Window window = DevelWindow::Get(source);
195   if(!window)
196   {
197     DALI_LOG_ERROR("The source is not added on the scene\n");
198     return;
199   }
200
201   mSource = source;
202
203   if(!mCameraActor)
204   {
205     mCameraActor = Dali::CameraActor::New(size);
206     // Because input position and size are for 2 dimentional area,
207     // default z-directional position of the camera is required to be used for the new camera position.
208     float   cameraDefaultZPosition = mCameraActor.GetProperty<float>(Dali::Actor::Property::POSITION_Z);
209     Vector2 positionTransition     = position + size / 2;
210     mCameraActor.SetProperty(Dali::Actor::Property::POSITION, Vector3(positionTransition.x, positionTransition.y, cameraDefaultZPosition));
211     mCameraActor.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
212     mCameraActor.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
213   }
214
215   window.Add(mCameraActor);
216
217   if(!mFrameBuffer)
218   {
219     DALI_LOG_ERROR("Frame buffer is not created.\n");
220     return;
221   }
222
223   Dali::RenderTaskList taskList = window.GetRenderTaskList();
224   mRenderTask                   = taskList.CreateTask();
225   mRenderTask.SetRefreshRate(Dali::RenderTask::REFRESH_ONCE);
226   mRenderTask.SetSourceActor(source);
227   mRenderTask.SetCameraActor(mCameraActor);
228   mRenderTask.SetScreenToFrameBufferFunction(Dali::RenderTask::FULLSCREEN_FRAMEBUFFER_FUNCTION);
229   mRenderTask.SetFrameBuffer(mFrameBuffer);
230   mRenderTask.SetClearColor(clearColor);
231   mRenderTask.SetClearEnabled(true);
232   mRenderTask.SetProperty(Dali::RenderTask::Property::REQUIRES_SYNC, true);
233   mRenderTask.FinishedSignal().Connect(this, &Capture::OnRenderFinished);
234   mRenderTask.GetCameraActor().SetInvertYAxis(true);
235
236   mTimer = Dali::Timer::New(TIME_OUT_DURATION);
237   mTimer.TickSignal().Connect(this, &Capture::OnTimeOut);
238   mTimer.Start();
239 }
240
241 void Capture::UnsetRenderTask()
242 {
243   mTimer.Reset();
244
245   if(mCameraActor)
246   {
247     mCameraActor.Unparent();
248     mCameraActor.Reset();
249   }
250
251   if(mRenderTask)
252   {
253     Dali::Window         window   = DevelWindow::Get(mSource);
254     Dali::RenderTaskList taskList = window.GetRenderTaskList();
255     taskList.RemoveTask(mRenderTask);
256     mRenderTask.Reset();
257   }
258   mSource.Reset();
259 }
260
261 bool Capture::IsRenderTaskSetup()
262 {
263   return mCameraActor && mRenderTask;
264 }
265
266 void Capture::SetupResources(const Dali::Vector2& position, const Dali::Vector2& size, const Dali::Vector4& clearColor, Dali::Actor source)
267 {
268   CreateTexture(size);
269
270   CreateFrameBuffer();
271
272   SetupRenderTask(position, size, source, clearColor);
273 }
274
275 void Capture::UnsetResources()
276 {
277   if(IsRenderTaskSetup())
278   {
279     UnsetRenderTask();
280   }
281
282   if(IsFrameBufferCreated())
283   {
284     DeleteFrameBuffer();
285   }
286 }
287
288 void Capture::OnRenderFinished(Dali::RenderTask& task)
289 {
290   Dali::Capture::FinishState state = Dali::Capture::FinishState::SUCCEEDED;
291
292   mTimer.Stop();
293
294   if(mFileSave)
295   {
296     if(!SaveFile())
297     {
298       DALI_LOG_ERROR("Fail to Capture Path[%s]\n", mPath.c_str());
299       state = Dali::Capture::FinishState::FAILED;
300     }
301   }
302
303   Dali::Capture handle(this);
304   mFinishedSignal.Emit(handle, state);
305
306   UnsetResources();
307
308   // Decrease the reference count forcely. It is increased at Start().
309   Unreference();
310 }
311
312 bool Capture::OnTimeOut()
313 {
314   Dali::Capture::FinishState state = Dali::Capture::FinishState::FAILED;
315
316   Dali::Capture handle(this);
317   mFinishedSignal.Emit(handle, state);
318
319   UnsetResources();
320
321   // Decrease the reference count forcely. It is increased at Start().
322   Unreference();
323
324   return false;
325 }
326
327 bool Capture::SaveFile()
328 {
329   if(mNativeImageSourcePtr)
330   {
331     return Dali::DevelNativeImageSource::EncodeToFile(*mNativeImageSourcePtr, mPath, mQuality);
332   }
333   return false;
334 }
335
336 } // End of namespace Adaptor
337
338 } // End of namespace Internal
339
340 } // End of namespace Dali