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