[NUI] Capture binding (#1752)
authordongsug-song <35130733+dongsug-song@users.noreply.github.com>
Wed, 15 Jul 2020 05:15:43 +0000 (14:15 +0900)
committerGitHub <noreply@github.com>
Wed, 15 Jul 2020 05:15:43 +0000 (14:15 +0900)
src/Tizen.NUI/src/internal/Disposable.cs
src/Tizen.NUI/src/internal/Interop/Interop.Capture.cs [new file with mode: 0755]
src/Tizen.NUI/src/public/BaseHandle.cs
src/Tizen.NUI/src/public/Capture.cs [new file with mode: 0755]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/CaptureTest.cs [new file with mode: 0755]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/DimTest.cs
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Tizen.NUI.Samples.csproj

index 17b2348..67be1d3 100755 (executable)
@@ -137,5 +137,10 @@ namespace Tizen.NUI
         protected virtual void ReleaseSwigCPtr(System.Runtime.InteropServices.HandleRef swigCPtr)
         {
         }
+
+        internal global::System.Runtime.InteropServices.HandleRef SwigCPtr
+        {
+            get => swigCPtr;
+        }
     }
 }
diff --git a/src/Tizen.NUI/src/internal/Interop/Interop.Capture.cs b/src/Tizen.NUI/src/internal/Interop/Interop.Capture.cs
new file mode 100755 (executable)
index 0000000..bc8bf37
--- /dev/null
@@ -0,0 +1,67 @@
+
+namespace Tizen.NUI
+{
+    using global::System;
+    using global::System.Runtime.InteropServices;
+
+    internal static partial class Interop
+    {
+        internal static partial class Capture
+        {
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_Upcast")]
+            public static extern IntPtr Upcast(IntPtr jarg1);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_new_Capture")]
+            public static extern IntPtr NewEmpty();
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_New")]
+            public static extern IntPtr New();
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_DownCast")]
+            public static extern IntPtr Downcast(HandleRef jarg1);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_delete_Capture")]
+            public static extern void Delete(HandleRef jarg1);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_Assign")]
+            public static extern IntPtr Assign(HandleRef jarg1, HandleRef jarg2);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_Start_1")]
+            public static extern void Start1(HandleRef jarg0, HandleRef jarg1, HandleRef jarg2, string jarg3, HandleRef jarg4);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_Start_2")]
+            public static extern void Start2(HandleRef capture, HandleRef source, HandleRef size, string path);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_Signal_Empty")]
+            public static extern bool SignalEmpty(HandleRef jarg1);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_Signal_GetConnectionCount")]
+            public static extern uint SignalGetConnectionCount(HandleRef jarg1);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_Signal_Connect")]
+            public static extern void SignalConnect(HandleRef jarg1, HandleRef jarg2);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_Signal_Disconnect")]
+            public static extern void SignalDisconnect(HandleRef jarg1, HandleRef jarg2);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_Signal_Emit")]
+            public static extern void SignalEmit(HandleRef jarg1, HandleRef jarg2, int jarg3);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_new_Capture_Signal")]
+            public static extern IntPtr NewSignal();
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_delete_Capture_Signal")]
+            public static extern void DeleteSignal(HandleRef jarg1);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_Signal_Get")]
+            public static extern IntPtr Get(HandleRef jarg1);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_GetNativeImageSource")]
+            public static extern IntPtr GetNativeImageSourcePtr(HandleRef jarg1);
+
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Capture_GenerateUrl")]
+            public static extern string GenerageUrl(HandleRef capture);
+
+        }
+    }
+}
index b7c3cc6..816611a 100755 (executable)
@@ -520,6 +520,11 @@ namespace Tizen.NUI
             /// </summary>
             public bool Result { get; set; }
         }
+
+        internal global::System.Runtime.InteropServices.HandleRef SwigCPtr
+        {
+            get => swigCPtr;
+        }
     }
 
 }
diff --git a/src/Tizen.NUI/src/public/Capture.cs b/src/Tizen.NUI/src/public/Capture.cs
new file mode 100755 (executable)
index 0000000..06dc9cf
--- /dev/null
@@ -0,0 +1,298 @@
+using System.Diagnostics;
+using System;
+using System.Drawing;
+/*
+ * Copyright(c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+namespace Tizen.NUI
+{
+    using global::System;
+    using global::System.ComponentModel;
+    using global::System.Runtime.InteropServices;
+    using global::System.ComponentModel;
+    using Tizen.NUI.BaseComponents;
+
+    /// <summary>
+    /// Capture snapshots the current scene and save as a file.
+    /// Applications should follow the example below to create capture :
+    /// <code>
+    /// Capture capture = new Capture();
+    /// </code>
+    /// If required, you can also subscribe Finished event :
+    /// <code>
+    /// capture.Finished += onCaptureFinished;
+    /// </code>
+    /// At the subcribed event handler, user can know whether capture finish succeeded state.
+    /// <code>
+    /// private void onCaptureFinished(object sender, CaptureFinishedEventArgs e)
+    /// {
+    ///   if(e.Success) { //capture success, do something. }
+    ///   else { //capture failure, do something. }
+    /// }
+    /// </code>
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class Capture : BaseHandle
+    {
+        /// <summary>
+        /// Create an Capture.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Capture() : this(Interop.Capture.New(), true)
+        {
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+        }
+
+        internal Capture(IntPtr cPtr, bool cMemoryOwn) : base(Interop.Capture.Upcast(cPtr), cMemoryOwn)
+        {
+        }
+
+        /// <summary>
+        /// Dispose
+        /// </summary>
+        /// <param name="type"></param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override void Dispose(DisposeTypes type)
+        {
+            if (disposed)
+            {
+                return;
+            }
+
+            if (type == DisposeTypes.Explicit)
+            {
+                //Called by User
+                //Release your own managed resources here.
+                //You should release all of your own disposable objects here.
+            }
+
+            base.Dispose(type);
+        }
+
+        /// This will not be public opened.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override void ReleaseSwigCPtr(HandleRef swigCPtr)
+        {
+            Interop.Capture.Delete(swigCPtr);
+        }
+
+        /// <summary>
+        /// Start capture and save the image as a file.
+        /// </summary>
+        /// <param name="source">source View or Layer to be used for capture.</param>
+        /// <param name="size">captured size.</param>
+        /// <param name="path">image file path to be saved as a file.
+        /// If path is empty string, the captured result is not be saved as a file.</param>
+        /// <param name="color">background color of captured scene.</param>
+        /// <exception cref="InvalidOperationException">This exception can be due to the invalid size values, of when width or height is lower than zero.</exception>
+        /// <exception cref="ArgumentNullException">This exception is due to the path is null.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Start(Container source, Size size, string path, Color color)
+        {
+            if (size.Width <= 0 || size.Height <=0)
+            {
+                throw new InvalidOperationException("size should larger than zero");
+            }
+            else if (null == path)
+            {
+                throw new ArgumentNullException("path should not be null");
+            }
+
+            if (source is View || source is Layer)
+            {
+                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);
+
+                if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            }
+        }
+
+        /// <summary>
+        /// Start capture and save the image as a file.
+        /// </summary>
+        /// <remarks>
+        /// Background color of captured scene is transparent.
+        /// </remarks>
+        /// <param name="source">source View or Layer to be used for capture.</param>
+        /// <param name="size">captured size.</param>
+        /// <param name="path">image file path to be saved as a file.
+        /// If path is empty string, the captured result is not be saved as a file.</param>
+        /// <exception cref="InvalidOperationException">This exception can be due to the invalid size values, of when width or height is lower than zero.</exception>
+        /// <exception cref="ArgumentNullException">This exception is due to the path is null.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Start(Container source, Size size, string path)
+        {
+            if (size.Width <= 0 || size.Height <=0)
+            {
+                throw new InvalidOperationException("size should larger than zero");
+            }
+            else if (null == path)
+            {
+                throw new ArgumentNullException("path should not be null");
+            }
+            
+            if (source is View || source is Layer)
+            {
+                Interop.Capture.Start2(swigCPtr, source.SwigCPtr, new Vector2(size.Width, size.Height).SwigCPtr, path);
+
+                if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            }
+        }
+
+        private void onFinished(IntPtr data, int state)
+        {
+            if (data != IntPtr.Zero)
+            {
+                var arg = new CaptureFinishedEventArgs();
+                // dali native definition :
+                // enum class FinishState
+                // {
+                //   SUCCEEDED, ///< Succeeded in saving the result after capture
+                //   FAILED     ///< Failed to capture by time out or to save the result
+                // };
+                arg.Success = (state == 0) ? true : false;
+                finishedEventHandler?.Invoke(this, arg);
+            }
+        }
+
+        private CaptureSignal finishedSignal;
+        private delegate void finishedCallbackType(IntPtr data, int state);
+        private finishedCallbackType finishedCallback;
+        private EventHandler<CaptureFinishedEventArgs> finishedEventHandler;
+
+        /// <summary>
+        /// For subscribing Finished event sent by this class.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler<CaptureFinishedEventArgs> Finished
+        {
+            add
+            {
+                if (finishedEventHandler == null && disposed == false)
+                {
+                    finishedSignal = new CaptureSignal(Interop.Capture.Get(swigCPtr), false);
+                    finishedCallback = onFinished;
+                    finishedSignal.Connect(finishedCallback);
+                }
+                finishedEventHandler += value;
+            }
+            remove
+            {
+                finishedEventHandler -= value;
+
+                if (finishedEventHandler == null && finishedSignal?.Empty() == false)
+                {
+                    finishedCallback = onFinished;
+                    finishedSignal.Disconnect(finishedCallback);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Get NativeImageSource that is saved captured image.
+        /// </summary>
+        /// <returns>NativeImageSource that is saved captured image.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public NativeImageSource GetNativeImageSource()
+        {
+            Tizen.Log.Debug("NUI", $"GetNativeImageSource()");
+            return new NativeImageSource(Interop.Capture.GetNativeImageSourcePtr(swigCPtr), true);
+        }
+
+        /// <summary>
+        /// Generate captured image's Url
+        /// </summary>
+        /// <returns>The Url string representing this captured image source</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string GenerateUrl()
+        {
+            string url = "";
+            url = Interop.Capture.GenerageUrl(swigCPtr);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return url;
+        }
+
+    }
+
+    /// <summary>
+    /// CaptureFinishedEventArgs
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class CaptureFinishedEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Status of saving the result after capture.
+        /// </summary>
+        /// <value>
+        /// true when succeeded in saving the result after capture.
+        /// false when failed to capture by time out or to save the result.
+        /// </value>
+        public bool Success { get; internal set; }
+    }
+
+    internal class CaptureSignal : Disposable
+    {
+        internal CaptureSignal(IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
+        {
+        }
+
+        protected override void ReleaseSwigCPtr(HandleRef swigCPtr)
+        {
+            if (swigCMemOwn)
+            {
+                Interop.Capture.DeleteSignal(swigCPtr);
+            }
+        }
+
+        public bool Empty()
+        {
+            bool ret = Interop.Capture.SignalEmpty(swigCPtr);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        public uint GetConnectionCount()
+        {
+            uint ret = Interop.Capture.SignalGetConnectionCount(swigCPtr);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        public void Connect(Delegate func)
+        {
+            IntPtr ip = Marshal.GetFunctionPointerForDelegate<Delegate>(func);
+            {
+                Interop.Capture.SignalConnect(swigCPtr, new HandleRef(this, ip));
+                if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            }
+        }
+
+        public void Disconnect(Delegate func)
+        {
+            IntPtr ip = Marshal.GetFunctionPointerForDelegate<Delegate>(func);
+            {
+                Interop.Capture.SignalDisconnect(swigCPtr, new HandleRef(this, ip));
+                if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            }
+        }
+
+        public void Emit(Capture src, bool success)
+        {
+            Interop.Capture.SignalEmit(swigCPtr, src.SwigCPtr, (success ? 0 : 1));
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+        }
+    }
+}
diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/CaptureTest.cs b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/CaptureTest.cs
new file mode 100755 (executable)
index 0000000..c4e2612
--- /dev/null
@@ -0,0 +1,117 @@
+
+using global::System;
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using NUnit.Framework;
+
+namespace Tizen.NUI.Samples
+{
+    using log = Tizen.Log;
+    public class CaptureTest : IExample
+    {
+        public void Activate()
+        {
+            log.Debug(tag, $"Activate(): start \n");
+            resourcePath = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
+
+            window = NUIApplication.GetDefaultWindow();
+            window.TouchEvent += Win_TouchEvent;
+
+            root = new View()
+            {
+                Name = "test_root",
+                Size = new Size(500, 500),
+                Position = new Position(10, 10),
+                BackgroundColor = Color.White,
+            };
+
+            window.Add(root);
+
+            log.Debug(tag, $"root view added \n");
+
+            capturedView0 = new ImageView(resourcePath + "/images/image1.jpg")
+            {
+                Name = "test_v0",
+                Size = new Size(100, 100),
+                BackgroundColor = Color.Red,
+            };
+            root.Add(capturedView0);
+
+            capturedView1 = new ImageView(resourcePath + "/images/image2.jpg")
+            {
+                Name = "test_v1",
+                Size = new Size(150, 150),
+                Position = new Position(150, 150),
+                BackgroundColor = Color.Yellow,
+            };
+            root.Add(capturedView1);
+
+            //TDD
+            //tddTest();
+            //checkCaptureNew();
+        }
+
+        private void onCaptureFinished(object sender, CaptureFinishStateEventArgs e)
+        {
+            log.Debug(tag, $"onCaptureFinished() statue={e.Success} \n");
+
+            if (sender is Capture)
+            {
+                log.Debug(tag, $"sender is Capture \n");
+                var url = capture.GenerateUrl();
+                capturedImage = new ImageView(url);
+                log.Debug(tag, $"url={url} \n");
+
+                capturedImage.Size = new Size(300, 300);
+                capturedImage.Position = new Position(10, 10);
+                root.Add(capturedImage);
+                done = false;
+            }
+        }
+
+        private void Win_TouchEvent(object sender, Window.TouchEventArgs e)
+        {
+            if (e.Touch.GetState(0) == PointStateType.Down)
+            {
+                if (!done)
+                {
+                    done = true;
+                    capture = new Capture();
+                    capture.Start(root, new Size(345, 543), @"/opt/usr/nui_captured.jpg");
+                    capture.Finished += onCaptureFinished;
+                    log.Debug(tag, $"capture done \n");
+                }
+            }
+        }
+
+        private void tddTest()
+        {
+            log.Debug(tag, $"TDD test before Assert");
+
+            Assert.IsFalse(true, "TDD test, Exception throw");
+
+            Assert.IsFalse(false, "TDD test, Exception throw");
+
+            log.Debug(tag, $"TDD test after Assert");
+        }
+
+        private void checkCaptureNew()
+        {
+            var target = new Capture();
+            Assert.IsNotNull(target, "target should not be null");
+            Assert.IsTrue(target is Capture, "target should be Capture class");
+        }
+
+        public void Deactivate()
+        {
+        }
+
+        const string tag = "NUITEST";
+        private Window window;
+        private View root, capturedView0, capturedView1;
+        private Capture capture;
+        private ImageView capturedImage;
+        private bool done = false;
+        private string resourcePath;
+    }
+}
index 16cbd7d..3370786 100755 (executable)
@@ -1,6 +1,7 @@
 
 using System;
 using Tizen.NUI.BaseComponents;
+using NUnit.Framework;
 
 namespace Tizen.NUI.Samples
 {
@@ -63,6 +64,9 @@ namespace Tizen.NUI.Samples
         View filter;
         public void Activate()
         {
+
+            Assert.IsFalse(true, "TDD test, Exception throw");
+
             win = Window.Instance;
             win.BackgroundColor = Color.Green;
             win.KeyEvent += Window_KeyEvent;
index 865c32e..6989666 100755 (executable)
@@ -24,6 +24,7 @@
   </ItemGroup>
 
   <ItemGroup>
+    <PackageReference Include="NUnit" Version="3.12.0" />
     <PackageReference Include="Tizen.NET.Sdk" Version="1.0.9" />
     <ProjectReference Include="../../../src/Tizen/Tizen.csproj" />
     <ProjectReference Include="../../../src/Tizen.Applications.Common/Tizen.Applications.Common.csproj" />