[Tizen] Import Extension::Capture to support backward compatibility
[platform/core/uifw/dali-extension.git] / dali-extension / internal / capture / capture-impl.cpp
1 /*
2  * Copyright (c) 2019 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 // EXTERNAL INCLUDES
19 #include <dali/public-api/common/vector-wrapper.h>
20
21 #include <dali/integration-api/debug.h>
22 #include <dali/integration-api/adaptors/adaptor.h>
23
24 #include <fstream>
25 #include <string.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-extension/internal/capture/capture-impl.h>
29
30 #define ENABLED_CAPTURE_LOGGING
31
32 #ifdef ENABLED_CAPTURE_LOGGING
33 #define DALI_CAPTURE_STATE(format, args...) Dali::Integration::Log::LogMessage(Dali::Integration::Log::DebugInfo, "%s:%d " format "\n", __PRETTY_FUNCTION__, __LINE__, ## args)
34 #else
35 #define DALI_CAPTURE_STATE(format, args...)
36 #endif
37
38 namespace
39 {
40 unsigned int TIME_OUT_DURATION = 1000;
41 }
42
43 namespace Dali
44 {
45
46 namespace Extension
47 {
48
49 namespace Internal
50 {
51
52 Capture::Capture()
53 : mProjectionMode(Dali::Camera::PERSPECTIVE_PROJECTION)
54 , mTbmSurface(NULL)
55 , mFinishState(Dali::Extension::Capture::FAILED)
56 {
57 }
58
59 Capture::Capture(Dali::Camera::ProjectionMode mode)
60 : mProjectionMode(mode)
61 , mTbmSurface(NULL)
62 , mFinishState(Dali::Extension::Capture::FAILED)
63 {
64 }
65
66 CapturePtr Capture::New()
67 {
68   CapturePtr pWorker = new Capture();
69
70   // Second-phase construction
71   pWorker->Initialize();
72
73   return pWorker;
74 }
75
76 CapturePtr Capture::New(Dali::Camera::ProjectionMode mode)
77 {
78   CapturePtr pWorker = new Capture(mode);
79
80   // Second-phase construction
81   pWorker->Initialize();
82
83   return pWorker;
84 }
85
86 void Capture::Start(Actor source, const Vector2& size, const std::string &path, const Vector4& clearColor)
87 {
88   DALI_ASSERT_ALWAYS(path.size() > 4 && "Path is invalid.");
89
90   // Increase the reference count focely to avoid application mistake.
91   Reference();
92
93   mPath = path;
94
95   DALI_CAPTURE_STATE("Start Size[%.2f, %.2f] Path[%s]", size.width, size.height, path.c_str());
96
97   DALI_ASSERT_ALWAYS(source && "Source is NULL.");
98
99   UnsetResources();
100   SetupResources(size, clearColor, source);
101 }
102
103 Dali::Extension::Capture::FinishState Capture::GetFinishState()
104 {
105   return mFinishState;
106 }
107
108 Dali::Extension::Capture::CaptureSignalType& Capture::FinishedSignal()
109 {
110   return mFinishedSignal;
111 }
112
113 void Capture::Initialize()
114 {
115 }
116
117 Capture::~Capture()
118 {
119 }
120
121 void Capture::CreateSurface(const Vector2& size)
122 {
123   DALI_ASSERT_ALWAYS(!mTbmSurface && "mTbmSurface is already created.");
124
125   mTbmSurface = tbm_surface_create(size.width, size.height, TBM_FORMAT_RGBA8888);
126   DALI_CAPTURE_STATE("Create mTbmSurface[%p]", mTbmSurface);
127 }
128
129 void Capture::DeleteSurface()
130 {
131   DALI_ASSERT_ALWAYS(mTbmSurface && "mTbmSurface is empty.");
132
133   DALI_CAPTURE_STATE("Delete mTbmSurface[%p]", mTbmSurface);
134
135   tbm_surface_destroy(mTbmSurface);
136   mTbmSurface = NULL;
137 }
138
139 void Capture::ClearSurface(const Vector2& size)
140 {
141   DALI_ASSERT_ALWAYS(mTbmSurface && "mTbmSurface is empty.");
142
143   tbm_surface_info_s surface_info;
144
145   if( tbm_surface_map( mTbmSurface, TBM_SURF_OPTION_WRITE, &surface_info) == TBM_SURFACE_ERROR_NONE )
146   {
147     //DALI_ASSERT_ALWAYS(surface_info.bpp == 32 && "unsupported tbm format");
148
149     unsigned char* ptr = surface_info.planes[0].ptr;
150     memset( ptr, 0, surface_info.size ); // TODO: support color
151
152     if( tbm_surface_unmap( mTbmSurface ) != TBM_SURFACE_ERROR_NONE )
153     {
154       DALI_CAPTURE_STATE( "Fail to unmap tbm_surface\n" );
155     }
156   }
157   else
158   {
159      DALI_ASSERT_ALWAYS(0 && "tbm_surface_map failed");
160   }
161
162   DALI_CAPTURE_STATE("Clear mTbmSurface[%p]", mTbmSurface);
163 }
164
165 bool Capture::IsSurfaceCreated()
166 {
167   return mTbmSurface != 0;
168 }
169
170 void Capture::CreateNativeImageSource()
171 {
172   Dali::Adaptor& adaptor = Dali::Adaptor::Get();
173
174   DALI_ASSERT_ALWAYS(adaptor.IsAvailable() && "Dali::Adaptor is not available.");
175
176   DALI_ASSERT_ALWAYS(mTbmSurface && "mTbmSurface is empty.");
177
178   DALI_ASSERT_ALWAYS(!mNativeImageSourcePtr && "NativeImageSource is already created.");
179
180   // create the NativeImageSource object with our surface
181   mNativeImageSourcePtr = NativeImageSource::New(Dali::Any(mTbmSurface));
182
183   DALI_CAPTURE_STATE("Create NativeImageSource[0x%X]", mNativeImageSourcePtr.Get());
184 }
185
186 void Capture::DeleteNativeImageSource()
187 {
188   DALI_ASSERT_ALWAYS(mNativeImageSourcePtr && "mNativeImageSource is NULL.");
189
190   DALI_CAPTURE_STATE("Delete NativeImageSource[0x%X]", mNativeImageSourcePtr.Get());
191
192   mNativeImageSourcePtr.Reset();
193 }
194
195 bool Capture::IsNativeImageSourceCreated()
196 {
197   return mNativeImageSourcePtr;
198 }
199
200 void Capture::CreateFrameBuffer()
201 {
202   DALI_ASSERT_ALWAYS(mNativeImageSourcePtr && "NativeImageSource is NULL.");
203
204   DALI_ASSERT_ALWAYS(!mFrameBuffer && "FrameBuffer is already created.");
205
206   mNativeTexture = Texture::New( *mNativeImageSourcePtr );
207
208   // Create a FrameBuffer object with no default attachments.
209   mFrameBuffer = FrameBuffer::New( mNativeTexture.GetWidth(), mNativeTexture.GetHeight(), FrameBuffer::Attachment::NONE );
210   // Add a color attachment to the FrameBuffer object.
211   mFrameBuffer.AttachColorTexture( mNativeTexture );
212
213   DALI_CAPTURE_STATE("Create FrameBuffer");
214 }
215
216 void Capture::DeleteFrameBuffer()
217 {
218   DALI_ASSERT_ALWAYS(mFrameBuffer && "FrameBuffer is NULL.");
219
220   DALI_CAPTURE_STATE("Delete FrameBuffer");
221
222   mFrameBuffer.Reset();
223   mNativeTexture.Reset();
224 }
225
226 bool Capture::IsFrameBufferCreated()
227 {
228   return mFrameBuffer;
229 }
230
231 void Capture::SetupRenderTask(Actor source, const Vector4& clearColor)
232 {
233   DALI_ASSERT_ALWAYS(source && "Source is empty.");
234
235   mSource = source;
236
237   // Check the original parent about source.
238   mParent = mSource.GetParent();
239
240   Stage stage = Stage::GetCurrent();
241   Size stageSize = stage.GetSize();
242
243   // Add to stage for rendering the source. If source isn't on the stage then it never be rendered.
244   stage.Add(mSource);
245
246   DALI_ASSERT_ALWAYS(!mCameraActor && "CameraActor is already created.");
247
248   mCameraActor = CameraActor::New( stageSize );
249   mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
250   mCameraActor.SetAnchorPoint(AnchorPoint::CENTER);
251
252   if(mProjectionMode == Camera::ORTHOGRAPHIC_PROJECTION)
253   {
254         mCameraActor.SetOrthographicProjection(stageSize);
255   }
256   stage.Add(mCameraActor);
257
258   DALI_ASSERT_ALWAYS(mFrameBuffer && "Framebuffer is NULL.");
259
260   DALI_ASSERT_ALWAYS(!mRenderTask && "RenderTask is already created.");
261
262   RenderTaskList taskList = stage.GetRenderTaskList();
263   mRenderTask = taskList.CreateTask();
264   mRenderTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
265   mRenderTask.SetSourceActor(source);
266   mRenderTask.SetCameraActor(mCameraActor);
267   mRenderTask.SetScreenToFrameBufferFunction(RenderTask::FULLSCREEN_FRAMEBUFFER_FUNCTION);
268   mRenderTask.SetFrameBuffer(mFrameBuffer);
269   mRenderTask.SetClearColor( clearColor );
270   mRenderTask.SetClearEnabled( true );
271   mRenderTask.SetProperty( RenderTask::Property::REQUIRES_SYNC, true );
272   mRenderTask.FinishedSignal().Connect(this, &Capture::OnRenderFinished);
273   mRenderTask.GetCameraActor().SetInvertYAxis( true );
274
275   mTimer = Timer::New(TIME_OUT_DURATION);
276   mTimer.TickSignal().Connect(this, &Capture::OnTimeOut);
277   mTimer.Start();
278
279   DALI_CAPTURE_STATE("Setup Camera and RenderTask.");
280 }
281
282 void Capture::UnsetRenderTask()
283 {
284   DALI_ASSERT_ALWAYS(mCameraActor && "CameraActor is NULL.");
285
286   DALI_CAPTURE_STATE("Unset Camera and RenderTask");
287
288   if (mParent)
289   {
290     // Restore the parent of source.
291     mParent.Add(mSource);
292     mParent.Reset();
293   }
294   else
295   {
296     mSource.Unparent();
297   }
298
299   mSource.Reset();
300
301   mTimer.Reset();
302
303   mCameraActor.Unparent();
304   mCameraActor.Reset();
305
306   DALI_ASSERT_ALWAYS(mRenderTask && "RenderTask is NULL.");
307
308   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
309   RenderTask firstTask = taskList.GetTask( 0u );
310
311   // Stop rendering via frame-buffers as empty handle is used to clear target
312   firstTask.SetFrameBuffer(FrameBuffer());
313
314   taskList.RemoveTask(mRenderTask);
315   mRenderTask.Reset();
316 }
317
318 bool Capture::IsRenderTaskSetup()
319 {
320   return mCameraActor && mRenderTask;
321 }
322
323 void Capture::SetupResources(const Vector2& size, const Vector4& clearColor, Actor source)
324 {
325   CreateSurface(size);
326   ClearSurface(size);
327
328   CreateNativeImageSource();
329
330   CreateFrameBuffer();
331
332   SetupRenderTask(source, clearColor);
333
334   DALI_CAPTURE_STATE("Setup Resources");
335 }
336
337 void Capture::UnsetResources()
338 {
339   if (IsRenderTaskSetup())
340   {
341     UnsetRenderTask();
342   }
343
344   if (IsFrameBufferCreated())
345   {
346     DeleteFrameBuffer();
347   }
348
349   if (IsNativeImageSourceCreated())
350   {
351     DeleteNativeImageSource();
352   }
353
354   if (IsSurfaceCreated())
355   {
356     DeleteSurface();
357   }
358
359   DALI_CAPTURE_STATE("Unset Resources");
360 }
361
362 void Capture::OnRenderFinished(RenderTask& task)
363 {
364   DALI_CAPTURE_STATE("Render finished");
365
366   mFinishState = Dali::Extension::Capture::SUCCESSED;
367
368   mTimer.Stop();
369
370   if (!Save())
371   {
372     mFinishState = Dali::Extension::Capture::FAILED;
373     DALI_LOG_ERROR("Fail to Capture mTbmSurface[%p] Path[%s]", mTbmSurface, mPath.c_str());
374   }
375
376   Dali::Extension::Capture handle(this);
377   mFinishedSignal.Emit(handle);
378
379   UnsetResources();
380
381   // Decrease the reference count forcely. It is increased at Start().
382   Unreference();
383 }
384
385 bool Capture::OnTimeOut()
386 {
387   DALI_CAPTURE_STATE("Timeout");
388
389   mFinishState = Dali::Extension::Capture::FAILED;
390
391   Dali::Extension::Capture handle(this);
392   mFinishedSignal.Emit(handle);
393
394   UnsetResources();
395
396   // Decrease the reference count forcely. It is increased at Start().
397   Unreference();
398
399   return false;
400 }
401
402 bool Capture::Save()
403 {
404   DALI_ASSERT_ALWAYS(mNativeImageSourcePtr && "mNativeImageSourcePtr is NULL");
405
406   return mNativeImageSourcePtr->EncodeToFile(mPath);
407 }
408
409 }  // End of namespace Internal
410
411 }  // End of namespace Extension
412
413 }  // End of namespace Dali