[ImageUtil] Support JPEG XL image encoder/decoder (#4333)
authorHaesu Gwon <haesu.gwon@samsung.com>
Tue, 5 Jul 2022 06:16:27 +0000 (15:16 +0900)
committerGitHub <noreply@github.com>
Tue, 5 Jul 2022 06:16:27 +0000 (15:16 +0900)
* [ImageUtil] Support JPEG XL encoder/decoder

src/Tizen.Multimedia.Util/ImageUtil/ImageDecoder.cs
src/Tizen.Multimedia.Util/ImageUtil/ImageEncoder.cs
src/Tizen.Multimedia.Util/ImageUtil/ImageFormat.cs
src/Tizen.Multimedia.Util/Interop/Interop.ImageUtil.Encode.cs

index 0cd47e7..1ec4f87 100644 (file)
@@ -119,7 +119,7 @@ namespace Tizen.Multimedia.Util
                 throw new ArgumentException("path is empty.", nameof(inputFilePath));
             }
 
-            if (CheckHeader(inputFilePath) == false)
+            if (CheckHeaders(inputFilePath) == false)
             {
                 throw new FileFormatException("The file has an invalid header.");
             }
@@ -160,7 +160,7 @@ namespace Tizen.Multimedia.Util
                 throw new ArgumentException("buffer is empty.", nameof(inputBuffer));
             }
 
-            if (CheckHeader(inputBuffer) == false)
+            if (CheckHeaders(inputBuffer) == false)
             {
                 throw new FileFormatException("buffer has an invalid header.");
             }
@@ -171,36 +171,62 @@ namespace Tizen.Multimedia.Util
             return DecodeAsync();
         }
 
-        private bool CheckHeader(byte[] input)
+        private bool CheckHeaders(byte[] inputBuffer)
         {
-            if (input.Length < Header.Length)
+            foreach (var header in Headers)
             {
-                return false;
-            }
+                var inputBufferHeader = new byte[header.Id.Length];
+                Buffer.BlockCopy(inputBuffer, header.Offset, inputBufferHeader, 0, header.Id.Length);
 
-            for (int i = 0; i < Header.Length; ++i)
-            {
-                if (input[i] != Header[i])
+                if (CheckHeader(inputBufferHeader, header.Id))
                 {
-                    return false;
+                    return true;
                 }
             }
 
-            return true;
+            return false;
         }
 
-        private bool CheckHeader(string inputFile)
+        private bool CheckHeaders(string inputFile)
         {
             using (var fs = File.OpenRead(inputFile))
             {
-                byte[] fileHeader = new byte[Header.Length];
+                foreach (var header in Headers)
+                {
+                    fs.Position = header.Offset;
+
+                    byte[] fileHeader = new byte[header.Id.Length];
+                    if (fs.Read(fileHeader, 0, fileHeader.Length) < header.Id.Length)
+                    {
+                        continue;
+                    }
+
+                    if (CheckHeader(fileHeader, header.Id))
+                    {
+                        return true;
+                    }
+                }
 
-                if (fs.Read(fileHeader, HeaderOffset, fileHeader.Length) < Header.Length)
+                return false;
+            }
+        }
+
+        private bool CheckHeader(byte[] input, byte[] header)
+        {
+            if (input.Length < header.Length)
+            {
+                return false;
+            }
+
+            for (int i = 0; i < header.Length; ++i)
+            {
+                if (input[i] != header[i])
                 {
                     return false;
                 }
-                return CheckHeader(fileHeader);
             }
+
+            return true;
         }
 
         private IEnumerable<BitmapFrame> RunDecoding()
@@ -244,13 +270,12 @@ namespace Tizen.Multimedia.Util
         {
             if (_colorSpace.HasValue)
             {
-                NativeDecoder.SetColorspace(Handle, _colorSpace.Value.ToImageColorSpace()).ThrowIfFailed("Failed to set color space");
+                NativeDecoder.SetColorspace(Handle, _colorSpace.Value.ToImageColorSpace()).
+                    ThrowIfFailed("Failed to set color space");
             }
         }
 
-        internal abstract byte[] Header { get; }
-
-        internal abstract int HeaderOffset { get; }
+        internal abstract (byte[] Id, int Offset)[] Headers { get; }
 
         #region IDisposable Support
         private bool _disposed = false;
@@ -258,7 +283,9 @@ namespace Tizen.Multimedia.Util
         /// <summary>
         /// 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>
+        /// <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)
         {
@@ -294,15 +321,15 @@ namespace Tizen.Multimedia.Util
         /// <summary>
         /// Initializes a new instance of the <see cref="BmpDecoder"/> class.
         /// </summary>
-        /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Bmp"/>.</remarks>
+        /// <remarks>
+        /// <see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Bmp"/>.
+        /// </remarks>
         /// <since_tizen> 4 </since_tizen>
         public BmpDecoder() : base(ImageFormat.Bmp)
         {
         }
 
-        internal override byte[] Header => _header;
-
-        internal override int HeaderOffset => 0;
+        internal override (byte[], int)[] Headers => new (byte[], int)[] { (_header, 0) };
     }
 
     /// <summary>
@@ -316,15 +343,15 @@ namespace Tizen.Multimedia.Util
         /// <summary>
         /// Initializes a new instance of the <see cref="PngDecoder"/> class.
         /// </summary>
-        /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Png"/>.</remarks>
+        /// <remarks>
+        /// <see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Png"/>.
+        /// </remarks>
         /// <since_tizen> 4 </since_tizen>
         public PngDecoder() : base(ImageFormat.Png)
         {
         }
 
-        internal override byte[] Header => _header;
-
-        internal override int HeaderOffset => 0;
+        internal override (byte[], int)[] Headers => new (byte[], int)[] { (_header, 0) };
     }
 
     /// <summary>
@@ -346,7 +373,9 @@ namespace Tizen.Multimedia.Util
         /// <summary>
         /// Initializes a new instance of the <see cref="JpegDecoder"/> class.
         /// </summary>
-        /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Jpeg"/>.</remarks>
+        /// <remarks>
+        /// <see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Jpeg"/>.
+        /// </remarks>
         /// <since_tizen> 4 </since_tizen>
         public JpegDecoder() : base(ImageFormat.Jpeg)
         {
@@ -375,12 +404,11 @@ namespace Tizen.Multimedia.Util
         {
             base.Initialize(handle);
 
-            NativeDecoder.SetJpegDownscale(handle, Downscale).ThrowIfFailed("Failed to set downscale for decoding");
+            NativeDecoder.SetJpegDownscale(handle, Downscale).
+                ThrowIfFailed("Failed to set downscale for decoding");
         }
 
-        internal override byte[] Header => _header;
-
-        internal override int HeaderOffset => 0;
+        internal override (byte[], int)[] Headers => new (byte[], int)[] { (_header, 0) };
     }
 
     /// <summary>
@@ -394,15 +422,15 @@ namespace Tizen.Multimedia.Util
         /// <summary>
         /// Initializes a new instance of the <see cref="GifDecoder"/> class.
         /// </summary>
-        /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Gif"/>.</remarks>
+        /// <remarks>
+        /// <see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Gif"/>.
+        /// </remarks>
         /// <since_tizen> 4 </since_tizen>
         public GifDecoder() : base(ImageFormat.Gif)
         {
         }
 
-        internal override byte[] Header => _header;
-
-        internal override int HeaderOffset => 0;
+        internal override (byte[], int)[] Headers => new (byte[], int)[] { (_header, 0) };
     }
 
     /// <summary>
@@ -416,15 +444,15 @@ namespace Tizen.Multimedia.Util
         /// <summary>
         /// Initializes a new instance of the <see cref="WebPDecoder"/> class.
         /// </summary>
-        /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.WebP"/>.</remarks>
+        /// <remarks>
+        /// <see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.WebP"/>.
+        /// </remarks>
         /// <since_tizen> 8 </since_tizen>
         public WebPDecoder() : base(ImageFormat.WebP)
         {
         }
 
-        internal override byte[] Header => _header;
-
-        internal override int HeaderOffset => 8;
+        internal override (byte[], int)[] Headers => new (byte[], int)[] { (_header, 8) };
     }
 
     /// <summary>
@@ -438,14 +466,38 @@ namespace Tizen.Multimedia.Util
         /// <summary>
         /// Initializes a new instance of the <see cref="HeifDecoder"/> class.
         /// </summary>
-        /// <remarks><see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Heif"/>.</remarks>
+        /// <remarks>
+        /// <see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.Heif"/>.
+        /// </remarks>
         /// <since_tizen> 9 </since_tizen>
         public HeifDecoder() : base(ImageFormat.Heif)
         {
         }
 
-        internal override byte[] Header => _header;
+        internal override (byte[], int)[] Headers => new (byte[], int)[] { (_header, 4) };
+    }
+
+    /// <summary>
+    /// Provides the ability to decode the JPEG (Joint Photographic Experts Group) XL encoded images.
+    /// </summary>
+    /// <since_tizen> 10 </since_tizen>
+    public class JpegXlDecoder : ImageDecoder
+    {
+        private static readonly byte[] _headerJpegXl = { (byte)'J', (byte)'X', (byte)'L' };
+        private static readonly byte[] _headerJpegXlCodeStream = { 0xFF, 0x0A };
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="JpegXlDecoder"/> class.
+        /// </summary>
+        /// <remarks>
+        /// <see cref="ImageDecoder.InputFormat"/> will be the <see cref="ImageFormat.JpegXl"/>.
+        /// </remarks>
+        /// <since_tizen> 10 </since_tizen>
+        public JpegXlDecoder() : base(ImageFormat.JpegXl)
+        {
+        }
 
-        internal override int HeaderOffset => 4;
+        internal override (byte[], int)[] Headers =>
+            new (byte[], int)[] { (_headerJpegXl, 4), (_headerJpegXlCodeStream, 0) };
     }
 }
index 758dce5..df40539 100644 (file)
@@ -514,13 +514,59 @@ namespace Tizen.Multimedia.Util
         /// The property determining whether the WebP compression is lossless or lossy.<br/>
         /// The default is false(lossy).</value>
         /// <since_tizen> 8 </since_tizen>
-        public bool Lossless { get; set; }
+        public bool Lossless { get; set; } = false;
 
         internal override void Configure(ImageEncoderHandle handle)
         {
-            NativeEncoder.SetWebPLossless(handle, Lossless).
+            NativeEncoder.SetLossless(handle, Lossless).
                 ThrowIfFailed("Failed to configure encoder; Lossless");
         }
     }
 
+    /// <summary>
+    /// Provides the ability to encode the JPEG(Joint Photographic Experts Group) XL format images.
+    /// </summary>
+    /// <since_tizen> 10 </since_tizen>
+    public class JpegXlEncoder : ImageEncoder
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="JpegXlEncoder"/> class.
+        /// </summary>
+        /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.JpegXl"/>.</remarks>
+        /// <since_tizen> 10 </since_tizen>
+        public JpegXlEncoder() :
+            base(ImageFormat.JpegXl)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="JpegXlEncoder"/> class for lossless or lossy compression.
+        /// </summary>
+        /// <remarks><see cref="ImageEncoder.OutputFormat"/> will be the <see cref="ImageFormat.JpegXl"/>.</remarks>
+        /// <param name="lossless">
+        /// The flag determining whether the compression is lossless or lossy: true for lossless, false for lossy.<br/>
+        /// The default value is false.
+        /// </param>
+        /// <since_tizen> 10 </since_tizen>
+        public JpegXlEncoder(bool lossless) :
+            base(ImageFormat.JpegXl)
+        {
+            Lossless = lossless;
+        }
+
+        /// <summary>
+        /// Gets or sets the lossless or lossy JpegXl compression.
+        /// </summary>
+        /// <value>
+        /// The property determining whether the JpegXl compression is lossless or lossy.<br/>
+        /// The default is false(lossy).</value>
+        /// <since_tizen> 10 </since_tizen>
+        public bool Lossless { get; set; } = false;
+
+        internal override void Configure(ImageEncoderHandle handle)
+        {
+            NativeEncoder.SetLossless(handle, Lossless).
+                ThrowIfFailed($"Failed to configure encoder; Lossless={Lossless}");
+        }
+    }
 }
index ade56aa..69dc371 100644 (file)
@@ -23,15 +23,15 @@ namespace Tizen.Multimedia.Util
     public enum ImageFormat
     {
         /// <summary>
-        /// The Joint Photographic Experts Group format.
+        /// The JPEG(Joint Photographic Experts Group) format.
         /// </summary>
         Jpeg,
         /// <summary>
-        /// The Portable Network Graphics format.
+        /// The PNG(Portable Network Graphics) format.
         /// </summary>
         Png,
         /// <summary>
-        /// The Graphics Interchange Format.
+        /// The GIF(Graphics Interchange) Format.
         /// </summary>
         Gif,
         /// <summary>
@@ -44,10 +44,15 @@ namespace Tizen.Multimedia.Util
         /// <since_tizen> 8 </since_tizen>
         WebP,
         /// <summary>
-        /// The HEIF format.
+        /// The HEIF(High Efficiency Image Format) format.
         /// </summary>
         /// <remarks>ImageUtil supports Heif decoder only.</remarks>
         /// <since_tizen> 9 </since_tizen>
-        Heif
+        Heif,
+        /// <summary>
+        /// The JPEG(Joint Photographic Experts Group) XL format.
+        /// </summary>
+        /// <since_tizen> 10 </since_tizen>
+        JpegXl
     }
 }
index 0391c3b..71e9a15 100644 (file)
@@ -65,8 +65,8 @@ internal static partial class Interop
             [DllImport(Libraries.ImageUtil, EntryPoint = "image_util_encode_run")]
             internal static extern ImageUtilError Run(ImageEncoderHandle handle, out ulong size);
 
-            [DllImport(Libraries.ImageUtil, EntryPoint = "image_util_encode_set_webp_lossless")]
-            internal static extern ImageUtilError SetWebPLossless(ImageEncoderHandle handle, bool lossless);
+            [DllImport(Libraries.ImageUtil, EntryPoint = "image_util_encode_set_lossless")]
+            internal static extern ImageUtilError SetLossless(ImageEncoderHandle handle, bool lossless);
         }
     }