[ImageUtil] Fix crash issue (#919)
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Util / ImageUtil / ImageDecoder.cs
index fcf1cab..81824ec 100644 (file)
@@ -20,6 +20,7 @@ using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Runtime.InteropServices;
+using System.Threading;
 using System.Threading.Tasks;
 using static Interop;
 using static Interop.Decode;
@@ -29,6 +30,7 @@ namespace Tizen.Multimedia.Util
     /// <summary>
     /// This is a base class for image decoders.
     /// </summary>
+    /// <since_tizen> 4 </since_tizen>
     public abstract class ImageDecoder : IDisposable
     {
         private ImageDecoderHandle _handle;
@@ -47,6 +49,7 @@ namespace Tizen.Multimedia.Util
         /// <summary>
         /// Gets the image format of this decoder.
         /// </summary>
+        /// <since_tizen> 4 </since_tizen>
         public ImageFormat InputFormat { get; }
 
         private ImageDecoderHandle Handle
@@ -68,6 +71,7 @@ namespace Tizen.Multimedia.Util
         /// <exception cref="ArgumentException"><paramref name="colorSpace"/> is invalid.</exception>
         /// <exception cref="NotSupportedException"><paramref name="colorSpace"/> is not supported by the decoder.</exception>
         /// <seealso cref="ImageUtil.GetSupportedColorSpaces(ImageFormat)"/>
+        /// <since_tizen> 4 </since_tizen>
         public void SetColorSpace(ColorSpace colorSpace)
         {
             ValidationUtil.ValidateEnum(typeof(ColorSpace), colorSpace, nameof(ColorSpace));
@@ -86,23 +90,22 @@ namespace Tizen.Multimedia.Util
         /// <param name="inputFilePath">The input file path from which to decode.</param>
         /// <returns>A task that represents the asynchronous decoding operation.</returns>
         /// <remarks>
-        ///     Only Graphics Interchange Format(GIF) codec returns more than one frame.\n
-        ///     \n
-        ///     http://tizen.org/privilege/mediastorage is needed if <paramref name="inputFilePath"/> is relevant to the media storage.\n
+        ///     Only Graphics Interchange Format(GIF) codec returns more than one frame.<br/>
+        ///     <br/>
+        ///     http://tizen.org/privilege/mediastorage is needed if <paramref name="inputFilePath"/> is relevant to the media storage.<br/>
         ///     http://tizen.org/privilege/externalstorage is needed if <paramref name="inputFilePath"/> is relevant to the external storage.
         /// </remarks>
         /// <exception cref="ArgumentNullException"><paramref name="inputFilePath"/> is null.</exception>
         /// <exception cref="ArgumentException">
-        ///     <paramref name="inputFilePath"/> is an empty string.\n
-        ///     - or -\n
-        ///     <paramref name="inputFilePath"/> is not a image file.\n
-        ///     - or -\n
-        ///     The format of <paramref name="inputFilePath"/> is not <see cref="InputFormat"/>.
+        ///     <paramref name="inputFilePath"/> is an empty string.<br/>
+        ///     -or-<br/>
+        ///     <paramref name="inputFilePath"/> is not a image file.
         /// </exception>
         /// <exception cref="FileNotFoundException"><paramref name="inputFilePath"/> does not exists.</exception>
         /// <exception cref="UnauthorizedAccessException">The caller does not have required permission to access the path.</exception>
         /// <exception cref="FileFormatException">The format of <paramref name="inputFilePath"/> is not <see cref="InputFormat"/>.</exception>
         /// <exception cref="ObjectDisposedException">The <see cref="ImageDecoder"/> has already been disposed of.</exception>
+        /// <since_tizen> 4 </since_tizen>
         public async Task<IEnumerable<BitmapFrame>> DecodeAsync(string inputFilePath)
         {
             if (inputFilePath == null)
@@ -123,7 +126,6 @@ namespace Tizen.Multimedia.Util
             var pathPtr = Marshal.StringToHGlobalAnsi(inputFilePath);
             try
             {
-
                 SetInputPath(Handle, pathPtr).ThrowIfFailed("Failed to set input file path for decoding");
                 return await DecodeAsync();
             }
@@ -138,17 +140,12 @@ namespace Tizen.Multimedia.Util
         /// </summary>
         /// <param name="inputBuffer">The image buffer from which to decode.</param>
         /// <returns>A task that represents the asynchronous decoding operation.</returns>
-        /// <remarks>
-        ///     Only Graphics Interchange Format(GIF) codec returns more than one frame.\n
-        /// </remarks>
+        /// <remarks>Only Graphics Interchange Format(GIF) codec returns more than one frame.</remarks>
         /// <exception cref="ArgumentNullException"><paramref name="inputBuffer"/> is null.</exception>
-        /// <exception cref="ArgumentException">
-        ///     <paramref name="inputBuffer"/> is an empty array.\n
-        ///     - or -\n
-        ///     The format of <paramref name="inputBuffer"/> is not <see cref="InputFormat"/>.
-        /// </exception>
+        /// <exception cref="ArgumentException"><paramref name="inputBuffer"/> is an empty array.</exception>
         /// <exception cref="FileFormatException">The format of <paramref name="inputBuffer"/> is not <see cref="InputFormat"/>.</exception>
         /// <exception cref="ObjectDisposedException">The <see cref="ImageDecoder"/> has already been disposed of.</exception>
+        /// <since_tizen> 4 </since_tizen>
         public Task<IEnumerable<BitmapFrame>> DecodeAsync(byte[] inputBuffer)
         {
             if (inputBuffer == null)
@@ -204,37 +201,38 @@ namespace Tizen.Multimedia.Util
             }
         }
 
-        internal Task<IEnumerable<BitmapFrame>> DecodeAsync()
+        private IEnumerable<BitmapFrame> RunDecoding()
         {
-            Initialize(Handle);
+            IntPtr outBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
+            Marshal.WriteIntPtr(outBuffer, IntPtr.Zero);
 
-            IntPtr outBuffer = IntPtr.Zero;
-            SetOutputBuffer(Handle, out outBuffer).ThrowIfFailed("Failed to decode given image");
+            try
+            {
+                SetOutputBuffer(Handle, outBuffer).ThrowIfFailed("Failed to decode given image");
 
-            var tcs = new TaskCompletionSource<IEnumerable<BitmapFrame>>();
+                DecodeRun(Handle, out var width, out var height, out var size).
+                    ThrowIfFailed("Failed to decode");
 
-            Task.Run(() =>
+                yield return new BitmapFrame(Marshal.ReadIntPtr(outBuffer), width, height, (int)size);
+            }
+            finally
             {
-                try
+                if (Marshal.ReadIntPtr(outBuffer) != IntPtr.Zero)
                 {
-                    int width, height;
-                    ulong size;
+                    LibcSupport.Free(Marshal.ReadIntPtr(outBuffer));
+                }
 
-                    DecodeRun(Handle, out width, out height, out size).ThrowIfFailed("Failed to decode");
+                Marshal.FreeHGlobal(outBuffer);
+            }
+        }
 
-                    tcs.SetResult(new[] { new BitmapFrame(outBuffer, width, height, (int)size) });
-                }
-                catch (Exception e)
-                {
-                    tcs.TrySetException(e);
-                }
-                finally
-                {
-                    LibcSupport.Free(outBuffer);
-                }
-            });
+        internal Task<IEnumerable<BitmapFrame>> DecodeAsync()
+        {
+            Initialize(Handle);
 
-            return tcs.Task;
+            return Task.Factory.StartNew(RunDecoding, CancellationToken.None,
+                TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning,
+                TaskScheduler.Default);
         }
 
         internal virtual void Initialize(ImageDecoderHandle handle)
@@ -254,6 +252,7 @@ namespace Tizen.Multimedia.Util
         /// Releases the unmanaged resources used by the ImageDecoder.
         /// </summary>
         /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
+        /// <since_tizen> 4 </since_tizen>
         protected virtual void Dispose(bool disposing)
         {
             if (!_disposed)
@@ -269,6 +268,7 @@ namespace Tizen.Multimedia.Util
         /// <summary>
         /// Releases all resources used by the ImageDecoder.
         /// </summary>
+        /// <since_tizen> 4 </since_tizen>
         public void Dispose()
         {
             Dispose(true);
@@ -279,6 +279,7 @@ namespace Tizen.Multimedia.Util
     /// <summary>
     /// Provides the ability to decode Bitmap (BMP) encoded images.
     /// </summary>
+    /// <since_tizen> 4 </since_tizen>
     public class BmpDecoder : ImageDecoder
     {
         private static readonly byte[] _header = { (byte)'B', (byte)'M' };
@@ -287,6 +288,7 @@ namespace Tizen.Multimedia.Util
         /// Initializes a new instance of the <see cref="BmpDecoder"/> class.
         /// </summary>
         /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Bmp"/>.</remarks>
+        /// <since_tizen> 4 </since_tizen>
         public BmpDecoder() : base(ImageFormat.Bmp)
         {
         }
@@ -297,6 +299,7 @@ namespace Tizen.Multimedia.Util
     /// <summary>
     /// Provides the ability to decode the Portable Network Graphics (PNG) encoded images.
     /// </summary>
+    /// <since_tizen> 4 </since_tizen>
     public class PngDecoder : ImageDecoder
     {
         private static readonly byte[] _header = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
@@ -305,6 +308,7 @@ namespace Tizen.Multimedia.Util
         /// Initializes a new instance of the <see cref="PngDecoder"/> class.
         /// </summary>
         /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Png"/>.</remarks>
+        /// <since_tizen> 4 </since_tizen>
         public PngDecoder() : base(ImageFormat.Png)
         {
         }
@@ -315,6 +319,7 @@ namespace Tizen.Multimedia.Util
     /// <summary>
     /// Provides the ability to decode the Joint Photographic Experts Group (JPEG) encoded images.
     /// </summary>
+    /// <since_tizen> 4 </since_tizen>
     public class JpegDecoder : ImageDecoder
     {
         private static readonly byte[] _header = { 0xFF, 0xD8 };
@@ -322,6 +327,7 @@ namespace Tizen.Multimedia.Util
         /// <summary>
         /// A read-only field that represents the default value of <see cref="Downscale"/>.
         /// </summary>
+        /// <since_tizen> 4 </since_tizen>
         public static readonly JpegDownscale DefaultJpegDownscale = JpegDownscale.None;
 
         private JpegDownscale _jpegDownscale = DefaultJpegDownscale;
@@ -330,6 +336,7 @@ namespace Tizen.Multimedia.Util
         /// Initializes a new instance of the <see cref="JpegDecoder"/> class.
         /// </summary>
         /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Jpeg"/>.</remarks>
+        /// <since_tizen> 4 </since_tizen>
         public JpegDecoder() : base(ImageFormat.Jpeg)
         {
         }
@@ -338,6 +345,7 @@ namespace Tizen.Multimedia.Util
         /// Gets or sets the downscale at which the jpeg image should be decoded.
         /// </summary>
         /// <exception cref="ArgumentException"><paramref name="value"/> is invalid.</exception>
+        /// <since_tizen> 4 </since_tizen>
         public JpegDownscale Downscale
         {
             get
@@ -365,6 +373,7 @@ namespace Tizen.Multimedia.Util
     /// <summary>
     /// Provides the ability to decode the Graphics Interchange Format (GIF) encoded images.
     /// </summary>
+    /// <since_tizen> 4 </since_tizen>
     public class GifDecoder : ImageDecoder
     {
         private static readonly byte[] _header = { (byte)'G', (byte)'I', (byte)'F' };
@@ -373,6 +382,7 @@ namespace Tizen.Multimedia.Util
         /// Initializes a new instance of the <see cref="GifDecoder"/> class.
         /// </summary>
         /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Gif"/>.</remarks>
+        /// <since_tizen> 4 </since_tizen>
         public GifDecoder() : base(ImageFormat.Gif)
         {
         }