[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
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   mIsNativeImageSourcePossible(true)
57 {
58 }
59
60 Capture::Capture(Dali::CameraActor cameraActor)
61 : mQuality(DEFAULT_QUALITY),
62   mCameraActor(cameraActor),
63   mTimer(),
64   mPath(),
65   mNativeImageSourcePtr(NULL),
66   mFileSave(false),
67   mUseDefaultCamera(!cameraActor),
68   mSceneOffCameraAfterCaptureFinished(false),
69   mIsNativeImageSourcePossible(true)
70 {
71 }
72
73 Capture::~Capture()
74 {
75   DeleteNativeImageSource();
76   mTexture.Reset();
77 }
78
79 CapturePtr Capture::New()
80 {
81   CapturePtr pWorker = new Capture();
82
83   return pWorker;
84 }
85
86 CapturePtr Capture::New(Dali::CameraActor cameraActor)
87 {
88   CapturePtr pWorker = new Capture(cameraActor);
89
90   return pWorker;
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, const uint32_t quality)
94 {
95   mQuality = quality;
96   Start(source, position, size, path, clearColor);
97 }
98
99 void Capture::Start(Dali::Actor source, const Dali::Vector2& position, const Dali::Vector2& size, const std::string& path, const Dali::Vector4& clearColor)
100 {
101   if(!source)
102   {
103     return;
104   }
105
106   // Increase the reference count focely to avoid application mistake.
107   Reference();
108
109   mPath = path;
110   if(!mPath.empty())
111   {
112     mFileSave = true;
113   }
114
115   UnsetResources();
116   SetupResources(position, size, clearColor, source);
117 }
118
119 void Capture::SetImageQuality(uint32_t quality)
120 {
121   mQuality = quality;
122 }
123
124 Dali::NativeImageSourcePtr Capture::GetNativeImageSource() const
125 {
126   return mNativeImageSourcePtr;
127 }
128
129 Dali::Texture Capture::GetTexture()
130 {
131   return mTexture;
132 }
133
134 Dali::Devel::PixelBuffer Capture::GetCapturedBuffer()
135 {
136   if(!mPixelBuffer || (mPixelBuffer && !mPixelBuffer.GetBuffer()))
137   {
138     uint32_t             width, height;
139     Dali::Pixel::Format  pixelFormat;
140     if(mIsNativeImageSourcePossible)
141     {
142       std::vector<uint8_t> buffer;
143       if(!mNativeImageSourcePtr || !mNativeImageSourcePtr->GetPixels(buffer, width, height, pixelFormat))
144       {
145         return Dali::Devel::PixelBuffer();
146       }
147       mPixelBuffer = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
148       memcpy(mPixelBuffer.GetBuffer(), &buffer[0], width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat));
149     }
150     else
151     {
152       if(!mFrameBuffer || !mTexture)
153       {
154         DALI_LOG_ERROR("Capture is not started yet.");
155         return Dali::Devel::PixelBuffer();
156       }
157       uint8_t* buffer = mFrameBuffer.GetRenderedBuffer();
158       if(buffer == nullptr)
159       {
160         DALI_LOG_ERROR("Capture is not finished.");
161         return Dali::Devel::PixelBuffer();
162       }
163       width = mTexture.GetWidth();
164       height = mTexture.GetHeight();
165       pixelFormat = mTexture.GetPixelFormat();
166       mPixelBuffer = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
167       memcpy(mPixelBuffer.GetBuffer(), buffer, width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat));
168     }
169   }
170   return mPixelBuffer;
171 }
172
173 Dali::Capture::CaptureFinishedSignalType& Capture::FinishedSignal()
174 {
175   return mFinishedSignal;
176 }
177
178 void Capture::CreateTexture(const Vector2& size)
179 {
180   if(mIsNativeImageSourcePossible)
181   {
182     if(!mNativeImageSourcePtr)
183     {
184       mNativeImageSourcePtr = Dali::NativeImageSource::New(size.width, size.height, Dali::NativeImageSource::COLOR_DEPTH_DEFAULT);
185       mTexture              = Dali::Texture::New(*mNativeImageSourcePtr);
186     }
187   }
188   else
189   {
190     mTexture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, unsigned(size.width), unsigned(size.height));
191   }
192 }
193
194 void Capture::DeleteNativeImageSource()
195 {
196   if(mNativeImageSourcePtr)
197   {
198     mNativeImageSourcePtr.Reset();
199   }
200 }
201
202 void Capture::CreateFrameBuffer()
203 {
204   if(!mFrameBuffer)
205   {
206     // Create a FrameBuffer object with depth attachments.
207     mFrameBuffer = Dali::FrameBuffer::New(mTexture.GetWidth(), mTexture.GetHeight(), Dali::FrameBuffer::Attachment::DEPTH);
208     // Add a color attachment to the FrameBuffer object.
209     mFrameBuffer.AttachColorTexture(mTexture);
210   }
211 }
212
213 void Capture::DeleteFrameBuffer()
214 {
215   if(mFrameBuffer)
216   {
217     mFrameBuffer.Reset();
218   }
219 }
220
221 bool Capture::IsFrameBufferCreated()
222 {
223   return static_cast<bool>(mFrameBuffer);
224 }
225
226 void Capture::SetupRenderTask(const Dali::Vector2& position, const Dali::Vector2& size, Dali::Actor source, const Dali::Vector4& clearColor)
227 {
228   if(!source)
229   {
230     DALI_LOG_ERROR("Source is empty\n");
231     return;
232   }
233
234   Dali::Window window = DevelWindow::Get(source);
235   if(!window)
236   {
237     DALI_LOG_ERROR("The source is not added on the scene\n");
238     return;
239   }
240
241   mSource = source;
242
243   if(!mCameraActor)
244   {
245     mUseDefaultCamera = true;
246     mCameraActor      = Dali::CameraActor::New(size);
247     // Because input position and size are for 2 dimentional area,
248     // default z-directional position of the camera is required to be used for the new camera position.
249     float   cameraDefaultZPosition = mCameraActor.GetProperty<float>(Dali::Actor::Property::POSITION_Z);
250     Vector2 positionTransition     = position + size / 2;
251     mCameraActor.SetProperty(Dali::Actor::Property::POSITION, Vector3(positionTransition.x, positionTransition.y, cameraDefaultZPosition));
252     mCameraActor.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
253     mCameraActor.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
254   }
255
256   // Camera must be scene on. Add camera to window.
257   if(!mCameraActor.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE))
258   {
259     if(!mUseDefaultCamera)
260     {
261       DALI_LOG_ERROR("Camera must be on scene. Camera is connected to window now.\n");
262     }
263     window.Add(mCameraActor);
264     mSceneOffCameraAfterCaptureFinished = true;
265   }
266
267   if(!mFrameBuffer)
268   {
269     DALI_LOG_ERROR("Frame buffer is not created.\n");
270     return;
271   }
272
273   Dali::RenderTaskList taskList = window.GetRenderTaskList();
274   mRenderTask                   = taskList.CreateTask();
275   mRenderTask.SetRefreshRate(Dali::RenderTask::REFRESH_ONCE);
276   mRenderTask.SetSourceActor(source);
277   mRenderTask.SetCameraActor(mCameraActor);
278   mRenderTask.SetScreenToFrameBufferFunction(Dali::RenderTask::FULLSCREEN_FRAMEBUFFER_FUNCTION);
279   mRenderTask.SetFrameBuffer(mFrameBuffer);
280   mRenderTask.SetClearColor(clearColor);
281   mRenderTask.SetClearEnabled(true);
282   mRenderTask.SetProperty(Dali::RenderTask::Property::REQUIRES_SYNC, true);
283   mRenderTask.FinishedSignal().Connect(this, &Capture::OnRenderFinished);
284   mRenderTask.GetCameraActor().SetInvertYAxis(true);
285   if(!mIsNativeImageSourcePossible)
286   {
287     mFrameBuffer.CaptureRenderedResult();
288   }
289
290   mTimer = Dali::Timer::New(TIME_OUT_DURATION);
291   mTimer.TickSignal().Connect(this, &Capture::OnTimeOut);
292   mTimer.Start();
293 }
294
295 void Capture::UnsetRenderTask()
296 {
297   mTimer.Reset();
298
299   if(mSceneOffCameraAfterCaptureFinished && mCameraActor)
300   {
301     if(!mUseDefaultCamera)
302     {
303       DALI_LOG_ERROR("Camera is disconnected from window now.\n");
304     }
305     mSceneOffCameraAfterCaptureFinished = false;
306     mCameraActor.Unparent();
307     mCameraActor.Reset();
308   }
309
310   if(mRenderTask)
311   {
312     Dali::Window         window   = DevelWindow::Get(mSource);
313     Dali::RenderTaskList taskList = window.GetRenderTaskList();
314     taskList.RemoveTask(mRenderTask);
315     mRenderTask.Reset();
316   }
317   mSource.Reset();
318 }
319
320 bool Capture::IsRenderTaskSetup()
321 {
322   return mCameraActor && mRenderTask;
323 }
324
325 void Capture::SetupResources(const Dali::Vector2& position, const Dali::Vector2& size, const Dali::Vector4& clearColor, Dali::Actor source)
326 {
327   Dali::Internal::Adaptor::Adaptor& adaptor     = Internal::Adaptor::Adaptor::GetImplementation(Internal::Adaptor::Adaptor::Get());
328   GraphicsInterface*                graphics    = &adaptor.GetGraphicsInterface();
329   auto                              eglGraphics = static_cast<EglGraphics*>(graphics);
330
331   if(eglGraphics->GetEglImplementation().GetGlesVersion() < GL_VERSION_NATIVE_IMAGE_SOURCE_AVAILABLE)
332   {
333     DALI_LOG_ERROR("GLES is 2.0, we can't use native image source \n");
334     mIsNativeImageSourcePossible = false;
335   }
336
337   CreateTexture(size);
338
339   CreateFrameBuffer();
340
341   SetupRenderTask(position, size, source, clearColor);
342 }
343
344 void Capture::UnsetResources()
345 {
346   if(IsRenderTaskSetup())
347   {
348     UnsetRenderTask();
349   }
350
351   if(IsFrameBufferCreated())
352   {
353     DeleteFrameBuffer();
354   }
355 }
356
357 void Capture::OnRenderFinished(Dali::RenderTask& task)
358 {
359   Dali::Capture::FinishState state = Dali::Capture::FinishState::SUCCEEDED;
360
361   mTimer.Stop();
362
363   if(mFileSave)
364   {
365     if(!SaveFile())
366     {
367       DALI_LOG_ERROR("Fail to Capture Path[%s]\n", mPath.c_str());
368       state = Dali::Capture::FinishState::FAILED;
369     }
370   }
371
372   Dali::Capture handle(this);
373   mFinishedSignal.Emit(handle, state);
374
375   UnsetResources();
376
377   // Decrease the reference count forcely. It is increased at Start().
378   Unreference();
379 }
380
381 bool Capture::OnTimeOut()
382 {
383   Dali::Capture::FinishState state = Dali::Capture::FinishState::FAILED;
384
385   Dali::Capture handle(this);
386   mFinishedSignal.Emit(handle, state);
387
388   UnsetResources();
389
390   // Decrease the reference count forcely. It is increased at Start().
391   Unreference();
392
393   return false;
394 }
395
396 bool Capture::SaveFile()
397 {
398   if(mIsNativeImageSourcePossible)
399   {
400     if(mNativeImageSourcePtr)
401     {
402       return Dali::DevelNativeImageSource::EncodeToFile(*mNativeImageSourcePtr, mPath, mQuality);
403     }
404   }
405   else
406   {
407     uint8_t* buffer = mFrameBuffer.GetRenderedBuffer();
408     return Dali::EncodeToFile(buffer, mPath, Dali::Pixel::RGBA8888, mTexture.GetWidth(), mTexture.GetHeight(), mQuality);
409   }
410
411   return false;
412 }
413
414 } // End of namespace Adaptor
415
416 } // End of namespace Internal
417
418 } // End of namespace Dali