[NUI] Fix capture issue of to capture sub-scene. (#1931)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Capture.cs
1 using System.Diagnostics;
2 using System;
3 using System.Drawing;
4 /*
5  * Copyright(c) 2020 Samsung Electronics Co., Ltd.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  */
20
21 namespace Tizen.NUI
22 {
23     using global::System;
24     using global::System.ComponentModel;
25     using global::System.Runtime.InteropServices;
26     using global::System.ComponentModel;
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(Interop.Capture.Upcast(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 due to the path is null.</exception>
109         [EditorBrowsable(EditorBrowsableState.Never)]
110         public void Start(Container source, Position position, Size size, string path, Color color )
111         {
112             if (size.Width <= 0 || size.Height <=0)
113             {
114                 throw new InvalidOperationException("size should larger than zero");
115             }
116             else if (null == path)
117             {
118                 throw new ArgumentNullException("path should not be null");
119             }
120
121             if (source is View || source is Layer)
122             {
123                 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);
124
125                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
126             }
127         }
128
129         /// <summary>
130         /// Start capture and save the image as a file.
131         /// </summary>
132         /// <param name="source">source View or Layer to be used for capture.
133         /// This source must be added on the window in advance.</param>
134         /// <param name="size">captured size.</param>
135         /// <param name="path">image file path to be saved as a file.
136         /// If path is empty string, the captured result is not be saved as a file.</param>
137         /// <param name="color">background color of captured scene.</param>
138         /// <param name="quality">The value to control image quality for jpeg file format in the range [1, 100].</param>
139         /// <exception cref="InvalidOperationException">This exception can be due to the invalid size values, of when width or height is lower than zero.</exception>
140         /// <exception cref="ArgumentNullException">This exception is due to the path is null.</exception>
141         [EditorBrowsable(EditorBrowsableState.Never)]
142         public void Start(Container source, Size size, string path, Color color, uint quality )
143         {
144             if (size.Width <= 0 || size.Height <=0)
145             {
146                 throw new InvalidOperationException("size should larger than zero");
147             }
148             else if (null == path)
149             {
150                 throw new ArgumentNullException("path should not be null");
151             }
152             else if (quality > 100)
153             {
154                 throw new InvalidOperationException("quality should between 0 to 100");
155             }
156
157             if (source is View || source is Layer)
158             {
159                 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);
160
161                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
162             }
163         }
164
165         /// <summary>
166         /// Start capture and save the image as a file.
167         /// </summary>
168         /// <param name="source">source View or Layer to be used for capture.
169         /// This source must be added on the window in advance.</param>
170         /// <param name="size">captured size.</param>
171         /// <param name="path">image file path to be saved as a file.
172         /// If path is empty string, the captured result is not be saved as a file.</param>
173         /// <param name="color">background color of captured scene.</param>
174         /// <exception cref="InvalidOperationException">This exception can be due to the invalid size values, of when width or height is lower than zero.</exception>
175         /// <exception cref="ArgumentNullException">This exception is due to the path is null.</exception>
176         [EditorBrowsable(EditorBrowsableState.Never)]
177         public void Start(Container source, Size size, string path, Color color)
178         {
179             if (size.Width <= 0 || size.Height <=0)
180             {
181                 throw new InvalidOperationException("size should larger than zero");
182             }
183             else if (null == path)
184             {
185                 throw new ArgumentNullException("path should not be null");
186             }
187
188             if (source is View || source is Layer)
189             {
190                 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);
191
192                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
193             }
194         }
195
196         /// <summary>
197         /// Start capture and save the image as a file.
198         /// </summary>
199         /// <remarks>
200         /// Background color of captured scene is transparent.
201         /// </remarks>
202         /// <param name="source">source View or Layer to be used for capture.
203         /// This source must be added on the window in advance.</param>
204         /// <param name="size">captured size.</param>
205         /// <param name="path">image file path to be saved as a file.
206         /// If path is empty string, the captured result is not be saved as a file.</param>
207         /// <exception cref="InvalidOperationException">This exception can be due to the invalid size values, of when width or height is lower than zero.</exception>
208         /// <exception cref="ArgumentNullException">This exception is due to the path is null.</exception>
209         [EditorBrowsable(EditorBrowsableState.Never)]
210         public void Start(Container source, Size size, string path)
211         {
212             if (size.Width <= 0 || size.Height <=0)
213             {
214                 throw new InvalidOperationException("size should larger than zero");
215             }
216             else if (null == path)
217             {
218                 throw new ArgumentNullException("path should not be null");
219             }
220             
221             if (source is View || source is Layer)
222             {
223                 Interop.Capture.Start2(swigCPtr, source.SwigCPtr, new Vector2(size.Width, size.Height).SwigCPtr, path);
224
225                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
226             }
227         }
228
229         /// <summary>
230         /// Set result image quality in case of jpeg.
231         /// </summary>
232         /// <param name="quality">quality The value to control image quality for jpeg file format in the range [1, 100]</param>
233         /// <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>
234         [EditorBrowsable(EditorBrowsableState.Never)]
235         public void SetImageQuality(uint quality)
236         {
237             if (quality < 0 || quality > 100)
238             {
239                 throw new InvalidOperationException("quality should between zero to 100");
240             }
241             
242             Interop.Capture.SetImageQuality(swigCPtr, quality);
243         }
244
245         private void onFinished(IntPtr data, int state)
246         {
247             if (data != IntPtr.Zero)
248             {
249                 var arg = new CaptureFinishedEventArgs();
250                 // dali native definition :
251                 // enum class FinishState
252                 // {
253                 //   SUCCEEDED, ///< Succeeded in saving the result after capture
254                 //   FAILED     ///< Failed to capture by time out or to save the result
255                 // };
256                 arg.Success = (state == 0) ? true : false;
257                 finishedEventHandler?.Invoke(this, arg);
258             }
259         }
260
261         private CaptureSignal finishedSignal;
262         private delegate void finishedCallbackType(IntPtr data, int state);
263         private finishedCallbackType finishedCallback;
264         private EventHandler<CaptureFinishedEventArgs> finishedEventHandler;
265
266         /// <summary>
267         /// For subscribing Finished event sent by this class.
268         /// </summary>
269         [EditorBrowsable(EditorBrowsableState.Never)]
270         public event EventHandler<CaptureFinishedEventArgs> Finished
271         {
272             add
273             {
274                 if (finishedEventHandler == null && disposed == false)
275                 {
276                     finishedSignal = new CaptureSignal(Interop.Capture.Get(swigCPtr), false);
277                     finishedCallback = onFinished;
278                     finishedSignal.Connect(finishedCallback);
279                 }
280                 finishedEventHandler += value;
281             }
282             remove
283             {
284                 finishedEventHandler -= value;
285
286                 if (finishedEventHandler == null && finishedSignal?.Empty() == false)
287                 {
288                     finishedCallback = onFinished;
289                     finishedSignal.Disconnect(finishedCallback);
290                 }
291             }
292         }
293
294         /// <summary>
295         /// Get NativeImageSource that is saved captured image.
296         /// </summary>
297         /// <returns>NativeImageSource that is saved captured image.</returns>
298         [EditorBrowsable(EditorBrowsableState.Never)]
299         public NativeImageSource GetNativeImageSource()
300         {
301             Tizen.Log.Debug("NUI", $"GetNativeImageSource()");
302             return new NativeImageSource(Interop.Capture.GetNativeImageSourcePtr(swigCPtr), true);
303         }
304
305         /// <summary>
306         /// Generate captured image's Url
307         /// </summary>
308         /// <returns>The Url string representing this captured image source</returns>
309         [EditorBrowsable(EditorBrowsableState.Never)]
310         public string GenerateUrl()
311         {
312             string url = "";
313             url = Interop.Capture.GenerageUrl(swigCPtr);
314             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
315             return url;
316         }
317
318     }
319
320     /// <summary>
321     /// CaptureFinishedEventArgs
322     /// </summary>
323     [EditorBrowsable(EditorBrowsableState.Never)]
324     public class CaptureFinishedEventArgs : EventArgs
325     {
326         /// <summary>
327         /// Status of saving the result after capture.
328         /// </summary>
329         /// <value>
330         /// true when succeeded in saving the result after capture.
331         /// false when failed to capture by time out or to save the result.
332         /// </value>
333         public bool Success { get; internal set; }
334     }
335
336     internal class CaptureSignal : Disposable
337     {
338         internal CaptureSignal(IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
339         {
340         }
341
342         protected override void ReleaseSwigCPtr(HandleRef swigCPtr)
343         {
344             if (swigCMemOwn)
345             {
346                 Interop.Capture.DeleteSignal(swigCPtr);
347             }
348         }
349
350         public bool Empty()
351         {
352             bool ret = Interop.Capture.SignalEmpty(swigCPtr);
353             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
354             return ret;
355         }
356
357         public uint GetConnectionCount()
358         {
359             uint ret = Interop.Capture.SignalGetConnectionCount(swigCPtr);
360             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
361             return ret;
362         }
363
364         public void Connect(Delegate func)
365         {
366             IntPtr ip = Marshal.GetFunctionPointerForDelegate<Delegate>(func);
367             {
368                 Interop.Capture.SignalConnect(swigCPtr, new HandleRef(this, ip));
369                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
370             }
371         }
372
373         public void Disconnect(Delegate func)
374         {
375             IntPtr ip = Marshal.GetFunctionPointerForDelegate<Delegate>(func);
376             {
377                 Interop.Capture.SignalDisconnect(swigCPtr, new HandleRef(this, ip));
378                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
379             }
380         }
381
382         public void Emit(Capture src, bool success)
383         {
384             Interop.Capture.SignalEmit(swigCPtr, src.SwigCPtr, (success ? 0 : 1));
385             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
386         }
387     }
388 }