[NUI] Make ReleaseSwigCPtr didn't check HasBody()
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Utility / Capture.cs
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 using System.Diagnostics;
19 using System;
20 using System.Drawing;
21
22 namespace Tizen.NUI
23 {
24     using global::System;
25     using global::System.ComponentModel;
26     using global::System.Runtime.InteropServices;
27     using Tizen.NUI.BaseComponents;
28
29     /// <summary>
30     /// Capture snapshots the current scene and save as a file.
31     /// Applications should follow the example below to create capture :
32     /// <code>
33     /// Capture capture = new Capture();
34     /// </code>
35     /// If required, you can also subscribe Finished event :
36     /// <code>
37     /// capture.Finished += onCaptureFinished;
38     /// </code>
39     /// At the subcribed event handler, user can know whether capture finish succeeded state.
40     /// <code>
41     /// private void onCaptureFinished(object sender, CaptureFinishedEventArgs e)
42     /// {
43     ///   if(e.Success) { //capture success, do something. }
44     ///   else { //capture failure, do something. }
45     /// }
46     /// </code>
47     /// suppose that we want to capture View 'A'. And, the View 'A' is overlapped by another View 'B' that is not a child of 'A'.
48     /// in this case, if source is root of scene, the captured image includes a part of View 'B' on the 'A'.
49     /// however, if source is just View 'A', the result includes only 'A'.
50     /// </summary>
51     [EditorBrowsable(EditorBrowsableState.Never)]
52     public class Capture : BaseHandle
53     {
54         /// <summary>
55         /// Create an Capture.
56         /// </summary>
57         [EditorBrowsable(EditorBrowsableState.Never)]
58         public Capture() : this(Interop.Capture.New(), true)
59         {
60             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
61         }
62
63         internal Capture(IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
64         {
65         }
66
67         /// <summary>
68         /// Dispose
69         /// </summary>
70         /// <param name="type"></param>
71         [EditorBrowsable(EditorBrowsableState.Never)]
72         protected override void Dispose(DisposeTypes type)
73         {
74             if (disposed)
75             {
76                 return;
77             }
78
79             if (type == DisposeTypes.Explicit)
80             {
81                 //Called by User
82                 //Release your own managed resources here.
83                 //You should release all of your own disposable objects here.
84             }
85
86             base.Dispose(type);
87         }
88
89         /// This will not be public opened.
90         [EditorBrowsable(EditorBrowsableState.Never)]
91         protected override void ReleaseSwigCPtr(HandleRef swigCPtr)
92         {
93             Interop.Capture.Delete(swigCPtr);
94         }
95
96         /// <summary>
97         /// Start capture and save the image as a file.
98         /// </summary>
99         /// <param name="source">source View or Layer to be used for capture.
100         /// This source must be added on the window in advance.</param>
101         /// <param name="position">top-left position of area to be captured.
102         /// this position is defined in the window.</param>
103         /// <param name="size">captured size.</param>
104         /// <param name="path">image file path to be saved as a file.
105         /// If path is empty string, the captured result is not be saved as a file.</param>
106         /// <param name="color">background color of captured scene.</param>
107         /// <exception cref="InvalidOperationException">This exception can be due to the invalid size values, of when width or height is lower than zero.</exception>
108         /// <exception cref="ArgumentNullException">This exception is thrown when size or path or position or color is null.</exception>
109         [EditorBrowsable(EditorBrowsableState.Never)]
110         public void Start(Container source, Position position, Size size, string path, Color color)
111         {
112             if (null == size)
113             {
114                 throw new ArgumentNullException(nameof(size));
115             }
116             else if (size.Width <= 0 || size.Height <= 0)
117             {
118                 throw new InvalidOperationException("size should larger than zero");
119             }
120             else if (null == path)
121             {
122                 throw new ArgumentNullException("path should not be null");
123             }
124             else if (null == position)
125             {
126                 throw new ArgumentNullException(nameof(position));
127             }
128             else if (null == color)
129             {
130                 throw new ArgumentNullException(nameof(color));
131             }
132
133             if (source is View || source is Layer)
134             {
135                 Interop.Capture.Start4(SwigCPtr, source.SwigCPtr, new Vector2(position.X, position.Y).SwigCPtr, new Vector2(size.Width, size.Height).SwigCPtr, path, new Vector4(color.R, color.G, color.B, color.A).SwigCPtr);
136
137                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
138             }
139         }
140
141         /// <summary>
142         /// Start capture and save the image as a file.
143         /// </summary>
144         /// <param name="source">source View or Layer to be used for capture.
145         /// This source must be added on the window in advance.</param>
146         /// <param name="size">captured size.</param>
147         /// <param name="path">image file path to be saved as a file.
148         /// If path is empty string, the captured result is not be saved as a file.</param>
149         /// <param name="color">background color of captured scene.</param>
150         /// <param name="quality">The value to control image quality for jpeg file format in the range [1, 100].</param>
151         /// <exception cref="InvalidOperationException">This exception can be due to the invalid size values, of when width or height is lower than zero.</exception>
152         /// <exception cref="ArgumentNullException">This exception is thrown when size or path or color is null.</exception>
153         [EditorBrowsable(EditorBrowsableState.Never)]
154         public void Start(Container source, Size size, string path, Color color, uint quality)
155         {
156             if (null == size)
157             {
158                 throw new ArgumentNullException(nameof(size));
159             }
160             else if (size.Width <= 0 || size.Height <= 0)
161             {
162                 throw new InvalidOperationException("size should larger than zero");
163             }
164             else if (null == path)
165             {
166                 throw new ArgumentNullException("path should not be null");
167             }
168             else if (quality > 100)
169             {
170                 throw new InvalidOperationException("quality should between 0 to 100");
171             }
172             else if (null == color)
173             {
174                 throw new ArgumentNullException(nameof(color));
175             }
176
177             if (source is View || source is Layer)
178             {
179                 Interop.Capture.Start3(SwigCPtr, source.SwigCPtr, new Vector2(size.Width, size.Height).SwigCPtr, path, new Vector4(color.R, color.G, color.B, color.A).SwigCPtr, quality);
180
181                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
182             }
183         }
184
185         /// <summary>
186         /// Start capture and save the image as a file.
187         /// </summary>
188         /// <param name="source">source View or Layer to be used for capture.
189         /// This source must be added on the window in advance.</param>
190         /// <param name="size">captured size.</param>
191         /// <param name="path">image file path to be saved as a file.
192         /// If path is empty string, the captured result is not be saved as a file.</param>
193         /// <param name="color">background color of captured scene.</param>
194         /// <exception cref="InvalidOperationException">This exception can be due to the invalid size values, of when width or height is lower than zero.</exception>
195         /// <exception cref="ArgumentNullException">This exception is thrown when size or path or color is null.</exception>
196         [EditorBrowsable(EditorBrowsableState.Never)]
197         public void Start(Container source, Size size, string path, Color color)
198         {
199             if (null == size)
200             {
201                 throw new ArgumentNullException(nameof(size));
202             }
203             else if (size.Width <= 0 || size.Height <= 0)
204             {
205                 throw new InvalidOperationException("size should larger than zero");
206             }
207             else if (null == path)
208             {
209                 throw new ArgumentNullException("path should not be null");
210             }
211             else if (null == color)
212             {
213                 throw new ArgumentNullException(nameof(color));
214             }
215
216             if (source is View || source is Layer)
217             {
218                 Interop.Capture.Start1(SwigCPtr, source.SwigCPtr, new Vector2(size.Width, size.Height).SwigCPtr, path, new Vector4(color.R, color.G, color.B, color.A).SwigCPtr);
219
220                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
221             }
222         }
223
224         /// <summary>
225         /// Start capture and save the image as a file.
226         /// </summary>
227         /// <remarks>
228         /// Background color of captured scene is transparent.
229         /// </remarks>
230         /// <param name="source">source View or Layer to be used for capture.
231         /// This source must be added on the window in advance.</param>
232         /// <param name="size">captured size.</param>
233         /// <param name="path">image file path to be saved as a file.
234         /// If path is empty string, the captured result is not be saved as a file.</param>
235         /// <exception cref="InvalidOperationException">This exception can be due to the invalid size values, of when width or height is lower than zero.</exception>
236         /// <exception cref="ArgumentNullException">This exception is thrown when size or path is null.</exception>
237         [EditorBrowsable(EditorBrowsableState.Never)]
238         public void Start(Container source, Size size, string path)
239         {
240             if (null == size)
241             {
242                 throw new ArgumentNullException(nameof(size));
243             }
244             else if (size.Width <= 0 || size.Height <= 0)
245             {
246                 throw new InvalidOperationException("size should larger than zero");
247             }
248             else if (null == path)
249             {
250                 throw new ArgumentNullException("path should not be null");
251             }
252
253             if (source is View || source is Layer)
254             {
255                 Interop.Capture.Start2(SwigCPtr, source.SwigCPtr, new Vector2(size.Width, size.Height).SwigCPtr, path);
256
257                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
258             }
259         }
260
261         /// <summary>
262         /// Set result image quality in case of jpeg.
263         /// </summary>
264         /// <param name="quality">quality The value to control image quality for jpeg file format in the range [1, 100]</param>
265         /// <exception cref="InvalidOperationException">This exception can be due to the invalid size values, of when quality is lower than zero or over than 100.</exception>
266         [EditorBrowsable(EditorBrowsableState.Never)]
267         public void SetImageQuality(uint quality)
268         {
269             if (quality < 0 || quality > 100)
270             {
271                 throw new InvalidOperationException("quality should between zero to 100");
272             }
273
274             Interop.Capture.SetImageQuality(SwigCPtr, quality);
275         }
276
277         private void onFinished(IntPtr data, int state)
278         {
279             if (data != IntPtr.Zero)
280             {
281                 var arg = new CaptureFinishedEventArgs();
282                 // dali native definition :
283                 // enum class FinishState
284                 // {
285                 //   SUCCEEDED, ///< Succeeded in saving the result after capture
286                 //   FAILED     ///< Failed to capture by time out or to save the result
287                 // };
288                 arg.Success = (state == 0) ? true : false;
289                 finishedEventHandler?.Invoke(this, arg);
290             }
291         }
292
293         private CaptureSignal finishedSignal;
294         private delegate void finishedCallbackType(IntPtr data, int state);
295         private finishedCallbackType finishedCallback;
296         private EventHandler<CaptureFinishedEventArgs> finishedEventHandler;
297
298         /// <summary>
299         /// For subscribing Finished event sent by this class.
300         /// </summary>
301         [EditorBrowsable(EditorBrowsableState.Never)]
302         public event EventHandler<CaptureFinishedEventArgs> Finished
303         {
304             add
305             {
306                 if (finishedEventHandler == null && disposed == false)
307                 {
308                     finishedSignal = new CaptureSignal(Interop.Capture.Get(SwigCPtr));
309                     finishedCallback = onFinished;
310                     finishedSignal.Connect(finishedCallback);
311                 }
312                 finishedEventHandler += value;
313             }
314             remove
315             {
316                 finishedEventHandler -= value;
317
318                 if (finishedEventHandler == null && finishedSignal?.Empty() == false)
319                 {
320                     finishedCallback = onFinished;
321                     finishedSignal.Disconnect(finishedCallback);
322                 }
323             }
324         }
325
326         /// <summary>
327         /// Get NativeImageSource that is saved captured image.
328         /// </summary>
329         /// <returns>NativeImageSource that is saved captured image.</returns>
330         [EditorBrowsable(EditorBrowsableState.Never)]
331         public NativeImageSource GetNativeImageSource()
332         {
333             Tizen.Log.Debug("NUI", $"GetNativeImageSource()");
334             return new NativeImageSource(Interop.Capture.GetNativeImageSourcePtr(SwigCPtr), true);
335         }
336
337         /// <summary>
338         /// Get Captured buffer.
339         /// </summary>
340         /// <returns>PixelBuffer of captured buffer</returns>
341         [EditorBrowsable(EditorBrowsableState.Never)]
342         public PixelBuffer GetCapturedBuffer()
343         {
344             return new PixelBuffer(Interop.Capture.GetCapturedBuffer(SwigCPtr), true);
345         }
346     }
347
348     /// <summary>
349     /// CaptureFinishedEventArgs
350     /// </summary>
351     [EditorBrowsable(EditorBrowsableState.Never)]
352     public class CaptureFinishedEventArgs : EventArgs
353     {
354         /// <summary>
355         /// Status of saving the result after capture.
356         /// </summary>
357         /// <value>
358         /// true when succeeded in saving the result after capture.
359         /// false when failed to capture by time out or to save the result.
360         /// </value>
361         public bool Success { get; internal set; }
362     }
363
364     internal class CaptureSignal : Disposable
365     {
366         internal CaptureSignal(IntPtr cPtr) : base(cPtr, false)
367         {
368         }
369
370         protected override void ReleaseSwigCPtr(HandleRef swigCPtr)
371         {
372             // Do nothing because native didn't create new signal handle.
373         }
374
375         public bool Empty()
376         {
377             bool ret = Interop.Capture.SignalEmpty(SwigCPtr);
378             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
379             return ret;
380         }
381
382         public uint GetConnectionCount()
383         {
384             uint ret = Interop.Capture.SignalGetConnectionCount(SwigCPtr);
385             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
386             return ret;
387         }
388
389         public void Connect(Delegate func)
390         {
391             IntPtr ip = Marshal.GetFunctionPointerForDelegate<Delegate>(func);
392             {
393                 Interop.Capture.SignalConnect(SwigCPtr, new HandleRef(this, ip));
394                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
395             }
396         }
397
398         public void Disconnect(Delegate func)
399         {
400             IntPtr ip = Marshal.GetFunctionPointerForDelegate<Delegate>(func);
401             {
402                 Interop.Capture.SignalDisconnect(SwigCPtr, new HandleRef(this, ip));
403                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
404             }
405         }
406
407         public void Emit(Capture src, bool success)
408         {
409             Interop.Capture.SignalEmit(SwigCPtr, src.SwigCPtr, (success ? 0 : 1));
410             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
411         }
412     }
413 }