[Tizen] Return captured results with PixelBuffer
[platform/core/uifw/dali-adaptor.git] / dali / internal / system / common / capture-impl.cpp
1 /*
2  * Copyright (c) 2020 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 <fstream>
23 #include <string.h>
24 #include <dali/public-api/common/vector-wrapper.h>
25 #include <dali/public-api/render-tasks/render-task-list.h>
26 #include <dali/integration-api/debug.h>
27
28 // INTERNAL INCLUDES
29 #include <dali/integration-api/adaptor-framework/adaptor.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/devel-api/adaptor-framework/bitmap-saver.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
39 namespace Internal
40 {
41
42 namespace Adaptor
43 {
44
45 namespace
46 {
47 constexpr int32_t VERSION_NATIVE_IMAGE_SOURCE = 30;
48 constexpr uint32_t TIME_OUT_DURATION = 1000;
49 }
50
51 Capture::Capture()
52 : mQuality( DEFAULT_QUALITY ),
53   mTimer(),
54   mPath(),
55   mNativeImageSourcePtr( NULL ),
56   mFileSave( false ),
57   mIsNativeImageSourcePossible(true)
58 {
59 }
60
61 Capture::Capture( Dali::CameraActor cameraActor )
62 : mQuality( DEFAULT_QUALITY ),
63   mCameraActor( cameraActor ),
64   mTimer(),
65   mPath(),
66   mNativeImageSourcePtr( NULL ),
67   mFileSave( false ),
68   mIsNativeImageSourcePossible(true)
69 {
70 }
71
72 Capture::~Capture()
73 {
74   DeleteNativeImageSource();
75   mTexture.Reset();
76 }
77
78 CapturePtr Capture::New()
79 {
80   CapturePtr pWorker = new Capture();
81
82   return pWorker;
83 }
84
85 CapturePtr Capture::New( Dali::CameraActor cameraActor )
86 {
87   CapturePtr pWorker = new Capture( cameraActor );
88
89   return pWorker;
90 }
91
92 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 )
93 {
94   mQuality = quality;
95   Start( source, position, size, path, clearColor );
96 }
97
98 void Capture::Start( Dali::Actor source, const Dali::Vector2& position, const Dali::Vector2& size, const std::string &path, const Dali::Vector4& clearColor )
99 {
100   DALI_ASSERT_ALWAYS(path.size() > 4 && "Path is invalid.");
101
102   // Increase the reference count focely to avoid application mistake.
103   Reference();
104
105   mPath = path;
106   if( mPath.size() > 0 )
107   {
108     mFileSave = true;
109   }
110
111   DALI_ASSERT_ALWAYS(source && "Source is NULL.");
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::Texture Capture::GetTexture()
128 {
129   return mTexture;
130 }
131
132 Dali::Devel::PixelBuffer Capture::GetCapturedBuffer()
133 {
134   if(!mPixelBuffer || !mPixelBuffer.GetBuffer())
135   {
136     uint8_t* bufferPointer;
137     uint32_t             width, height;
138     Dali::Pixel::Format  pixelFormat;
139     if(mIsNativeImageSourcePossible)
140     {
141       std::vector<uint8_t> buffer;
142       if(!mNativeImageSourcePtr->GetPixels(buffer, width, height, pixelFormat))
143       {
144         return Dali::Devel::PixelBuffer();
145       }
146       bufferPointer = &buffer[0];
147     }
148     else
149     {
150       width = mTexture.GetWidth();
151       height = mTexture.GetHeight();
152       pixelFormat = Dali::Pixel::RGBA8888;
153       bufferPointer = mFrameBuffer.GetRenderedBuffer();
154     }
155     mPixelBuffer = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
156
157     memcpy(mPixelBuffer.GetBuffer(), bufferPointer, width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat));
158   }
159
160   return mPixelBuffer;
161 }
162
163 Dali::Capture::CaptureFinishedSignalType& Capture::FinishedSignal()
164 {
165   return mFinishedSignal;
166 }
167
168 void Capture::CreateTexture(const Vector2& size)
169 {
170   Dali::Adaptor& adaptor = Dali::Adaptor::Get();
171
172   DALI_ASSERT_ALWAYS(adaptor.IsAvailable() && "Dali::Adaptor is not available.");
173
174   if(mIsNativeImageSourcePossible)
175   {
176     DALI_ASSERT_ALWAYS(!mNativeImageSourcePtr && "NativeImageSource is already created.");
177     // create the NativeImageSource object with our surface
178     mNativeImageSourcePtr = Dali::NativeImageSource::New(size.width, size.height, Dali::NativeImageSource::COLOR_DEPTH_DEFAULT);
179     mTexture              = Dali::Texture::New(*mNativeImageSourcePtr);
180   }
181   else
182   {
183     mTexture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGB888, 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   DALI_ASSERT_ALWAYS(!mFrameBuffer && "FrameBuffer is already created.");
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 void Capture::DeleteFrameBuffer()
206 {
207   DALI_ASSERT_ALWAYS(mFrameBuffer && "FrameBuffer is NULL.");
208
209   mFrameBuffer.Reset();
210 }
211
212 bool Capture::IsFrameBufferCreated()
213 {
214   return mFrameBuffer;
215 }
216
217 void Capture::SetupRenderTask( const Dali::Vector2& position, const Dali::Vector2& size, Dali::Actor source, const Dali::Vector4& clearColor )
218 {
219   DALI_ASSERT_ALWAYS(source && "Source is empty.");
220
221   Dali::Window window = DevelWindow::Get( source );
222   if( !window )
223   {
224     DALI_LOG_ERROR("The source is not added on the window\n");
225     return;
226   }
227
228   mSource = source;
229
230   if( !mCameraActor )
231   {
232     mCameraActor = Dali::CameraActor::New( size );
233     // Because input position and size are for 2 dimentional area,
234     // default z-directional position of the camera is required to be used for the new camera position.
235     float cameraDefaultZPosition = mCameraActor.GetProperty<float>( Dali::Actor::Property::POSITION_Z );
236     Vector2 positionTransition = position + size / 2;
237     mCameraActor.SetProperty( Dali::Actor::Property::POSITION, Vector3( positionTransition.x, positionTransition.y, cameraDefaultZPosition ) );
238     mCameraActor.SetProperty( Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT );
239     mCameraActor.SetProperty( Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
240   }
241
242   window.Add( mCameraActor );
243
244   DALI_ASSERT_ALWAYS(mFrameBuffer && "Framebuffer is NULL.");
245
246   DALI_ASSERT_ALWAYS(!mRenderTask && "RenderTask is already created.");
247
248   Dali::RenderTaskList taskList = window.GetRenderTaskList();
249   mRenderTask = taskList.CreateTask();
250   mRenderTask.SetRefreshRate( Dali::RenderTask::REFRESH_ONCE );
251   mRenderTask.SetSourceActor( source );
252   mRenderTask.SetCameraActor( mCameraActor );
253   mRenderTask.SetScreenToFrameBufferFunction( Dali::RenderTask::FULLSCREEN_FRAMEBUFFER_FUNCTION );
254   mRenderTask.SetFrameBuffer( mFrameBuffer );
255   mRenderTask.SetClearColor( clearColor );
256   mRenderTask.SetClearEnabled( true );
257   mRenderTask.SetProperty( Dali::RenderTask::Property::REQUIRES_SYNC, true );
258   mRenderTask.FinishedSignal().Connect( this, &Capture::OnRenderFinished );
259   mRenderTask.GetCameraActor().SetInvertYAxis( true );
260   if(!mIsNativeImageSourcePossible)
261   {
262     mFrameBuffer.CaptureRenderedResult();
263   }
264
265   mTimer = Dali::Timer::New( TIME_OUT_DURATION );
266   mTimer.TickSignal().Connect( this, &Capture::OnTimeOut );
267   mTimer.Start();
268 }
269
270 void Capture::UnsetRenderTask()
271 {
272   DALI_ASSERT_ALWAYS(mCameraActor && "CameraActor is NULL.");
273
274   mTimer.Reset();
275
276   mCameraActor.Unparent();
277   mCameraActor.Reset();
278
279   DALI_ASSERT_ALWAYS( mRenderTask && "RenderTask is NULL." );
280
281   Dali::Window window = DevelWindow::Get( mSource );
282   Dali::RenderTaskList taskList = window.GetRenderTaskList();
283   taskList.RemoveTask( mRenderTask );
284   mRenderTask.Reset();
285   mSource.Reset();
286 }
287
288 bool Capture::IsRenderTaskSetup()
289 {
290   return mCameraActor && mRenderTask;
291 }
292
293 void Capture::SetupResources(const Dali::Vector2& position, const Dali::Vector2& size, const Dali::Vector4& clearColor, Dali::Actor source)
294 {
295   Dali::Internal::Adaptor::Adaptor& adaptor     = Internal::Adaptor::Adaptor::GetImplementation(Internal::Adaptor::Adaptor::Get());
296   GraphicsInterface*                graphics    = &adaptor.GetGraphicsInterface();
297   auto                              eglGraphics = static_cast<EglGraphics*>(graphics);
298
299   if(eglGraphics->GetEglImplementation().GetGlesVersion() < VERSION_NATIVE_IMAGE_SOURCE)
300   {
301     mIsNativeImageSourcePossible = false;
302   }
303
304   CreateTexture(size);
305
306   CreateFrameBuffer();
307
308   SetupRenderTask( position, size, source, clearColor );
309 }
310
311 void Capture::UnsetResources()
312 {
313   if( IsRenderTaskSetup() )
314   {
315     UnsetRenderTask();
316   }
317
318   if( IsFrameBufferCreated() )
319   {
320     DeleteFrameBuffer();
321   }
322 }
323
324 void Capture::OnRenderFinished( Dali::RenderTask& task )
325 {
326   Dali::Capture::FinishState state = Dali::Capture::FinishState::SUCCEEDED;
327
328   mTimer.Stop();
329
330   if( mFileSave )
331   {
332     if( !SaveFile() )
333     {
334       state = Dali::Capture::FinishState::FAILED;
335       DALI_LOG_ERROR( "Fail to Capture Path[%s]", mPath.c_str() );
336     }
337   }
338
339   Dali::Capture handle( this );
340   mFinishedSignal.Emit( handle, state );
341
342   UnsetResources();
343
344   // Decrease the reference count forcely. It is increased at Start().
345   Unreference();
346 }
347
348 bool Capture::OnTimeOut()
349 {
350   Dali::Capture::FinishState state = Dali::Capture::FinishState::FAILED;
351
352   Dali::Capture handle( this );
353   mFinishedSignal.Emit( handle, state );
354
355   UnsetResources();
356
357   // Decrease the reference count forcely. It is increased at Start().
358   Unreference();
359
360   return false;
361 }
362
363 bool Capture::SaveFile()
364 {
365   if(mIsNativeImageSourcePossible)
366   {
367     DALI_ASSERT_ALWAYS(mNativeImageSourcePtr && "mNativeImageSourcePtr is NULL");
368     return Dali::DevelNativeImageSource::EncodeToFile(*mNativeImageSourcePtr, mPath, mQuality);
369   }
370   else
371   {
372     uint8_t* buffer = mFrameBuffer.GetRenderedBuffer();
373     return Dali::EncodeToFile(buffer, mPath, Dali::Pixel::RGBA8888, mTexture.GetWidth(), mTexture.GetHeight(), mQuality);
374   }
375 }
376
377 }  // End of namespace Adaptor
378
379 }  // End of namespace Internal
380
381 }  // End of namespace Dali